diff --git a/.travis.yml b/.travis.yml index 208ad7f..d394743 100644 --- a/.travis.yml +++ b/.travis.yml @@ -16,6 +16,7 @@ before_install: install: - export PATH=/usr/local/bin:$PATH +- export QT_PREFERRED_BINDING="PySide" - pip install twisted appnope pyobjc py2app before_deploy: diff --git a/buildPy2app.py b/buildPy2app.py index 773c846..a68338b 100644 --- a/buildPy2app.py +++ b/buildPy2app.py @@ -5,17 +5,21 @@ Usage: python setup.py py2app """ -from setuptools import setup +from setuptools import setup, Command from glob import glob +import shutil import syncplay +cmdlist = {} + APP = ['syncplayClient.py'] DATA_FILES = [ ('resources', glob('resources/*.png') + glob('resources/*.rtf')), ] OPTIONS = { 'iconfile':'resources/icon.icns', - 'plist': { + 'includes': {'PySide.QtCore', 'PySide.QtUiTools', 'PySide.QtGui'}, + 'plist': { 'CFBundleName':'Syncplay', 'CFBundleShortVersionString':syncplay.version, 'CFBundleIdentifier':'pl.syncplay.Syncplay', @@ -23,10 +27,32 @@ OPTIONS = { } } +class Fix(Command): + user_options = [] + + def initialize_options(self): + pass + + def finalize_options(self): + pass + + def trim_packages(self): + """Remove big files in external dependencies that Syncplay doesn't need""" + + shutil.rmtree('dist/Syncplay.app/Contents/Frameworks/QtDesigner.framework', ignore_errors=True) + shutil.rmtree('dist/Syncplay.app/Contents/Frameworks/QtScript.framework', ignore_errors=True) + shutil.rmtree('dist/Syncplay.app/Contents/Frameworks/QtXml.framework', ignore_errors=True) + + def run(self): + self.trim_packages() + +cmdlist['fix'] = Fix + setup( app=APP, name='Syncplay', data_files=DATA_FILES, options={'py2app': OPTIONS}, setup_requires=['py2app'], + cmdclass=cmdlist ) diff --git a/buildPy2exe.py b/buildPy2exe.py old mode 100644 new mode 100755 diff --git a/resources/third-party-notices.rtf b/resources/third-party-notices.rtf index 129b285..492a24a 100644 --- a/resources/third-party-notices.rtf +++ b/resources/third-party-notices.rtf @@ -8,6 +8,22 @@ \f0\fs24 \cf0 Syncplay relies on the following softwares, in compliance with their licenses. \ \ +\b Qt.py +\b0 \ +\ +Copyright (c) 2016 Marcus Ottosson\ +\ +Permission is hereby granted, free of charge, to any person obtaining a copy\ +of this software and associated documentation files (the "Software"), to deal\ +in the Software without restriction, including without limitation the rights\ +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ +copies of the Software, and to permit persons to whom the Software is\ +furnished to do so, subject to the following conditions:\ +\ +The above copyright notice and this permission notice shall be included in all\ +copies or substantial portions of the Software.\ +\ + \b PySide\ \b0 \ @@ -97,8 +113,7 @@ The above copyright notice and this permission notice shall be\ included in all copies or substantial portions of the Software.\ \b \ -\pard\tx566\tx1133\tx1700\tx2267\tx2834\tx3401\tx3968\tx4535\tx5102\tx5669\tx6236\tx6803\pardirnatural\partightenfactor0 -\cf0 Qt 4\ +Qt 4\ \ \b0 Copyright (C) 2015 The Qt Company Ltd.\ @@ -119,8 +134,7 @@ along with this program. If not, see \ \pard\tx566\tx1133\tx1700\tx2267\tx2834\tx3401\tx3968\tx4535\tx5102\tx5669\tx6236\tx6803\pardirnatural\partightenfactor0 \b \cf0 \ -\pard\tx566\tx1133\tx1700\tx2267\tx2834\tx3401\tx3968\tx4535\tx5102\tx5669\tx6236\tx6803\pardirnatural\partightenfactor0 -\cf0 appnope\ +appnope\ \b0 \ Copyright (c) 2013, Min Ragan-Kelley\ @@ -143,11 +157,9 @@ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\ -\pard\tx566\tx1133\tx1700\tx2267\tx2834\tx3401\tx3968\tx4535\tx5102\tx5669\tx6236\tx6803\pardirnatural\partightenfactor0 -\b \cf0 \ -\pard\tx566\tx1133\tx1700\tx2267\tx2834\tx3401\tx3968\tx4535\tx5102\tx5669\tx6236\tx6803\pardirnatural\partightenfactor0 -\cf0 PyObjC\ +\b \ +PyObjC\ \b0 \ Copyright 2002, 2003 - Bill Bumgarner, Ronald Oussoren, Steve Majewski, Lele Gaifax, et.al.\ diff --git a/syncplay/messages_de.py b/syncplay/messages_de.py index a5a6b35..7af6bff 100644 --- a/syncplay/messages_de.py +++ b/syncplay/messages_de.py @@ -286,7 +286,7 @@ de = { #About dialog - TODO: Translate "about-menu-label": u"&About Syncplay", "about-dialog-title": u"About Syncplay", - "about-dialog-release": u"Version {} release {}", + "about-dialog-release": u"Version {} release {} on {}", "about-dialog-license-text" : u"Licensed under the Apache License, Version 2.0", "about-dialog-license-button": u"License", "about-dialog-dependencies": u"Dependencies", diff --git a/syncplay/messages_en.py b/syncplay/messages_en.py index 051f314..dd97e2b 100644 --- a/syncplay/messages_en.py +++ b/syncplay/messages_en.py @@ -286,7 +286,7 @@ en = { #About dialog "about-menu-label": u"&About Syncplay", "about-dialog-title": u"About Syncplay", - "about-dialog-release": u"Version {} release {}", + "about-dialog-release": u"Version {} release {} on {}", "about-dialog-license-text" : u"Licensed under the Apache License, Version 2.0", "about-dialog-license-button": u"License", "about-dialog-dependencies": u"Dependencies", diff --git a/syncplay/messages_ru.py b/syncplay/messages_ru.py index 7e071e2..e56f3de 100644 --- a/syncplay/messages_ru.py +++ b/syncplay/messages_ru.py @@ -288,7 +288,7 @@ ru = { #About dialog - TODO: Translate "about-menu-label": u"&About Syncplay", "about-dialog-title": u"About Syncplay", - "about-dialog-release": u"Version {} release {}", + "about-dialog-release": u"Version {} release {} on {}", "about-dialog-license-text" : u"Licensed under the Apache License, Version 2.0", "about-dialog-license-button": u"License", "about-dialog-dependencies": u"Dependencies", diff --git a/syncplay/ui/ConfigurationGetter.py b/syncplay/ui/ConfigurationGetter.py index ca8d7de..79e4f1e 100755 --- a/syncplay/ui/ConfigurationGetter.py +++ b/syncplay/ui/ConfigurationGetter.py @@ -403,15 +403,17 @@ class ConfigurationGetter(object): self._overrideConfigWithArgs(args) if not self._config['noGui']: try: - from PySide import QtGui # @UnresolvedImport - from PySide.QtCore import QCoreApplication - from syncplay.vendor import qt4reactor - if QCoreApplication.instance() is None: - self.app = QtGui.QApplication(sys.argv) - qt4reactor.install() - if sys.platform.startswith('darwin'): - import appnope - appnope.nope() + from syncplay.vendor.Qt import QtWidgets, IsPySide, IsPySide2 + from syncplay.vendor.Qt.QtCore import QCoreApplication + from syncplay.vendor import qt5reactor + if not (IsPySide2 or IsPySide): + raise ImportError + if QCoreApplication.instance() is None: + self.app = QtWidgets.QApplication(sys.argv) + qt5reactor.install() + if sys.platform.startswith('darwin'): + import appnope + appnope.nope() except ImportError: print getMessage("unable-import-gui-error") self._config['noGui'] = True diff --git a/syncplay/ui/GuiConfiguration.py b/syncplay/ui/GuiConfiguration.py old mode 100644 new mode 100755 index 3e712bf..2cda809 --- a/syncplay/ui/GuiConfiguration.py +++ b/syncplay/ui/GuiConfiguration.py @@ -1,6 +1,9 @@ -from PySide import QtCore, QtGui -from PySide.QtCore import QSettings, Qt, QCoreApplication, QUrl -from PySide.QtGui import QApplication, QLineEdit, QCursor, QLabel, QCheckBox, QDesktopServices, QIcon, QImage, QButtonGroup, QRadioButton, QDoubleSpinBox, QPlainTextEdit +from syncplay.vendor.Qt import QtCore, QtWidgets, QtGui, __binding__, IsPySide, IsPySide2 +from syncplay.vendor.Qt.QtCore import Qt, QSettings, QCoreApplication, QSize, QPoint, QUrl, QLine +from syncplay.vendor.Qt.QtWidgets import QApplication, QLineEdit, QLabel, QCheckBox, QButtonGroup, QRadioButton, QDoubleSpinBox, QPlainTextEdit +from syncplay.vendor.Qt.QtGui import QCursor, QIcon, QImage, QDesktopServices +if IsPySide2: + from PySide2.QtCore import QStandardPaths from syncplay.players.playerFactory import PlayerFactory from datetime import datetime from syncplay import utils @@ -19,7 +22,7 @@ class GuiConfiguration: def run(self): if QCoreApplication.instance() is None: - self.app = QtGui.QApplication(sys.argv) + self.app = QtWidgets.QApplication(sys.argv) dialog = ConfigDialog(self.config, self._availablePlayerPaths, self.error, self.defaultConfig) dialog.exec_() @@ -65,22 +68,22 @@ class GetPlayerIconThread(threading.Thread, QtCore.QObject): self.done.emit(iconpath, playerpath) -class ConfigDialog(QtGui.QDialog): +class ConfigDialog(QtWidgets.QDialog): pressedclosebutton = True moreToggling = False def automaticUpdatePromptCheck(self): if self.automaticupdatesCheckbox.checkState() == Qt.PartiallyChecked: - reply = QtGui.QMessageBox.question(self, "Syncplay", - getMessage("promptforupdate-label"), QtGui.QMessageBox.StandardButton.Yes | QtGui.QMessageBox.StandardButton.No) - if reply == QtGui.QMessageBox.Yes: + reply = QtWidgets.QMessageBox.question(self, "Syncplay", + getMessage("promptforupdate-label"), QtWidgets.QMessageBox.StandardButton.Yes | QtWidgets.QMessageBox.StandardButton.No) + if reply == QtWidgets.QMessageBox.Yes: self.automaticupdatesCheckbox.setChecked(True) else: self.automaticupdatesCheckbox.setChecked(False) def moreToggled(self): - self.setSizePolicy(QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Minimum) + self.setSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Minimum) if self.moreToggling == False: self.moreToggling = True @@ -220,10 +223,10 @@ class ConfigDialog(QtGui.QDialog): def languageChanged(self): setLanguage(unicode(self.languageCombobox.itemData(self.languageCombobox.currentIndex()))) - QtGui.QMessageBox.information(self, "Syncplay", getMessage("language-changed-msgbox-label")) + QtWidgets.QMessageBox.information(self, "Syncplay", getMessage("language-changed-msgbox-label")) def browsePlayerpath(self): - options = QtGui.QFileDialog.Options() + options = QtWidgets.QFileDialog.Options() defaultdirectory = "" browserfilter = "All files (*)" @@ -242,7 +245,7 @@ class ConfigDialog(QtGui.QDialog): elif "bsd" in sys.platform or sys.platform.startswith('dragonfly'): defaultdirectory = "/usr/local/bin" - fileName, filtr = QtGui.QFileDialog.getOpenFileName(self, + fileName, filtr = QtWidgets.QFileDialog.getOpenFileName(self, "Browse for media player executable", defaultdirectory, browserfilter, "", options) @@ -285,13 +288,13 @@ class ConfigDialog(QtGui.QDialog): def loadLastUpdateCheckDate(self): settings = QSettings("Syncplay", "Interface") settings.beginGroup("Update") - self.lastCheckedForUpdates = settings.value("lastChecked", None) + self.lastCheckedForUpdates = settings.value("lastCheckedQt", None) if self.lastCheckedForUpdates: if self.config["lastCheckedForUpdates"] is not None and self.config["lastCheckedForUpdates"] is not "": - if self.lastCheckedForUpdates > datetime.strptime(self.config["lastCheckedForUpdates"], "%Y-%m-%d %H:%M:%S.%f"): - self.config["lastCheckedForUpdates"] = str(self.lastCheckedForUpdates) + if self.lastCheckedForUpdates.toPython() > datetime.strptime(self.config["lastCheckedForUpdates"], "%Y-%m-%d %H:%M:%S.%f"): + self.config["lastCheckedForUpdates"] = self.lastCheckedForUpdates.toString("yyyy-MM-d HH:mm:ss.z") else: - self.config["lastCheckedForUpdates"] = str(self.lastCheckedForUpdates) + self.config["lastCheckedForUpdates"] = self.lastCheckedForUpdates.toString("yyyy-MM-d HH:mm:ss.z") def loadSavedPublicServerList(self): settings = QSettings("Syncplay", "Interface") @@ -346,23 +349,35 @@ class ConfigDialog(QtGui.QDialog): self.hostCombobox.setEditText(currentServer) def showErrorMessage(self, errorMessage): - QtGui.QMessageBox.warning(self, "Syncplay", errorMessage) + QtWidgets.QMessageBox.warning(self, "Syncplay", errorMessage) def browseMediapath(self): self.loadMediaBrowseSettings() - options = QtGui.QFileDialog.Options() - if self.config["mediaSearchDirectories"] and os.path.isdir(self.config["mediaSearchDirectories"][0]): - defaultdirectory = self.config["mediaSearchDirectories"][0] - elif os.path.isdir(self.mediadirectory): - defaultdirectory = self.mediadirectory - elif os.path.isdir(QDesktopServices.storageLocation(QDesktopServices.MoviesLocation)): - defaultdirectory = QDesktopServices.storageLocation(QDesktopServices.MoviesLocation) - elif os.path.isdir(QDesktopServices.storageLocation(QDesktopServices.HomeLocation)): - defaultdirectory = QDesktopServices.storageLocation(QDesktopServices.HomeLocation) - else: - defaultdirectory = "" + options = QtWidgets.QFileDialog.Options() + if IsPySide: + if self.config["mediaSearchDirectories"] and os.path.isdir(self.config["mediaSearchDirectories"][0]): + defaultdirectory = self.config["mediaSearchDirectories"][0] + elif os.path.isdir(self.mediadirectory): + defaultdirectory = self.mediadirectory + elif os.path.isdir(QDesktopServices.storageLocation(QDesktopServices.MoviesLocation)): + defaultdirectory = QDesktopServices.storageLocation(QDesktopServices.MoviesLocation) + elif os.path.isdir(QDesktopServices.storageLocation(QDesktopServices.HomeLocation)): + defaultdirectory = QDesktopServices.storageLocation(QDesktopServices.HomeLocation) + else: + defaultdirectory = "" + elif IsPySide2: + if self.config["mediaSearchDirectories"] and os.path.isdir(self.config["mediaSearchDirectories"][0]): + defaultdirectory = self.config["mediaSearchDirectories"][0] + elif os.path.isdir(self.mediadirectory): + defaultdirectory = self.mediadirectory + elif os.path.isdir(QStandardPaths.standardLocations(QStandardPaths.MoviesLocation)[0]): + defaultdirectory = QStandardPaths.standardLocations(QStandardPaths.MoviesLocation)[0] + elif os.path.isdir(QStandardPaths.standardLocations(QStandardPaths.HomeLocation)[0]): + defaultdirectory = QStandardPaths.standardLocations(QStandardPaths.HomeLocation)[0] + else: + defaultdirectory = "" browserfilter = "All files (*)" - fileName, filtr = QtGui.QFileDialog.getOpenFileName(self, "Browse for media files", defaultdirectory, + fileName, filtr = QtWidgets.QFileDialog.getOpenFileName(self, "Browse for media files", defaultdirectory, browserfilter, "", options) if fileName: self.mediapathTextbox.setText(os.path.normpath(fileName)) @@ -531,9 +546,9 @@ class ConfigDialog(QtGui.QDialog): self.mediaSearchDirectories = self.config["mediaSearchDirectories"] self.trustedDomains = self.config["trustedDomains"] - self.connectionSettingsGroup = QtGui.QGroupBox(getMessage("connection-group-title")) + self.connectionSettingsGroup = QtWidgets.QGroupBox(getMessage("connection-group-title")) self.loadSavedPublicServerList() - self.hostCombobox = QtGui.QComboBox(self) + self.hostCombobox = QtWidgets.QComboBox(self) if self.publicServers: i = 0 for publicServer in self.publicServers: @@ -548,7 +563,7 @@ class ConfigDialog(QtGui.QDialog): self.hostCombobox.setEditText(host) self.hostCombobox.setFixedWidth(165) self.hostLabel = QLabel(getMessage("host-label"), self) - self.findServerButton = QtGui.QPushButton(QtGui.QIcon(resourcespath + 'arrow_refresh.png'), getMessage("update-server-list-label")) + self.findServerButton = QtWidgets.QPushButton(QtGui.QIcon(resourcespath + 'arrow_refresh.png'), getMessage("update-server-list-label")) self.findServerButton.clicked.connect(self.updateServerList) self.findServerButton.setToolTip(getMessage("update-server-list-tooltip")) self.usernameTextbox = QLineEdit(self) @@ -575,7 +590,7 @@ class ConfigDialog(QtGui.QDialog): self.usernameTextbox.setMaxLength(constants.MAX_USERNAME_LENGTH) self.defaultroomTextbox.setMaxLength(constants.MAX_ROOM_NAME_LENGTH) - self.connectionSettingsLayout = QtGui.QGridLayout() + self.connectionSettingsLayout = QtWidgets.QGridLayout() self.connectionSettingsLayout.addWidget(self.hostLabel, 0, 0) self.connectionSettingsLayout.addWidget(self.hostCombobox, 0, 1) self.connectionSettingsLayout.addWidget(self.findServerButton, 0, 2) @@ -592,12 +607,12 @@ class ConfigDialog(QtGui.QDialog): self.playerargsTextbox.textEdited.connect(self.changedPlayerArgs) self.playerargsLabel = QLabel(getMessage("player-arguments-label"), self) - self.mediaplayerSettingsGroup = QtGui.QGroupBox(getMessage("media-setting-title")) + self.mediaplayerSettingsGroup = QtWidgets.QGroupBox(getMessage("media-setting-title")) self.executableiconImage = QtGui.QImage() self.executableiconLabel = QLabel(self) self.executableiconLabel.setMinimumWidth(16) self.executableiconLabel.setAlignment(Qt.AlignRight | Qt.AlignVCenter) - self.executablepathCombobox = QtGui.QComboBox(self) + self.executablepathCombobox = QtWidgets.QComboBox(self) self.executablepathCombobox.setEditable(True) self.executablepathCombobox.currentIndexChanged.connect(self.updateExecutableIcon) self.executablepathCombobox.setEditText(self._tryToFillPlayerPath(config['playerPath'], playerpaths)) @@ -605,11 +620,11 @@ class ConfigDialog(QtGui.QDialog): self.executablepathCombobox.editTextChanged.connect(self.updateExecutableIcon) self.executablepathLabel = QLabel(getMessage("executable-path-label"), self) - self.executablebrowseButton = QtGui.QPushButton(QtGui.QIcon(resourcespath + 'folder_explore.png'), getMessage("browse-label")) + self.executablebrowseButton = QtWidgets.QPushButton(QtGui.QIcon(resourcespath + 'folder_explore.png'), getMessage("browse-label")) self.executablebrowseButton.clicked.connect(self.browsePlayerpath) self.mediapathTextbox = QLineEdit(config['file'], self) self.mediapathLabel = QLabel(getMessage("media-path-label"), self) - self.mediabrowseButton = QtGui.QPushButton(QtGui.QIcon(resourcespath + 'folder_explore.png'), getMessage("browse-label")) + self.mediabrowseButton = QtWidgets.QPushButton(QtGui.QIcon(resourcespath + 'folder_explore.png'), getMessage("browse-label")) self.mediabrowseButton.clicked.connect(self.browseMediapath) self.executablepathLabel.setObjectName("executable-path") @@ -619,7 +634,7 @@ class ConfigDialog(QtGui.QDialog): self.playerargsLabel.setObjectName("player-arguments") self.playerargsTextbox.setObjectName(constants.LOAD_SAVE_MANUALLY_MARKER + "player-arguments") - self.mediaplayerSettingsLayout = QtGui.QGridLayout() + self.mediaplayerSettingsLayout = QtWidgets.QGridLayout() self.mediaplayerSettingsLayout.addWidget(self.executablepathLabel, 0, 0) self.mediaplayerSettingsLayout.addWidget(self.executableiconLabel, 0, 1) self.mediaplayerSettingsLayout.addWidget(self.executablepathCombobox, 0, 2) @@ -634,8 +649,8 @@ class ConfigDialog(QtGui.QDialog): self.showmoreCheckbox = QCheckBox(getMessage("more-title")) self.showmoreCheckbox.setObjectName(constants.LOAD_SAVE_MANUALLY_MARKER + "more") - self.basicOptionsFrame = QtGui.QFrame() - self.basicOptionsLayout = QtGui.QVBoxLayout() + self.basicOptionsFrame = QtWidgets.QFrame() + self.basicOptionsLayout = QtWidgets.QVBoxLayout() if error: self.errorLabel = QLabel(self) if error[:1] != constants.ERROR_MESSAGE_MARKER: @@ -651,19 +666,19 @@ class ConfigDialog(QtGui.QDialog): self.basicOptionsLayout.addWidget(self.connectionSettingsGroup) self.basicOptionsLayout.addSpacing(5) self.basicOptionsLayout.addWidget(self.mediaplayerSettingsGroup) - self.basicOptionsFrame.setSizePolicy(QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Minimum) + self.basicOptionsFrame.setSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Minimum) self.basicOptionsFrame.setLayout(self.basicOptionsLayout) self.stackedLayout.addWidget(self.basicOptionsFrame) def addReadinessTab(self): - self.readyFrame = QtGui.QFrame() - self.readyLayout = QtGui.QVBoxLayout() + self.readyFrame = QtWidgets.QFrame() + self.readyLayout = QtWidgets.QVBoxLayout() self.readyFrame.setLayout(self.readyLayout) # Initial state - self.readyInitialGroup = QtGui.QGroupBox(getMessage("readiness-title")) - self.readyInitialLayout = QtGui.QVBoxLayout() + self.readyInitialGroup = QtWidgets.QGroupBox(getMessage("readiness-title")) + self.readyInitialLayout = QtWidgets.QVBoxLayout() self.readyInitialGroup.setLayout(self.readyInitialLayout) self.readyatstartCheckbox = QCheckBox(getMessage("readyatstart-label")) self.readyatstartCheckbox.setObjectName("readyAtStart") @@ -671,8 +686,8 @@ class ConfigDialog(QtGui.QDialog): self.readyLayout.addWidget(self.readyInitialGroup) # Automatically pausing - self.readyPauseGroup = QtGui.QGroupBox(getMessage("pausing-title")) - self.readyPauseLayout = QtGui.QVBoxLayout() + self.readyPauseGroup = QtWidgets.QGroupBox(getMessage("pausing-title")) + self.readyPauseLayout = QtWidgets.QVBoxLayout() self.readyPauseGroup.setLayout(self.readyPauseLayout) self.pauseonleaveCheckbox = QCheckBox(getMessage("pauseonleave-label")) self.pauseonleaveCheckbox.setObjectName("pauseOnLeave") @@ -680,8 +695,8 @@ class ConfigDialog(QtGui.QDialog): self.readyLayout.addWidget(self.readyPauseGroup) # Unpausing - self.readyUnpauseGroup = QtGui.QGroupBox(getMessage("unpause-title")) - self.readyUnpauseLayout = QtGui.QVBoxLayout() + self.readyUnpauseGroup = QtWidgets.QGroupBox(getMessage("unpause-title")) + self.readyUnpauseLayout = QtWidgets.QVBoxLayout() self.readyUnpauseGroup.setLayout(self.readyUnpauseLayout) self.readyUnpauseButtonGroup = QButtonGroup() self.unpauseIfAlreadyReadyOption = QRadioButton(getMessage("unpause-ifalreadyready-option")) @@ -710,12 +725,12 @@ class ConfigDialog(QtGui.QDialog): self.stackedLayout.addWidget(self.readyFrame) def addMiscTab(self): - self.miscFrame = QtGui.QFrame() - self.miscLayout = QtGui.QVBoxLayout() + self.miscFrame = QtWidgets.QFrame() + self.miscLayout = QtWidgets.QVBoxLayout() self.miscFrame.setLayout(self.miscLayout) - self.coreSettingsGroup = QtGui.QGroupBox(getMessage("core-behaviour-title")) - self.coreSettingsLayout = QtGui.QGridLayout() + self.coreSettingsGroup = QtWidgets.QGroupBox(getMessage("core-behaviour-title")) + self.coreSettingsLayout = QtWidgets.QGridLayout() self.coreSettingsGroup.setLayout(self.coreSettingsLayout) ### Privacy: @@ -758,8 +773,8 @@ class ConfigDialog(QtGui.QDialog): ## Syncplay internals - self.internalSettingsGroup = QtGui.QGroupBox(getMessage("syncplay-internals-title")) - self.internalSettingsLayout = QtGui.QVBoxLayout() + self.internalSettingsGroup = QtWidgets.QGroupBox(getMessage("syncplay-internals-title")) + self.internalSettingsLayout = QtWidgets.QVBoxLayout() self.internalSettingsGroup.setLayout(self.internalSettingsLayout) self.alwaysshowCheckbox = QCheckBox(getMessage("forceguiprompt-label")) @@ -772,13 +787,13 @@ class ConfigDialog(QtGui.QDialog): ## Media path directories - self.mediasearchSettingsGroup = QtGui.QGroupBox(getMessage("syncplay-mediasearchdirectories-title")) - self.mediasearchSettingsLayout = QtGui.QVBoxLayout() + self.mediasearchSettingsGroup = QtWidgets.QGroupBox(getMessage("syncplay-mediasearchdirectories-title")) + self.mediasearchSettingsLayout = QtWidgets.QVBoxLayout() self.mediasearchSettingsGroup.setLayout(self.mediasearchSettingsLayout) self.mediasearchTextEdit = QPlainTextEdit(utils.getListAsMultilineString(self.mediaSearchDirectories)) self.mediasearchTextEdit.setObjectName(constants.LOAD_SAVE_MANUALLY_MARKER + "mediasearcdirectories-arguments") - self.mediasearchTextEdit.setLineWrapMode(QtGui.QPlainTextEdit.NoWrap) + self.mediasearchTextEdit.setLineWrapMode(QtWidgets.QPlainTextEdit.NoWrap) self.mediasearchSettingsLayout.addWidget(self.mediasearchTextEdit) self.mediasearchSettingsGroup.setMaximumHeight(self.mediasearchSettingsGroup.minimumSizeHint().height()) @@ -789,12 +804,12 @@ class ConfigDialog(QtGui.QDialog): self.stackedLayout.addWidget(self.miscFrame) def addSyncTab(self): - self.syncSettingsFrame = QtGui.QFrame() - self.syncSettingsLayout = QtGui.QVBoxLayout() + self.syncSettingsFrame = QtWidgets.QFrame() + self.syncSettingsLayout = QtWidgets.QVBoxLayout() - self.desyncSettingsGroup = QtGui.QGroupBox(getMessage("sync-otherslagging-title")) - self.desyncOptionsFrame = QtGui.QFrame() - self.desyncSettingsOptionsLayout = QtGui.QHBoxLayout() + self.desyncSettingsGroup = QtWidgets.QGroupBox(getMessage("sync-otherslagging-title")) + self.desyncOptionsFrame = QtWidgets.QFrame() + self.desyncSettingsOptionsLayout = QtWidgets.QHBoxLayout() config = self.config self.slowdownCheckbox = QCheckBox(getMessage("slowondesync-label")) @@ -804,9 +819,9 @@ class ConfigDialog(QtGui.QDialog): self.fastforwardCheckbox = QCheckBox(getMessage("fastforwardondesync-label")) self.fastforwardCheckbox.setObjectName("fastforwardOnDesync") - self.desyncSettingsLayout = QtGui.QGridLayout() + self.desyncSettingsLayout = QtWidgets.QGridLayout() self.desyncSettingsLayout.setSpacing(2) - self.desyncFrame = QtGui.QFrame() + self.desyncFrame = QtWidgets.QFrame() self.desyncFrame.setLineWidth(0) self.desyncFrame.setMidLineWidth(0) @@ -819,9 +834,9 @@ class ConfigDialog(QtGui.QDialog): self.desyncFrame.setLayout(self.syncSettingsLayout) - self.othersyncSettingsGroup = QtGui.QGroupBox(getMessage("sync-youlaggging-title")) - self.othersyncOptionsFrame = QtGui.QFrame() - self.othersyncSettingsLayout = QtGui.QGridLayout() + self.othersyncSettingsGroup = QtWidgets.QGroupBox(getMessage("sync-youlaggging-title")) + self.othersyncOptionsFrame = QtWidgets.QFrame() + self.othersyncSettingsLayout = QtWidgets.QGridLayout() self.dontslowwithmeCheckbox = QCheckBox(getMessage("dontslowdownwithme-label")) self.dontslowwithmeCheckbox.setObjectName("dontSlowDownWithMe") @@ -834,13 +849,13 @@ class ConfigDialog(QtGui.QDialog): ## Trusted domains - self.trusteddomainsSettingsGroup = QtGui.QGroupBox(getMessage("syncplay-trusteddomains-title")) - self.trusteddomainsSettingsLayout = QtGui.QVBoxLayout() + self.trusteddomainsSettingsGroup = QtWidgets.QGroupBox(getMessage("syncplay-trusteddomains-title")) + self.trusteddomainsSettingsLayout = QtWidgets.QVBoxLayout() self.trusteddomainsSettingsGroup.setLayout(self.trusteddomainsSettingsLayout) self.trusteddomainsTextEdit = QPlainTextEdit(utils.getListAsMultilineString(self.trustedDomains)) self.trusteddomainsTextEdit.setObjectName(constants.LOAD_SAVE_MANUALLY_MARKER + "trusteddomains-arguments") - self.trusteddomainsTextEdit.setLineWrapMode(QtGui.QPlainTextEdit.NoWrap) + self.trusteddomainsTextEdit.setLineWrapMode(QtWidgets.QPlainTextEdit.NoWrap) self.trusteddomainsSettingsLayout.addWidget(self.trusteddomainsTextEdit) self.trusteddomainsSettingsGroup.setMaximumHeight(self.trusteddomainsSettingsGroup.minimumSizeHint().height()) @@ -855,14 +870,14 @@ class ConfigDialog(QtGui.QDialog): self.stackedLayout.addWidget(self.syncSettingsFrame) def addMessageTab(self): - self.messageFrame = QtGui.QFrame() - self.messageLayout = QtGui.QVBoxLayout() + self.messageFrame = QtWidgets.QFrame() + self.messageLayout = QtWidgets.QVBoxLayout() self.messageLayout.setAlignment(Qt.AlignTop) # OSD - self.osdSettingsGroup = QtGui.QGroupBox(getMessage("messages-osd-title")) - self.osdSettingsLayout = QtGui.QVBoxLayout() - self.osdSettingsFrame = QtGui.QFrame() + self.osdSettingsGroup = QtWidgets.QGroupBox(getMessage("messages-osd-title")) + self.osdSettingsLayout = QtWidgets.QVBoxLayout() + self.osdSettingsFrame = QtWidgets.QFrame() self.showOSDCheckbox = QCheckBox(getMessage("showosd-label")) self.showOSDCheckbox.setObjectName("showOSD") @@ -902,23 +917,23 @@ class ConfigDialog(QtGui.QDialog): # Other display - self.displaySettingsGroup = QtGui.QGroupBox(getMessage("messages-other-title")) - self.displaySettingsLayout = QtGui.QVBoxLayout() + self.displaySettingsGroup = QtWidgets.QGroupBox(getMessage("messages-other-title")) + self.displaySettingsLayout = QtWidgets.QVBoxLayout() self.displaySettingsLayout.setAlignment(Qt.AlignTop & Qt.AlignLeft) - self.displaySettingsFrame = QtGui.QFrame() + self.displaySettingsFrame = QtWidgets.QFrame() self.showDurationNotificationCheckbox = QCheckBox(getMessage("showdurationnotification-label")) self.showDurationNotificationCheckbox.setObjectName("showDurationNotification") self.displaySettingsLayout.addWidget(self.showDurationNotificationCheckbox) - self.languageFrame = QtGui.QFrame() - self.languageLayout = QtGui.QHBoxLayout() + self.languageFrame = QtWidgets.QFrame() + self.languageLayout = QtWidgets.QHBoxLayout() self.languageLayout.setContentsMargins(0, 0, 0, 0) self.languageFrame.setLayout(self.languageLayout) - self.languageFrame.setSizePolicy(QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Minimum) + self.languageFrame.setSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Minimum) self.languageLayout.setAlignment(Qt.AlignTop & Qt.AlignLeft) self.languageLabel = QLabel(getMessage("language-label"), self) - self.languageCombobox = QtGui.QComboBox(self) + self.languageCombobox = QtWidgets.QComboBox(self) self.languageCombobox.addItem(getMessage("automatic-language").format(getMessage("LANGUAGE", getInitialLanguage()))) self.languages = getLanguages() @@ -948,22 +963,22 @@ class ConfigDialog(QtGui.QDialog): config = self.config resourcespath = self.resourcespath - self.bottomButtonFrame = QtGui.QFrame() - self.bottomButtonLayout = QtGui.QHBoxLayout() - self.helpButton = QtGui.QPushButton(QtGui.QIcon(self.resourcespath + u'help.png'), getMessage("help-label")) + self.bottomButtonFrame = QtWidgets.QFrame() + self.bottomButtonLayout = QtWidgets.QHBoxLayout() + self.helpButton = QtWidgets.QPushButton(QtGui.QIcon(self.resourcespath + u'help.png'), getMessage("help-label")) self.helpButton.setObjectName("help") self.helpButton.setMaximumSize(self.helpButton.sizeHint()) self.helpButton.pressed.connect(self.openHelp) - self.resetButton = QtGui.QPushButton(QtGui.QIcon(resourcespath + u'cog_delete.png'),getMessage("reset-label")) + self.resetButton = QtWidgets.QPushButton(QtGui.QIcon(resourcespath + u'cog_delete.png'),getMessage("reset-label")) self.resetButton.setMaximumSize(self.resetButton.sizeHint()) self.resetButton.setObjectName("reset") self.resetButton.pressed.connect(self.resetSettings) - self.runButton = QtGui.QPushButton(QtGui.QIcon(resourcespath + u'accept.png'), getMessage("run-label")) + self.runButton = QtWidgets.QPushButton(QtGui.QIcon(resourcespath + u'accept.png'), getMessage("run-label")) self.runButton.pressed.connect(self._runWithoutStoringConfig) self.runButton.setToolTip(getMessage("nostore-tooltip")) - self.storeAndRunButton = QtGui.QPushButton(QtGui.QIcon(resourcespath + u'accept.png'), getMessage("storeandrun-label")) + self.storeAndRunButton = QtWidgets.QPushButton(QtGui.QIcon(resourcespath + u'accept.png'), getMessage("storeandrun-label")) self.storeAndRunButton.pressed.connect(self._saveDataAndLeave) self.bottomButtonLayout.addWidget(self.helpButton) self.bottomButtonLayout.addWidget(self.resetButton) @@ -973,9 +988,9 @@ class ConfigDialog(QtGui.QDialog): self.bottomButtonLayout.setContentsMargins(5,0,5,0) self.mainLayout.addWidget(self.bottomButtonFrame, 1, 0, 1, 2) - self.bottomCheckboxFrame = QtGui.QFrame() + self.bottomCheckboxFrame = QtWidgets.QFrame() self.bottomCheckboxFrame.setContentsMargins(0,0,0,0) - self.bottomCheckboxLayout = QtGui.QGridLayout() + self.bottomCheckboxLayout = QtWidgets.QGridLayout() self.alwaysshowCheckbox = QCheckBox(getMessage("forceguiprompt-label")) self.enableplaylistsCheckbox = QCheckBox(getMessage("sharedplaylistenabled-label")) @@ -988,14 +1003,14 @@ class ConfigDialog(QtGui.QDialog): self.mainLayout.addWidget(self.bottomCheckboxFrame, 2, 0, 1, 2) def tabList(self): - self.tabListLayout = QtGui.QHBoxLayout() - self.tabListFrame = QtGui.QFrame() - self.tabListWidget = QtGui.QListWidget() - self.tabListWidget.addItem(QtGui.QListWidgetItem(QtGui.QIcon(self.resourcespath + u"house.png"),getMessage("basics-label"))) - self.tabListWidget.addItem(QtGui.QListWidgetItem(QtGui.QIcon(self.resourcespath + u"control_pause_blue.png"),getMessage("readiness-label"))) - self.tabListWidget.addItem(QtGui.QListWidgetItem(QtGui.QIcon(self.resourcespath + u"film_link.png"),getMessage("sync-label"))) - self.tabListWidget.addItem(QtGui.QListWidgetItem(QtGui.QIcon(self.resourcespath + u"comments.png"),getMessage("messages-label"))) - self.tabListWidget.addItem(QtGui.QListWidgetItem(QtGui.QIcon(self.resourcespath + u"cog.png"),getMessage("misc-label"))) + self.tabListLayout = QtWidgets.QHBoxLayout() + self.tabListFrame = QtWidgets.QFrame() + self.tabListWidget = QtWidgets.QListWidget() + self.tabListWidget.addItem(QtWidgets.QListWidgetItem(QtGui.QIcon(self.resourcespath + u"house.png"),getMessage("basics-label"))) + self.tabListWidget.addItem(QtWidgets.QListWidgetItem(QtGui.QIcon(self.resourcespath + u"control_pause_blue.png"),getMessage("readiness-label"))) + self.tabListWidget.addItem(QtWidgets.QListWidgetItem(QtGui.QIcon(self.resourcespath + u"film_link.png"),getMessage("sync-label"))) + self.tabListWidget.addItem(QtWidgets.QListWidgetItem(QtGui.QIcon(self.resourcespath + u"comments.png"),getMessage("messages-label"))) + self.tabListWidget.addItem(QtWidgets.QListWidgetItem(QtGui.QIcon(self.resourcespath + u"cog.png"),getMessage("misc-label"))) self.tabListLayout.addWidget(self.tabListWidget) self.tabListFrame.setLayout(self.tabListLayout) self.tabListFrame.setFixedWidth(self.tabListFrame.minimumSizeHint().width() + constants.TAB_PADDING) @@ -1035,6 +1050,7 @@ class ConfigDialog(QtGui.QDialog): settings = QSettings("Syncplay", "Interface") settings.beginGroup("Update") settings.setValue("lastChecked", None) + settings.setValue("lastCheckedQt", None) settings.endGroup() settings.beginGroup("PublicServerList") settings.setValue("publicServers", None) @@ -1090,6 +1106,7 @@ class ConfigDialog(QtGui.QDialog): self.config['clearGUIData'] = False self.clearGUIData() + self.QtWidgets = QtWidgets self.QtGui = QtGui self.error = error if sys.platform.startswith('win'): @@ -1105,11 +1122,11 @@ class ConfigDialog(QtGui.QDialog): self.setWindowFlags(self.windowFlags() & Qt.WindowCloseButtonHint & ~Qt.WindowContextHelpButtonHint) self.setWindowIcon(QtGui.QIcon(resourcespath + u"syncplay.png")) - self.stackedLayout = QtGui.QStackedLayout() - self.stackedFrame = QtGui.QFrame() + self.stackedLayout = QtWidgets.QStackedLayout() + self.stackedFrame = QtWidgets.QFrame() self.stackedFrame.setLayout(self.stackedLayout) - self.mainLayout = QtGui.QGridLayout() + self.mainLayout = QtWidgets.QGridLayout() self.mainLayout.setSpacing(0) self.mainLayout.setContentsMargins(0,0,0,0) diff --git a/syncplay/ui/__init__.py b/syncplay/ui/__init__.py index f0c4c02..74885ff 100644 --- a/syncplay/ui/__init__.py +++ b/syncplay/ui/__init__.py @@ -1,3 +1,9 @@ +import os +if "QT_PREFERRED_BINDING" not in os.environ: + os.environ["QT_PREFERRED_BINDING"] = os.pathsep.join( + ["PySide2", "PySide", "PyQt5", "PyQt4"] + ) + try: from syncplay.ui.gui import MainWindow as GraphicalUI except ImportError: diff --git a/syncplay/ui/gui.py b/syncplay/ui/gui.py index a5c7311..de9c823 100755 --- a/syncplay/ui/gui.py +++ b/syncplay/ui/gui.py @@ -1,5 +1,8 @@ -from PySide import QtGui -from PySide.QtCore import Qt, QSettings, QSize, QPoint, QUrl, QLine +from syncplay.vendor import Qt +from syncplay.vendor.Qt import QtWidgets, QtGui, __binding__, __binding_version__, IsPySide, IsPySide2 +from syncplay.vendor.Qt.QtCore import Qt, QSettings, QSize, QPoint, QUrl, QLine, QDateTime +if IsPySide2: + from PySide2.QtCore import QStandardPaths from syncplay import utils, constants, version, release_number from syncplay.messages import getMessage import sys @@ -11,16 +14,16 @@ import os from syncplay.utils import formatTime, sameFilename, sameFilesize, sameFileduration, RoomPasswordProvider, formatSize, isURL from functools import wraps from twisted.internet import task -if sys.platform.startswith('darwin'): +if sys.platform.startswith('darwin') and IsPySide: from Foundation import NSURL lastCheckedForUpdates = None -class UserlistItemDelegate(QtGui.QStyledItemDelegate): +class UserlistItemDelegate(QtWidgets.QStyledItemDelegate): def __init__(self): - QtGui.QStyledItemDelegate.__init__(self) + QtWidgets.QStyledItemDelegate.__init__(self) def sizeHint(self, option, index): - size = QtGui.QStyledItemDelegate.sizeHint(self, option, index) + size = QtWidgets.QStyledItemDelegate.sizeHint(self, option, index) if (index.column() == constants.USERLIST_GUI_USERNAME_COLUMN): size.setWidth(size.width() + constants.USERLIST_GUI_USERNAME_OFFSET) return size @@ -84,9 +87,9 @@ class UserlistItemDelegate(QtGui.QStyledItemDelegate): midY - 8, streamSwitchIconQPixmap.scaled(16, 16, Qt.KeepAspectRatio)) optionQStyleOptionViewItem.rect.setX(optionQStyleOptionViewItem.rect.x()+16) - QtGui.QStyledItemDelegate.paint(self, itemQPainter, optionQStyleOptionViewItem, indexQModelIndex) + QtWidgets.QStyledItemDelegate.paint(self, itemQPainter, optionQStyleOptionViewItem, indexQModelIndex) -class AboutDialog(QtGui.QDialog): +class AboutDialog(QtWidgets.QDialog): if sys.platform.startswith('win'): resourcespath = utils.findWorkingDir() + u"\\resources\\" else: @@ -99,31 +102,31 @@ class AboutDialog(QtGui.QDialog): else: self.setWindowTitle(getMessage("about-dialog-title")) if sys.platform.startswith('win'): - self.setWindowFlags(self.windowFlags() & ~Qt.WindowContextHelpButtonHint) - nameLabel = QtGui.QLabel("
Syncplay
") + self.setWindowFlags(self.windowFlags() & ~Qt.WindowContextHelpButtonHint) + nameLabel = QtWidgets.QLabel("
Syncplay
") nameLabel.setFont(QtGui.QFont("Helvetica", 20)) - linkLabel = QtGui.QLabel("
syncplay.pl
") + linkLabel = QtWidgets.QLabel("
syncplay.pl
") linkLabel.setOpenExternalLinks(True) - versionLabel = QtGui.QLabel("
" + getMessage("about-dialog-release").format(version, release_number) + "
") - licenseLabel = QtGui.QLabel("

