From b82fde62cac349cafd2c9ecf64afdc0ac71faa66 Mon Sep 17 00:00:00 2001 From: Et0h Date: Mon, 3 Jun 2013 22:06:39 +0100 Subject: [PATCH 01/15] Initial code for shiny new main GUI --- syncplay/ui/mainUI.py | 168 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 168 insertions(+) create mode 100644 syncplay/ui/mainUI.py diff --git a/syncplay/ui/mainUI.py b/syncplay/ui/mainUI.py new file mode 100644 index 0000000..5af0a8e --- /dev/null +++ b/syncplay/ui/mainUI.py @@ -0,0 +1,168 @@ +from PySide import QtCore, QtGui +from PySide.QtCore import QSettings, Qt +from PySide.QtGui import QApplication, QLineEdit, QCursor, QLabel, QCheckBox, QDesktopServices, QIcon +from syncplay import utils + +import os +import sys + +class MainDialog(QtGui.QDialog): + + def joinRoom(self): + print "JOIN ROOM: " + self.roomInput.text() + self.roomInput.setText("") + + def seekPosition(self): + print "SEEK POSITION: " + self.seekInput.text() + self.seekInput.setText("") + + def showList(self): + print "SHOW USER LIST" + + def undoseek(self): + print "UNDO LAST SEEK" + + def togglepause(self): + print "PAUSE/UNPAUSE" + + def exitSyncplay(self): + reply = QtGui.QMessageBox.question(self, "Syncplay", + "Are you sure you want to exit Syncplay?", + QtGui.QMessageBox.Yes | QtGui.QMessageBox.No) + if reply == QtGui.QMessageBox.Yes: + sys.exit() + + def setOffset(self): + newoffset, ok = QtGui.QInputDialog.getText(self,"Set Offset", + "Offset (+/-):", QtGui.QLineEdit.Normal, + "") + if ok and newoffset != '': + print "SET OFFSET: " + newoffset + + def openUserGuide(self): + self.QtGui.QDesktopServices.openUrl("http://syncplay.pl/guide/") + + def addBottomLayout(self, dialog): + dialog.bottomLayout = QtGui.QHBoxLayout() + + dialog.addRoomBox(MainDialog) + dialog.addSeekBox(MainDialog) + dialog.addMiscBox(MainDialog) + + dialog.bottomLayout.addWidget(dialog.roomGroup, Qt.AlignLeft) + dialog.bottomLayout.addWidget(dialog.seekGroup, Qt.AlignLeft) + dialog.bottomLayout.addWidget(dialog.miscGroup, Qt.AlignLeft) + + dialog.mainLayout.addLayout(dialog.bottomLayout, Qt.AlignLeft) + + + def addRoomBox(self, dialog): + dialog.roomGroup = QtGui.QGroupBox("Room") + + dialog.roomInput = QtGui.QLineEdit() + dialog.roomButton = QtGui.QPushButton("Join room") + dialog.roomButton.pressed.connect(self.joinRoom) + dialog.roomLayout = QtGui.QHBoxLayout() + dialog.roomInput.setMaximumWidth(150) + + dialog.roomLayout.addWidget(dialog.roomInput) + dialog.roomLayout.addWidget(dialog.roomButton) + + dialog.roomGroup.setLayout(dialog.roomLayout) + dialog.roomGroup.setFixedSize(dialog.roomGroup.sizeHint()) + + def addSeekBox(self, dialog): + dialog.seekGroup = QtGui.QGroupBox("Seek") + + dialog.seekInput = QtGui.QLineEdit() + dialog.seekButton = QtGui.QPushButton("Seek to position") + dialog.seekButton.pressed.connect(self.seekPosition) + + dialog.seekLayout = QtGui.QHBoxLayout() + dialog.seekInput.setMaximumWidth(100) + + dialog.seekLayout.addWidget(dialog.seekInput) + dialog.seekLayout.addWidget(dialog.seekButton) + + dialog.seekGroup.setLayout(dialog.seekLayout) + dialog.seekGroup.setFixedSize(dialog.seekGroup.sizeHint()) + + def addMiscBox(self, dialog): + dialog.miscGroup = QtGui.QGroupBox("Other Commands") + + dialog.showListButton = QtGui.QPushButton("Show list") + dialog.showListButton.pressed.connect(self.showList) + dialog.unseekButton = QtGui.QPushButton("Undo last seek") + dialog.unseekButton.pressed.connect(self.undoseek) + dialog.pauseButton = QtGui.QPushButton("Toggle pause") + dialog.pauseButton.pressed.connect(self.togglepause) + + dialog.miscLayout = QtGui.QHBoxLayout() + dialog.miscLayout.addWidget(dialog.showListButton) + dialog.miscLayout.addWidget(dialog.unseekButton) + dialog.miscLayout.addWidget(dialog.pauseButton) + + dialog.miscGroup.setLayout(dialog.miscLayout) + dialog.miscGroup.setFixedSize(dialog.miscGroup.sizeHint()) + + + + + def addMenubar(self, dialog): + dialog.menuBar = QtGui.QMenuBar() + + dialog.fileMenu = QtGui.QMenu("&File", self) + dialog.exitAction = dialog.fileMenu.addAction("E&xit") + dialog.exitAction.triggered.connect(self.exitSyncplay) + dialog.menuBar.addMenu(dialog.fileMenu) + + dialog.advancedMenu = QtGui.QMenu("&Advanced", self) + dialog.setoffsetAction = dialog.advancedMenu.addAction("Set &Offset") + dialog.setoffsetAction.triggered.connect(self.setOffset) + dialog.menuBar.addMenu(dialog.advancedMenu) + + dialog.helpMenu = QtGui.QMenu("&Help", self) + dialog.userguideAction = dialog.helpMenu.addAction("Open User &Guide") + dialog.userguideAction.triggered.connect(self.openUserGuide) + + dialog.menuBar.addMenu(dialog.helpMenu) + dialog.mainLayout.setMenuBar(dialog.menuBar) + + def NewMessage(self, NewMessage): + self.outputbox.insertHtml(NewMessage) + self.outputbox.moveCursor(QtGui.QTextCursor.End) + + + def __init__(self): + + super(MainDialog, self).__init__() + self.QtGui = QtGui + + self.setWindowTitle("Syncplay - Main Window") + self.mainLayout = QtGui.QVBoxLayout() + + self.outputbox = QtGui.QTextEdit() + self.outputbox.setReadOnly(True) + + self.mainLayout.addWidget(self.outputbox) + self.addBottomLayout(self) + self.addMenubar(self) + self.setLayout(self.mainLayout) + self.resize(700,500) + + if sys.platform.startswith('linux'): + resourcespath = utils.findWorkingDir() + "/resources/" + else: + resourcespath = utils.findWorkingDir() + "\\resources\\" + self.setWindowIcon(QtGui.QIcon(resourcespath + "syncplay.png")) + + + self.show() + +if __name__ == '__main__': + + import sys + + app = QtGui.QApplication(sys.argv) + dialog = MainDialog() + sys.exit(app.exec_()) From 0b86cc23cf99ea1fbd207104efb712094581171e Mon Sep 17 00:00:00 2001 From: Etoh Date: Tue, 4 Jun 2013 00:35:17 +0200 Subject: [PATCH 02/15] NewGUI: Ensure new messages are added to the end of outputbox --- syncplay/ui/mainUI.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/syncplay/ui/mainUI.py b/syncplay/ui/mainUI.py index 5af0a8e..cc990f8 100644 --- a/syncplay/ui/mainUI.py +++ b/syncplay/ui/mainUI.py @@ -128,8 +128,9 @@ class MainDialog(QtGui.QDialog): dialog.menuBar.addMenu(dialog.helpMenu) dialog.mainLayout.setMenuBar(dialog.menuBar) - def NewMessage(self, NewMessage): - self.outputbox.insertHtml(NewMessage) + def NewMessage(self, message): + self.outputbox.moveCursor(QtGui.QTextCursor.End) + self.outputbox.insertHtml(message) self.outputbox.moveCursor(QtGui.QTextCursor.End) From ab8b58ad64b009b77053b8f54a87c67d24f7700f Mon Sep 17 00:00:00 2001 From: Etoh Date: Tue, 4 Jun 2013 00:37:12 +0200 Subject: [PATCH 03/15] NewGUI: Move list button to the right --- syncplay/ui/mainUI.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/syncplay/ui/mainUI.py b/syncplay/ui/mainUI.py index cc990f8..5f2920e 100644 --- a/syncplay/ui/mainUI.py +++ b/syncplay/ui/mainUI.py @@ -90,17 +90,17 @@ class MainDialog(QtGui.QDialog): def addMiscBox(self, dialog): dialog.miscGroup = QtGui.QGroupBox("Other Commands") - dialog.showListButton = QtGui.QPushButton("Show list") - dialog.showListButton.pressed.connect(self.showList) dialog.unseekButton = QtGui.QPushButton("Undo last seek") dialog.unseekButton.pressed.connect(self.undoseek) dialog.pauseButton = QtGui.QPushButton("Toggle pause") dialog.pauseButton.pressed.connect(self.togglepause) + dialog.showListButton = QtGui.QPushButton("Show list") + dialog.showListButton.pressed.connect(self.showList) dialog.miscLayout = QtGui.QHBoxLayout() - dialog.miscLayout.addWidget(dialog.showListButton) dialog.miscLayout.addWidget(dialog.unseekButton) dialog.miscLayout.addWidget(dialog.pauseButton) + dialog.miscLayout.addWidget(dialog.showListButton) dialog.miscGroup.setLayout(dialog.miscLayout) dialog.miscGroup.setFixedSize(dialog.miscGroup.sizeHint()) From dafb4896f41578ce2bbeedeee5ffed7a1167ce9f Mon Sep 17 00:00:00 2001 From: Etoh Date: Tue, 4 Jun 2013 00:40:44 +0200 Subject: [PATCH 04/15] NewGUI: Add separate box to list who is playing what --- syncplay/ui/mainUI.py | 55 ++++++++++++++++++++++++++++++++++++------- 1 file changed, 47 insertions(+), 8 deletions(-) diff --git a/syncplay/ui/mainUI.py b/syncplay/ui/mainUI.py index 5f2920e..30531ad 100644 --- a/syncplay/ui/mainUI.py +++ b/syncplay/ui/mainUI.py @@ -17,7 +17,7 @@ class MainDialog(QtGui.QDialog): self.seekInput.setText("") def showList(self): - print "SHOW USER LIST" + print "UPDATE USER LIST" def undoseek(self): print "UNDO LAST SEEK" @@ -42,6 +42,42 @@ class MainDialog(QtGui.QDialog): def openUserGuide(self): self.QtGui.QDesktopServices.openUrl("http://syncplay.pl/guide/") + def addTopLayout(self, dialog): + dialog.topSplit = QtGui.QSplitter(Qt.Horizontal) + + + dialog.outputLayout = QtGui.QVBoxLayout() + dialog.outputbox = QtGui.QTextEdit() + dialog.outputbox.setReadOnly(True) + dialog.outputlabel = QtGui.QLabel("Notifications") + dialog.outputFrame = QtGui.QFrame() + dialog.outputFrame.setLineWidth(0) + dialog.outputFrame.setMidLineWidth(0) + dialog.outputLayout.setContentsMargins(0,0,0,0) + dialog.outputLayout.addWidget(dialog.outputlabel) + dialog.outputLayout.addWidget(dialog.outputbox) + dialog.outputFrame.setLayout(dialog.outputLayout) + + dialog.listLayout = QtGui.QVBoxLayout() + dialog.listbox = QtGui.QTextEdit() + dialog.listbox.setReadOnly(True) + dialog.listlabel = QtGui.QLabel("List of who is playing what") + dialog.listFrame = QtGui.QFrame() + dialog.listFrame.setLineWidth(0) + dialog.listFrame.setMidLineWidth(0) + dialog.listLayout.setContentsMargins(0,0,0,0) + dialog.listLayout.addWidget(dialog.listlabel) + dialog.listLayout.addWidget(dialog.listbox) + dialog.listFrame.setLayout(dialog.listLayout) + + dialog.topSplit.addWidget(dialog.outputFrame) + dialog.topSplit.addWidget(dialog.listFrame) + dialog.topSplit.setStretchFactor(0,3) + dialog.topSplit.setStretchFactor(1,2) + dialog.mainLayout.addWidget(dialog.topSplit) + dialog.topSplit.setSizePolicy(QtGui.QSizePolicy.Preferred,QtGui.QSizePolicy.Expanding) + + def addBottomLayout(self, dialog): dialog.bottomLayout = QtGui.QHBoxLayout() @@ -94,7 +130,7 @@ class MainDialog(QtGui.QDialog): dialog.unseekButton.pressed.connect(self.undoseek) dialog.pauseButton = QtGui.QPushButton("Toggle pause") dialog.pauseButton.pressed.connect(self.togglepause) - dialog.showListButton = QtGui.QPushButton("Show list") + dialog.showListButton = QtGui.QPushButton("Update list") dialog.showListButton.pressed.connect(self.showList) dialog.miscLayout = QtGui.QHBoxLayout() @@ -105,9 +141,7 @@ class MainDialog(QtGui.QDialog): dialog.miscGroup.setLayout(dialog.miscLayout) dialog.miscGroup.setFixedSize(dialog.miscGroup.sizeHint()) - - def addMenubar(self, dialog): dialog.menuBar = QtGui.QMenuBar() @@ -132,6 +166,14 @@ class MainDialog(QtGui.QDialog): self.outputbox.moveCursor(QtGui.QTextCursor.End) self.outputbox.insertHtml(message) self.outputbox.moveCursor(QtGui.QTextCursor.End) + + def ResetList(self): + self.listbox.setText("") + + def NewListItem(self, item): + self.listbox.moveCursor(QtGui.QTextCursor.End) + self.listbox.insertHtml(item) + self.listbox.moveCursor(QtGui.QTextCursor.End) def __init__(self): @@ -142,10 +184,7 @@ class MainDialog(QtGui.QDialog): self.setWindowTitle("Syncplay - Main Window") self.mainLayout = QtGui.QVBoxLayout() - self.outputbox = QtGui.QTextEdit() - self.outputbox.setReadOnly(True) - - self.mainLayout.addWidget(self.outputbox) + self.addTopLayout(self) self.addBottomLayout(self) self.addMenubar(self) self.setLayout(self.mainLayout) From fa774f9428aaa59b22d78e55c3562d9439f05f8e Mon Sep 17 00:00:00 2001 From: Et0h Date: Tue, 4 Jun 2013 09:24:42 +0100 Subject: [PATCH 05/15] Add initial NewMainUI icons --- resources/arrow_undo.png | Bin 0 -> 631 bytes resources/clock_go.png | Bin 0 -> 959 bytes resources/control_pause_blue.png | Bin 0 -> 721 bytes resources/door_in.png | Bin 0 -> 693 bytes resources/table_refresh.png | Bin 0 -> 795 bytes 5 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 resources/arrow_undo.png create mode 100644 resources/clock_go.png create mode 100644 resources/control_pause_blue.png create mode 100644 resources/door_in.png create mode 100644 resources/table_refresh.png diff --git a/resources/arrow_undo.png b/resources/arrow_undo.png new file mode 100644 index 0000000000000000000000000000000000000000..6972c5e5946080ca1dec4de09d9430d3edf6c555 GIT binary patch literal 631 zcmV--0*L*IP)`NY~_X_4^RN+9OmbDu*=G*+_}E z3jR}&gbG0=R0#?Z-=09KI|YP8=#E5@+>uO|=1VUCOole%Li*0J2^Gy8+;b?&km6gg zf;aDGp1r#I_V|We$e$~WifFF9R7MCMwsmwsME9<<@yx?a+v)qKuKbNe>k1{IrVt}k zbbSs~bXr8Se}V_b4opa0GhLA`RHceaP=N}IElbSa8@d(ij1Q|4CvOirm9(VP)oo zJ(%d?Wzl5Jm^p}!WsC%9H2cHC#$XJ)(Fv?T;kAA5_5J;MF^2fLf4|A+n|vi91lp)1 z0|P)0U;^EdgTiD7+NjhBoCnIlTakle0YKXNnE-G+ka*+uik5eF8JX{M^Q-FO>F^l` z==e83+pE7SrxLN0KUgQ3^T+N0y)``|D?Rv;qm`|1eo)!`T2mo2ynQdb;J?sxR#Ad3 zp@0W~Var&C#(}k7eRtbod~QM0Z=J{jow8*jOO?%)TZ_Zni%Sd2bmAwaC2CP?7?^+M z(H8S41&vs=f~$=u!mF9-N?%=Ptq8R#mPxC$^ex@C6#i~g-V>in{t`}v{RDNFeS2!D zsge;$-^p>;Txglc-!)rBx!K_ndJ{_-+o7blp#K7;Yt1&oyv ziZKK=5llnt0FVJJLqpST49mlG!S*1h7Ktbdv)5-h^hytwaERBpu_C`qN!PFpjhzLv zg`$q+_sFd2xRT75BZtT}o<>dn$`9B2saN8(#hxWm_c(WpeVFC-xt$7^3bKEHUI~)E z_JCBf8ppGE>B)Xv;o*8N9)cA$OGmtml4WqE{9f0-WBt{B0IRH>v&vfEE!UTl;|?c> z1!c#=6(09C7qLAD$Fs3rn}S*7sYEv=TbKUGOm4~mlSA)cw#)iZGP14DW;VIGoTcoP zaa@PSsz-@cHS&skTo@V0CcwV>c?v+6?=LZ-W8EF8{HZ(M#l~lE~b@yv^;>iA; zXS$O5?0HEJ1#aBG!6)rsRmVbc^S=@E`Md%Aold7)fEQ*?eR`As(K>Kk8^3)i@L|j6 h#R?-_%o}+P;2$6cuY95`2W0>N002ovPDHLkV1i7#$^-xa literal 0 HcmV?d00001 diff --git a/resources/control_pause_blue.png b/resources/control_pause_blue.png new file mode 100644 index 0000000000000000000000000000000000000000..ec61099b0b82dcbb0277392c855aa5ed5b17a6d9 GIT binary patch literal 721 zcmV;?0xtcDP)%qypxm=P~KCwUk1<-E^%&v%&sfQfOPbAd^dMUz$2)JWFR zn(5E_apM_H=DRN?Tj1j{9obJa2Z=)=&L)N3y%h6eCr3~F>o%;x+TQ>p^I2c0Y>yvi zIld-=tn@%V9S}8IT_{!R5Z+_Ch0UxTYjubYFr%4GeU;lC74TEKeW=Sl5HvMAeX=Gi zUD%DrWY78$Ld*n!b75@ktedM+7b-uxz>7nb#SWC}9Rsqt73OZQ@HmbM0hR#sjmFrz z`Qn2R+;S%k^W^LfTz4f%8vIhZ??Cw@GT3qQ2$+f;Bm~?nEIi6bESWz7m0cmx39em$$Y&@l88-}q`@)p_|Tj# z*1_TN$urdYuZuK(mFK)s`?7zHi^MWe;3cvrICC;Dz(yzzbJPMi?R@k1RUUs#H=pHg zvrjkZN3JD?ns*D2owV@K;bCSR0s0c1`uAh{pXdA&89xkjaU%7W00000NkvXXu0mjf DW6MO* literal 0 HcmV?d00001 diff --git a/resources/door_in.png b/resources/door_in.png new file mode 100644 index 0000000000000000000000000000000000000000..41676a0a5be0f026fb136315fafb6c4566522d7a GIT binary patch literal 693 zcmV;m0!safP)vaG^ z2pyX9GZt*w3IyWpBqouZ9TBDUCjj71ahpsggwSp?!_6p~6Ug^zToTz0k=O|cA@C)B z=4SI+2S7F9CYUB%6Rwf$PD%xpU1zsvH^-J3qhIec&_95xadrGMyZiUG;F`#)xFk#y zurJ!PJ?Af*(aYIc}SHy&e#bYkLxyZiN`;lMHCXuWKOBa9QbyiY}R8)+l!c;Lu zGC@p$z~K6Ij22J0)wsbx511;JB7MDISvY@^uJR9Nq@bi~L`zglHEQK03t2do(0Qs6*PW_{1G!HjEs!rv9U46 z$HzPJ|EKO<)u6Ypk6L#x)$Sg;sx@Y2o4k8D`Z@f|JD)qaVu_iqHj_a!UF&Z3Ql+}r bBu@VX$p^JhV-$eD00000NkvXXu0mjfw@)|D literal 0 HcmV?d00001 diff --git a/resources/table_refresh.png b/resources/table_refresh.png new file mode 100644 index 0000000000000000000000000000000000000000..ab92010c2c970d77e0412120172b4c04278fab5b GIT binary patch literal 795 zcmV+$1LXXPP)pL8W^+72efa&*(*OEc&2j); z!-o+QLL07Cg!0AT$Kd7{8Gf8Hp+ zj(3rXQmk%lW6j()UR}_};#?C=4b9B1n`Z4-uMGjjP7U*(ID*b|qeM~2>ERx#siwXv zPydZRhR3epDvmzgT)$l+Z1++h(Cl2B7yO-#{(Z{jNKT??rIrPSN z9C@>oa%mbXMC#($jpie0=Rjn!qCWFM2!?VK6qs( zTZa1hc}GtfAar7Q?@2`P`to{a#Q_WQDe7yI z;s_Q#m1cHrnuFcDnX=nN3FV!yUt-f2FH&NH4eje#v1}QxA}41u@SY{}GK}0T^GrjE z;`K>(9eaz~Yz?i8T2ND#ENVu*VqO_64)e>Io?aC_8f-mu>8v Date: Tue, 4 Jun 2013 10:25:49 +0200 Subject: [PATCH 06/15] Update MainUI to use initial icons --- syncplay/ui/mainUI.py | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/syncplay/ui/mainUI.py b/syncplay/ui/mainUI.py index 30531ad..d07185a 100644 --- a/syncplay/ui/mainUI.py +++ b/syncplay/ui/mainUI.py @@ -96,7 +96,7 @@ class MainDialog(QtGui.QDialog): dialog.roomGroup = QtGui.QGroupBox("Room") dialog.roomInput = QtGui.QLineEdit() - dialog.roomButton = QtGui.QPushButton("Join room") + dialog.roomButton = QtGui.QPushButton(QtGui.QIcon(self.resourcespath + 'door_in.png'), "Join room") dialog.roomButton.pressed.connect(self.joinRoom) dialog.roomLayout = QtGui.QHBoxLayout() dialog.roomInput.setMaximumWidth(150) @@ -111,7 +111,7 @@ class MainDialog(QtGui.QDialog): dialog.seekGroup = QtGui.QGroupBox("Seek") dialog.seekInput = QtGui.QLineEdit() - dialog.seekButton = QtGui.QPushButton("Seek to position") + dialog.seekButton = QtGui.QPushButton(QtGui.QIcon(self.resourcespath + 'clock_go.png'),"Seek to position") dialog.seekButton.pressed.connect(self.seekPosition) dialog.seekLayout = QtGui.QHBoxLayout() @@ -126,11 +126,11 @@ class MainDialog(QtGui.QDialog): def addMiscBox(self, dialog): dialog.miscGroup = QtGui.QGroupBox("Other Commands") - dialog.unseekButton = QtGui.QPushButton("Undo last seek") + dialog.unseekButton = QtGui.QPushButton(QtGui.QIcon(self.resourcespath + 'arrow_undo.png'),"Undo last seek") dialog.unseekButton.pressed.connect(self.undoseek) - dialog.pauseButton = QtGui.QPushButton("Toggle pause") + dialog.pauseButton = QtGui.QPushButton(QtGui.QIcon(self.resourcespath + 'control_pause_blue.png'),"Toggle pause") dialog.pauseButton.pressed.connect(self.togglepause) - dialog.showListButton = QtGui.QPushButton("Update list") + dialog.showListButton = QtGui.QPushButton(QtGui.QIcon(self.resourcespath + 'table_refresh.png'),"Update list") dialog.showListButton.pressed.connect(self.showList) dialog.miscLayout = QtGui.QHBoxLayout() @@ -180,6 +180,10 @@ class MainDialog(QtGui.QDialog): super(MainDialog, self).__init__() self.QtGui = QtGui + if sys.platform.startswith('linux'): + self.resourcespath = utils.findWorkingDir() + "/resources/" + else: + self.resourcespath = utils.findWorkingDir() + "\\resources\\" self.setWindowTitle("Syncplay - Main Window") self.mainLayout = QtGui.QVBoxLayout() @@ -190,11 +194,8 @@ class MainDialog(QtGui.QDialog): self.setLayout(self.mainLayout) self.resize(700,500) - if sys.platform.startswith('linux'): - resourcespath = utils.findWorkingDir() + "/resources/" - else: - resourcespath = utils.findWorkingDir() + "\\resources\\" - self.setWindowIcon(QtGui.QIcon(resourcespath + "syncplay.png")) + + self.setWindowIcon(QtGui.QIcon(self.resourcespath + "syncplay.png")) self.show() From 13db83c33cf9ff852b975f1c51d83e007192ab60 Mon Sep 17 00:00:00 2001 From: Et0h Date: Tue, 4 Jun 2013 09:42:32 +0100 Subject: [PATCH 07/15] Add initial MainUI icons for menubar --- resources/cross.png | Bin 0 -> 655 bytes resources/timeline_marker.png | Bin 0 -> 327 bytes 2 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 resources/cross.png create mode 100644 resources/timeline_marker.png diff --git a/resources/cross.png b/resources/cross.png new file mode 100644 index 0000000000000000000000000000000000000000..1514d51a3cf1b67e1c5b9ada36f1fd474e2d214a GIT binary patch literal 655 zcmV;A0&x9_P)uEoyT++I zn$b9r%cFfhHe2K68PkBu*@^<$y+7xQ$wJ~;c5aBx$R=xq*41Wo zhwQus_VOgm0hughj}MhOvs#{>Vg09Y8WxjWUJY5YW zJ?&8eG!59Cz=|E%Ns@013KLWOLV)CObIIj_5{>{#k%TEAMs_GbdDV`x-iYsGH z#=Z{USAQA>NY(}X7=3{K8#C#5QQ<|d}62BjvZR2H60wE-%+>FMGaA|c6o&@rz!QG)Hl=lY!! z1v*=JI|P}v6PV8&74ykGbnHgML`m~0TYQt3?ajRZzjDrI(GNdQ-+es!^5)m?umAh| z_lML2hJx>}8mr}YRTjS1X5c%1=To>j zEp`{T<8b2%3kmpmE&u%$g;f!Ou2+6+ab?{tp`+Eo<@Tn7>)N`e4#yAy(KQ_jApxR2 zTwy>}5{4Z&HZE$4TztEY4eB^wEm!DE&<6VJfqm!9 Wxdqp5p1KL-FnGH9xvX Date: Tue, 4 Jun 2013 10:43:33 +0200 Subject: [PATCH 08/15] Update MainUI to use menubar icons --- syncplay/ui/mainUI.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/syncplay/ui/mainUI.py b/syncplay/ui/mainUI.py index d07185a..9f75056 100644 --- a/syncplay/ui/mainUI.py +++ b/syncplay/ui/mainUI.py @@ -146,17 +146,17 @@ class MainDialog(QtGui.QDialog): dialog.menuBar = QtGui.QMenuBar() dialog.fileMenu = QtGui.QMenu("&File", self) - dialog.exitAction = dialog.fileMenu.addAction("E&xit") + dialog.exitAction = dialog.fileMenu.addAction(QtGui.QIcon(self.resourcespath + 'cross.png'), "E&xit") dialog.exitAction.triggered.connect(self.exitSyncplay) dialog.menuBar.addMenu(dialog.fileMenu) dialog.advancedMenu = QtGui.QMenu("&Advanced", self) - dialog.setoffsetAction = dialog.advancedMenu.addAction("Set &Offset") + dialog.setoffsetAction = dialog.advancedMenu.addAction(QtGui.QIcon(self.resourcespath + 'timeline_marker.png'),"Set &Offset") dialog.setoffsetAction.triggered.connect(self.setOffset) dialog.menuBar.addMenu(dialog.advancedMenu) dialog.helpMenu = QtGui.QMenu("&Help", self) - dialog.userguideAction = dialog.helpMenu.addAction("Open User &Guide") + dialog.userguideAction = dialog.helpMenu.addAction(QtGui.QIcon(self.resourcespath + 'help.png'), "Open User &Guide") dialog.userguideAction.triggered.connect(self.openUserGuide) dialog.menuBar.addMenu(dialog.helpMenu) From b317a618e0421f465433041682fbc37f5631f2f3 Mon Sep 17 00:00:00 2001 From: Uriziel Date: Fri, 7 Jun 2013 21:16:45 +0200 Subject: [PATCH 09/15] First attempt to merge syncplay with PySide --- syncplay/client.py | 30 ++- syncplay/clientManager.py | 2 +- syncplay/ui/ConfigurationGetter.py | 8 +- syncplay/ui/__init__.py | 8 +- syncplay/ui/consoleUI.py | 9 + syncplay/ui/gui.py | 268 ++++++++++++++++++++- syncplay/ui/mainUI.py | 209 ----------------- syncplay/vendor/__init__.py | 0 syncplay/vendor/qt4reactor.py | 359 +++++++++++++++++++++++++++++ 9 files changed, 663 insertions(+), 230 deletions(-) delete mode 100644 syncplay/ui/mainUI.py create mode 100644 syncplay/vendor/__init__.py create mode 100644 syncplay/vendor/qt4reactor.py diff --git a/syncplay/client.py b/syncplay/client.py index 8be0f93..c65a7f6 100644 --- a/syncplay/client.py +++ b/syncplay/client.py @@ -505,7 +505,7 @@ class SyncplayUserlist(object): self.ui.showMessage(message) def addUser(self, username, room, file_, position = 0, noMessage = False): - self._roomUsersChanged = True + self.userListChange() if(username == self.currentUser.username): self.currentUser.lastPosition = position return @@ -515,7 +515,7 @@ class SyncplayUserlist(object): self.__showUserChangeMessage(username, room, file_) def removeUser(self, username): - self._roomUsersChanged = True + self.userListChange() if(self._users.has_key(username)): self._users.pop(username) message = getMessage("en", "left-notification").format(username) @@ -528,7 +528,7 @@ class SyncplayUserlist(object): self.__showUserChangeMessage(username, room, None) def modUser(self, username, room, file_): - self._roomUsersChanged = True + self.userListChange() if(self._users.has_key(username)): user = self._users[username] self.__displayModUserMessage(username, room, file_, user) @@ -576,27 +576,28 @@ class SyncplayUserlist(object): return message def __displayFileWatchersInRoomList(self, key, users): - self.ui.showMessage(getMessage("en", "file-played-by-notification").format(key), True, True) + self.ui.showListMessage(getMessage("en", "file-played-by-notification").format(key)) for user in sorted(users.itervalues()): message = "<"+user.username+">" if(self.currentUser.username == user.username): message = "*" + message + "*" message = self.__addDifferentFileMessageIfNecessary(user, message) - self.ui.showMessage("\t" + message, True, True) + self.ui.showListMessage("\t" + message) def __displayPeopleInRoomWithNoFile(self, noFileList): if (noFileList): - self.ui.showMessage(getMessage("en", "notplaying-notification"), True, True) + self.ui.showListMessage(getMessage("en", "notplaying-notification")) for user in sorted(noFileList.itervalues()): - self.ui.showMessage("\t<" + user.username + ">", True, True) + self.ui.showListMessage("\t<" + user.username + ">") def __displayListOfPeople(self, rooms): for roomName in sorted(rooms.iterkeys()): - self.ui.showMessage(getMessage("en", "userlist-room-notification").format(roomName), True, False) + self.ui.showListMessage(getMessage("en", "userlist-room-notification").format(roomName)) noFileList = rooms[roomName].pop("__noFile__") if (rooms[roomName].has_key("__noFile__")) else None for key in sorted(rooms[roomName].iterkeys()): self.__displayFileWatchersInRoomList(key, rooms[roomName][key]) self.__displayPeopleInRoomWithNoFile(noFileList) + self.ui.markEndOfUserlist() def areAllFilesInRoomSame(self): for user in self._users.itervalues(): @@ -610,6 +611,10 @@ class SyncplayUserlist(object): return False return True + def userListChange(self): + self._roomUsersChanged = True + self.ui.userListChange() + def roomStateConfirmed(self): self._roomUsersChanged = False @@ -633,6 +638,9 @@ class UiManager(object): if(not noPlayer): self.showOSDMessage(message) self.__ui.showMessage(message, noTimestamp) + def showListMessage(self, message): + self.__ui.showListMessage(message) + def showOSDMessage(self, message, duration = constants.OSD_DURATION): if(self._client._player): self._client._player.displayMessage(message, duration * 1000) @@ -642,5 +650,11 @@ class UiManager(object): def promptFor(self, prompt): return self.__ui.promptFor(prompt) + + def userListChange(self): + self.__ui.userListChange() + def markEndOfUserlist(self): + self.__ui.markEndOfUserlist() + \ No newline at end of file diff --git a/syncplay/clientManager.py b/syncplay/clientManager.py index 043a5f6..4bb1c1b 100644 --- a/syncplay/clientManager.py +++ b/syncplay/clientManager.py @@ -1,4 +1,3 @@ -from syncplay.client import SyncplayClient from syncplay.ui.ConfigurationGetter import ConfigurationGetter from syncplay import ui from syncplay.messages import getMessage @@ -6,6 +5,7 @@ from syncplay.messages import getMessage class SyncplayClientManager(object): def run(self): config = ConfigurationGetter().getConfiguration() + from syncplay.client import SyncplayClient #Imported later, so the proper reactor is installed interface = ui.getUi(graphical=not config["noGui"]) syncplayClient = SyncplayClient(config["playerClass"], interface, config) if(syncplayClient): diff --git a/syncplay/ui/ConfigurationGetter.py b/syncplay/ui/ConfigurationGetter.py index 34c5f0e..eb2e33e 100644 --- a/syncplay/ui/ConfigurationGetter.py +++ b/syncplay/ui/ConfigurationGetter.py @@ -205,8 +205,9 @@ class ConfigurationGetter(object): except InvalidConfigValue: pass try: - for key, value in self._promptForMissingArguments().items(): - self._config[key] = value + if(self._config['noGui'] == False): + for key, value in self._promptForMissingArguments().items(): + self._config[key] = value except: sys.exit() @@ -240,5 +241,8 @@ class ConfigurationGetter(object): self._saveConfig(iniPath) if(self._config['file']): self._loadRelativeConfiguration() + if(not self._config['noGui']): + from syncplay.vendor import qt4reactor + qt4reactor.install() return self._config diff --git a/syncplay/ui/__init__.py b/syncplay/ui/__init__.py index bca0a02..0848c4f 100644 --- a/syncplay/ui/__init__.py +++ b/syncplay/ui/__init__.py @@ -1,11 +1,11 @@ -from syncplay.ui.gui import GraphicalUI +from syncplay.ui.gui import MainDialog as GraphicalUI from syncplay.ui.consoleUI import ConsoleUI def getUi(graphical=True): - if(False): #graphical): #TODO: Add graphical ui + if(graphical): #TODO: Add graphical ui ui = GraphicalUI() else: ui = ConsoleUI() - ui.setDaemon(True) - ui.start() + ui.setDaemon(True) + ui.start() return ui diff --git a/syncplay/ui/consoleUI.py b/syncplay/ui/consoleUI.py index 33bdf7d..82761de 100644 --- a/syncplay/ui/consoleUI.py +++ b/syncplay/ui/consoleUI.py @@ -40,6 +40,15 @@ class ConsoleUI(threading.Thread): self.promptMode.wait() return self.PromptResult + def showListMessage(self, message): + self.showMessage(message, True) + + def markEndOfUserlist(self): + pass + + def userListChange(self): + pass + def showMessage(self, message, noTimestamp=False): message = message.encode(sys.stdout.encoding, 'replace') if(noTimestamp): diff --git a/syncplay/ui/gui.py b/syncplay/ui/gui.py index 0ae7f4d..d5cf6ea 100644 --- a/syncplay/ui/gui.py +++ b/syncplay/ui/gui.py @@ -1,9 +1,265 @@ -''' -Created on 05-07-2012 +from PySide import QtGui #@UnresolvedImport +from PySide.QtCore import Qt #@UnresolvedImport +from syncplay import utils, constants +import sys +import time +import re -@author: Uriziel -''' +class MainDialog(QtGui.QDialog): + def addClient(self, client): + self._syncplayClient = client + + def promptFor(self, prompt=">", message=""): + #TODO: Prompt user + return None -class GraphicalUI(object): - def __init__(self): + def showMessage(self, message, noTimestamp=False): + message = message.encode(sys.stdout.encoding, 'replace') + message = message.replace("&", "&").replace('"', """).replace("<", "<").replace(">", ">") + message = message.replace("\n", "
") + if(noTimestamp): + self.newMessage(message + "
") + else: + self.newMessage(time.strftime(constants.UI_TIME_FORMAT, time.localtime()) + message + "
") + + def showListMessage(self, message): + message = message.encode(sys.stdout.encoding, 'replace') + message = message.replace("&", "&").replace('"', """).replace("<", "<").replace(">", ">") + message = message.replace("\t", " "*4) + self._listBuffer += message + "
" + + def markEndOfUserlist(self): + self.resetList() + self.newListItem(self._listBuffer) + self._listBuffer = ""; + + def userListChange(self): + #self._syncplayClient.getUserList() pass + + def showDebugMessage(self, message): + print(message) + + def showErrorMessage(self, message): + print("ERROR:\t" + message) + + def joinRoom(self): + room = self.roomInput.text() + if room == "": + if self._syncplayClient.userlist.currentUser.file: + room = self._syncplayClient.userlist.currentUser.file["name"] + else: + room = self._syncplayClient.defaultRoom + self._syncplayClient.setRoom(room) + self._syncplayClient.sendRoom() + + def seekPosition(self): + s = re.match(constants.UI_SEEK_REGEX, self.seekInput.text()) + if(s): + sign = self._extractSign(s.group('sign')) + t = utils.parseTime(s.group('time')) + if(t is None): + return + if(sign): + t = self._syncplayClient.getGlobalPosition() + sign * t + self._syncplayClient.setPosition(t) + self.seekInput.setText("") + else: + self.showMessage("Invalid seek value", True) + + def showList(self): + self._syncplayClient.getUserList() #TODO: remove? + + def undoSeek(self): + tmp_pos = self._syncplayClient.getPlayerPosition() + self._syncplayClient.setPosition(self._syncplayClient.playerPositionBeforeLastSeek) + self._syncplayClient.playerPositionBeforeLastSeek = tmp_pos + + def togglePause(self): + self._syncplayClient.setPaused(not self._syncplayClient.getPlayerPaused()) + + def exitSyncplay(self): + reply = QtGui.QMessageBox.question(self, "Syncplay", + "Are you sure you want to exit Syncplay?", + QtGui.QMessageBox.Yes | QtGui.QMessageBox.No) + if reply == QtGui.QMessageBox.Yes: + sys.exit() + + def _extractSign(self, m): + if(m): + if(m == "-"): + return -1 + else: + return 1 + else: + return None + + def setOffset(self): + newoffset, ok = QtGui.QInputDialog.getText(self,"Set Offset", + "Offset (+/-):", QtGui.QLineEdit.Normal, + "") + if ok and newoffset != '': + o = re.match(constants.UI_OFFSET_REGEX, newoffset) + if(o): + sign = self._extractSign(o.group('sign')) + t = utils.parseTime(o.group('time')) + if(t is None): + return + if (o.group('sign') == "/"): + t = self._syncplayClient.getPlayerPosition() - t + elif(sign): + t = self._syncplayClient.getUserOffset() + sign * t + self._syncplayClient.setUserOffset(t) + else: + self.showMessage("Invalid offset value", True) + + def openUserGuide(self): + self.QtGui.QDesktopServices.openUrl("http://syncplay.pl/guide/") + + def addTopLayout(self, dialog): + dialog.topSplit = QtGui.QSplitter(Qt.Horizontal) + + dialog.outputLayout = QtGui.QVBoxLayout() + dialog.outputbox = QtGui.QTextEdit() + dialog.outputbox.setReadOnly(True) + dialog.outputlabel = QtGui.QLabel("Notifications") + dialog.outputFrame = QtGui.QFrame() + dialog.outputFrame.setLineWidth(0) + dialog.outputFrame.setMidLineWidth(0) + dialog.outputLayout.setContentsMargins(0,0,0,0) + dialog.outputLayout.addWidget(dialog.outputlabel) + dialog.outputLayout.addWidget(dialog.outputbox) + dialog.outputFrame.setLayout(dialog.outputLayout) + + dialog.listLayout = QtGui.QVBoxLayout() + dialog.listbox = QtGui.QTextEdit() + dialog.listbox.setReadOnly(True) + dialog.listlabel = QtGui.QLabel("List of who is playing what") + dialog.listFrame = QtGui.QFrame() + dialog.listFrame.setLineWidth(0) + dialog.listFrame.setMidLineWidth(0) + dialog.listLayout.setContentsMargins(0,0,0,0) + dialog.listLayout.addWidget(dialog.listlabel) + dialog.listLayout.addWidget(dialog.listbox) + dialog.listFrame.setLayout(dialog.listLayout) + + dialog.topSplit.addWidget(dialog.outputFrame) + dialog.topSplit.addWidget(dialog.listFrame) + dialog.topSplit.setStretchFactor(0,3) + dialog.topSplit.setStretchFactor(1,2) + dialog.mainLayout.addWidget(dialog.topSplit) + dialog.topSplit.setSizePolicy(QtGui.QSizePolicy.Preferred,QtGui.QSizePolicy.Expanding) + + def addBottomLayout(self, dialog): + dialog.bottomLayout = QtGui.QHBoxLayout() + + dialog.addRoomBox(MainDialog) + dialog.addSeekBox(MainDialog) + dialog.addMiscBox(MainDialog) + + dialog.bottomLayout.addWidget(dialog.roomGroup, Qt.AlignLeft) + dialog.bottomLayout.addWidget(dialog.seekGroup, Qt.AlignLeft) + dialog.bottomLayout.addWidget(dialog.miscGroup, Qt.AlignLeft) + + dialog.mainLayout.addLayout(dialog.bottomLayout, Qt.AlignLeft) + + def addRoomBox(self, dialog): + dialog.roomGroup = QtGui.QGroupBox("Room") + + dialog.roomInput = QtGui.QLineEdit() + dialog.roomButton = QtGui.QPushButton(QtGui.QIcon(self.resourcespath + 'door_in.png'), "Join room") + dialog.roomButton.pressed.connect(self.joinRoom) + dialog.roomLayout = QtGui.QHBoxLayout() + dialog.roomInput.setMaximumWidth(150) + + dialog.roomLayout.addWidget(dialog.roomInput) + dialog.roomLayout.addWidget(dialog.roomButton) + + dialog.roomGroup.setLayout(dialog.roomLayout) + dialog.roomGroup.setFixedSize(dialog.roomGroup.sizeHint()) + + def addSeekBox(self, dialog): + dialog.seekGroup = QtGui.QGroupBox("Seek") + + dialog.seekInput = QtGui.QLineEdit() + dialog.seekButton = QtGui.QPushButton(QtGui.QIcon(self.resourcespath + 'clock_go.png'),"Seek to position") + dialog.seekButton.pressed.connect(self.seekPosition) + + dialog.seekLayout = QtGui.QHBoxLayout() + dialog.seekInput.setMaximumWidth(100) + + dialog.seekLayout.addWidget(dialog.seekInput) + dialog.seekLayout.addWidget(dialog.seekButton) + + dialog.seekGroup.setLayout(dialog.seekLayout) + dialog.seekGroup.setFixedSize(dialog.seekGroup.sizeHint()) + + def addMiscBox(self, dialog): + dialog.miscGroup = QtGui.QGroupBox("Other Commands") + + dialog.unseekButton = QtGui.QPushButton(QtGui.QIcon(self.resourcespath + 'arrow_undo.png'),"Undo last seek") + dialog.unseekButton.pressed.connect(self.undoSeek) + dialog.pauseButton = QtGui.QPushButton(QtGui.QIcon(self.resourcespath + 'control_pause_blue.png'),"Toggle pause") + dialog.pauseButton.pressed.connect(self.togglePause) + dialog.showListButton = QtGui.QPushButton(QtGui.QIcon(self.resourcespath + 'table_refresh.png'),"Update list") + dialog.showListButton.pressed.connect(self.showList) + + dialog.miscLayout = QtGui.QHBoxLayout() + dialog.miscLayout.addWidget(dialog.unseekButton) + dialog.miscLayout.addWidget(dialog.pauseButton) + dialog.miscLayout.addWidget(dialog.showListButton) + + dialog.miscGroup.setLayout(dialog.miscLayout) + dialog.miscGroup.setFixedSize(dialog.miscGroup.sizeHint()) + + + def addMenubar(self, dialog): + dialog.menuBar = QtGui.QMenuBar() + + dialog.fileMenu = QtGui.QMenu("&File", self) + dialog.exitAction = dialog.fileMenu.addAction(QtGui.QIcon(self.resourcespath + 'cross.png'), "E&xit") + dialog.exitAction.triggered.connect(self.exitSyncplay) + dialog.menuBar.addMenu(dialog.fileMenu) + + dialog.advancedMenu = QtGui.QMenu("&Advanced", self) + dialog.setoffsetAction = dialog.advancedMenu.addAction(QtGui.QIcon(self.resourcespath + 'timeline_marker.png'),"Set &Offset") + dialog.setoffsetAction.triggered.connect(self.setOffset) + dialog.menuBar.addMenu(dialog.advancedMenu) + + dialog.helpMenu = QtGui.QMenu("&Help", self) + dialog.userguideAction = dialog.helpMenu.addAction(QtGui.QIcon(self.resourcespath + 'help.png'), "Open User &Guide") + dialog.userguideAction.triggered.connect(self.openUserGuide) + + dialog.menuBar.addMenu(dialog.helpMenu) + dialog.mainLayout.setMenuBar(dialog.menuBar) + + def newMessage(self, message): + self.outputbox.moveCursor(QtGui.QTextCursor.End) + self.outputbox.insertHtml(message) + self.outputbox.moveCursor(QtGui.QTextCursor.End) + + def resetList(self): + self.listbox.setText("") + + def newListItem(self, item): + self.listbox.moveCursor(QtGui.QTextCursor.End) + self.listbox.insertHtml(item) + self.listbox.moveCursor(QtGui.QTextCursor.End) + + def __init__(self): + super(MainDialog, self).__init__() + self.QtGui = QtGui + self._listBuffer = "" + if sys.platform.startswith('linux'): + self.resourcespath = utils.findWorkingDir() + "/resources/" + else: + self.resourcespath = utils.findWorkingDir() + "\\resources\\" + self.setWindowTitle("Syncplay - Main Window") + self.mainLayout = QtGui.QVBoxLayout() + self.addTopLayout(self) + self.addBottomLayout(self) + self.addMenubar(self) + self.setLayout(self.mainLayout) + self.resize(700,500) + self.setWindowIcon(QtGui.QIcon(self.resourcespath + "syncplay.png")) + self.show() diff --git a/syncplay/ui/mainUI.py b/syncplay/ui/mainUI.py deleted file mode 100644 index 9f75056..0000000 --- a/syncplay/ui/mainUI.py +++ /dev/null @@ -1,209 +0,0 @@ -from PySide import QtCore, QtGui -from PySide.QtCore import QSettings, Qt -from PySide.QtGui import QApplication, QLineEdit, QCursor, QLabel, QCheckBox, QDesktopServices, QIcon -from syncplay import utils - -import os -import sys - -class MainDialog(QtGui.QDialog): - - def joinRoom(self): - print "JOIN ROOM: " + self.roomInput.text() - self.roomInput.setText("") - - def seekPosition(self): - print "SEEK POSITION: " + self.seekInput.text() - self.seekInput.setText("") - - def showList(self): - print "UPDATE USER LIST" - - def undoseek(self): - print "UNDO LAST SEEK" - - def togglepause(self): - print "PAUSE/UNPAUSE" - - def exitSyncplay(self): - reply = QtGui.QMessageBox.question(self, "Syncplay", - "Are you sure you want to exit Syncplay?", - QtGui.QMessageBox.Yes | QtGui.QMessageBox.No) - if reply == QtGui.QMessageBox.Yes: - sys.exit() - - def setOffset(self): - newoffset, ok = QtGui.QInputDialog.getText(self,"Set Offset", - "Offset (+/-):", QtGui.QLineEdit.Normal, - "") - if ok and newoffset != '': - print "SET OFFSET: " + newoffset - - def openUserGuide(self): - self.QtGui.QDesktopServices.openUrl("http://syncplay.pl/guide/") - - def addTopLayout(self, dialog): - dialog.topSplit = QtGui.QSplitter(Qt.Horizontal) - - - dialog.outputLayout = QtGui.QVBoxLayout() - dialog.outputbox = QtGui.QTextEdit() - dialog.outputbox.setReadOnly(True) - dialog.outputlabel = QtGui.QLabel("Notifications") - dialog.outputFrame = QtGui.QFrame() - dialog.outputFrame.setLineWidth(0) - dialog.outputFrame.setMidLineWidth(0) - dialog.outputLayout.setContentsMargins(0,0,0,0) - dialog.outputLayout.addWidget(dialog.outputlabel) - dialog.outputLayout.addWidget(dialog.outputbox) - dialog.outputFrame.setLayout(dialog.outputLayout) - - dialog.listLayout = QtGui.QVBoxLayout() - dialog.listbox = QtGui.QTextEdit() - dialog.listbox.setReadOnly(True) - dialog.listlabel = QtGui.QLabel("List of who is playing what") - dialog.listFrame = QtGui.QFrame() - dialog.listFrame.setLineWidth(0) - dialog.listFrame.setMidLineWidth(0) - dialog.listLayout.setContentsMargins(0,0,0,0) - dialog.listLayout.addWidget(dialog.listlabel) - dialog.listLayout.addWidget(dialog.listbox) - dialog.listFrame.setLayout(dialog.listLayout) - - dialog.topSplit.addWidget(dialog.outputFrame) - dialog.topSplit.addWidget(dialog.listFrame) - dialog.topSplit.setStretchFactor(0,3) - dialog.topSplit.setStretchFactor(1,2) - dialog.mainLayout.addWidget(dialog.topSplit) - dialog.topSplit.setSizePolicy(QtGui.QSizePolicy.Preferred,QtGui.QSizePolicy.Expanding) - - - def addBottomLayout(self, dialog): - dialog.bottomLayout = QtGui.QHBoxLayout() - - dialog.addRoomBox(MainDialog) - dialog.addSeekBox(MainDialog) - dialog.addMiscBox(MainDialog) - - dialog.bottomLayout.addWidget(dialog.roomGroup, Qt.AlignLeft) - dialog.bottomLayout.addWidget(dialog.seekGroup, Qt.AlignLeft) - dialog.bottomLayout.addWidget(dialog.miscGroup, Qt.AlignLeft) - - dialog.mainLayout.addLayout(dialog.bottomLayout, Qt.AlignLeft) - - - def addRoomBox(self, dialog): - dialog.roomGroup = QtGui.QGroupBox("Room") - - dialog.roomInput = QtGui.QLineEdit() - dialog.roomButton = QtGui.QPushButton(QtGui.QIcon(self.resourcespath + 'door_in.png'), "Join room") - dialog.roomButton.pressed.connect(self.joinRoom) - dialog.roomLayout = QtGui.QHBoxLayout() - dialog.roomInput.setMaximumWidth(150) - - dialog.roomLayout.addWidget(dialog.roomInput) - dialog.roomLayout.addWidget(dialog.roomButton) - - dialog.roomGroup.setLayout(dialog.roomLayout) - dialog.roomGroup.setFixedSize(dialog.roomGroup.sizeHint()) - - def addSeekBox(self, dialog): - dialog.seekGroup = QtGui.QGroupBox("Seek") - - dialog.seekInput = QtGui.QLineEdit() - dialog.seekButton = QtGui.QPushButton(QtGui.QIcon(self.resourcespath + 'clock_go.png'),"Seek to position") - dialog.seekButton.pressed.connect(self.seekPosition) - - dialog.seekLayout = QtGui.QHBoxLayout() - dialog.seekInput.setMaximumWidth(100) - - dialog.seekLayout.addWidget(dialog.seekInput) - dialog.seekLayout.addWidget(dialog.seekButton) - - dialog.seekGroup.setLayout(dialog.seekLayout) - dialog.seekGroup.setFixedSize(dialog.seekGroup.sizeHint()) - - def addMiscBox(self, dialog): - dialog.miscGroup = QtGui.QGroupBox("Other Commands") - - dialog.unseekButton = QtGui.QPushButton(QtGui.QIcon(self.resourcespath + 'arrow_undo.png'),"Undo last seek") - dialog.unseekButton.pressed.connect(self.undoseek) - dialog.pauseButton = QtGui.QPushButton(QtGui.QIcon(self.resourcespath + 'control_pause_blue.png'),"Toggle pause") - dialog.pauseButton.pressed.connect(self.togglepause) - dialog.showListButton = QtGui.QPushButton(QtGui.QIcon(self.resourcespath + 'table_refresh.png'),"Update list") - dialog.showListButton.pressed.connect(self.showList) - - dialog.miscLayout = QtGui.QHBoxLayout() - dialog.miscLayout.addWidget(dialog.unseekButton) - dialog.miscLayout.addWidget(dialog.pauseButton) - dialog.miscLayout.addWidget(dialog.showListButton) - - dialog.miscGroup.setLayout(dialog.miscLayout) - dialog.miscGroup.setFixedSize(dialog.miscGroup.sizeHint()) - - - def addMenubar(self, dialog): - dialog.menuBar = QtGui.QMenuBar() - - dialog.fileMenu = QtGui.QMenu("&File", self) - dialog.exitAction = dialog.fileMenu.addAction(QtGui.QIcon(self.resourcespath + 'cross.png'), "E&xit") - dialog.exitAction.triggered.connect(self.exitSyncplay) - dialog.menuBar.addMenu(dialog.fileMenu) - - dialog.advancedMenu = QtGui.QMenu("&Advanced", self) - dialog.setoffsetAction = dialog.advancedMenu.addAction(QtGui.QIcon(self.resourcespath + 'timeline_marker.png'),"Set &Offset") - dialog.setoffsetAction.triggered.connect(self.setOffset) - dialog.menuBar.addMenu(dialog.advancedMenu) - - dialog.helpMenu = QtGui.QMenu("&Help", self) - dialog.userguideAction = dialog.helpMenu.addAction(QtGui.QIcon(self.resourcespath + 'help.png'), "Open User &Guide") - dialog.userguideAction.triggered.connect(self.openUserGuide) - - dialog.menuBar.addMenu(dialog.helpMenu) - dialog.mainLayout.setMenuBar(dialog.menuBar) - - def NewMessage(self, message): - self.outputbox.moveCursor(QtGui.QTextCursor.End) - self.outputbox.insertHtml(message) - self.outputbox.moveCursor(QtGui.QTextCursor.End) - - def ResetList(self): - self.listbox.setText("") - - def NewListItem(self, item): - self.listbox.moveCursor(QtGui.QTextCursor.End) - self.listbox.insertHtml(item) - self.listbox.moveCursor(QtGui.QTextCursor.End) - - - def __init__(self): - - super(MainDialog, self).__init__() - self.QtGui = QtGui - if sys.platform.startswith('linux'): - self.resourcespath = utils.findWorkingDir() + "/resources/" - else: - self.resourcespath = utils.findWorkingDir() + "\\resources\\" - - self.setWindowTitle("Syncplay - Main Window") - self.mainLayout = QtGui.QVBoxLayout() - - self.addTopLayout(self) - self.addBottomLayout(self) - self.addMenubar(self) - self.setLayout(self.mainLayout) - self.resize(700,500) - - - self.setWindowIcon(QtGui.QIcon(self.resourcespath + "syncplay.png")) - - - self.show() - -if __name__ == '__main__': - - import sys - - app = QtGui.QApplication(sys.argv) - dialog = MainDialog() - sys.exit(app.exec_()) diff --git a/syncplay/vendor/__init__.py b/syncplay/vendor/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/syncplay/vendor/qt4reactor.py b/syncplay/vendor/qt4reactor.py new file mode 100644 index 0000000..a337099 --- /dev/null +++ b/syncplay/vendor/qt4reactor.py @@ -0,0 +1,359 @@ +# Copyright (c) 2001-2011 Twisted Matrix Laboratories. +# See LICENSE for details. + + +""" +This module provides support for Twisted to be driven by the Qt mainloop. + +In order to use this support, simply do the following:: + | app = QApplication(sys.argv) # your code to init Qt + | import qt4reactor + | qt4reactor.install() + +alternatively: + + | from twisted.application import reactors + | reactors.installReactor('qt4') + +Then use twisted.internet APIs as usual. The other methods here are not +intended to be called directly. + +If you don't instantiate a QApplication or QCoreApplication prior to +installing the reactor, a QCoreApplication will be constructed +by the reactor. QCoreApplication does not require a GUI so trial testing +can occur normally. + +Twisted can be initialized after QApplication.exec_() with a call to +reactor.runReturn(). calling reactor.stop() will unhook twisted but +leave your Qt application running + +API Stability: stable + +Maintainer: U{Glenn H Tarbox, PhD} + +Previous maintainer: U{Itamar Shtull-Trauring} +Original port to QT4: U{Gabe Rudy} +Subsequent port by therve +""" + +import sys +import time +from zope.interface import implements +from twisted.internet.interfaces import IReactorFDSet +from twisted.python import log, runtime +from twisted.internet import posixbase +from twisted.python.runtime import platformType, platform + +try: + from PyQt4.QtCore import QSocketNotifier, QObject, SIGNAL, QTimer, QCoreApplication + from PyQt4.QtCore import QEventLoop +except ImportError: + from PySide.QtCore import QSocketNotifier, QObject, SIGNAL, QTimer, QCoreApplication + from PySide.QtCore import QEventLoop + + +class TwistedSocketNotifier(QObject): + """ + Connection between an fd event and reader/writer callbacks. + """ + + def __init__(self, parent, reactor, watcher, socketType): + QObject.__init__(self, parent) + self.reactor = reactor + self.watcher = watcher + fd = watcher.fileno() + self.notifier = QSocketNotifier(fd, socketType, parent) + self.notifier.setEnabled(True) + if socketType == QSocketNotifier.Read: + self.fn = self.read + else: + self.fn = self.write + QObject.connect(self.notifier, SIGNAL("activated(int)"), self.fn) + + + def shutdown(self): + self.notifier.setEnabled(False) + self.disconnect(self.notifier, SIGNAL("activated(int)"), self.fn) + self.fn = self.watcher = None + self.notifier.deleteLater() + self.deleteLater() + + + def read(self, fd): + if not self.watcher: + return + w = self.watcher + # doRead can cause self.shutdown to be called so keep a reference to self.watcher + def _read(): + #Don't call me again, until the data has been read + self.notifier.setEnabled(False) + why = None + try: + why = w.doRead() + inRead = True + except: + inRead = False + log.err() + why = sys.exc_info()[1] + if why: + self.reactor._disconnectSelectable(w, why, inRead) + elif self.watcher: + self.notifier.setEnabled(True) # Re enable notification following sucessfull read + self.reactor._iterate(fromqt=True) + log.callWithLogger(w, _read) + + def write(self, sock): + if not self.watcher: + return + w = self.watcher + def _write(): + why = None + self.notifier.setEnabled(False) + + try: + why = w.doWrite() + except: + log.err() + why = sys.exc_info()[1] + if why: + self.reactor._disconnectSelectable(w, why, False) + elif self.watcher: + self.notifier.setEnabled(True) + self.reactor._iterate(fromqt=True) + log.callWithLogger(w, _write) + + + +class QtReactor(posixbase.PosixReactorBase): + implements(IReactorFDSet) + + def __init__(self): + self._reads = {} + self._writes = {} + self._notifiers = {} + self._timer = QTimer() + self._timer.setSingleShot(True) + QObject.connect(self._timer, SIGNAL("timeout()"), self.iterate) + + if QCoreApplication.instance() is None: + # Application Object has not been started yet + self.qApp=QCoreApplication([]) + self._ownApp=True + else: + self.qApp = QCoreApplication.instance() + self._ownApp=False + self._blockApp = None + posixbase.PosixReactorBase.__init__(self) + + + def _add(self, xer, primary, type): + """ + Private method for adding a descriptor from the event loop. + + It takes care of adding it if new or modifying it if already added + for another state (read -> read/write for example). + """ + if xer not in primary: + primary[xer] = TwistedSocketNotifier(None, self, xer, type) + + + def addReader(self, reader): + """ + Add a FileDescriptor for notification of data available to read. + """ + self._add(reader, self._reads, QSocketNotifier.Read) + + + def addWriter(self, writer): + """ + Add a FileDescriptor for notification of data available to write. + """ + self._add(writer, self._writes, QSocketNotifier.Write) + + + def _remove(self, xer, primary): + """ + Private method for removing a descriptor from the event loop. + + It does the inverse job of _add, and also add a check in case of the fd + has gone away. + """ + if xer in primary: + notifier = primary.pop(xer) + notifier.shutdown() + + + def removeReader(self, reader): + """ + Remove a Selectable for notification of data available to read. + """ + self._remove(reader, self._reads) + + + def removeWriter(self, writer): + """ + Remove a Selectable for notification of data available to write. + """ + self._remove(writer, self._writes) + + + def removeAll(self): + """ + Remove all selectables, and return a list of them. + """ + rv = self._removeAll(self._reads, self._writes) + return rv + + + def getReaders(self): + return self._reads.keys() + + + def getWriters(self): + return self._writes.keys() + + + def callLater(self,howlong, *args, **kargs): + rval = super(QtReactor,self).callLater(howlong, *args, **kargs) + self.reactorInvocation() + return rval + + + def reactorInvocation(self): + self._timer.stop() + self._timer.setInterval(0) + self._timer.start() + + + def _iterate(self, delay=None, fromqt=False): + """See twisted.internet.interfaces.IReactorCore.iterate. + """ + self.runUntilCurrent() + self.doIteration(delay, fromqt) + + iterate = _iterate + + def doIteration(self, delay=None, fromqt=False): + 'This method is called by a Qt timer or by network activity on a file descriptor' + + if not self.running and self._blockApp: + self._blockApp.quit() + self._timer.stop() + delay = max(delay, 1) + if not fromqt: + self.qApp.processEvents(QEventLoop.AllEvents, delay * 1000) + if self.timeout() is None: + timeout = 0.1 + elif self.timeout() == 0: + timeout = 0 + else: + timeout = self.timeout() + self._timer.setInterval(timeout * 1000) + self._timer.start() + + + def runReturn(self, installSignalHandlers=True): + self.startRunning(installSignalHandlers=installSignalHandlers) + self.reactorInvocation() + + + def run(self, installSignalHandlers=True): + if self._ownApp: + self._blockApp = self.qApp + else: + self._blockApp = QEventLoop() + self.runReturn() + self._blockApp.exec_() + + +class QtEventReactor(QtReactor): + def __init__(self, *args, **kwargs): + self._events = {} + super(QtEventReactor, self).__init__() + + + def addEvent(self, event, fd, action): + """ + Add a new win32 event to the event loop. + """ + self._events[event] = (fd, action) + + + def removeEvent(self, event): + """ + Remove an event. + """ + if event in self._events: + del self._events[event] + + + def doEvents(self): + handles = self._events.keys() + if len(handles) > 0: + val = None + while val != WAIT_TIMEOUT: + val = MsgWaitForMultipleObjects(handles, 0, 0, QS_ALLINPUT | QS_ALLEVENTS) + if val >= WAIT_OBJECT_0 and val < WAIT_OBJECT_0 + len(handles): + event_id = handles[val - WAIT_OBJECT_0] + if event_id in self._events: + fd, action = self._events[event_id] + log.callWithLogger(fd, self._runAction, action, fd) + elif val == WAIT_TIMEOUT: + pass + else: + #print 'Got an unexpected return of %r' % val + return + + + def _runAction(self, action, fd): + try: + closed = getattr(fd, action)() + except: + closed = sys.exc_info()[1] + log.deferr() + + if closed: + self._disconnectSelectable(fd, closed, action == 'doRead') + + + def timeout(self): + t = super(QtEventReactor, self).timeout() + return min(t, 0.01) + + + def iterate(self, delay=None): + """See twisted.internet.interfaces.IReactorCore.iterate. + """ + self.runUntilCurrent() + self.doEvents() + self.doIteration(delay) + + +def posixinstall(): + """ + Install the Qt reactor. + """ + p = QtReactor() + from twisted.internet.main import installReactor + installReactor(p) + + +def win32install(): + """ + Install the Qt reactor. + """ + p = QtEventReactor() + from twisted.internet.main import installReactor + installReactor(p) + + +if runtime.platform.getType() == 'win32': + from win32event import CreateEvent, MsgWaitForMultipleObjects + from win32event import WAIT_OBJECT_0, WAIT_TIMEOUT, QS_ALLINPUT, QS_ALLEVENTS + install = win32install +else: + install = posixinstall + + +__all__ = ["install"] + From 630166da9ba6abf5302474d64336eaf998e04c44 Mon Sep 17 00:00:00 2001 From: Uriziel Date: Fri, 7 Jun 2013 21:45:21 +0200 Subject: [PATCH 10/15] Updating list in gui actually works now --- syncplay/client.py | 13 ++++++++----- syncplay/ui/gui.py | 3 +-- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/syncplay/client.py b/syncplay/client.py index c65a7f6..be7bb81 100644 --- a/syncplay/client.py +++ b/syncplay/client.py @@ -318,6 +318,9 @@ class SyncplayClient(object): if(self._protocol and self._protocol.logged): self._protocol.sendList() + def showUserList(self): + self.userlist.showUserList() + def getPassword(self): return self._serverPassword @@ -505,7 +508,6 @@ class SyncplayUserlist(object): self.ui.showMessage(message) def addUser(self, username, room, file_, position = 0, noMessage = False): - self.userListChange() if(username == self.currentUser.username): self.currentUser.lastPosition = position return @@ -513,14 +515,15 @@ class SyncplayUserlist(object): self._users[username] = user if(not noMessage): self.__showUserChangeMessage(username, room, file_) + self.userListChange() def removeUser(self, username): - self.userListChange() if(self._users.has_key(username)): self._users.pop(username) message = getMessage("en", "left-notification").format(username) self.ui.showMessage(message) - + self.userListChange() + def __displayModUserMessage(self, username, room, file_, user): if (file_ and not user.isFileSame(file_)): self.__showUserChangeMessage(username, room, file_) @@ -528,7 +531,6 @@ class SyncplayUserlist(object): self.__showUserChangeMessage(username, room, None) def modUser(self, username, room, file_): - self.userListChange() if(self._users.has_key(username)): user = self._users[username] self.__displayModUserMessage(username, room, file_, user) @@ -538,7 +540,8 @@ class SyncplayUserlist(object): self.__showUserChangeMessage(username, room, file_) else: self.addUser(username, room, file_) - + self.userListChange() + def __addUserWithFileToList(self, rooms, user): currentPosition = utils.formatTime(user.lastPosition) file_key = '\'{}\' ({}/{})'.format(user.file['name'], currentPosition, utils.formatTime(user.file['duration'])) diff --git a/syncplay/ui/gui.py b/syncplay/ui/gui.py index d5cf6ea..3bfd179 100644 --- a/syncplay/ui/gui.py +++ b/syncplay/ui/gui.py @@ -34,8 +34,7 @@ class MainDialog(QtGui.QDialog): self._listBuffer = ""; def userListChange(self): - #self._syncplayClient.getUserList() - pass + self._syncplayClient.showUserList() def showDebugMessage(self, message): print(message) From f176c75b8bab0ae788e365d79e18afc5060decf5 Mon Sep 17 00:00:00 2001 From: Etoh Date: Fri, 7 Jun 2013 21:59:50 +0200 Subject: [PATCH 11/15] Improved shinyGUI closing code --- syncplay/ui/gui.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/syncplay/ui/gui.py b/syncplay/ui/gui.py index 3bfd179..d3f9c72 100644 --- a/syncplay/ui/gui.py +++ b/syncplay/ui/gui.py @@ -82,7 +82,11 @@ class MainDialog(QtGui.QDialog): "Are you sure you want to exit Syncplay?", QtGui.QMessageBox.Yes | QtGui.QMessageBox.No) if reply == QtGui.QMessageBox.Yes: - sys.exit() + self._syncplayClient.stop() + + def closeEvent(self, event): + self.exitSyncplay() + event.ignore() def _extractSign(self, m): if(m): From be830a11b9c4e0d4e483018dc4fed3ddbbcc44e5 Mon Sep 17 00:00:00 2001 From: Etoh Date: Fri, 7 Jun 2013 22:03:02 +0200 Subject: [PATCH 12/15] Remove Syncplay close confirmation dialogue --- syncplay/ui/gui.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/syncplay/ui/gui.py b/syncplay/ui/gui.py index d3f9c72..cedec7a 100644 --- a/syncplay/ui/gui.py +++ b/syncplay/ui/gui.py @@ -78,11 +78,7 @@ class MainDialog(QtGui.QDialog): self._syncplayClient.setPaused(not self._syncplayClient.getPlayerPaused()) def exitSyncplay(self): - reply = QtGui.QMessageBox.question(self, "Syncplay", - "Are you sure you want to exit Syncplay?", - QtGui.QMessageBox.Yes | QtGui.QMessageBox.No) - if reply == QtGui.QMessageBox.Yes: - self._syncplayClient.stop() + self._syncplayClient.stop() def closeEvent(self, event): self.exitSyncplay() From 4a48214a45cefe377ac8d5b3590744f9e3407f0f Mon Sep 17 00:00:00 2001 From: Uriziel Date: Sat, 8 Jun 2013 09:10:58 +0200 Subject: [PATCH 13/15] Fixed disconnecing upon file switch. Hardly clean patch will need a lot of refactoring. --- syncplay/players/mpc.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/syncplay/players/mpc.py b/syncplay/players/mpc.py index 36cb091..95ed286 100644 --- a/syncplay/players/mpc.py +++ b/syncplay/players/mpc.py @@ -307,6 +307,8 @@ class MPCHCAPIPlayer(BasePlayer): speedSupported = False def __init__(self, client): + from twisted.internet import reactor + self.reactor = reactor self.__client = client self._mpcApi = MpcHcApi() self._mpcApi.callbacks.onUpdateFilename = lambda _: self.__makePing() @@ -448,7 +450,8 @@ class MPCHCAPIPlayer(BasePlayer): def __handleUpdatedFilename(self): with self.__fileUpdate: self.__setUpStateForNewlyOpenedFile() - self.__client.updateFile(self._mpcApi.filePlaying, self._mpcApi.fileDuration, self._mpcApi.filePath) + args = (self._mpcApi.filePlaying, self._mpcApi.fileDuration, self._mpcApi.filePath) + self.reactor.callFromThread(self.__client.updateFile, *args) def __mpcError(self, err=""): self.__client.ui.showErrorMessage(err) From ed4c8d1674f463edd294724fb2c81000eb9cf07d Mon Sep 17 00:00:00 2001 From: Uriziel Date: Sat, 8 Jun 2013 13:15:58 +0200 Subject: [PATCH 14/15] MPC pretty much works with GUI now Needs refactoring --- syncplay/players/mpc.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/syncplay/players/mpc.py b/syncplay/players/mpc.py index 95ed286..9d9310b 100644 --- a/syncplay/players/mpc.py +++ b/syncplay/players/mpc.py @@ -312,7 +312,7 @@ class MPCHCAPIPlayer(BasePlayer): self.__client = client self._mpcApi = MpcHcApi() self._mpcApi.callbacks.onUpdateFilename = lambda _: self.__makePing() - self._mpcApi.callbacks.onMpcClosed = lambda _: self.__client.stop(False) + self._mpcApi.callbacks.onMpcClosed = lambda _: self.reactor.callFromThread(self.__client.stop, (False),) self._mpcApi.callbacks.onFileStateChange = lambda _: self.__lockAsking() self._mpcApi.callbacks.onUpdatePlaystate = lambda _: self.__unlockAsking() self._mpcApi.callbacks.onGetCurrentPosition = lambda _: self.__onGetPosition() @@ -357,7 +357,7 @@ class MPCHCAPIPlayer(BasePlayer): self._mpcApi.askForVersion() if(not self.__versionUpdate.wait(0.1) or not self._mpcApi.version): self.__mpcError(getMessage("en", "mpc-version-insufficient-error").format(constants.MPC_MIN_VER)) - self.__client.stop(True) + self.reactor.callFromThread(self.__client.stop, (True),) def __testMpcReady(self): if(not self.__preventAsking.wait(10)): @@ -367,12 +367,12 @@ class MPCHCAPIPlayer(BasePlayer): try: self.__testMpcReady() self._mpcApi.callbacks.onUpdateFilename = lambda _: self.__handleUpdatedFilename() - self.__client.initPlayer(self) + self.reactor.callFromThread(self.__client.initPlayer, (self)) self.__handleUpdatedFilename() self.askForStatus() except Exception, err: self.__client.ui.showErrorMessage(err.message) - self.__client.stop() + self.reactor.callFromThread(self.__client.stop) def initPlayer(self, filePath): self.__dropIfNotSufficientVersion() @@ -455,7 +455,7 @@ class MPCHCAPIPlayer(BasePlayer): def __mpcError(self, err=""): self.__client.ui.showErrorMessage(err) - self.__client.stop() + self.reactor.callFromThread(self.__client.stop) def sendCustomCommand(self, cmd, val): self._mpcApi.sendRawCommand(cmd, val) From c925515249a7ea753c074cfc826fd89b5fe94a95 Mon Sep 17 00:00:00 2001 From: Uriziel Date: Sat, 8 Jun 2013 13:28:11 +0200 Subject: [PATCH 15/15] GUI starts properly when ConfigurationWindow wasn't executed --- syncplay/ui/ConfigurationGetter.py | 4 ++++ syncplay/ui/GuiConfiguration.py | 5 +++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/syncplay/ui/ConfigurationGetter.py b/syncplay/ui/ConfigurationGetter.py index eb2e33e..0a29827 100644 --- a/syncplay/ui/ConfigurationGetter.py +++ b/syncplay/ui/ConfigurationGetter.py @@ -8,6 +8,8 @@ from syncplay.players.playerFactory import PlayerFactory import codecs try: from syncplay.ui.GuiConfiguration import GuiConfiguration + from PySide import QtGui #@UnresolvedImport + from PySide.QtCore import Qt, QCoreApplication except ImportError: GuiConfiguration = None @@ -243,6 +245,8 @@ class ConfigurationGetter(object): self._loadRelativeConfiguration() if(not self._config['noGui']): from syncplay.vendor import qt4reactor + if QCoreApplication.instance() is None: + self.app = QtGui.QApplication(sys.argv) qt4reactor.install() return self._config diff --git a/syncplay/ui/GuiConfiguration.py b/syncplay/ui/GuiConfiguration.py index 4f4ee0e..304525f 100644 --- a/syncplay/ui/GuiConfiguration.py +++ b/syncplay/ui/GuiConfiguration.py @@ -1,5 +1,5 @@ from PySide import QtCore, QtGui -from PySide.QtCore import QSettings, Qt +from PySide.QtCore import QSettings, Qt, QCoreApplication from PySide.QtGui import QApplication, QLineEdit, QCursor, QLabel, QCheckBox, QDesktopServices, QIcon import os @@ -12,7 +12,8 @@ class GuiConfiguration: self._availablePlayerPaths = [] def run(self): - self.app = QtGui.QApplication(sys.argv) + if QCoreApplication.instance() is None: + self.app = QtGui.QApplication(sys.argv) dialog = ConfigDialog(self.config, self._availablePlayerPaths) dialog.exec_()