diff --git a/buildPy2exe.py b/buildPy2exe.py index 6e4269d..af07026 100644 --- a/buildPy2exe.py +++ b/buildPy2exe.py @@ -102,14 +102,14 @@ NSIS_SCRIPT_TEMPLATE = r""" LangString ^UninstConfig $${LANG_RUSSIAN} "Удалить файл настроек." LangString ^SyncplayLanguage $${LANG_GERMAN} "de" - LangString ^Associate $${LANG_GERMAN} "Syncplay mit Multimedia-Dateien assoziieren." + LangString ^Associate $${LANG_GERMAN} "Syncplay als Standardprogramm für Multimedia-Dateien verwenden." LangString ^VLC $${LANG_GERMAN} "Syncplay-Interface für VLC installieren (ab VLC 2+)" LangString ^Shortcut $${LANG_GERMAN} "Erstelle Verknüpfungen an folgenden Orten:" LangString ^BrowseVLCBtn $${LANG_GERMAN} "VLC-Ordner wählen" LangString ^StartMenu $${LANG_GERMAN} "Startmenü" LangString ^Desktop $${LANG_GERMAN} "Desktop" LangString ^QuickLaunchBar $${LANG_GERMAN} "Schnellstartleiste" - LangString ^AutomaticUpdates $${LANG_GERMAN} "Automatisch nach Updates suchen"; TODO: Confirm German translation ("Check for updates automatically") + LangString ^AutomaticUpdates $${LANG_GERMAN} "Automatisch nach Updates suchen"; LangString ^UninstConfig $${LANG_GERMAN} "Konfigurationsdatei löschen." ; Remove text to save space @@ -655,7 +655,7 @@ guiIcons = ['resources/accept.png', 'resources/arrow_undo.png', 'resources/clock 'resources/tick.png', 'resources/lock_open.png', 'resources/empty_checkbox.png', 'resources/tick_checkbox.png', 'resources/world_explore.png', 'resources/application_get.png', 'resources/cog.png', 'resources/arrow_switch.png', 'resources/film_go.png', 'resources/world_go.png', 'resources/arrow_refresh.png', 'resources/bullet_right_grey.png', - 'resources/world_add.png', 'resources/film_add.png', 'resources/delete.png' + 'resources/world_add.png', 'resources/film_add.png', 'resources/delete.png', 'resources/spinner.mng' ] resources = ["resources/icon.ico", "resources/syncplay.png"] resources.extend(guiIcons) diff --git a/resources/spinner.mng b/resources/spinner.mng new file mode 100755 index 0000000..435d749 Binary files /dev/null and b/resources/spinner.mng differ diff --git a/syncplay/messages.py b/syncplay/messages.py index 7abe1ad..7a3349b 100755 --- a/syncplay/messages.py +++ b/syncplay/messages.py @@ -834,7 +834,7 @@ de = { "vlc-interface-oldversion-warning": u"Warnung: Es ist eine alte Version des Syncplay Interface-Moduls für VLC im VLC-Verzeichnis installiert. In der Syncplay-Anleitung unter http://syncplay.pl/guide/ [Englisch] findest du Details zur Installation des syncplay.lua-Skripts.", "vlc-interface-not-installed": u"Warnung: Es wurde kein Syncplay Interface-Modul für VLC im VLC-Verzeichnis gefunden. Daher wird, wenn du VLC 2.0 nutzt, die syncplay.lua die mit Syncplay mitgeliefert wurde, verwendet. Dies bedeutet allerdings, dass keine anderen Interface-Skripts und Erweiterungen geladen werden. In der Syncplay-Anleitung unter http://syncplay.pl/guide/ [Englisch] findest du Details zur Installation des syncplay.lua-Skripts.", "media-player-latency-warning": u"Warnung: Der Mediaplayer brauchte {} Sekunden zum Antworten. Wenn Probleme bei der Synchronisation auftreten, schließe bitte andere Anwendungen, um Ressourcen freizugeben. Sollte das nicht funktionieren, versuche es mit einem anderen Media-Player.", # Seconds to respond - "mpv-unresponsive-error": u"mpv has not responded for {} seconds so appears to have malfunctioned. Please restart Syncplay.", # Seconds to respond # TODO: Translate to German + "mpv-unresponsive-error": u"MPV hat für {} Sekunden nicht geantwortet und scheint abgestürzt zu sein. Bitte starte Syncplay neu.", # Seconds to respond # Client prompts "enter-to-exit-prompt" : u"Enter drücken zum Beenden\n", diff --git a/syncplay/ui/ConfigurationGetter.py b/syncplay/ui/ConfigurationGetter.py index db6f7a1..f2f2929 100755 --- a/syncplay/ui/ConfigurationGetter.py +++ b/syncplay/ui/ConfigurationGetter.py @@ -379,7 +379,7 @@ class ConfigurationGetter(object): self._argparser.add_argument('-p', '--password', metavar='password', type=str, nargs='?', help=getMessage("password-argument")) self._argparser.add_argument('--player-path', metavar='path', type=str, help=getMessage("player-path-argument")) self._argparser.add_argument('--language', metavar='language', type=str, help=getMessage("language-argument")) - self._argparser.add_argument('file', metavar='file', type=str, nargs='?', help=getMessage("file-argument")) + self._argparser.add_argument('file', metavar='file', type=lambda s: unicode(s, 'utf8'), nargs='?', help=getMessage("file-argument")) self._argparser.add_argument('--clear-gui-data', action='store_true', help=getMessage("clear-gui-data-argument")) self._argparser.add_argument('-v', '--version', action='store_true', help=getMessage("version-argument")) self._argparser.add_argument('_args', metavar='options', type=str, nargs='*', help=getMessage("args-argument")) diff --git a/syncplay/ui/GuiConfiguration.py b/syncplay/ui/GuiConfiguration.py index 4d87aa1..c158034 100644 --- a/syncplay/ui/GuiConfiguration.py +++ b/syncplay/ui/GuiConfiguration.py @@ -6,6 +6,7 @@ from datetime import datetime from syncplay import utils import os import sys +import threading from syncplay.messages import getMessage, getLanguages, setLanguage, getInitialLanguage from syncplay import constants @@ -31,6 +32,39 @@ class GuiConfiguration: class WindowClosed(Exception): pass + +class GetPlayerIconThread(threading.Thread, QtCore.QObject): + daemon = True + done = QtCore.Signal(str, str) + + def __init__(self): + threading.Thread.__init__(self, name='GetPlayerIcon') + QtCore.QObject.__init__(self) + self.condvar = threading.Condition() + self.playerpath = None + + def setPlayerPath(self, playerpath): + self.condvar.acquire() + was_none = self.playerpath is None + self.playerpath = playerpath + if was_none: + self.condvar.notify() + self.condvar.release() + + def run(self): + while True: + self.condvar.acquire() + if self.playerpath is None: + self.condvar.wait() + playerpath = self.playerpath + self.playerpath = None + self.condvar.release() + + self.done.emit('spinner.mng', '') + iconpath = PlayerFactory().getPlayerIconByPath(playerpath) + self.done.emit(iconpath, playerpath) + + class ConfigDialog(QtGui.QDialog): pressedclosebutton = False @@ -140,15 +174,32 @@ class ConfigDialog(QtGui.QDialog): settings.endGroup() return foundpath - def updateExecutableIcon(self): - currentplayerpath = unicode(self.executablepathCombobox.currentText()) - iconpath = PlayerFactory().getPlayerIconByPath(currentplayerpath) - if iconpath != None and iconpath != "": - self.executableiconImage.load(self.resourcespath + iconpath) - self.executableiconLabel.setPixmap(QtGui.QPixmap.fromImage(self.executableiconImage)) + @QtCore.Slot(str, str) + def _updateExecutableIcon(self, iconpath, playerpath): + if iconpath is not None and iconpath != "": + if iconpath.endswith('.mng'): + movie = QtGui.QMovie(self.resourcespath + iconpath) + movie.setCacheMode(QtGui.QMovie.CacheMode.CacheAll) + self.executableiconLabel.setMovie(movie) + movie.start() + else: + self.executableiconImage.load(self.resourcespath + iconpath) + self.executableiconLabel.setPixmap(QtGui.QPixmap.fromImage(self.executableiconImage)) else: self.executableiconLabel.setPixmap(QtGui.QPixmap.fromImage(QtGui.QImage())) - self.updatePlayerArguments(currentplayerpath) + self.updatePlayerArguments(playerpath) + + def updateExecutableIcon(self): + """ + Start getting the icon path in another thread, which will set the GUI + icon if valid. + + This is performed outside the main thread because networked players may + take a long time to perform their checks and hang the GUI while doing + so. + """ + currentplayerpath = unicode(self.executablepathCombobox.currentText()) + self._playerProbeThread.setPlayerPath(currentplayerpath) def updatePlayerArguments(self, currentplayerpath): argumentsForPath = utils.getPlayerArgumentsByPathAsText(self.perPlayerArgs, currentplayerpath) @@ -925,7 +976,6 @@ class ConfigDialog(QtGui.QDialog): self.hostCombobox.setEditText(currentServer) def __init__(self, config, playerpaths, error, defaultConfig): - self.config = config self.defaultConfig = defaultConfig self.playerpaths = playerpaths @@ -934,6 +984,10 @@ class ConfigDialog(QtGui.QDialog): self.subitems = {} self.publicServers = None + self._playerProbeThread = GetPlayerIconThread() + self._playerProbeThread.done.connect(self._updateExecutableIcon) + self._playerProbeThread.start() + if self.config['clearGUIData'] == True: self.config['clearGUIData'] = False self.clearGUIData() @@ -997,4 +1051,4 @@ class ConfigDialog(QtGui.QDialog): self.processWidget(self, lambda w: self.loadTooltips(w)) self.processWidget(self, lambda w: self.loadValues(w)) self.processWidget(self, lambda w: self.connectChildren(w)) - self.populateEmptyServerList() \ No newline at end of file + self.populateEmptyServerList() diff --git a/syncplay/ui/gui.py b/syncplay/ui/gui.py index abe1f91..828e82b 100644 --- a/syncplay/ui/gui.py +++ b/syncplay/ui/gui.py @@ -1262,7 +1262,8 @@ class MainWindow(QtGui.QMainWindow): window.updateAction.triggered.connect(self.userCheckForUpdates) window.menuBar.addMenu(window.helpMenu) - window.mainLayout.setMenuBar(window.menuBar) + if not sys.platform.startswith('darwin'): + window.mainLayout.setMenuBar(window.menuBar) def addMainFrame(self, window): window.mainFrame = QtGui.QFrame()