Copyright © 2017 Syncplay

" + getMessage("about-dialog-license-text") + "

") + versionLabel = QtWidgets.QLabel("
" + getMessage("about-dialog-release").format(version, release_number, __binding__) + "
") + licenseLabel = QtWidgets.QLabel("

Copyright © 2017 Syncplay

" + getMessage("about-dialog-license-text") + "

") aboutIconPixmap = QtGui.QPixmap(self.resourcespath + u"syncplay.png") - aboutIconLabel = QtGui.QLabel() + aboutIconLabel = QtWidgets.QLabel() aboutIconLabel.setPixmap(aboutIconPixmap.scaled(120, 120, Qt.KeepAspectRatio)) - aboutLayout = QtGui.QGridLayout() + aboutLayout = QtWidgets.QGridLayout() aboutLayout.addWidget(aboutIconLabel, 0, 0, 4, 2) aboutLayout.addWidget(nameLabel, 0, 2, 1, 2) aboutLayout.addWidget(linkLabel, 1, 2, 1, 2) aboutLayout.addWidget(versionLabel, 2, 2, 1, 2) aboutLayout.addWidget(licenseLabel, 3, 2, 1, 2) - licenseButton = QtGui.QPushButton(getMessage("about-dialog-license-button")) + licenseButton = QtWidgets.QPushButton(getMessage("about-dialog-license-button")) licenseButton.setAutoDefault(False) licenseButton.clicked.connect(self.openLicense) aboutLayout.addWidget(licenseButton, 4, 2) - dependenciesButton = QtGui.QPushButton(getMessage("about-dialog-dependencies")) + dependenciesButton = QtWidgets.QPushButton(getMessage("about-dialog-dependencies")) dependenciesButton.setAutoDefault(False) dependenciesButton.clicked.connect(self.openDependencies) aboutLayout.addWidget(dependenciesButton, 4, 3) - aboutLayout.setSizeConstraint(QtGui.QLayout.SetFixedSize) + aboutLayout.setSizeConstraint(QtWidgets.QLayout.SetFixedSize) self.setSizeGripEnabled(False) self.setLayout(aboutLayout) @@ -139,7 +142,7 @@ class AboutDialog(QtGui.QDialog): else: QtGui.QDesktopServices.openUrl(QUrl("file://" + self.resourcespath + u"third-party-notices.rtf")) -class MainWindow(QtGui.QMainWindow): +class MainWindow(QtWidgets.QMainWindow): insertPosition = None playlistState = [] updatingPlaylist = False @@ -152,7 +155,7 @@ class MainWindow(QtGui.QMainWindow): MainWindow.insertPosition = newPosition self.playlist.forceUpdate() - class PlaylistItemDelegate(QtGui.QStyledItemDelegate): + class PlaylistItemDelegate(QtWidgets.QStyledItemDelegate): def paint(self, itemQPainter, optionQStyleOptionViewItem, indexQModelIndex): itemQPainter.save() currentQAbstractItemModel = indexQModelIndex.model() @@ -170,7 +173,7 @@ class MainWindow(QtGui.QMainWindow): currentlyplayingIconQPixmap.scaled(6, 16, Qt.KeepAspectRatio)) optionQStyleOptionViewItem.rect.setX(optionQStyleOptionViewItem.rect.x()+10) - QtGui.QStyledItemDelegate.paint(self, itemQPainter, optionQStyleOptionViewItem, indexQModelIndex) + QtWidgets.QStyledItemDelegate.paint(self, itemQPainter, optionQStyleOptionViewItem, indexQModelIndex) lineAbove = False lineBelow = False @@ -186,7 +189,7 @@ class MainWindow(QtGui.QMainWindow): itemQPainter.drawLine(line) itemQPainter.restore() - class PlaylistGroupBox(QtGui.QGroupBox): + class PlaylistGroupBox(QtWidgets.QGroupBox): def dragEnterEvent(self, event): data = event.mimeData() @@ -216,10 +219,10 @@ class MainWindow(QtGui.QMainWindow): indexRow = window.playlist.count() if window.clearedPlaylistNote else 0 for url in urls[::-1]: - if sys.platform.startswith('darwin'): + if sys.platform.startswith('darwin') and IsPySide: dropfilepath = os.path.abspath(NSURL.URLWithString_(str(url.toString())).filePathURL().path()) else: - dropfilepath = os.path.abspath(unicode(url.toLocalFile())) + dropfilepath = os.path.abspath(unicode(url.toLocalFile())) if os.path.isfile(dropfilepath): window.addFileToPlaylist(dropfilepath, indexRow) elif os.path.isdir(dropfilepath): @@ -227,7 +230,7 @@ class MainWindow(QtGui.QMainWindow): else: super(MainWindow.PlaylistWidget, self).dropEvent(event) - class PlaylistWidget(QtGui.QListWidget): + class PlaylistWidget(QtWidgets.QListWidget): selfWindow = None playlistIndexFilename = None @@ -321,10 +324,10 @@ class MainWindow(QtGui.QMainWindow): if indexRow == -1: indexRow = window.playlist.count() for url in urls[::-1]: - if sys.platform.startswith('darwin'): + if sys.platform.startswith('darwin') and IsPySide: dropfilepath = os.path.abspath(NSURL.URLWithString_(str(url.toString())).filePathURL().path()) else: - dropfilepath = os.path.abspath(unicode(url.toLocalFile())) + dropfilepath = os.path.abspath(unicode(url.toLocalFile())) if os.path.isfile(dropfilepath): window.addFileToPlaylist(dropfilepath, indexRow) elif os.path.isdir(dropfilepath): @@ -334,17 +337,17 @@ class MainWindow(QtGui.QMainWindow): - class topSplitter(QtGui.QSplitter): + class topSplitter(QtWidgets.QSplitter): def createHandle(self): return self.topSplitterHandle(self.orientation(), self) - class topSplitterHandle(QtGui.QSplitterHandle): + class topSplitterHandle(QtWidgets.QSplitterHandle): def mouseReleaseEvent(self, event): - QtGui.QSplitterHandle.mouseReleaseEvent(self, event) + QtWidgets.QSplitterHandle.mouseReleaseEvent(self, event) self.parent().parent().parent().updateListGeometry() def mouseMoveEvent(self, event): - QtGui.QSplitterHandle.mouseMoveEvent(self, event) + QtWidgets.QSplitterHandle.mouseMoveEvent(self, event) self.parent().parent().parent().updateListGeometry() def needsClient(f): # @NoSelf @@ -470,11 +473,11 @@ class MainWindow(QtGui.QMainWindow): if isControlledRoom: if room == currentUser.room and currentUser.isController(): - roomitem.setIcon(QtGui.QIcon(self.resourcespath + 'lock_open.png')) + roomitem.setIcon(QtGui.QPixmap(self.resourcespath + 'lock_open.png')) else: - roomitem.setIcon(QtGui.QIcon(self.resourcespath + 'lock.png')) + roomitem.setIcon(QtGui.QPixmap(self.resourcespath + 'lock.png')) else: - roomitem.setIcon(QtGui.QIcon(self.resourcespath + 'chevrons_right.png')) + roomitem.setIcon(QtGui.QPixmap(self.resourcespath + 'chevrons_right.png')) for user in rooms[room]: useritem = QtGui.QStandardItem(user.username) @@ -572,7 +575,7 @@ class MainWindow(QtGui.QMainWindow): item = self.playlist.selectedIndexes()[0] else: item = None - menu = QtGui.QMenu() + menu = QtWidgets.QMenu() if item: firstFile = item.sibling(item.row(), 0).data() @@ -615,7 +618,7 @@ class MainWindow(QtGui.QMainWindow): else: return - menu = QtGui.QMenu() + menu = QtWidgets.QMenu() username = item.sibling(item.row(), 0).data() if username == self._syncplayClient.userlist.currentUser.username: shortUsername = getMessage("item-is-yours-indicator") @@ -663,15 +666,24 @@ class MainWindow(QtGui.QMainWindow): self.listTreeView.setFirstColumnSpanned(roomtocheck, self.listTreeView.rootIndex(), True) roomtocheck += 1 self.listTreeView.header().setStretchLastSection(False) - self.listTreeView.header().setResizeMode(0, QtGui.QHeaderView.ResizeToContents) - self.listTreeView.header().setResizeMode(1, QtGui.QHeaderView.ResizeToContents) - self.listTreeView.header().setResizeMode(2, QtGui.QHeaderView.ResizeToContents) - self.listTreeView.header().setResizeMode(3, QtGui.QHeaderView.ResizeToContents) + if IsPySide2: + self.listTreeView.header().setSectionResizeMode(0, QtWidgets.QHeaderView.ResizeToContents) + self.listTreeView.header().setSectionResizeMode(1, QtWidgets.QHeaderView.ResizeToContents) + self.listTreeView.header().setSectionResizeMode(2, QtWidgets.QHeaderView.ResizeToContents) + self.listTreeView.header().setSectionResizeMode(3, QtWidgets.QHeaderView.ResizeToContents) + if IsPySide: + self.listTreeView.header().setResizeMode(0, QtWidgets.QHeaderView.ResizeToContents) + self.listTreeView.header().setResizeMode(1, QtWidgets.QHeaderView.ResizeToContents) + self.listTreeView.header().setResizeMode(2, QtWidgets.QHeaderView.ResizeToContents) + self.listTreeView.header().setResizeMode(3, QtWidgets.QHeaderView.ResizeToContents) NarrowTabsWidth = self.listTreeView.header().sectionSize(0)+self.listTreeView.header().sectionSize(1)+self.listTreeView.header().sectionSize(2) if self.listTreeView.header().width() < (NarrowTabsWidth+self.listTreeView.header().sectionSize(3)): self.listTreeView.header().resizeSection(3,self.listTreeView.header().width()-NarrowTabsWidth) else: - self.listTreeView.header().setResizeMode(3, QtGui.QHeaderView.Stretch) + if IsPySide2: + self.listTreeView.header().setSectionResizeMode(3, QtWidgets.QHeaderView.Stretch) + if IsPySide: + self.listTreeView.header().setResizeMode(3, QtWidgets.QHeaderView.Stretch) self.listTreeView.expandAll() except: pass @@ -744,7 +756,7 @@ class MainWindow(QtGui.QMainWindow): def showErrorMessage(self, message, criticalerror=False): message = unicode(message) if criticalerror: - QtGui.QMessageBox.critical(self, "Syncplay", message) + QtWidgets.QMessageBox.critical(self, "Syncplay", message) message = message.replace(u"&", u"&").replace(u'"', u""").replace(u"<", u"<").replace(u">", u">") message = message.replace(u"\n", u"
") message = u"".format(constants.STYLE_ERRORNOTIFICATION) + message + u"" @@ -765,8 +777,8 @@ class MainWindow(QtGui.QMainWindow): self._syncplayClient.sendRoom() def seekPositionDialog(self): - seekTime, ok = QtGui.QInputDialog.getText(self, getMessage("seektime-menu-label"), - getMessage("seektime-msgbox-label"), QtGui.QLineEdit.Normal, + seekTime, ok = QtWidgets.QInputDialog.getText(self, getMessage("seektime-menu-label"), + getMessage("seektime-msgbox-label"), QtWidgets.QLineEdit.Normal, u"0:00") if ok and seekTime != '': self.seekPosition(seekTime) @@ -829,16 +841,28 @@ class MainWindow(QtGui.QMainWindow): settings.endGroup() def getInitialMediaDirectory(self, includeUserSpecifiedDirectories=True): - if self.config["mediaSearchDirectories"] and os.path.isdir(self.config["mediaSearchDirectories"][0]) and includeUserSpecifiedDirectories: - defaultdirectory = self.config["mediaSearchDirectories"][0] - elif includeUserSpecifiedDirectories and os.path.isdir(self.mediadirectory): - defaultdirectory = self.mediadirectory - elif os.path.isdir(QtGui.QDesktopServices.storageLocation(QtGui.QDesktopServices.MoviesLocation)): - defaultdirectory = QtGui.QDesktopServices.storageLocation(QtGui.QDesktopServices.MoviesLocation) - elif os.path.isdir(QtGui.QDesktopServices.storageLocation(QtGui.QDesktopServices.HomeLocation)): - defaultdirectory = QtGui.QDesktopServices.storageLocation(QtGui.QDesktopServices.HomeLocation) - else: - defaultdirectory = "" + if IsPySide: + if self.config["mediaSearchDirectories"] and os.path.isdir(self.config["mediaSearchDirectories"][0]) and includeUserSpecifiedDirectories: + defaultdirectory = self.config["mediaSearchDirectories"][0] + elif includeUserSpecifiedDirectories and os.path.isdir(self.mediadirectory): + defaultdirectory = self.mediadirectory + elif os.path.isdir(QtGui.QDesktopServices.storageLocation(QtGui.QDesktopServices.MoviesLocation)): + defaultdirectory = QtGui.QDesktopServices.storageLocation(QtGui.QDesktopServices.MoviesLocation) + elif os.path.isdir(QtGui.QDesktopServices.storageLocation(QtGui.QDesktopServices.HomeLocation)): + defaultdirectory = QtGui.QDesktopServices.storageLocation(QtGui.QDesktopServices.HomeLocation) + else: + defaultdirectory = "" + elif IsPySide2: + if self.config["mediaSearchDirectories"] and os.path.isdir(self.config["mediaSearchDirectories"][0]) and includeUserSpecifiedDirectories: + defaultdirectory = self.config["mediaSearchDirectories"][0] + elif includeUserSpecifiedDirectories and os.path.isdir(self.mediadirectory): + defaultdirectory = self.mediadirectory + elif os.path.isdir(QStandardPaths.standardLocations(QStandardPaths.MoviesLocation)[0]): + defaultdirectory = QStandardPaths.standardLocations(QStandardPaths.MoviesLocation)[0] + elif os.path.isdir(QStandardPaths.standardLocations(QStandardPaths.HomeLocation)[0]): + defaultdirectory = QStandardPaths.standardLocations(QStandardPaths.HomeLocation)[0] + else: + defaultdirectory = "" return defaultdirectory @needsClient @@ -848,10 +872,10 @@ class MainWindow(QtGui.QMainWindow): return self.loadMediaBrowseSettings() - if sys.platform.startswith('darwin'): - options = QtGui.QFileDialog.Options(QtGui.QFileDialog.DontUseNativeDialog) + if sys.platform.startswith('darwin') and IsPySide: + options = QtWidgets.QFileDialog.Options(QtWidgets.QFileDialog.DontUseNativeDialog) else: - options = QtGui.QFileDialog.Options() + options = QtWidgets.QFileDialog.Options() self.mediadirectory = "" currentdirectory = os.path.dirname(self._syncplayClient.userlist.currentUser.file["path"]) if self._syncplayClient.userlist.currentUser.file else None if currentdirectory and os.path.isdir(currentdirectory): @@ -859,7 +883,7 @@ class MainWindow(QtGui.QMainWindow): else: defaultdirectory = self.getInitialMediaDirectory() browserfilter = "All files (*)" - fileName, filtr = QtGui.QFileDialog.getOpenFileName(self, getMessage("browseformedia-label"), defaultdirectory, + fileName, filtr = QtWidgets.QFileDialog.getOpenFileName(self, getMessage("browseformedia-label"), defaultdirectory, browserfilter, "", options) if fileName: if sys.platform.startswith('win'): @@ -876,10 +900,10 @@ class MainWindow(QtGui.QMainWindow): return self.loadMediaBrowseSettings() - if sys.platform.startswith('darwin'): - options = QtGui.QFileDialog.Options(QtGui.QFileDialog.DontUseNativeDialog) + if sys.platform.startswith('darwin') and IsPySide: + options = QtWidgets.QFileDialog.Options(QtWidgets.QFileDialog.DontUseNativeDialog) else: - options = QtGui.QFileDialog.Options() + options = QtWidgets.QFileDialog.Options() self.mediadirectory = "" currentdirectory = os.path.dirname(self._syncplayClient.userlist.currentUser.file["path"]) if self._syncplayClient.userlist.currentUser.file else None if currentdirectory and os.path.isdir(currentdirectory): @@ -887,7 +911,7 @@ class MainWindow(QtGui.QMainWindow): else: defaultdirectory = self.getInitialMediaDirectory() browserfilter = "All files (*)" - fileNames, filtr = QtGui.QFileDialog.getOpenFileNames(self, getMessage("browseformedia-label"), defaultdirectory, + fileNames, filtr = QtWidgets.QFileDialog.getOpenFileNames(self, getMessage("browseformedia-label"), defaultdirectory, browserfilter, "", options) self.updatingPlaylist = True if fileNames: @@ -903,17 +927,17 @@ class MainWindow(QtGui.QMainWindow): @needsClient def OpenAddURIsToPlaylistDialog(self): - URIsDialog = QtGui.QDialog() + URIsDialog = QtWidgets.QDialog() URIsDialog.setWindowTitle(getMessage("adduris-msgbox-label")) - URIsLayout = QtGui.QGridLayout() - URIsLabel = QtGui.QLabel(getMessage("adduris-msgbox-label")) + URIsLayout = QtWidgets.QGridLayout() + URIsLabel = QtWidgets.QLabel(getMessage("adduris-msgbox-label")) URIsLayout.addWidget(URIsLabel, 0, 0, 1, 1) - URIsTextbox = QtGui.QPlainTextEdit() - URIsTextbox.setLineWrapMode(QtGui.QPlainTextEdit.NoWrap) + URIsTextbox = QtWidgets.QPlainTextEdit() + URIsTextbox.setLineWrapMode(QtWidgets.QPlainTextEdit.NoWrap) URIsLayout.addWidget(URIsTextbox, 1, 0, 1, 1) - URIsButtonBox = QtGui.QDialogButtonBox() + URIsButtonBox = QtWidgets.QDialogButtonBox() URIsButtonBox.setOrientation(Qt.Horizontal) - URIsButtonBox.setStandardButtons(QtGui.QDialogButtonBox.Ok|QtGui.QDialogButtonBox.Cancel) + URIsButtonBox.setStandardButtons(QtWidgets.QDialogButtonBox.Ok|QtWidgets.QDialogButtonBox.Cancel) URIsButtonBox.accepted.connect(URIsDialog.accept) URIsButtonBox.rejected.connect(URIsDialog.reject) URIsLayout.addWidget(URIsButtonBox, 2, 0, 1, 1) @@ -921,7 +945,7 @@ class MainWindow(QtGui.QMainWindow): URIsDialog.setModal(True) URIsDialog.show() result = URIsDialog.exec_() - if result == QtGui.QDialog.Accepted: + if result == QtWidgets.QDialog.Accepted: URIsToAdd = utils.convertMultilineStringToList(URIsTextbox.toPlainText()) self.updatingPlaylist = True for URI in URIsToAdd: @@ -939,17 +963,17 @@ class MainWindow(QtGui.QMainWindow): @needsClient def openEditPlaylistDialog(self): oldPlaylist = utils.getListAsMultilineString(self.getPlaylistState()) - editPlaylistDialog = QtGui.QDialog() + editPlaylistDialog = QtWidgets.QDialog() editPlaylistDialog.setWindowTitle(getMessage("editplaylist-msgbox-label")) - editPlaylistLayout = QtGui.QGridLayout() - editPlaylistLabel = QtGui.QLabel(getMessage("editplaylist-msgbox-label")) + editPlaylistLayout = QtWidgets.QGridLayout() + editPlaylistLabel = QtWidgets.QLabel(getMessage("editplaylist-msgbox-label")) editPlaylistLayout.addWidget(editPlaylistLabel, 0, 0, 1, 1) - editPlaylistTextbox = QtGui.QPlainTextEdit(oldPlaylist) - editPlaylistTextbox.setLineWrapMode(QtGui.QPlainTextEdit.NoWrap) + editPlaylistTextbox = QtWidgets.QPlainTextEdit(oldPlaylist) + editPlaylistTextbox.setLineWrapMode(QtWidgets.QPlainTextEdit.NoWrap) editPlaylistLayout.addWidget(editPlaylistTextbox, 1, 0, 1, 1) - editPlaylistButtonBox = QtGui.QDialogButtonBox() + editPlaylistButtonBox = QtWidgets.QDialogButtonBox() editPlaylistButtonBox.setOrientation(Qt.Horizontal) - editPlaylistButtonBox.setStandardButtons(QtGui.QDialogButtonBox.Ok|QtGui.QDialogButtonBox.Cancel) + editPlaylistButtonBox.setStandardButtons(QtWidgets.QDialogButtonBox.Ok|QtWidgets.QDialogButtonBox.Cancel) editPlaylistButtonBox.accepted.connect(editPlaylistDialog.accept) editPlaylistButtonBox.rejected.connect(editPlaylistDialog.reject) editPlaylistLayout.addWidget(editPlaylistButtonBox, 2, 0, 1, 1) @@ -959,7 +983,7 @@ class MainWindow(QtGui.QMainWindow): editPlaylistDialog.setMinimumHeight(500) editPlaylistDialog.show() result = editPlaylistDialog.exec_() - if result == QtGui.QDialog.Accepted: + if result == QtWidgets.QDialog.Accepted: newPlaylist = utils.convertMultilineStringToList(editPlaylistTextbox.toPlainText()) if newPlaylist <> self.playlistState and self._syncplayClient and not self.updatingPlaylist: self.setPlaylist(newPlaylist) @@ -968,46 +992,46 @@ class MainWindow(QtGui.QMainWindow): @needsClient def openSetMediaDirectoriesDialog(self): - MediaDirectoriesDialog = QtGui.QDialog() + MediaDirectoriesDialog = QtWidgets.QDialog() MediaDirectoriesDialog.setWindowTitle(getMessage("syncplay-mediasearchdirectories-title")) # TODO: Move to messages_*.py - MediaDirectoriesLayout = QtGui.QGridLayout() - MediaDirectoriesLabel = QtGui.QLabel(getMessage("syncplay-mediasearchdirectories-title")) + MediaDirectoriesLayout = QtWidgets.QGridLayout() + MediaDirectoriesLabel = QtWidgets.QLabel(getMessage("syncplay-mediasearchdirectories-title")) MediaDirectoriesLayout.addWidget(MediaDirectoriesLabel, 0, 0, 1, 2) - MediaDirectoriesTextbox = QtGui.QPlainTextEdit() - MediaDirectoriesTextbox.setLineWrapMode(QtGui.QPlainTextEdit.NoWrap) + MediaDirectoriesTextbox = QtWidgets.QPlainTextEdit() + MediaDirectoriesTextbox.setLineWrapMode(QtWidgets.QPlainTextEdit.NoWrap) MediaDirectoriesTextbox.setPlainText(utils.getListAsMultilineString(self.config["mediaSearchDirectories"])) MediaDirectoriesLayout.addWidget(MediaDirectoriesTextbox, 1, 0, 1, 1) - MediaDirectoriesButtonBox = QtGui.QDialogButtonBox() + MediaDirectoriesButtonBox = QtWidgets.QDialogButtonBox() MediaDirectoriesButtonBox.setOrientation(Qt.Horizontal) - MediaDirectoriesButtonBox.setStandardButtons(QtGui.QDialogButtonBox.Ok|QtGui.QDialogButtonBox.Cancel) + MediaDirectoriesButtonBox.setStandardButtons(QtWidgets.QDialogButtonBox.Ok|QtWidgets.QDialogButtonBox.Cancel) MediaDirectoriesButtonBox.accepted.connect(MediaDirectoriesDialog.accept) MediaDirectoriesButtonBox.rejected.connect(MediaDirectoriesDialog.reject) MediaDirectoriesLayout.addWidget(MediaDirectoriesButtonBox, 2, 0, 1, 1) - MediaDirectoriesAddFolderButton = QtGui.QPushButton(getMessage("addfolder-label")) + MediaDirectoriesAddFolderButton = QtWidgets.QPushButton(getMessage("addfolder-label")) MediaDirectoriesAddFolderButton.pressed.connect(lambda: self.openAddMediaDirectoryDialog(MediaDirectoriesTextbox, MediaDirectoriesDialog)) MediaDirectoriesLayout.addWidget(MediaDirectoriesAddFolderButton, 1, 1, 1, 1, Qt.AlignTop) MediaDirectoriesDialog.setLayout(MediaDirectoriesLayout) MediaDirectoriesDialog.setModal(True) MediaDirectoriesDialog.show() result = MediaDirectoriesDialog.exec_() - if result == QtGui.QDialog.Accepted: + if result == QtWidgets.QDialog.Accepted: newMediaDirectories = utils.convertMultilineStringToList(MediaDirectoriesTextbox.toPlainText()) self._syncplayClient.fileSwitch.changeMediaDirectories(newMediaDirectories) @needsClient def openSetTrustedDomainsDialog(self): - TrustedDomainsDialog = QtGui.QDialog() + TrustedDomainsDialog = QtWidgets.QDialog() TrustedDomainsDialog.setWindowTitle(getMessage("syncplay-trusteddomains-title")) - TrustedDomainsLayout = QtGui.QGridLayout() - TrustedDomainsLabel = QtGui.QLabel(getMessage("trusteddomains-msgbox-label")) + TrustedDomainsLayout = QtWidgets.QGridLayout() + TrustedDomainsLabel = QtWidgets.QLabel(getMessage("trusteddomains-msgbox-label")) TrustedDomainsLayout.addWidget(TrustedDomainsLabel, 0, 0, 1, 1) - TrustedDomainsTextbox = QtGui.QPlainTextEdit() - TrustedDomainsTextbox.setLineWrapMode(QtGui.QPlainTextEdit.NoWrap) + TrustedDomainsTextbox = QtWidgets.QPlainTextEdit() + TrustedDomainsTextbox.setLineWrapMode(QtWidgets.QPlainTextEdit.NoWrap) TrustedDomainsTextbox.setPlainText(utils.getListAsMultilineString(self.config["trustedDomains"])) TrustedDomainsLayout.addWidget(TrustedDomainsTextbox, 1, 0, 1, 1) - TrustedDomainsButtonBox = QtGui.QDialogButtonBox() + TrustedDomainsButtonBox = QtWidgets.QDialogButtonBox() TrustedDomainsButtonBox.setOrientation(Qt.Horizontal) - TrustedDomainsButtonBox.setStandardButtons(QtGui.QDialogButtonBox.Ok|QtGui.QDialogButtonBox.Cancel) + TrustedDomainsButtonBox.setStandardButtons(QtWidgets.QDialogButtonBox.Ok|QtWidgets.QDialogButtonBox.Cancel) TrustedDomainsButtonBox.accepted.connect(TrustedDomainsDialog.accept) TrustedDomainsButtonBox.rejected.connect(TrustedDomainsDialog.reject) TrustedDomainsLayout.addWidget(TrustedDomainsButtonBox, 2, 0, 1, 1) @@ -1015,7 +1039,7 @@ class MainWindow(QtGui.QMainWindow): TrustedDomainsDialog.setModal(True) TrustedDomainsDialog.show() result = TrustedDomainsDialog.exec_() - if result == QtGui.QDialog.Accepted: + if result == QtWidgets.QDialog.Accepted: newTrustedDomains = utils.convertMultilineStringToList(TrustedDomainsTextbox.toPlainText()) self._syncplayClient.setTrustedDomains(newTrustedDomains) @needsClient @@ -1027,11 +1051,12 @@ class MainWindow(QtGui.QMainWindow): @needsClient def openAddMediaDirectoryDialog(self, MediaDirectoriesTextbox, MediaDirectoriesDialog): - if sys.platform.startswith('darwin'): - options = QtGui.QFileDialog.Options(QtGui.QFileDialog.ShowDirsOnly | QtGui.QFileDialog.DontUseNativeDialog) + if sys.platform.startswith('darwin') and IsPySide: + options = QtWidgets.QFileDialog.Options(QtWidgets.QFileDialog.ShowDirsOnly | QtWidgets.QFileDialog.DontUseNativeDialog) else: - options = QtGui.QFileDialog.Options(QtGui.QFileDialog.ShowDirsOnly) - folderName = unicode(QtGui.QFileDialog.getExistingDirectory(self,None,self.getInitialMediaDirectory(includeUserSpecifiedDirectories=False),options)) + options = QtWidgets.QFileDialog.Options(QtWidgets.QFileDialog.ShowDirsOnly) + folderName = unicode(QtWidgets.QFileDialog.getExistingDirectory(self,None,self.getInitialMediaDirectory(includeUserSpecifiedDirectories=False),options)) + if folderName: existingMediaDirs = MediaDirectoriesTextbox.toPlainText() if existingMediaDirs == "": @@ -1044,16 +1069,16 @@ class MainWindow(QtGui.QMainWindow): @needsClient def promptForStreamURL(self): - streamURL, ok = QtGui.QInputDialog.getText(self, getMessage("promptforstreamurl-msgbox-label"), - getMessage("promptforstreamurlinfo-msgbox-label"), QtGui.QLineEdit.Normal, + streamURL, ok = QtWidgets.QInputDialog.getText(self, getMessage("promptforstreamurl-msgbox-label"), + getMessage("promptforstreamurlinfo-msgbox-label"), QtWidgets.QLineEdit.Normal, "") if ok and streamURL != '': self._syncplayClient._player.openFile(streamURL) @needsClient def createControlledRoom(self): - controlroom, ok = QtGui.QInputDialog.getText(self, getMessage("createcontrolledroom-msgbox-label"), - getMessage("controlledroominfo-msgbox-label"), QtGui.QLineEdit.Normal, + controlroom, ok = QtWidgets.QInputDialog.getText(self, getMessage("createcontrolledroom-msgbox-label"), + getMessage("controlledroominfo-msgbox-label"), QtWidgets.QLineEdit.Normal, utils.stripRoomName(self._syncplayClient.getRoom())) if ok and controlroom != '': self._syncplayClient.createControlledRoom(controlroom) @@ -1062,7 +1087,7 @@ class MainWindow(QtGui.QMainWindow): def identifyAsController(self): msgboxtitle = getMessage("identifyascontroller-msgbox-label") msgboxtext = getMessage("identifyinfo-msgbox-label") - controlpassword, ok = QtGui.QInputDialog.getText(self, msgboxtitle, msgboxtext, QtGui.QLineEdit.Normal, "") + controlpassword, ok = QtWidgets.QInputDialog.getText(self, msgboxtitle, msgboxtext, QtWidgets.QLineEdit.Normal, "") if ok and controlpassword != '': self._syncplayClient.identifyAsController(controlpassword) @@ -1077,8 +1102,8 @@ class MainWindow(QtGui.QMainWindow): @needsClient def setOffset(self): - newoffset, ok = QtGui.QInputDialog.getText(self, getMessage("setoffset-msgbox-label"), - getMessage("offsetinfo-msgbox-label"), QtGui.QLineEdit.Normal, + newoffset, ok = QtWidgets.QInputDialog.getText(self, getMessage("setoffset-msgbox-label"), + getMessage("offsetinfo-msgbox-label"), QtWidgets.QLineEdit.Normal, "") if ok and newoffset != '': o = re.match(constants.UI_OFFSET_REGEX, "o " + newoffset) @@ -1131,8 +1156,8 @@ class MainWindow(QtGui.QMainWindow): def addTopLayout(self, window): window.topSplit = self.topSplitter(Qt.Horizontal, self) - window.outputLayout = QtGui.QVBoxLayout() - window.outputbox = QtGui.QTextBrowser() + window.outputLayout = QtWidgets.QVBoxLayout() + window.outputbox = QtWidgets.QTextBrowser() window.outputbox.setReadOnly(True) window.outputbox.setTextInteractionFlags(window.outputbox.textInteractionFlags() | Qt.TextSelectableByKeyboard) window.outputbox.setOpenExternalLinks(True) @@ -1142,24 +1167,24 @@ class MainWindow(QtGui.QMainWindow): window.outputbox.moveCursor(QtGui.QTextCursor.End) window.outputbox.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOn) - window.outputlabel = QtGui.QLabel(getMessage("notifications-heading-label")) - window.chatInput = QtGui.QLineEdit() + window.outputlabel = QtWidgets.QLabel(getMessage("notifications-heading-label")) + window.chatInput = QtWidgets.QLineEdit() window.chatInput.setMaxLength(constants.MAX_CHAT_MESSAGE_LENGTH) window.chatInput.returnPressed.connect(self.sendChatMessage) - window.chatButton = QtGui.QPushButton(QtGui.QIcon(self.resourcespath + 'email_go.png'), + window.chatButton = QtWidgets.QPushButton(QtGui.QPixmap(self.resourcespath + 'email_go.png'), getMessage("sendmessage-label")) window.chatButton.pressed.connect(self.sendChatMessage) - window.chatLayout = QtGui.QHBoxLayout() - window.chatFrame = QtGui.QFrame() + window.chatLayout = QtWidgets.QHBoxLayout() + window.chatFrame = QtWidgets.QFrame() window.chatFrame.setLayout(self.chatLayout) window.chatFrame.setContentsMargins(0,0,0,0) - window.chatFrame.setSizePolicy(QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Minimum) + window.chatFrame.setSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Minimum) window.chatLayout.setContentsMargins(0,0,0,0) self.chatButton.setToolTip(getMessage("sendmessage-tooltip")) window.chatLayout.addWidget(window.chatInput) window.chatLayout.addWidget(window.chatButton) window.chatFrame.setMaximumHeight(window.chatFrame.sizeHint().height()) - window.outputFrame = QtGui.QFrame() + window.outputFrame = QtWidgets.QFrame() window.outputFrame.setLineWidth(0) window.outputFrame.setMidLineWidth(0) window.outputLayout.setContentsMargins(0, 0, 0, 0) @@ -1168,47 +1193,47 @@ class MainWindow(QtGui.QMainWindow): window.outputLayout.addWidget(window.chatFrame) window.outputFrame.setLayout(window.outputLayout) - window.listLayout = QtGui.QVBoxLayout() + window.listLayout = QtWidgets.QVBoxLayout() window.listTreeModel = QtGui.QStandardItemModel() - window.listTreeView = QtGui.QTreeView() + window.listTreeView = QtWidgets.QTreeView() window.listTreeView.setModel(window.listTreeModel) window.listTreeView.setIndentation(21) window.listTreeView.doubleClicked.connect(self.roomClicked) self.listTreeView.setContextMenuPolicy(Qt.CustomContextMenu) - self.listTreeView.setSelectionBehavior(QtGui.QAbstractItemView.SelectRows) + self.listTreeView.setSelectionBehavior(QtWidgets.QAbstractItemView.SelectRows) self.listTreeView.customContextMenuRequested.connect(self.openRoomMenu) - window.listlabel = QtGui.QLabel(getMessage("userlist-heading-label")) - window.listFrame = QtGui.QFrame() + window.listlabel = QtWidgets.QLabel(getMessage("userlist-heading-label")) + window.listFrame = QtWidgets.QFrame() window.listFrame.setLineWidth(0) window.listFrame.setMidLineWidth(0) - window.listFrame.setSizePolicy(QtGui.QSizePolicy.Preferred, QtGui.QSizePolicy.Preferred) + window.listFrame.setSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Preferred) window.listLayout.setContentsMargins(0, 0, 0, 0) - window.userlistLayout = QtGui.QVBoxLayout() - window.userlistFrame = QtGui.QFrame() + window.userlistLayout = QtWidgets.QVBoxLayout() + window.userlistFrame = QtWidgets.QFrame() window.userlistFrame.setLineWidth(0) window.userlistFrame.setMidLineWidth(0) - window.userlistFrame.setSizePolicy(QtGui.QSizePolicy.Preferred, QtGui.QSizePolicy.Preferred) + window.userlistFrame.setSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Preferred) window.userlistLayout.setContentsMargins(0, 0, 0, 0) window.userlistFrame.setLayout(window.userlistLayout) window.userlistLayout.addWidget(window.listlabel) window.userlistLayout.addWidget(window.listTreeView) - window.listSplit = QtGui.QSplitter(Qt.Vertical, self) + window.listSplit = QtWidgets.QSplitter(Qt.Vertical, self) window.listSplit.addWidget(window.userlistFrame) window.listLayout.addWidget(window.listSplit) - window.roomInput = QtGui.QLineEdit() + window.roomInput = QtWidgets.QLineEdit() window.roomInput.setMaxLength(constants.MAX_ROOM_NAME_LENGTH) window.roomInput.returnPressed.connect(self.joinRoom) - window.roomButton = QtGui.QPushButton(QtGui.QIcon(self.resourcespath + 'door_in.png'), + window.roomButton = QtWidgets.QPushButton(QtGui.QPixmap(self.resourcespath + 'door_in.png'), getMessage("joinroom-label")) window.roomButton.pressed.connect(self.joinRoom) - window.roomLayout = QtGui.QHBoxLayout() - window.roomFrame = QtGui.QFrame() + window.roomLayout = QtWidgets.QHBoxLayout() + window.roomFrame = QtWidgets.QFrame() window.roomFrame.setLayout(self.roomLayout) window.roomFrame.setContentsMargins(0,0,0,0) - window.roomFrame.setSizePolicy(QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Minimum) + window.roomFrame.setSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Minimum) window.roomLayout.setContentsMargins(0,0,0,0) self.roomButton.setToolTip(getMessage("joinroom-tooltip")) window.roomLayout.addWidget(window.roomInput) @@ -1223,11 +1248,11 @@ class MainWindow(QtGui.QMainWindow): window.topSplit.setStretchFactor(0,4) window.topSplit.setStretchFactor(1,5) window.mainLayout.addWidget(window.topSplit) - window.topSplit.setSizePolicy(QtGui.QSizePolicy.Preferred, QtGui.QSizePolicy.Expanding) + window.topSplit.setSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Expanding) def addBottomLayout(self, window): - window.bottomLayout = QtGui.QHBoxLayout() - window.bottomFrame = QtGui.QFrame() + window.bottomLayout = QtWidgets.QHBoxLayout() + window.bottomFrame = QtWidgets.QFrame() window.bottomFrame.setLayout(window.bottomLayout) window.bottomLayout.setContentsMargins(0,0,0,0) @@ -1236,8 +1261,8 @@ class MainWindow(QtGui.QMainWindow): window.playlistGroup = self.PlaylistGroupBox(getMessage("sharedplaylistenabled-label")) window.playlistGroup.setCheckable(True) window.playlistGroup.toggled.connect(self.changePlaylistEnabledState) - window.playlistLayout = QtGui.QHBoxLayout() - window.playlistGroup.setSizePolicy(QtGui.QSizePolicy.Preferred, QtGui.QSizePolicy.Preferred) + window.playlistLayout = QtWidgets.QHBoxLayout() + window.playlistGroup.setSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Preferred) window.playlistGroup.setAcceptDrops(True) window.playlist = self.PlaylistWidget() window.playlist.setWindow(window) @@ -1245,18 +1270,18 @@ class MainWindow(QtGui.QMainWindow): window.playlist.setDragEnabled(True) window.playlist.setAcceptDrops(True) window.playlist.setDropIndicatorShown(True) - window.playlist.setSelectionMode(QtGui.QAbstractItemView.ExtendedSelection) + window.playlist.setSelectionMode(QtWidgets.QAbstractItemView.ExtendedSelection) window.playlist.setDefaultDropAction(Qt.MoveAction) - window.playlist.setDragDropMode(QtGui.QAbstractItemView.InternalMove) + window.playlist.setDragDropMode(QtWidgets.QAbstractItemView.InternalMove) window.playlist.doubleClicked.connect(self.playlistItemClicked) window.playlist.setContextMenuPolicy(Qt.CustomContextMenu) - window.playlist.setSelectionBehavior(QtGui.QAbstractItemView.SelectRows) + window.playlist.setSelectionBehavior(QtWidgets.QAbstractItemView.SelectRows) window.playlist.customContextMenuRequested.connect(self.openPlaylistMenu) self.playlistUpdateTimer = task.LoopingCall(self.playlistChangeCheck) self.playlistUpdateTimer.start(0.1, True) noteFont = QtGui.QFont() noteFont.setItalic(True) - playlistItem = QtGui.QListWidgetItem(getMessage("playlist-instruction-item-message")) + playlistItem = QtWidgets.QListWidgetItem(getMessage("playlist-instruction-item-message")) playlistItem.setFont(noteFont) window.playlist.addItem(playlistItem) playlistItem.setFont(noteFont) @@ -1266,7 +1291,7 @@ class MainWindow(QtGui.QMainWindow): window.playlistGroup.setLayout(window.playlistLayout) window.listSplit.addWidget(window.playlistGroup) - window.readyPushButton = QtGui.QPushButton() + window.readyPushButton = QtWidgets.QPushButton() readyFont = QtGui.QFont() readyFont.setWeight(QtGui.QFont.Bold) window.readyPushButton.setText(getMessage("ready-guipushbuttonlabel")) @@ -1278,12 +1303,12 @@ class MainWindow(QtGui.QMainWindow): window.readyPushButton.setToolTip(getMessage("ready-tooltip")) window.listLayout.addWidget(window.readyPushButton, Qt.AlignRight) - window.autoplayLayout = QtGui.QHBoxLayout() - window.autoplayFrame = QtGui.QFrame() + window.autoplayLayout = QtWidgets.QHBoxLayout() + window.autoplayFrame = QtWidgets.QFrame() window.autoplayFrame.setVisible(False) window.autoplayLayout.setContentsMargins(0,0,0,0) window.autoplayFrame.setLayout(window.autoplayLayout) - window.autoplayPushButton = QtGui.QPushButton() + window.autoplayPushButton = QtWidgets.QPushButton() autoPlayFont = QtGui.QFont() autoPlayFont.setWeight(QtGui.QFont.Bold) window.autoplayPushButton.setText(getMessage("autoplay-guipushbuttonlabel")) @@ -1292,13 +1317,13 @@ class MainWindow(QtGui.QMainWindow): window.autoplayPushButton.toggled.connect(self.changeAutoplayState) window.autoplayPushButton.setFont(autoPlayFont) window.autoplayPushButton.setStyleSheet(constants.STYLE_AUTO_PLAY_PUSHBUTTON) - window.autoplayPushButton.setSizePolicy(QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Expanding) + window.autoplayPushButton.setSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding) window.autoplayPushButton.setToolTip(getMessage("autoplay-tooltip")) - window.autoplayLabel = QtGui.QLabel(getMessage("autoplay-minimum-label")) - window.autoplayLabel.setSizePolicy(QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Minimum) + window.autoplayLabel = QtWidgets.QLabel(getMessage("autoplay-minimum-label")) + window.autoplayLabel.setSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Minimum) window.autoplayLabel.setMaximumWidth(window.autoplayLabel.minimumSizeHint().width()) window.autoplayLabel.setToolTip(getMessage("autoplay-tooltip")) - window.autoplayThresholdSpinbox = QtGui.QSpinBox() + window.autoplayThresholdSpinbox = QtWidgets.QSpinBox() window.autoplayThresholdSpinbox.setMaximumWidth(window.autoplayThresholdSpinbox.minimumSizeHint().width()) window.autoplayThresholdSpinbox.setMinimum(2) window.autoplayThresholdSpinbox.setMaximum(99) @@ -1314,33 +1339,33 @@ class MainWindow(QtGui.QMainWindow): window.bottomFrame.setMaximumHeight(window.bottomFrame.minimumSizeHint().height()) def addPlaybackLayout(self, window): - window.playbackFrame = QtGui.QFrame() + window.playbackFrame = QtWidgets.QFrame() window.playbackFrame.setVisible(False) window.playbackFrame.setContentsMargins(0,0,0,0) - window.playbackLayout = QtGui.QHBoxLayout() + window.playbackLayout = QtWidgets.QHBoxLayout() window.playbackLayout.setAlignment(Qt.AlignLeft) window.playbackLayout.setContentsMargins(0,0,0,0) window.playbackFrame.setLayout(window.playbackLayout) - window.seekInput = QtGui.QLineEdit() + window.seekInput = QtWidgets.QLineEdit() window.seekInput.returnPressed.connect(self.seekFromButton) - window.seekButton = QtGui.QPushButton(QtGui.QIcon(self.resourcespath + u'clock_go.png'), "") + window.seekButton = QtWidgets.QPushButton(QtGui.QPixmap(self.resourcespath + u'clock_go.png'), "") window.seekButton.setToolTip(getMessage("seektime-menu-label")) window.seekButton.pressed.connect(self.seekFromButton) window.seekInput.setText("0:00") window.seekInput.setFixedWidth(60) window.playbackLayout.addWidget(window.seekInput) window.playbackLayout.addWidget(window.seekButton) - window.unseekButton = QtGui.QPushButton(QtGui.QIcon(self.resourcespath + u'arrow_undo.png'), "") + window.unseekButton = QtWidgets.QPushButton(QtGui.QPixmap(self.resourcespath + u'arrow_undo.png'), "") window.unseekButton.setToolTip(getMessage("undoseek-menu-label")) window.unseekButton.pressed.connect(self.undoSeek) - window.miscLayout = QtGui.QHBoxLayout() + window.miscLayout = QtWidgets.QHBoxLayout() window.playbackLayout.addWidget(window.unseekButton) - window.playButton = QtGui.QPushButton(QtGui.QIcon(self.resourcespath + u'control_play_blue.png'), "") + window.playButton = QtWidgets.QPushButton(QtGui.QPixmap(self.resourcespath + u'control_play_blue.png'), "") window.playButton.setToolTip(getMessage("play-menu-label")) window.playButton.pressed.connect(self.play) window.playbackLayout.addWidget(window.playButton) - window.pauseButton = QtGui.QPushButton(QtGui.QIcon(self.resourcespath + 'control_pause_blue.png'), "") + window.pauseButton = QtWidgets.QPushButton(QtGui.QPixmap(self.resourcespath + 'control_pause_blue.png'), "") window.pauseButton.setToolTip(getMessage("pause-menu-label")) window.pauseButton.pressed.connect(self.pause) window.playbackLayout.addWidget(window.pauseButton) @@ -1349,54 +1374,54 @@ class MainWindow(QtGui.QMainWindow): window.outputLayout.addWidget(window.playbackFrame) def addMenubar(self, window): - window.menuBar = QtGui.QMenuBar() + window.menuBar = QtWidgets.QMenuBar() # File menu - window.fileMenu = QtGui.QMenu(getMessage("file-menu-label"), self) - window.openAction = window.fileMenu.addAction(QtGui.QIcon(self.resourcespath + 'folder_explore.png'), + window.fileMenu = QtWidgets.QMenu(getMessage("file-menu-label"), self) + window.openAction = window.fileMenu.addAction(QtGui.QPixmap(self.resourcespath + 'folder_explore.png'), getMessage("openmedia-menu-label")) window.openAction.triggered.connect(self.browseMediapath) - window.openAction = window.fileMenu.addAction(QtGui.QIcon(self.resourcespath + 'world_explore.png'), + window.openAction = window.fileMenu.addAction(QtGui.QPixmap(self.resourcespath + 'world_explore.png'), getMessage("openstreamurl-menu-label")) window.openAction.triggered.connect(self.promptForStreamURL) - window.openAction = window.fileMenu.addAction(QtGui.QIcon(self.resourcespath + 'film_folder_edit.png'), + window.openAction = window.fileMenu.addAction(QtGui.QPixmap(self.resourcespath + 'film_folder_edit.png'), getMessage("setmediadirectories-menu-label")) window.openAction.triggered.connect(self.openSetMediaDirectoriesDialog) - window.exitAction = window.fileMenu.addAction(QtGui.QIcon(self.resourcespath + 'cross.png'), + window.exitAction = window.fileMenu.addAction(QtGui.QPixmap(self.resourcespath + 'cross.png'), getMessage("exit-menu-label")) window.exitAction.triggered.connect(self.exitSyncplay) window.menuBar.addMenu(window.fileMenu) # Playback menu - window.playbackMenu = QtGui.QMenu(getMessage("playback-menu-label"), self) - window.playAction = window.playbackMenu.addAction(QtGui.QIcon(self.resourcespath + 'control_play_blue.png'), getMessage("play-menu-label")) + window.playbackMenu = QtWidgets.QMenu(getMessage("playback-menu-label"), self) + window.playAction = window.playbackMenu.addAction(QtGui.QPixmap(self.resourcespath + 'control_play_blue.png'), getMessage("play-menu-label")) window.playAction.triggered.connect(self.play) - window.pauseAction = window.playbackMenu.addAction(QtGui.QIcon(self.resourcespath + 'control_pause_blue.png'), getMessage("pause-menu-label")) + window.pauseAction = window.playbackMenu.addAction(QtGui.QPixmap(self.resourcespath + 'control_pause_blue.png'), getMessage("pause-menu-label")) window.pauseAction.triggered.connect(self.pause) - window.seekAction = window.playbackMenu.addAction(QtGui.QIcon(self.resourcespath + 'clock_go.png'), getMessage("seektime-menu-label")) + window.seekAction = window.playbackMenu.addAction(QtGui.QPixmap(self.resourcespath + 'clock_go.png'), getMessage("seektime-menu-label")) window.seekAction.triggered.connect(self.seekPositionDialog) - window.unseekAction = window.playbackMenu.addAction(QtGui.QIcon(self.resourcespath + 'arrow_undo.png'), getMessage("undoseek-menu-label")) + window.unseekAction = window.playbackMenu.addAction(QtGui.QPixmap(self.resourcespath + 'arrow_undo.png'), getMessage("undoseek-menu-label")) window.unseekAction.triggered.connect(self.undoSeek) window.menuBar.addMenu(window.playbackMenu) # Advanced menu - window.advancedMenu = QtGui.QMenu(getMessage("advanced-menu-label"), self) - window.setoffsetAction = window.advancedMenu.addAction(QtGui.QIcon(self.resourcespath + 'timeline_marker.png'), + window.advancedMenu = QtWidgets.QMenu(getMessage("advanced-menu-label"), self) + window.setoffsetAction = window.advancedMenu.addAction(QtGui.QPixmap(self.resourcespath + 'timeline_marker.png'), getMessage("setoffset-menu-label")) window.setoffsetAction.triggered.connect(self.setOffset) - window.setTrustedDomainsAction = window.advancedMenu.addAction(QtGui.QIcon(self.resourcespath + 'shield_edit.png'), + window.setTrustedDomainsAction = window.advancedMenu.addAction(QtGui.QPixmap(self.resourcespath + 'shield_edit.png'), getMessage("settrusteddomains-menu-label")) window.setTrustedDomainsAction.triggered.connect(self.openSetTrustedDomainsDialog) window.createcontrolledroomAction = window.advancedMenu.addAction( - QtGui.QIcon(self.resourcespath + 'page_white_key.png'), getMessage("createcontrolledroom-menu-label")) + QtGui.QPixmap(self.resourcespath + 'page_white_key.png'), getMessage("createcontrolledroom-menu-label")) window.createcontrolledroomAction.triggered.connect(self.createControlledRoom) - window.identifyascontroller = window.advancedMenu.addAction(QtGui.QIcon(self.resourcespath + 'key_go.png'), + window.identifyascontroller = window.advancedMenu.addAction(QtGui.QPixmap(self.resourcespath + 'key_go.png'), getMessage("identifyascontroller-menu-label")) window.identifyascontroller.triggered.connect(self.identifyAsController) @@ -1404,7 +1429,7 @@ class MainWindow(QtGui.QMainWindow): # Window menu - window.windowMenu = QtGui.QMenu(getMessage("window-menu-label"), self) + window.windowMenu = QtWidgets.QMenu(getMessage("window-menu-label"), self) window.playbackAction = window.windowMenu.addAction(getMessage("playbackbuttons-menu-label")) window.playbackAction.setCheckable(True) @@ -1418,18 +1443,18 @@ class MainWindow(QtGui.QMainWindow): # Help menu - window.helpMenu = QtGui.QMenu(getMessage("help-menu-label"), self) - - window.userguideAction = window.helpMenu.addAction(QtGui.QIcon(self.resourcespath + 'help.png'), + window.helpMenu = QtWidgets.QMenu(getMessage("help-menu-label"), self) + + window.userguideAction = window.helpMenu.addAction(QtGui.QPixmap(self.resourcespath + 'help.png'), getMessage("userguide-menu-label")) window.userguideAction.triggered.connect(self.openUserGuide) - window.updateAction = window.helpMenu.addAction(QtGui.QIcon(self.resourcespath + 'application_get.png'), + window.updateAction = window.helpMenu.addAction(QtGui.QPixmap(self.resourcespath + 'application_get.png'), getMessage("update-menu-label")) window.updateAction.triggered.connect(self.userCheckForUpdates) - + if not sys.platform.startswith('darwin'): window.helpMenu.addSeparator() - window.about = window.helpMenu.addAction(QtGui.QIcon(self.resourcespath + 'syncplay.png'), + window.about = window.helpMenu.addAction(QtGui.QPixmap(self.resourcespath + 'syncplay.png'), getMessage("about-menu-label")) else: window.about = window.helpMenu.addAction("&About") @@ -1442,9 +1467,9 @@ class MainWindow(QtGui.QMainWindow): def openAbout(self): aboutMsgBox = AboutDialog() aboutMsgBox.exec_() - + def addMainFrame(self, window): - window.mainFrame = QtGui.QFrame() + window.mainFrame = QtWidgets.QFrame() window.mainFrame.setLineWidth(0) window.mainFrame.setMidLineWidth(0) window.mainFrame.setContentsMargins(0, 0, 0, 0) @@ -1504,29 +1529,29 @@ class MainWindow(QtGui.QMainWindow): def updateReadyIcon(self): ready = self.readyPushButton.isChecked() if ready: - self.readyPushButton.setIcon(QtGui.QIcon(self.resourcespath + 'tick_checkbox.png')) + self.readyPushButton.setIcon(QtGui.QPixmap(self.resourcespath + 'tick_checkbox.png')) else: - self.readyPushButton.setIcon(QtGui.QIcon(self.resourcespath + 'empty_checkbox.png')) + self.readyPushButton.setIcon(QtGui.QPixmap(self.resourcespath + 'empty_checkbox.png')) def updateAutoPlayIcon(self): ready = self.autoplayPushButton.isChecked() if ready: - self.autoplayPushButton.setIcon(QtGui.QIcon(self.resourcespath + 'tick_checkbox.png')) + self.autoplayPushButton.setIcon(QtGui.QPixmap(self.resourcespath + 'tick_checkbox.png')) else: - self.autoplayPushButton.setIcon(QtGui.QIcon(self.resourcespath + 'empty_checkbox.png')) + self.autoplayPushButton.setIcon(QtGui.QPixmap(self.resourcespath + 'empty_checkbox.png')) def automaticUpdateCheck(self): - currentDateTime = datetime.utcnow() + currentDateTimeValue = QDateTime.currentDateTime() if not self.config['checkForUpdatesAutomatically']: return if self.config['lastCheckedForUpdates']: configLastChecked = datetime.strptime(self.config["lastCheckedForUpdates"], "%Y-%m-%d %H:%M:%S.%f") - if self.lastCheckedForUpdates is None or configLastChecked > self.lastCheckedForUpdates: - self.lastCheckedForUpdates = configLastChecked + if self.lastCheckedForUpdates is None or configLastChecked > self.lastCheckedForUpdates.toPython(): + self.lastCheckedForUpdates = QDateTime.fromString(self.config["lastCheckedForUpdates"],'yyyy-MM-dd HH-mm-ss') if self.lastCheckedForUpdates is None: self.checkForUpdates() else: - timeDelta = currentDateTime - self.lastCheckedForUpdates + timeDelta = currentDateTimeValue.toPython() - self.lastCheckedForUpdates.toPython() if timeDelta.total_seconds() > constants.AUTOMATIC_UPDATE_CHECK_FREQUENCY: self.checkForUpdates() @@ -1535,7 +1560,7 @@ class MainWindow(QtGui.QMainWindow): @needsClient def checkForUpdates(self, userInitiated=False): - self.lastCheckedForUpdates = datetime.utcnow() + self.lastCheckedForUpdates = QDateTime.currentDateTime() updateStatus, updateMessage, updateURL, self.publicServerList = self._syncplayClient.checkForUpdate(userInitiated) if updateMessage is None: @@ -1549,12 +1574,12 @@ class MainWindow(QtGui.QMainWindow): if userInitiated == True: updateURL = constants.SYNCPLAY_DOWNLOAD_URL if updateURL is not None: - reply = QtGui.QMessageBox.question(self, "Syncplay", - updateMessage, QtGui.QMessageBox.StandardButton.Yes | QtGui.QMessageBox.StandardButton.No) - if reply == QtGui.QMessageBox.Yes: + reply = QtWidgets.QMessageBox.question(self, "Syncplay", + updateMessage, QtWidgets.QMessageBox.StandardButton.Yes | QtWidgets.QMessageBox.StandardButton.No) + if reply == QtWidgets.QMessageBox.Yes: self.QtGui.QDesktopServices.openUrl(QUrl(updateURL)) elif userInitiated: - QtGui.QMessageBox.information(self, "Syncplay", updateMessage) + QtWidgets.QMessageBox.information(self, "Syncplay", updateMessage) else: self.showMessage(updateMessage) @@ -1572,10 +1597,10 @@ class MainWindow(QtGui.QMainWindow): data = event.mimeData() urls = data.urls() if urls and urls[0].scheme() == 'file': - if sys.platform.startswith('darwin'): - dropfilepath = os.path.abspath(NSURL.URLWithString_(str(event.mimeData().urls()[0].toString())).filePathURL().path()) + if sys.platform.startswith('darwin') and IsPySide: + dropfilepath = os.path.abspath(NSURL.URLWithString_(str(url.toString())).filePathURL().path()) else: - dropfilepath = os.path.abspath(unicode(event.mimeData().urls()[0].toLocalFile())) + dropfilepath = os.path.abspath(unicode(url.toLocalFile())) if rewindFile == False: self._syncplayClient._player.openFile(dropfilepath) else: @@ -1667,7 +1692,7 @@ class MainWindow(QtGui.QMainWindow): settings.endGroup() settings = QSettings("Syncplay", "Interface") settings.beginGroup("Update") - settings.setValue("lastChecked", self.lastCheckedForUpdates) + settings.setValue("lastCheckedQt", self.lastCheckedForUpdates) settings.endGroup() settings.beginGroup("PublicServerList") if self.publicServerList: @@ -1694,7 +1719,7 @@ class MainWindow(QtGui.QMainWindow): settings.endGroup() settings = QSettings("Syncplay", "Interface") settings.beginGroup("Update") - self.lastCheckedForUpdates = settings.value("lastChecked", None) + self.lastCheckedForUpdates = settings.value("lastCheckedQt", None) settings.endGroup() settings.beginGroup("PublicServerList") self.publicServerList = settings.value("publicServers", None) @@ -1716,13 +1741,13 @@ class MainWindow(QtGui.QMainWindow): else: self.setWindowFlags(self.windowFlags() & Qt.AA_DontUseNativeMenuBar) self.setWindowTitle("Syncplay v" + version) - self.mainLayout = QtGui.QVBoxLayout() + self.mainLayout = QtWidgets.QVBoxLayout() self.addTopLayout(self) self.addBottomLayout(self) self.addMenubar(self) self.addMainFrame(self) self.loadSettings() - self.setWindowIcon(QtGui.QIcon(self.resourcespath + u"syncplay.png")) + self.setWindowIcon(QtGui.QPixmap(self.resourcespath + u"syncplay.png")) self.setWindowFlags(self.windowFlags() & Qt.WindowCloseButtonHint & Qt.AA_DontUseNativeMenuBar & Qt.WindowMinimizeButtonHint & ~Qt.WindowContextHelpButtonHint) self.show() self.setAcceptDrops(True) diff --git a/syncplay/vendor/Qt.py b/syncplay/vendor/Qt.py new file mode 100755 index 0000000..5115221 --- /dev/null +++ b/syncplay/vendor/Qt.py @@ -0,0 +1,1620 @@ +"""Minimal Python 2 & 3 shim around all Qt bindings + +DOCUMENTATION + Qt.py was born in the film and visual effects industry to address + the growing need for the development of software capable of running + with more than one flavour of the Qt bindings for Python - PySide, + PySide2, PyQt4 and PyQt5. + + 1. Build for one, run with all + 2. Explicit is better than implicit + 3. Support co-existence + + Default resolution order: + - PySide2 + - PyQt5 + - PySide + - PyQt4 + + Usage: + >> import sys + >> from Qt import QtWidgets + >> app = QtWidgets.QApplication(sys.argv) + >> button = QtWidgets.QPushButton("Hello World") + >> button.show() + >> app.exec_() + + All members of PySide2 are mapped from other bindings, should they exist. + If no equivalent member exist, it is excluded from Qt.py and inaccessible. + The idea is to highlight members that exist across all supported binding, + and guarantee that code that runs on one binding runs on all others. + + For more details, visit https://github.com/mottosso/Qt.py + +LICENSE + + See end of file for license (MIT, BSD) information. + +""" + +import os +import sys +import types +import shutil + + +__version__ = "1.1.0.b5" + +# Enable support for `from Qt import *` +__all__ = [] + +# Flags from environment variables +QT_VERBOSE = bool(os.getenv("QT_VERBOSE")) +QT_PREFERRED_BINDING = os.getenv("QT_PREFERRED_BINDING", "") +QT_SIP_API_HINT = os.getenv("QT_SIP_API_HINT") + +# Reference to Qt.py +Qt = sys.modules[__name__] +Qt.QtCompat = types.ModuleType("QtCompat") + +try: + long +except NameError: + # Python 3 compatibility + long = int + +"""Common members of all bindings + +This is where each member of Qt.py is explicitly defined. +It is based on a "lowest common denominator" of all bindings; +including members found in each of the 4 bindings. + +The "_common_members" dictionary is generated using the +build_membership.sh script. + +""" + +_common_members = { + "QtCore": [ + "QAbstractAnimation", + "QAbstractEventDispatcher", + "QAbstractItemModel", + "QAbstractListModel", + "QAbstractState", + "QAbstractTableModel", + "QAbstractTransition", + "QAnimationGroup", + "QBasicTimer", + "QBitArray", + "QBuffer", + "QByteArray", + "QByteArrayMatcher", + "QChildEvent", + "QCoreApplication", + "QCryptographicHash", + "QDataStream", + "QDate", + "QDateTime", + "QDir", + "QDirIterator", + "QDynamicPropertyChangeEvent", + "QEasingCurve", + "QElapsedTimer", + "QEvent", + "QEventLoop", + "QEventTransition", + "QFile", + "QFileInfo", + "QFileSystemWatcher", + "QFinalState", + "QGenericArgument", + "QGenericReturnArgument", + "QHistoryState", + "QIODevice", + "QLibraryInfo", + "QLine", + "QLineF", + "QLocale", + "QMargins", + "QMetaClassInfo", + "QMetaEnum", + "QMetaMethod", + "QMetaObject", + "QMetaProperty", + "QMimeData", + "QModelIndex", + "QMutex", + "QMutexLocker", + "QObject", + "QParallelAnimationGroup", + "QPauseAnimation", + "QPersistentModelIndex", + "QPluginLoader", + "QPoint", + "QPointF", + "QProcess", + "QProcessEnvironment", + "QPropertyAnimation", + "QReadLocker", + "QReadWriteLock", + "QRect", + "QRectF", + "QRegExp", + "QResource", + "QRunnable", + "QSemaphore", + "QSequentialAnimationGroup", + "QSettings", + "QSignalMapper", + "QSignalTransition", + "QSize", + "QSizeF", + "QSocketNotifier", + "QState", + "QStateMachine", + "QSysInfo", + "QSystemSemaphore", + "QT_TRANSLATE_NOOP", + "QT_TR_NOOP", + "QT_TR_NOOP_UTF8", + "QTemporaryFile", + "QTextBoundaryFinder", + "QTextCodec", + "QTextDecoder", + "QTextEncoder", + "QTextStream", + "QTextStreamManipulator", + "QThread", + "QThreadPool", + "QTime", + "QTimeLine", + "QTimer", + "QTimerEvent", + "QTranslator", + "QUrl", + "QVariantAnimation", + "QWaitCondition", + "QWriteLocker", + "QXmlStreamAttribute", + "QXmlStreamAttributes", + "QXmlStreamEntityDeclaration", + "QXmlStreamEntityResolver", + "QXmlStreamNamespaceDeclaration", + "QXmlStreamNotationDeclaration", + "QXmlStreamReader", + "QXmlStreamWriter", + "Qt", + "QtCriticalMsg", + "QtDebugMsg", + "QtFatalMsg", + "QtMsgType", + "QtSystemMsg", + "QtWarningMsg", + "qAbs", + "qAddPostRoutine", + "qChecksum", + "qCritical", + "qDebug", + "qFatal", + "qFuzzyCompare", + "qIsFinite", + "qIsInf", + "qIsNaN", + "qIsNull", + "qRegisterResourceData", + "qUnregisterResourceData", + "qVersion", + "qWarning", + "qrand", + "qsrand" + ], + "QtGui": [ + "QAbstractTextDocumentLayout", + "QActionEvent", + "QBitmap", + "QBrush", + "QClipboard", + "QCloseEvent", + "QColor", + "QConicalGradient", + "QContextMenuEvent", + "QCursor", + "QDesktopServices", + "QDoubleValidator", + "QDrag", + "QDragEnterEvent", + "QDragLeaveEvent", + "QDragMoveEvent", + "QDropEvent", + "QFileOpenEvent", + "QFocusEvent", + "QFont", + "QFontDatabase", + "QFontInfo", + "QFontMetrics", + "QFontMetricsF", + "QGradient", + "QHelpEvent", + "QHideEvent", + "QHoverEvent", + "QIcon", + "QIconDragEvent", + "QIconEngine", + "QImage", + "QImageIOHandler", + "QImageReader", + "QImageWriter", + "QInputEvent", + "QInputMethodEvent", + "QIntValidator", + "QKeyEvent", + "QKeySequence", + "QLinearGradient", + "QMatrix2x2", + "QMatrix2x3", + "QMatrix2x4", + "QMatrix3x2", + "QMatrix3x3", + "QMatrix3x4", + "QMatrix4x2", + "QMatrix4x3", + "QMatrix4x4", + "QMouseEvent", + "QMoveEvent", + "QMovie", + "QPaintDevice", + "QPaintEngine", + "QPaintEngineState", + "QPaintEvent", + "QPainter", + "QPainterPath", + "QPainterPathStroker", + "QPalette", + "QPen", + "QPicture", + "QPictureIO", + "QPixmap", + "QPixmapCache", + "QPolygon", + "QPolygonF", + "QQuaternion", + "QRadialGradient", + "QRegExpValidator", + "QRegion", + "QResizeEvent", + "QSessionManager", + "QShortcutEvent", + "QShowEvent", + "QStandardItem", + "QStandardItemModel", + "QStatusTipEvent", + "QSyntaxHighlighter", + "QTabletEvent", + "QTextBlock", + "QTextBlockFormat", + "QTextBlockGroup", + "QTextBlockUserData", + "QTextCharFormat", + "QTextCursor", + "QTextDocument", + "QTextDocumentFragment", + "QTextFormat", + "QTextFragment", + "QTextFrame", + "QTextFrameFormat", + "QTextImageFormat", + "QTextInlineObject", + "QTextItem", + "QTextLayout", + "QTextLength", + "QTextLine", + "QTextList", + "QTextListFormat", + "QTextObject", + "QTextObjectInterface", + "QTextOption", + "QTextTable", + "QTextTableCell", + "QTextTableCellFormat", + "QTextTableFormat", + "QTouchEvent", + "QTransform", + "QValidator", + "QVector2D", + "QVector3D", + "QVector4D", + "QWhatsThisClickedEvent", + "QWheelEvent", + "QWindowStateChangeEvent", + "qAlpha", + "qBlue", + "qGray", + "qGreen", + "qIsGray", + "qRed", + "qRgb", + "qRgba" + ], + "QtHelp": [ + "QHelpContentItem", + "QHelpContentModel", + "QHelpContentWidget", + "QHelpEngine", + "QHelpEngineCore", + "QHelpIndexModel", + "QHelpIndexWidget", + "QHelpSearchEngine", + "QHelpSearchQuery", + "QHelpSearchQueryWidget", + "QHelpSearchResultWidget" + ], + "QtMultimedia": [ + "QAbstractVideoBuffer", + "QAbstractVideoSurface", + "QAudio", + "QAudioDeviceInfo", + "QAudioFormat", + "QAudioInput", + "QAudioOutput", + "QVideoFrame", + "QVideoSurfaceFormat" + ], + "QtNetwork": [ + "QAbstractNetworkCache", + "QAbstractSocket", + "QAuthenticator", + "QHostAddress", + "QHostInfo", + "QLocalServer", + "QLocalSocket", + "QNetworkAccessManager", + "QNetworkAddressEntry", + "QNetworkCacheMetaData", + "QNetworkConfiguration", + "QNetworkConfigurationManager", + "QNetworkCookie", + "QNetworkCookieJar", + "QNetworkDiskCache", + "QNetworkInterface", + "QNetworkProxy", + "QNetworkProxyFactory", + "QNetworkProxyQuery", + "QNetworkReply", + "QNetworkRequest", + "QNetworkSession", + "QSsl", + "QTcpServer", + "QTcpSocket", + "QUdpSocket" + ], + "QtOpenGL": [ + "QGL", + "QGLContext", + "QGLFormat", + "QGLWidget" + ], + "QtSql": [ + "QSql", + "QSqlDatabase", + "QSqlDriver", + "QSqlDriverCreatorBase", + "QSqlError", + "QSqlField", + "QSqlIndex", + "QSqlQuery", + "QSqlQueryModel", + "QSqlRecord", + "QSqlRelation", + "QSqlRelationalDelegate", + "QSqlRelationalTableModel", + "QSqlResult", + "QSqlTableModel" + ], + "QtSvg": [ + "QGraphicsSvgItem", + "QSvgGenerator", + "QSvgRenderer", + "QSvgWidget" + ], + "QtTest": [ + "QTest" + ], + "QtWidgets": [ + "QAbstractButton", + "QAbstractGraphicsShapeItem", + "QAbstractItemDelegate", + "QAbstractItemView", + "QAbstractScrollArea", + "QAbstractSlider", + "QAbstractSpinBox", + "QAction", + "QActionGroup", + "QApplication", + "QBoxLayout", + "QButtonGroup", + "QCalendarWidget", + "QCheckBox", + "QColorDialog", + "QColumnView", + "QComboBox", + "QCommandLinkButton", + "QCommonStyle", + "QCompleter", + "QDataWidgetMapper", + "QDateEdit", + "QDateTimeEdit", + "QDesktopWidget", + "QDial", + "QDialog", + "QDialogButtonBox", + "QDirModel", + "QDockWidget", + "QDoubleSpinBox", + "QErrorMessage", + "QFileDialog", + "QFileIconProvider", + "QFileSystemModel", + "QFocusFrame", + "QFontComboBox", + "QFontDialog", + "QFormLayout", + "QFrame", + "QGesture", + "QGestureEvent", + "QGestureRecognizer", + "QGraphicsAnchor", + "QGraphicsAnchorLayout", + "QGraphicsBlurEffect", + "QGraphicsColorizeEffect", + "QGraphicsDropShadowEffect", + "QGraphicsEffect", + "QGraphicsEllipseItem", + "QGraphicsGridLayout", + "QGraphicsItem", + "QGraphicsItemGroup", + "QGraphicsLayout", + "QGraphicsLayoutItem", + "QGraphicsLineItem", + "QGraphicsLinearLayout", + "QGraphicsObject", + "QGraphicsOpacityEffect", + "QGraphicsPathItem", + "QGraphicsPixmapItem", + "QGraphicsPolygonItem", + "QGraphicsProxyWidget", + "QGraphicsRectItem", + "QGraphicsRotation", + "QGraphicsScale", + "QGraphicsScene", + "QGraphicsSceneContextMenuEvent", + "QGraphicsSceneDragDropEvent", + "QGraphicsSceneEvent", + "QGraphicsSceneHelpEvent", + "QGraphicsSceneHoverEvent", + "QGraphicsSceneMouseEvent", + "QGraphicsSceneMoveEvent", + "QGraphicsSceneResizeEvent", + "QGraphicsSceneWheelEvent", + "QGraphicsSimpleTextItem", + "QGraphicsTextItem", + "QGraphicsTransform", + "QGraphicsView", + "QGraphicsWidget", + "QGridLayout", + "QGroupBox", + "QHBoxLayout", + "QHeaderView", + "QInputDialog", + "QItemDelegate", + "QItemEditorCreatorBase", + "QItemEditorFactory", + "QKeyEventTransition", + "QLCDNumber", + "QLabel", + "QLayout", + "QLayoutItem", + "QLineEdit", + "QListView", + "QListWidget", + "QListWidgetItem", + "QMainWindow", + "QMdiArea", + "QMdiSubWindow", + "QMenu", + "QMenuBar", + "QMessageBox", + "QMouseEventTransition", + "QPanGesture", + "QPinchGesture", + "QPlainTextDocumentLayout", + "QPlainTextEdit", + "QProgressBar", + "QProgressDialog", + "QPushButton", + "QRadioButton", + "QRubberBand", + "QScrollArea", + "QScrollBar", + "QShortcut", + "QSizeGrip", + "QSizePolicy", + "QSlider", + "QSpacerItem", + "QSpinBox", + "QSplashScreen", + "QSplitter", + "QSplitterHandle", + "QStackedLayout", + "QStackedWidget", + "QStatusBar", + "QStyle", + "QStyleFactory", + "QStyleHintReturn", + "QStyleHintReturnMask", + "QStyleHintReturnVariant", + "QStyleOption", + "QStyleOptionButton", + "QStyleOptionComboBox", + "QStyleOptionComplex", + "QStyleOptionDockWidget", + "QStyleOptionFocusRect", + "QStyleOptionFrame", + "QStyleOptionGraphicsItem", + "QStyleOptionGroupBox", + "QStyleOptionHeader", + "QStyleOptionMenuItem", + "QStyleOptionProgressBar", + "QStyleOptionRubberBand", + "QStyleOptionSizeGrip", + "QStyleOptionSlider", + "QStyleOptionSpinBox", + "QStyleOptionTab", + "QStyleOptionTabBarBase", + "QStyleOptionTabWidgetFrame", + "QStyleOptionTitleBar", + "QStyleOptionToolBar", + "QStyleOptionToolBox", + "QStyleOptionToolButton", + "QStyleOptionViewItem", + "QStylePainter", + "QStyledItemDelegate", + "QSwipeGesture", + "QSystemTrayIcon", + "QTabBar", + "QTabWidget", + "QTableView", + "QTableWidget", + "QTableWidgetItem", + "QTableWidgetSelectionRange", + "QTapAndHoldGesture", + "QTapGesture", + "QTextBrowser", + "QTextEdit", + "QTimeEdit", + "QToolBar", + "QToolBox", + "QToolButton", + "QToolTip", + "QTreeView", + "QTreeWidget", + "QTreeWidgetItem", + "QTreeWidgetItemIterator", + "QUndoCommand", + "QUndoGroup", + "QUndoStack", + "QUndoView", + "QVBoxLayout", + "QWhatsThis", + "QWidget", + "QWidgetAction", + "QWidgetItem", + "QWizard", + "QWizardPage" + ], + "QtXml": [ + "QDomAttr", + "QDomCDATASection", + "QDomCharacterData", + "QDomComment", + "QDomDocument", + "QDomDocumentFragment", + "QDomDocumentType", + "QDomElement", + "QDomEntity", + "QDomEntityReference", + "QDomImplementation", + "QDomNamedNodeMap", + "QDomNode", + "QDomNodeList", + "QDomNotation", + "QDomProcessingInstruction", + "QDomText", + "QXmlAttributes", + "QXmlContentHandler", + "QXmlDTDHandler", + "QXmlDeclHandler", + "QXmlDefaultHandler", + "QXmlEntityResolver", + "QXmlErrorHandler", + "QXmlInputSource", + "QXmlLexicalHandler", + "QXmlLocator", + "QXmlNamespaceSupport", + "QXmlParseException", + "QXmlReader", + "QXmlSimpleReader" + ], + "QtXmlPatterns": [ + "QAbstractMessageHandler", + "QAbstractUriResolver", + "QAbstractXmlNodeModel", + "QAbstractXmlReceiver", + "QSourceLocation", + "QXmlFormatter", + "QXmlItem", + "QXmlName", + "QXmlNamePool", + "QXmlNodeModelIndex", + "QXmlQuery", + "QXmlResultItems", + "QXmlSchema", + "QXmlSchemaValidator", + "QXmlSerializer" + ] +} + + +"""Misplaced members + +These members from the original submodule are misplaced relative PySide2 + +""" +_misplaced_members = { + "PySide2": { + #"QtGui.QStringListModel": "QtCore.QStringListModel", + "QtCore.Property": "QtCore.Property", + "QtCore.Signal": "QtCore.Signal", + "QtCore.Slot": "QtCore.Slot", + "QtCore.QAbstractProxyModel": "QtCore.QAbstractProxyModel", + "QtCore.QSortFilterProxyModel": "QtCore.QSortFilterProxyModel", + "QtCore.QItemSelection": "QtCore.QItemSelection", + "QtCore.QItemSelectionModel": "QtCore.QItemSelectionModel", + }, + "PyQt5": { + "QtCore.pyqtProperty": "QtCore.Property", + "QtCore.pyqtSignal": "QtCore.Signal", + "QtCore.pyqtSlot": "QtCore.Slot", + "QtCore.QAbstractProxyModel": "QtCore.QAbstractProxyModel", + "QtCore.QSortFilterProxyModel": "QtCore.QSortFilterProxyModel", + "QtCore.QStringListModel": "QtCore.QStringListModel", + "QtCore.QItemSelection": "QtCore.QItemSelection", + "QtCore.QItemSelectionModel": "QtCore.QItemSelectionModel", + }, + "PySide": { + "QtGui.QAbstractProxyModel": "QtCore.QAbstractProxyModel", + "QtGui.QSortFilterProxyModel": "QtCore.QSortFilterProxyModel", + "QtGui.QStringListModel": "QtCore.QStringListModel", + "QtGui.QItemSelection": "QtCore.QItemSelection", + "QtGui.QItemSelectionModel": "QtCore.QItemSelectionModel", + "QtCore.Property": "QtCore.Property", + "QtCore.Signal": "QtCore.Signal", + "QtCore.Slot": "QtCore.Slot", + + }, + "PyQt4": { + "QtGui.QAbstractProxyModel": "QtCore.QAbstractProxyModel", + "QtGui.QSortFilterProxyModel": "QtCore.QSortFilterProxyModel", + "QtGui.QItemSelection": "QtCore.QItemSelection", + "QtGui.QStringListModel": "QtCore.QStringListModel", + "QtGui.QItemSelectionModel": "QtCore.QItemSelectionModel", + "QtCore.pyqtProperty": "QtCore.Property", + "QtCore.pyqtSignal": "QtCore.Signal", + "QtCore.pyqtSlot": "QtCore.Slot", + } +} + +""" Compatibility Members + +This dictionary is used to build Qt.QtCompat objects that provide a consistent +interface for obsolete members, and differences in binding return values. + +{ + "binding": { + "classname": { + "targetname": "binding_namespace", + } + } +} +""" +_compatibility_members = { + "PySide2": { + "QHeaderView": { + "sectionsClickable": "QtWidgets.QHeaderView.sectionsClickable", + "setSectionsClickable": + "QtWidgets.QHeaderView.setSectionsClickable", + "sectionResizeMode": "QtWidgets.QHeaderView.sectionResizeMode", + "setSectionResizeMode": + "QtWidgets.QHeaderView.setSectionResizeMode", + "sectionsMovable": "QtWidgets.QHeaderView.sectionsMovable", + "setSectionsMovable": "QtWidgets.QHeaderView.setSectionsMovable", + }, + "QFileDialog": { + "getOpenFileName": "QtWidgets.QFileDialog.getOpenFileName", + "getOpenFileNames": "QtWidgets.QFileDialog.getOpenFileNames", + "getSaveFileName": "QtWidgets.QFileDialog.getSaveFileName", + }, + }, + "PyQt5": { + "QHeaderView": { + "sectionsClickable": "QtWidgets.QHeaderView.sectionsClickable", + "setSectionsClickable": + "QtWidgets.QHeaderView.setSectionsClickable", + "sectionResizeMode": "QtWidgets.QHeaderView.sectionResizeMode", + "setSectionResizeMode": + "QtWidgets.QHeaderView.setSectionResizeMode", + "sectionsMovable": "QtWidgets.QHeaderView.sectionsMovable", + "setSectionsMovable": "QtWidgets.QHeaderView.setSectionsMovable", + }, + "QFileDialog": { + "getOpenFileName": "QtWidgets.QFileDialog.getOpenFileName", + "getOpenFileNames": "QtWidgets.QFileDialog.getOpenFileNames", + "getSaveFileName": "QtWidgets.QFileDialog.getSaveFileName", + }, + }, + "PySide": { + "QHeaderView": { + "sectionsClickable": "QtWidgets.QHeaderView.isClickable", + "setSectionsClickable": "QtWidgets.QHeaderView.setClickable", + "sectionResizeMode": "QtWidgets.QHeaderView.resizeMode", + "setSectionResizeMode": "QtWidgets.QHeaderView.setResizeMode", + "sectionsMovable": "QtWidgets.QHeaderView.isMovable", + "setSectionsMovable": "QtWidgets.QHeaderView.setMovable", + }, + "QFileDialog": { + "getOpenFileName": "QtWidgets.QFileDialog.getOpenFileName", + "getOpenFileNames": "QtWidgets.QFileDialog.getOpenFileNames", + "getSaveFileName": "QtWidgets.QFileDialog.getSaveFileName", + }, + }, + "PyQt4": { + "QHeaderView": { + "sectionsClickable": "QtWidgets.QHeaderView.isClickable", + "setSectionsClickable": "QtWidgets.QHeaderView.setClickable", + "sectionResizeMode": "QtWidgets.QHeaderView.resizeMode", + "setSectionResizeMode": "QtWidgets.QHeaderView.setResizeMode", + "sectionsMovable": "QtWidgets.QHeaderView.isMovable", + "setSectionsMovable": "QtWidgets.QHeaderView.setMovable", + }, + "QFileDialog": { + "getOpenFileName": "QtWidgets.QFileDialog.getOpenFileName", + "getOpenFileNames": "QtWidgets.QFileDialog.getOpenFileNames", + "getSaveFileName": "QtWidgets.QFileDialog.getSaveFileName", + }, + }, +} + + +def _apply_site_config(): + try: + import QtSiteConfig + except ImportError: + # If no QtSiteConfig module found, no modifications + # to _common_members are needed. + pass + else: + # Provide the ability to modify the dicts used to build Qt.py + if hasattr(QtSiteConfig, 'update_members'): + QtSiteConfig.update_members(_common_members) + + if hasattr(QtSiteConfig, 'update_misplaced_members'): + QtSiteConfig.update_misplaced_members(members=_misplaced_members) + + if hasattr(QtSiteConfig, 'update_compatibility_members'): + QtSiteConfig.update_compatibility_members( + members=_compatibility_members) + + +def _new_module(name): + return types.ModuleType(__name__ + "." + name) + + +def _import_sub_module(module, name): + """import_sub_module will mimic the function of importlib.import_module""" + module = __import__(module.__name__ + "." + name) + for level in name.split("."): + module = getattr(module, level) + return module + + +def _setup(module, extras): + """Install common submodules""" + + Qt.__binding__ = module.__name__ + + for name in list(_common_members) + extras: + try: + submodule = _import_sub_module( + module, name) + except ImportError: + continue + + setattr(Qt, "_" + name, submodule) + + if name not in extras: + # Store reference to original binding, + # but don't store speciality modules + # such as uic or QtUiTools + setattr(Qt, name, _new_module(name)) + + +def _wrapinstance(func, ptr, base=None): + """Enable implicit cast of pointer to most suitable class + + This behaviour is available in sip per default. + + Based on http://nathanhorne.com/pyqtpyside-wrap-instance + + Usage: + This mechanism kicks in under these circumstances. + 1. Qt.py is using PySide 1 or 2. + 2. A `base` argument is not provided. + + See :func:`QtCompat.wrapInstance()` + + Arguments: + func (function): Original function + ptr (long): Pointer to QObject in memory + base (QObject, optional): Base class to wrap with. Defaults to QObject, + which should handle anything. + + """ + + assert isinstance(ptr, long), "Argument 'ptr' must be of type " + assert (base is None) or issubclass(base, Qt.QtCore.QObject), ( + "Argument 'base' must be of type ") + + if base is None: + q_object = func(long(ptr), Qt.QtCore.QObject) + meta_object = q_object.metaObject() + class_name = meta_object.className() + super_class_name = meta_object.superClass().className() + + if hasattr(Qt.QtWidgets, class_name): + base = getattr(Qt.QtWidgets, class_name) + + elif hasattr(Qt.QtWidgets, super_class_name): + base = getattr(Qt.QtWidgets, super_class_name) + + else: + base = Qt.QtCore.QObject + + return func(long(ptr), base) + + +def _reassign_misplaced_members(binding): + """Apply misplaced members from `binding` to Qt.py + + Arguments: + binding (dict): Misplaced members + + """ + + for src, dst in _misplaced_members[binding].items(): + src_module, src_member = src.split(".") + dst_module, dst_member = dst.split(".") + + try: + src_object = getattr(Qt, dst_module) + except AttributeError: + # Skip reassignment of non-existing members. + # This can happen if a request was made to + # rename a member that didn't exist, for example + # if QtWidgets isn't available on the target platform. + continue + + dst_value = getattr(getattr(Qt, "_" + src_module), src_member) + + setattr( + src_object, + dst_member, + dst_value + ) + + +def _build_compatibility_members(binding, decorators=None): + """Apply `binding` to QtCompat + + Arguments: + binding (str): Top level binding in _compatibility_members. + decorators (dict, optional): Provides the ability to decorate the + original Qt methods when needed by a binding. This can be used + to change the returned value to a standard value. The key should + be the classname, the value is a dict where the keys are the + target method names, and the values are the decorator functions. + + """ + + decorators = decorators or dict() + + # Allow optional site-level customization of the compatibility members. + # This method does not need to be implemented in QtSiteConfig. + try: + import QtSiteConfig + except ImportError: + pass + else: + if hasattr(QtSiteConfig, 'update_compatibility_decorators'): + QtSiteConfig.update_compatibility_decorators(binding, decorators) + + _QtCompat = type("QtCompat", (object,), {}) + + for classname, bindings in _compatibility_members[binding].items(): + attrs = {} + for target, binding in bindings.items(): + namespaces = binding.split('.') + try: + src_object = getattr(Qt, "_" + namespaces[0]) + except AttributeError as e: + _log("QtCompat: AttributeError: %s" % e) + # Skip reassignment of non-existing members. + # This can happen if a request was made to + # rename a member that didn't exist, for example + # if QtWidgets isn't available on the target platform. + continue + + # Walk down any remaining namespace getting the object assuming + # that if the first namespace exists the rest will exist. + for namespace in namespaces[1:]: + src_object = getattr(src_object, namespace) + + # decorate the Qt method if a decorator was provided. + if target in decorators.get(classname, []): + # staticmethod must be called on the decorated method to + # prevent a TypeError being raised when the decorated method + # is called. + src_object = staticmethod( + decorators[classname][target](src_object)) + + attrs[target] = src_object + + # Create the QtCompat class and install it into the namespace + compat_class = type(classname, (_QtCompat,), attrs) + setattr(Qt.QtCompat, classname, compat_class) + + +def _pyside2(): + """Initialise PySide2 + + These functions serve to test the existence of a binding + along with set it up in such a way that it aligns with + the final step; adding members from the original binding + to Qt.py + + """ + + import PySide2 as module + _setup(module, ["QtUiTools"]) + + Qt.__binding_version__ = module.__version__ + + try: + try: + # Before merge of PySide and shiboken + import shiboken2 + except ImportError: + # After merge of PySide and shiboken, May 2017 + from PySide2 import shiboken2 + + Qt.QtCompat.wrapInstance = ( + lambda ptr, base=None: _wrapinstance( + shiboken2.wrapInstance, ptr, base) + ) + Qt.QtCompat.getCppPointer = lambda object: \ + shiboken2.getCppPointer(object)[0] + + except ImportError: + pass # Optional + + if hasattr(Qt, "_QtUiTools"): + Qt.QtCompat.loadUi = _loadUi + + if hasattr(Qt, "_QtCore"): + Qt.__qt_version__ = Qt._QtCore.qVersion() + Qt.QtCompat.translate = Qt._QtCore.QCoreApplication.translate + + if hasattr(Qt, "_QtWidgets"): + Qt.QtCompat.setSectionResizeMode = \ + Qt._QtWidgets.QHeaderView.setSectionResizeMode + + _reassign_misplaced_members("PySide2") + _build_compatibility_members("PySide2") + + +def _pyside(): + """Initialise PySide""" + + import PySide as module + _setup(module, ["QtUiTools"]) + + Qt.__binding_version__ = module.__version__ + + try: + try: + # Before merge of PySide and shiboken + import shiboken + except ImportError: + # After merge of PySide and shiboken, May 2017 + from PySide import shiboken + + Qt.QtCompat.wrapInstance = ( + lambda ptr, base=None: _wrapinstance( + shiboken.wrapInstance, ptr, base) + ) + Qt.QtCompat.getCppPointer = lambda object: \ + shiboken.getCppPointer(object)[0] + + except ImportError: + pass # Optional + + if hasattr(Qt, "_QtUiTools"): + Qt.QtCompat.loadUi = _loadUi + + if hasattr(Qt, "_QtGui"): + setattr(Qt, "QtWidgets", _new_module("QtWidgets")) + setattr(Qt, "_QtWidgets", Qt._QtGui) + + Qt.QtCompat.setSectionResizeMode = Qt._QtGui.QHeaderView.setResizeMode + + if hasattr(Qt, "_QtCore"): + Qt.__qt_version__ = Qt._QtCore.qVersion() + QCoreApplication = Qt._QtCore.QCoreApplication + Qt.QtCompat.translate = ( + lambda context, sourceText, disambiguation, n: + QCoreApplication.translate( + context, + sourceText, + disambiguation, + QCoreApplication.CodecForTr, + n + ) + ) + + _reassign_misplaced_members("PySide") + _build_compatibility_members("PySide") + + +def _pyqt5(): + """Initialise PyQt5""" + + import PyQt5 as module + _setup(module, ["uic"]) + + try: + import sip + Qt.QtCompat.wrapInstance = ( + lambda ptr, base=None: _wrapinstance( + sip.wrapinstance, ptr, base) + ) + Qt.QtCompat.getCppPointer = lambda object: \ + sip.unwrapinstance(object) + + except ImportError: + pass # Optional + + if hasattr(Qt, "_uic"): + Qt.QtCompat.loadUi = _loadUi + + if hasattr(Qt, "_QtCore"): + Qt.__binding_version__ = Qt._QtCore.PYQT_VERSION_STR + Qt.__qt_version__ = Qt._QtCore.QT_VERSION_STR + Qt.QtCompat.translate = Qt._QtCore.QCoreApplication.translate + + if hasattr(Qt, "_QtWidgets"): + Qt.QtCompat.setSectionResizeMode = \ + Qt._QtWidgets.QHeaderView.setSectionResizeMode + + _reassign_misplaced_members("PyQt5") + _build_compatibility_members('PyQt5') + + +def _pyqt4(): + """Initialise PyQt4""" + + import sip + + # Validation of envivornment variable. Prevents an error if + # the variable is invalid since it's just a hint. + try: + hint = int(QT_SIP_API_HINT) + except TypeError: + hint = None # Variable was None, i.e. not set. + except ValueError: + raise ImportError("QT_SIP_API_HINT=%s must be a 1 or 2") + + for api in ("QString", + "QVariant", + "QDate", + "QDateTime", + "QTextStream", + "QTime", + "QUrl"): + try: + sip.setapi(api, hint or 2) + except AttributeError: + raise ImportError("PyQt4 < 4.6 isn't supported by Qt.py") + except ValueError: + actual = sip.getapi(api) + if not hint: + raise ImportError("API version already set to %d" % actual) + else: + # Having provided a hint indicates a soft constraint, one + # that doesn't throw an exception. + sys.stderr.write( + "Warning: API '%s' has already been set to %d.\n" + % (api, actual) + ) + + import PyQt4 as module + _setup(module, ["uic"]) + + try: + import sip + Qt.QtCompat.wrapInstance = ( + lambda ptr, base=None: _wrapinstance( + sip.wrapinstance, ptr, base) + ) + Qt.QtCompat.getCppPointer = lambda object: \ + sip.unwrapinstance(object) + + except ImportError: + pass # Optional + + if hasattr(Qt, "_uic"): + Qt.QtCompat.loadUi = _loadUi + + if hasattr(Qt, "_QtGui"): + setattr(Qt, "QtWidgets", _new_module("QtWidgets")) + setattr(Qt, "_QtWidgets", Qt._QtGui) + + Qt.QtCompat.setSectionResizeMode = \ + Qt._QtGui.QHeaderView.setResizeMode + + if hasattr(Qt, "_QtCore"): + Qt.__binding_version__ = Qt._QtCore.PYQT_VERSION_STR + Qt.__qt_version__ = Qt._QtCore.QT_VERSION_STR + + QCoreApplication = Qt._QtCore.QCoreApplication + Qt.QtCompat.translate = ( + lambda context, sourceText, disambiguation, n: + QCoreApplication.translate( + context, + sourceText, + disambiguation, + QCoreApplication.CodecForTr, + n) + ) + + _reassign_misplaced_members("PyQt4") + + # QFileDialog QtCompat decorator + def _standardizeQFileDialog(some_function): + """Decorator that makes PyQt4 return conform to other bindings""" + def wrapper(*args, **kwargs): + ret = (some_function(*args, **kwargs)) + + # PyQt4 only returns the selected filename, force it to a + # standard return of the selected filename, and a empty string + # for the selected filter + return ret, '' + + wrapper.__doc__ = some_function.__doc__ + wrapper.__name__ = some_function.__name__ + + return wrapper + + decorators = { + "QFileDialog": { + "getOpenFileName": _standardizeQFileDialog, + "getOpenFileNames": _standardizeQFileDialog, + "getSaveFileName": _standardizeQFileDialog, + } + } + _build_compatibility_members('PyQt4', decorators) + + +def _none(): + """Internal option (used in installer)""" + + Mock = type("Mock", (), {"__getattr__": lambda Qt, attr: None}) + + Qt.__binding__ = "None" + Qt.__qt_version__ = "0.0.0" + Qt.__binding_version__ = "0.0.0" + Qt.QtCompat.loadUi = lambda uifile, baseinstance=None: None + Qt.QtCompat.setSectionResizeMode = lambda *args, **kwargs: None + + for submodule in _common_members.keys(): + setattr(Qt, submodule, Mock()) + setattr(Qt, "_" + submodule, Mock()) + + +def _log(text): + if QT_VERBOSE: + sys.stdout.write(text + "\n") + + +def _loadUi(uifile, baseinstance=None): + """Dynamically load a user interface from the given `uifile` + + This function calls `uic.loadUi` if using PyQt bindings, + else it implements a comparable binding for PySide. + + Documentation: + http://pyqt.sourceforge.net/Docs/PyQt5/designer.html#PyQt5.uic.loadUi + + Arguments: + uifile (str): Absolute path to Qt Designer file. + baseinstance (QWidget): Instantiated QWidget or subclass thereof + + Return: + baseinstance if `baseinstance` is not `None`. Otherwise + return the newly created instance of the user interface. + + """ + if hasattr(baseinstance, "layout") and baseinstance.layout(): + message = ("QLayout: Attempting to add Layout to %s which " + "already has a layout") + raise RuntimeError(message % (baseinstance)) + + if hasattr(Qt, "_uic"): + return Qt._uic.loadUi(uifile, baseinstance) + + elif hasattr(Qt, "_QtUiTools"): + # Implement `PyQt5.uic.loadUi` for PySide(2) + + class _UiLoader(Qt._QtUiTools.QUiLoader): + """Create the user interface in a base instance. + + Unlike `Qt._QtUiTools.QUiLoader` itself this class does not + create a new instance of the top-level widget, but creates the user + interface in an existing instance of the top-level class if needed. + + This mimics the behaviour of `PyQt5.uic.loadUi`. + + """ + + def __init__(self, baseinstance): + super(_UiLoader, self).__init__(baseinstance) + self.baseinstance = baseinstance + + def load(self, uifile, *args, **kwargs): + from xml.etree.ElementTree import ElementTree + + # For whatever reason, if this doesn't happen then + # reading an invalid or non-existing .ui file throws + # a RuntimeError. + etree = ElementTree() + etree.parse(uifile) + + widget = Qt._QtUiTools.QUiLoader.load( + self, uifile, *args, **kwargs) + + # Workaround for PySide 1.0.9, see issue #208 + widget.parentWidget() + + return widget + + def createWidget(self, class_name, parent=None, name=""): + """Called for each widget defined in ui file + + Overridden here to populate `baseinstance` instead. + + """ + + if parent is None and self.baseinstance: + # Supposed to create the top-level widget, + # return the base instance instead + return self.baseinstance + + # For some reason, Line is not in the list of available + # widgets, but works fine, so we have to special case it here. + if class_name in self.availableWidgets() + ["Line"]: + # Create a new widget for child widgets + widget = Qt._QtUiTools.QUiLoader.createWidget(self, + class_name, + parent, + name) + + else: + raise Exception("Custom widget '%s' not supported" + % class_name) + + if self.baseinstance: + # Set an attribute for the new child widget on the base + # instance, just like PyQt5.uic.loadUi does. + setattr(self.baseinstance, name, widget) + + return widget + + widget = _UiLoader(baseinstance).load(uifile) + Qt.QtCore.QMetaObject.connectSlotsByName(widget) + + return widget + + else: + raise NotImplementedError("No implementation available for loadUi") + + +def _convert(lines): + """Convert compiled .ui file from PySide2 to Qt.py + + Arguments: + lines (list): Each line of of .ui file + + Usage: + >> with open("myui.py") as f: + .. lines = _convert(f.readlines()) + + """ + + def parse(line): + line = line.replace("from PySide2 import", "from Qt import QtCompat,") + line = line.replace("QtWidgets.QApplication.translate", + "QtCompat.translate") + if "QtCore.SIGNAL" in line: + raise NotImplementedError("QtCore.SIGNAL is missing from PyQt5 " + "and so Qt.py does not support it: you " + "should avoid defining signals inside " + "your ui files.") + return line + + parsed = list() + for line in lines: + line = parse(line) + parsed.append(line) + + return parsed + + +def _cli(args): + """Qt.py command-line interface""" + import argparse + + parser = argparse.ArgumentParser() + parser.add_argument("--convert", + help="Path to compiled Python module, e.g. my_ui.py") + parser.add_argument("--compile", + help="Accept raw .ui file and compile with native " + "PySide2 compiler.") + parser.add_argument("--stdout", + help="Write to stdout instead of file", + action="store_true") + parser.add_argument("--stdin", + help="Read from stdin instead of file", + action="store_true") + + args = parser.parse_args(args) + + if args.stdout: + raise NotImplementedError("--stdout") + + if args.stdin: + raise NotImplementedError("--stdin") + + if args.compile: + raise NotImplementedError("--compile") + + if args.convert: + sys.stdout.write("#\n" + "# WARNING: --convert is an ALPHA feature.\n#\n" + "# See https://github.com/mottosso/Qt.py/pull/132\n" + "# for details.\n" + "#\n") + + # + # ------> Read + # + with open(args.convert) as f: + lines = _convert(f.readlines()) + + backup = "%s_backup%s" % os.path.splitext(args.convert) + sys.stdout.write("Creating \"%s\"..\n" % backup) + shutil.copy(args.convert, backup) + + # + # <------ Write + # + with open(args.convert, "w") as f: + f.write("".join(lines)) + + sys.stdout.write("Successfully converted \"%s\"\n" % args.convert) + + +def _install(): + # Default order (customise order and content via QT_PREFERRED_BINDING) + default_order = ("PySide2", "PyQt5", "PySide", "PyQt4") + preferred_order = list( + b for b in QT_PREFERRED_BINDING.split(os.pathsep) if b + ) + + order = preferred_order or default_order + + available = { + "PySide2": _pyside2, + "PyQt5": _pyqt5, + "PySide": _pyside, + "PyQt4": _pyqt4, + "None": _none + } + + _log("Order: '%s'" % "', '".join(order)) + + # Allow site-level customization of the available modules. + _apply_site_config() + + found_binding = False + for name in order: + _log("Trying %s" % name) + + try: + available[name]() + found_binding = True + break + + except ImportError as e: + _log("ImportError: %s" % e) + + except KeyError: + _log("ImportError: Preferred binding '%s' not found." % name) + + if not found_binding: + # If not binding were found, throw this error + raise ImportError("No Qt binding were found.") + + # Install individual members + for name, members in _common_members.items(): + try: + their_submodule = getattr(Qt, "_%s" % name) + except AttributeError: + continue + + our_submodule = getattr(Qt, name) + + # Enable import * + __all__.append(name) + + # Enable direct import of submodule, + # e.g. import Qt.QtCore + sys.modules[__name__ + "." + name] = our_submodule + + for member in members: + # Accept that a submodule may miss certain members. + try: + their_member = getattr(their_submodule, member) + except AttributeError: + _log("'%s.%s' was missing." % (name, member)) + continue + + setattr(our_submodule, member, their_member) + + # Backwards compatibility + if hasattr(Qt.QtCompat, 'loadUi'): + Qt.QtCompat.load_ui = Qt.QtCompat.loadUi + + +_install() + +# Setup Binding Enum states +Qt.IsPySide2 = Qt.__binding__ == 'PySide2' +Qt.IsPyQt5 = Qt.__binding__ == 'PyQt5' +Qt.IsPySide = Qt.__binding__ == 'PySide' +Qt.IsPyQt4 = Qt.__binding__ == 'PyQt4' + +"""Augment QtCompat + +QtCompat contains wrappers and added functionality +to the original bindings, such as the CLI interface +and otherwise incompatible members between bindings, +such as `QHeaderView.setSectionResizeMode`. + +""" + +Qt.QtCompat._cli = _cli +Qt.QtCompat._convert = _convert + +# Enable command-line interface +if __name__ == "__main__": + _cli(sys.argv[1:]) + + +# The MIT License (MIT) +# +# Copyright (c) 2016-2017 Marcus Ottosson +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +# +# In PySide(2), loadUi does not exist, so we implement it +# +# `_UiLoader` is adapted from the qtpy project, which was further influenced +# by qt-helpers which was released under a 3-clause BSD license which in turn +# is based on a solution at: +# +# - https://gist.github.com/cpbotha/1b42a20c8f3eb9bb7cb8 +# +# The License for this code is as follows: +# +# qt-helpers - a common front-end to various Qt modules +# +# Copyright (c) 2015, Chris Beaumont and Thomas Robitaille +# +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the +# distribution. +# * Neither the name of the Glue project nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS +# IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, +# THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# +# Which itself was based on the solution at +# +# https://gist.github.com/cpbotha/1b42a20c8f3eb9bb7cb8 +# +# which was released under the MIT license: +# +# Copyright (c) 2011 Sebastian Wiesner +# Modifications by Charl Botha +# +# Permission is hereby granted, free of charge, to any person obtaining a +# copy of this software and associated documentation files +# (the "Software"),to deal in the Software without restriction, +# including without limitation +# the rights to use, copy, modify, merge, publish, distribute, sublicense, +# and/or sell copies of the Software, and to permit persons to whom the +# Software is furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +# CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/syncplay/vendor/qt4reactor.py b/syncplay/vendor/qt5reactor.py similarity index 60% rename from syncplay/vendor/qt4reactor.py rename to syncplay/vendor/qt5reactor.py index a337099..cde2b5e 100644 --- a/syncplay/vendor/qt4reactor.py +++ b/syncplay/vendor/qt5reactor.py @@ -1,5 +1,76 @@ -# Copyright (c) 2001-2011 Twisted Matrix Laboratories. -# See LICENSE for details. +# -*- coding: utf-8 -*- +# Copyright (c) 2001-2017 +# Allen Short +# Andy Gayton +# Andrew Bennetts +# Antoine Pitrou +# Apple Computer, Inc. +# Ashwini Oruganti +# bakbuk +# Benjamin Bruheim +# Bob Ippolito +# Burak Nehbit +# Canonical Limited +# Christopher Armstrong +# Christopher R. Wood +# David Reid +# Donovan Preston +# Elvis Stansvik +# Eric Mangold +# Eyal Lotem +# Glenn Tarbox +# Google Inc. +# Hybrid Logic Ltd. +# Hynek Schlawack +# Itamar Turner-Trauring +# James Knight +# Jason A. Mobarak +# Jean-Paul Calderone +# Jessica McKellar +# Jonathan Jacobs +# Jonathan Lange +# Jonathan D. Simms +# Jürgen Hermann +# Julian Berman +# Kevin Horn +# Kevin Turner +# Kyle Altendorf +# Laurens Van Houtven +# Mary Gardiner +# Matthew Lefkowitz +# Massachusetts Institute of Technology +# Moshe Zadka +# Paul Swartz +# Pavel Pergamenshchik +# Ralph Meijer +# Richard Wall +# Sean Riley +# Software Freedom Conservancy +# Tarashish Mishra +# Travis B. Hartwell +# Thijs Triemstra +# Thomas Herve +# Timothy Allen +# Tom Prince + +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: + +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. + +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. """ @@ -7,13 +78,8 @@ 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') + | import qt5reactor + | qt5reactor.install() Then use twisted.internet APIs as usual. The other methods here are not intended to be called directly. @@ -27,65 +93,61 @@ 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 +Qt5 Port: U{Burak Nehbit} -Maintainer: U{Glenn H Tarbox, PhD} +Current maintainer: U{Christopher R. Wood} +Previous maintainer: U{Tarashish Mishra} +Previous 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 syncplay.vendor.Qt.QtCore import ( + QCoreApplication, QEventLoop, QObject, QSocketNotifier, QTimer, Signal) +from twisted.internet import posixbase 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 +from zope.interface import implementer class TwistedSocketNotifier(QObject): - """ - Connection between an fd event and reader/writer callbacks. - """ + """Connection between an fd event and reader/writer callbacks.""" + + activated = Signal(int) 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) + fd = self.watcher.fileno() + self.notifier = QSocketNotifier(watcher, 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) - + self.notifier.activated.connect(self.fn) def shutdown(self): self.notifier.setEnabled(False) - self.disconnect(self.notifier, SIGNAL("activated(int)"), self.fn) + self.notifier.activated.disconnect(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 + # 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 + # Don't call me again, until the data has been read self.notifier.setEnabled(False) why = None try: @@ -98,18 +160,20 @@ class TwistedSocketNotifier(QObject): if why: self.reactor._disconnectSelectable(w, why, inRead) elif self.watcher: - self.notifier.setEnabled(True) # Re enable notification following sucessfull read + 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: @@ -120,12 +184,13 @@ class TwistedSocketNotifier(QObject): elif self.watcher: self.notifier.setEnabled(True) self.reactor._iterate(fromqt=True) + log.callWithLogger(w, _write) - +@implementer(IReactorFDSet) class QtReactor(posixbase.PosixReactorBase): - implements(IReactorFDSet) + # implements(IReactorFDSet) def __init__(self): self._reads = {} @@ -133,19 +198,17 @@ class QtReactor(posixbase.PosixReactorBase): self._notifiers = {} self._timer = QTimer() self._timer.setSingleShot(True) - QObject.connect(self._timer, SIGNAL("timeout()"), self.iterate) - + self._timer.timeout.connect(self.iterate_qt) if QCoreApplication.instance() is None: # Application Object has not been started yet - self.qApp=QCoreApplication([]) - self._ownApp=True + self.qApp = QCoreApplication([]) + self._ownApp = True else: self.qApp = QCoreApplication.instance() - self._ownApp=False + 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. @@ -156,21 +219,14 @@ class QtReactor(posixbase.PosixReactorBase): 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. - """ + """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. - """ + """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. @@ -182,81 +238,66 @@ class QtReactor(posixbase.PosixReactorBase): notifier = primary.pop(xer) notifier.shutdown() - def removeReader(self, reader): - """ - Remove a Selectable for notification of data available to read. - """ + """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. - """ + """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 - + """Remove all selectables, and return a list of them.""" + return self._removeAll(self._reads, self._writes) 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) + 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. - """ + """See twisted.internet.interfaces.IReactorCore.iterate.""" self.runUntilCurrent() - self.doIteration(delay, fromqt) + self.doIteration(delay, fromqt=fromqt) iterate = _iterate + def iterate_qt(self, delay=None): + self.iterate(delay=delay, fromqt=True) + def doIteration(self, delay=None, fromqt=False): - 'This method is called by a Qt timer or by network activity on a file descriptor' - + """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() + if delay is None: + delay = 0 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 + t = self.timeout() + if t is None: + timeout = 0.01 else: - timeout = self.timeout() + timeout = min(t, 0.01) 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 @@ -264,6 +305,21 @@ class QtReactor(posixbase.PosixReactorBase): self._blockApp = QEventLoop() self.runReturn() self._blockApp.exec_() + if self.running: + self.stop() + self.runUntilCurrent() + + # def sigInt(self, *args): + # print('I received a sigint. BAIBAI') + # posixbase.PosixReactorBase.sigInt() + # + # def sigTerm(self, *args): + # print('I received a sigterm. BAIBAI') + # posixbase.PosixReactorBase.sigTerm() + # + # def sigBreak(self, *args): + # print('I received a sigbreak. BAIBAI') + # posixbase.PosixReactorBase.sigBreak() class QtEventReactor(QtReactor): @@ -271,22 +327,15 @@ class QtEventReactor(QtReactor): self._events = {} super(QtEventReactor, self).__init__() - def addEvent(self, event, fd, action): - """ - Add a new win32 event to the event loop. - """ + """Add a new win32 event to the event loop.""" self._events[event] = (fd, action) - def removeEvent(self, event): - """ - Remove an event. - """ + """Remove an event.""" if event in self._events: del self._events[event] - def doEvents(self): handles = self._events.keys() if len(handles) > 0: @@ -304,46 +353,33 @@ class QtEventReactor(QtReactor): #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. - """ + def iterate(self, delay=None, fromqt=False): + """See twisted.internet.interfaces.IReactorCore.iterate.""" self.runUntilCurrent() self.doEvents() - self.doIteration(delay) + self.doIteration(delay, fromqt=fromqt) def posixinstall(): - """ - Install the Qt reactor. - """ - p = QtReactor() + """Install the Qt reactor.""" from twisted.internet.main import installReactor + p = QtReactor() installReactor(p) def win32install(): - """ - Install the Qt reactor. - """ - p = QtEventReactor() + """Install the Qt reactor.""" from twisted.internet.main import installReactor + p = QtEventReactor() installReactor(p) @@ -356,4 +392,3 @@ else: __all__ = ["install"] -