From b317a618e0421f465433041682fbc37f5631f2f3 Mon Sep 17 00:00:00 2001 From: Uriziel Date: Fri, 7 Jun 2013 21:16:45 +0200 Subject: [PATCH] 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"] +