def deInitialize(): """ Deinitialize the active application such that the filters are in CONSTRUCTED state :return: """ assertMainThread() if Application.activeApplication is None: raise NexTRuntimeError("No active application to initialize") MethodInvoker(Application.activeApplication.stop, Qt.DirectConnection) MethodInvoker(Application.activeApplication.close, Qt.DirectConnection) MethodInvoker(Application.activeApplication.deinit, Qt.DirectConnection)
def activeAppStateChange(self, newState): """ Called when the active application changes its state. :param newState: the new application's state (see FilterState) :return: """ assertMainThread() if newState == FilterState.CONSTRUCTED: self.actActivate.setEnabled(True) else: self.actActivate.setEnabled(False) if newState == FilterState.ACTIVE: if self._waitForOpenState is not None: app, pbfile, startPlay = self._waitForOpenState self._waitForOpenState = None if app == Application.activeApplication.getApplication( ).getName(): pbsrv = Services.getService("PlaybackControl") if startPlay: pbsrv.playbackPaused.connect(self._singleShotPlay) QTimer.singleShot(2000, self._disconnectSingleShotPlay) MethodInvoker(pbsrv.browser.setActive, Qt.QueuedConnection, pbfile) self.actDeactivate.setEnabled(True) self.actSaveWithGuiState.setEnabled(False) else: self.actDeactivate.setEnabled(False) self.actSaveWithGuiState.setEnabled(True)
def registerThread(self): """ This slot shall be called from each activated nexxT thread with a direct connection. :return: """ t = QThread.currentThread() logger.internal("registering thread %s", t.objectName()) with self._lockThreadSpecific: if not t in self._threadSpecificProfiling: self._threadSpecificProfiling[t] = ThreadSpecificProfItem() self._threadSpecificProfiling[t].timer = QTimer( parent=self.sender()) self._threadSpecificProfiling[t].timer.timeout.connect( self._generateRecord, Qt.DirectConnection) self._threadSpecificProfiling[t].timer.setInterval( int(ThreadSpecificProfItem.THREAD_PROFILING_PERIOD_SEC * 1e3)) self.stopTimers.connect( self._threadSpecificProfiling[t].timer.stop) self.startTimers.connect( self._threadSpecificProfiling[t].timer.start) if self._loadMonitoringEnabled: self._threadSpecificProfiling[t].timer.start() tmain = QCoreApplication.instance().thread() if self._mi is None and not tmain in self._threadSpecificProfiling: self._mi = MethodInvoker( dict(object=self, method="registerThread", thread=tmain), Qt.QueuedConnection)
def setSequenceWrapper(filename): assertMainThread() if Application.activeApplication is None: return if Application.activeApplication.getState() not in [FilterState.ACTIVE, FilterState.OPENED]: return if QDir.match(nameFilters, pathlib.Path(filename).name): logger.debug("setSequence %s", filename) if Application.activeApplication.getState() == FilterState.ACTIVE: Application.activeApplication.stop() MethodInvoker(dict(object=playbackDevice, method="setSequence"), Qt.QueuedConnection, filename) Application.activeApplication.start() logger.debug("setSequence done") else: logger.debug("%s does not match filters: %s", filename, nameFilters) MethodInvoker(dict(object=playbackDevice, method="setSequence"), Qt.QueuedConnection, None)
def setupConnections(self, playbackDevice, nameFilters): """ Sets up signal/slot connections between this view/controller instance and the given playbackDevice. This function is thread safe and shall be called by a direct QT connection. It is intended, that this function is called in the onOpen(...) method of a filter. It expects playbackDevice to provide the following slots: - startPlayback() (starts generating DataSamples) - pausePlayback() (pause mode, stop generating DataSamples) - stepForward(QString stream) (optional; in case given, a single step operation shall be performed. if stream is not None, the playback shall stop when receiving the next data sample of stream; otherwise the playback shall proceed to the next data sample of any stream) - stepBackward(QString stream) (optional; see stepForward) - seekBeginning(QString stream) (optional; goes to the beginning of the sequence) - seekEnd() (optional; goes to the end of the stream) - seekTime(qint64) (optional; goes to the specified time stamp) - setSequence(QString) (optional; opens the given sequence) - setTimeFactor(float) (optional; sets the playback time factor, factor > 0) It expects playbackDevice to provide the following signals (all signals are optional): - sequenceOpened(QString filename, qint64 begin, qint64 end, QStringList streams) - currentTimestampChanged(qint64 currentTime) - playbackStarted() - playbackPaused() - timeRatioChanged(float) :param playbackDevice: a QObject providing the aforementioned signals and slots :param nameFilters: a QStringList providing information about suported fileextensions (e.g. ["*.avi", "*.mp4"]) :return: """ with QMutexLocker(self._mutex): for devid in self._registeredDevices: if self._registeredDevices[devid]["object"] is playbackDevice: raise NexTRuntimeError("Trying to register a playbackDevice object twice.") proxy = PlaybackDeviceProxy(self, playbackDevice, nameFilters) featureset = proxy.featureSet() for feature in ["stepForward", "stepBackward", "seekTime", "seekBeginning", "seekEnd", "setTimeFactor", "startPlayback", "pausePlayback"]: signal = getattr(self, "_" + feature) slot = getattr(proxy, feature, None) if slot is not None: signal.connect(slot) for feature in ["sequenceOpened", "currentTimestampChanged", "playbackStarted", "playbackPaused", "timeRatioChanged"]: slot = getattr(self, "_" + feature) signal = getattr(proxy, feature, None) if signal is not None: signal.connect(slot, Qt.UniqueConnection) self._registeredDevices[self._deviceId] = dict(object=playbackDevice, featureset=featureset, nameFilters=nameFilters, proxy=proxy) self._deviceId += 1 MethodInvoker(dict(object=self, method="_updateFeatureSet", thread=mainThread()), Qt.QueuedConnection)
def initialize(): """ Initialize the active application such that the filters are active. :return: """ assertMainThread() if Application.activeApplication is None: raise NexTRuntimeError("No active application to initialize") # make sure that the application is re-created before initializing it # this is needed to synchronize the properties, etc. MethodInvoker( Application.activeApplication.getApplication().getConfiguration(). activate, Qt.DirectConnection, Application.activeApplication.getApplication().getName()) MethodInvoker(Application.activeApplication.init, Qt.DirectConnection) MethodInvoker(Application.activeApplication.open, Qt.DirectConnection) MethodInvoker(Application.activeApplication.start, Qt.DirectConnection)
def createFilterAndUpdate(self, immediate=True): """ Creates the filter, performs init() operation and updates the port information. :return: None """ if immediate: self._createFilterAndUpdate() self._createFilterAndUpdatePending = None elif self._createFilterAndUpdatePending is None: self._createFilterAndUpdatePending = MethodInvoker( dict(object=self, method="_createFilterAndUpdate"), Qt.QueuedConnection)
def createFilterAndUpdate(self, immediate=True): """ Creates the filter, performs init() operation and updates the port information. :return: None """ # TODO is it really a good idea to use queued connections for dynamic port changes? # maybe there is a better way to prevent too many calls to _createFilterAndUpdate if immediate: self._createFilterAndUpdate() self._createFilterAndUpdatePending = None elif self._createFilterAndUpdatePending is None: self._createFilterAndUpdatePending = MethodInvoker(dict(object=self, method="_createFilterAndUpdate"), Qt.QueuedConnection)
def appActivated(self, name, app): # pylint: disable=unused-argument """ Called when the application is activated. :param name: the application name :param app: An ActiveApplication instance. :return: """ assertMainThread() if app is not None: self.activeAppStateChange(app.getState()) app.stateChanged.connect(self.activeAppStateChange) if self._waitForActivated == app.getApplication(): MethodInvoker(self.activate, Qt.QueuedConnection) else: self.actActivate.setEnabled(False) self.actDeactivate.setEnabled(False) self._waitForActivated = None
def removeConnections(self, playbackDevice): """ unregisters the given playbackDevice and disconnects all. It is intended that this function is called in the onClose(...) method of a filter. :param playbackDevice: the playback device to be unregistered. :return: None """ with QMutexLocker(self._mutex): found = [] for devid in self._registeredDevices: if self._registeredDevices[devid]["object"] is playbackDevice: found.append(devid) if len(found) > 0: for devid in found: del self._registeredDevices[devid] logger.debug("disconnected connections of playback device. number of devices left: %d", len(self._registeredDevices)) MethodInvoker(dict(object=self, method="_updateFeatureSet", thread=mainThread()), Qt.QueuedConnection)
def startNexT(cfgfile, active, execScripts, execCode, withGui): """ Starts next with the given config file and activates the given application. :param cfgfile: path to config file :param active: active application (if None, the first application in the config will be used) :return: None """ logger.debug("Starting nexxT...") config = Configuration() lcl = QLocale.system() lcl.setNumberOptions(QLocale.c().numberOptions()) QLocale.setDefault(lcl) if withGui: app = QApplication() if QApplication.instance() is None else QApplication.instance() app.setWindowIcon(QIcon(":icons/nexxT.svg")) app.setOrganizationName("nexxT") app.setApplicationName("nexxT") setupGuiServices(config) else: app = QCoreApplication() if QCoreApplication.instance() is None else QCoreApplication.instance() app.setOrganizationName("nexxT") app.setApplicationName("nexxT") setupConsoleServices(config) if cfgfile is not None: ConfigFileLoader.load(config, cfgfile) if withGui: mainWindow = Services.getService("MainWindow") mainWindow.restoreState() mainWindow.show() # the reference will still be held by the service, but here we don't need it anymore del mainWindow if active is not None: config.activate(active) # pylint: disable=unused-variable # need to hold the reference of this until the method is called i2 = MethodInvoker(dict(object=Application, method="initialize", thread=app.thread()), MethodInvoker.IDLE_TASK) # pylint: disable=unused-variable waitForSignal(config.appActivated) if Application.activeApplication.getState() != FilterState.ACTIVE: waitForSignal(Application.activeApplication.stateChanged, lambda s: s == FilterState.ACTIVE) logger.info("done") def cleanup(): logger.debug("cleaning up loaded services") Services.removeAll() logger.debug("cleaning up loaded plugins") for v in ("last_traceback", "last_type", "last_value"): if hasattr(sys, v): del sys.__dict__[v] #PluginManager.singleton().unloadAll() logger.debug("cleaning up complete") code_globals = {} for c in execCode: logger.info("Executing code '%s'", c) # note that exec is used intentionally here to provide the user with scripting posibilities exec(compile(c, "<string>", 'exec'), code_globals) # pylint: disable=exec-used logger.debug("Executing code done") for s in execScripts: logger.info("Executing script '%s'", s) with open(s) as fscript: # note that exec is used intentionally here to provide the user with scripting possibilities exec(compile(fscript.read(), s, 'exec'), code_globals) # pylint: disable=exec-used logger.debug("Executing script done") res = app.exec_() logger.debug("closing config") config.close() cleanup() logger.internal("app.exec_ returned") return res
def execute_0(): """ Creates a new application with an app and a composite filter and saves it to a file; finally activate the created app. :return: """ logger.info("execute_0:begin") cfg = Services.getService("Configuration") # create a new configuration with a composite graph and an application execute_0.i = MethodInvoker(cfg.newConfig, Qt.QueuedConnection, "basicworkflow.json") waitForSignal(cfg.configuration().configNameChanged) # create simple composite filter cfg.configuration().renameComposite( cfg.configuration().addNewCompositeFilter(), "composite") comp = cfg.configuration().compositeFilterByName("composite") node = comp.getGraph().addNode(library="pyfile://./SimpleStaticFilter.py", factoryFunction="SimpleStaticFilter") idx = _getIndex(cfg.model, ["composite", "composite", node, "sleep_time"]) cfg.model.setData(idx.siblingAtColumn(1), 0.01, Qt.EditRole) execute_0.i = MethodInvoker(comp.getGraph().addDynamicInputPort, Qt.QueuedConnection, "CompositeOutput", "out") waitForSignal(comp.getGraph().dynInputPortAdded) execute_0.i = MethodInvoker(comp.getGraph().addDynamicOutputPort, Qt.QueuedConnection, "CompositeInput", "in") waitForSignal(comp.getGraph().dynOutputPortAdded) comp.getGraph().addConnection("CompositeInput", "in", node, "inPort") comp.getGraph().addConnection(node, "outPort", "CompositeOutput", "out") # create simple application (live/recorder) cfg.configuration().renameApp(cfg.configuration().addNewApplication(), "myApp") app = cfg.configuration().applicationByName("myApp") import nexxT if nexxT.useCImpl: src = app.getGraph().addNode( library="entry_point://tests.nexxT.CSimpleSource", factoryFunction="entry_point") else: src = app.getGraph().addNode( library="pyfile://./SimpleStaticFilter.py", factoryFunction="SimpleSource") src2 = app.getGraph().addNode(library="pyfile://./SimpleStaticFilter.py", factoryFunction="SimpleSource") idx = _getIndex(cfg.model, ["apps", "myApp", src, "frequency"]) cfg.model.setData(idx.siblingAtColumn(1), 10.0, Qt.EditRole) idx = _getIndex(cfg.model, ["apps", "myApp", src2, "frequency"]) cfg.model.setData(idx.siblingAtColumn(1), 2.0, Qt.EditRole) idx = _getIndex(cfg.model, ["apps", "myApp", src2, "log_tr"]) cfg.model.setData(idx.siblingAtColumn(1), False, Qt.EditRole) flt = app.getGraph().addNode(library=comp, factoryFunction="compositeNode") app.getGraph().addConnection(src, "outPort", flt, "in") # add a recorder rec = app.getGraph().addNode(library="pymod://nexxT.filters.hdf5", factoryFunction="Hdf5Writer") execute_0.i = MethodInvoker(app.getGraph().addDynamicInputPort, Qt.QueuedConnection, rec, "stream1") waitForSignal(app.getGraph().dynInputPortAdded) execute_0.i = MethodInvoker(app.getGraph().addDynamicInputPort, Qt.QueuedConnection, rec, "stream2") waitForSignal(app.getGraph().dynInputPortAdded) app.getGraph().addConnection(src, "outPort", rec, "stream1") app.getGraph().addConnection(src2, "outPort", rec, "stream2") # assign threads app.getGraph().getMockup(src).propertyCollection().getChildCollection( "_nexxT").setProperty("thread", "source-thread") app.getGraph().getMockup(src2).propertyCollection().getChildCollection( "_nexxT").setProperty("thread", "source2-thread") app.getGraph().getMockup(rec).propertyCollection().getChildCollection( "_nexxT").setProperty("thread", "rec-thread") # create another application (playback) # hdf5 reader cfg.configuration().renameApp(cfg.configuration().addNewApplication(), "pbApp") app = cfg.configuration().applicationByName("pbApp") pb = app.getGraph().addNode(library="pymod://nexxT.filters.hdf5", factoryFunction="Hdf5Reader", suggestedName="player") assert pb == "player" execute_0.i = MethodInvoker(app.getGraph().addDynamicOutputPort, Qt.QueuedConnection, pb, "stream1") waitForSignal(app.getGraph().dynOutputPortAdded) execute_0.i = MethodInvoker(app.getGraph().addDynamicOutputPort, Qt.QueuedConnection, pb, "stream2") waitForSignal(app.getGraph().dynOutputPortAdded) # 2 filters flt1 = app.getGraph().addNode(library="pyfile://./SimpleStaticFilter.py", factoryFunction="SimpleStaticFilter", suggestedName="flt1") assert flt1 == "flt1" idx = _getIndex(cfg.model, ["apps", "pbApp", "flt1", "sleep_time"]) cfg.model.setData(idx.siblingAtColumn(1), 0.01, Qt.EditRole) idx = _getIndex(cfg.model, ["apps", "pbApp", "flt1", "log_rcv"]) cfg.model.setData(idx.siblingAtColumn(1), True, Qt.EditRole) idx = _getIndex(cfg.model, ["apps", "pbApp", "flt1", "log_prefix"]) cfg.model.setData(idx.siblingAtColumn(1), "(flt1) ", Qt.EditRole) flt2 = app.getGraph().addNode(library="pyfile://./SimpleStaticFilter.py", factoryFunction="SimpleStaticFilter", suggestedName="flt2") assert flt2 == "flt2" idx = _getIndex(cfg.model, ["apps", "pbApp", "flt2", "sleep_time"]) cfg.model.setData(idx.siblingAtColumn(1), 0.01, Qt.EditRole) idx = _getIndex(cfg.model, ["apps", "pbApp", "flt2", "log_rcv"]) cfg.model.setData(idx.siblingAtColumn(1), True, Qt.EditRole) idx = _getIndex(cfg.model, ["apps", "pbApp", "flt2", "log_prefix"]) cfg.model.setData(idx.siblingAtColumn(1), "(flt2) ", Qt.EditRole) app.getGraph().addConnection("player", "stream1", "flt1", "inPort") app.getGraph().addConnection("player", "stream2", "flt2", "inPort") # save application execute_0.i = MethodInvoker(cfg.saveConfig, Qt.QueuedConnection) waitForSignal(cfg.configuration().configNameChanged) # change active app execute_1.i = MethodInvoker(cfg.changeActiveApp, Qt.QueuedConnection, "myApp") waitForSignal(cfg.configuration().appActivated) # activate execute_1.i = MethodInvoker(cfg.activate, Qt.QueuedConnection) waitForSignal(cfg.configuration().appActivated) if app.activeApplication.getState() != FilterState.ACTIVE: waitForSignal(app.activeApplication.stateChanged, lambda s: s == FilterState.ACTIVE) logger.info("execute_0:end")
def execute_2(): logger.info("execute_2:begin") cfg = Services.getService("Configuration") pbc = Services.getService("PlaybackControl") app = cfg.configuration().applicationByName("pbApp") logger.info("app activated") execute_2.i = MethodInvoker(pbc.setSequence, Qt.QueuedConnection, glob.glob("./*.h5")[0]) _, tbegin, tend, _ = waitForSignal(pbc.sequenceOpened) execute_2.i = MethodInvoker(pbc.startPlayback, Qt.QueuedConnection) waitForSignal(pbc.playbackStarted) waitForSignal(pbc.playbackPaused) logger.info("played") execute_2.i = MethodInvoker(pbc.seekBeginning, Qt.QueuedConnection) waitForSignal(pbc.currentTimestampChanged) logger.info("seekBeginning") for i in range(10): execute_2.i = MethodInvoker(pbc.stepForward, Qt.QueuedConnection, None) waitForSignal(pbc.playbackPaused) logger.info("stepForward[None]") for i in range(10): execute_2.i = MethodInvoker(pbc.stepForward, Qt.QueuedConnection, "stream1") waitForSignal(pbc.playbackPaused) logger.info("stepForward[stream1]") for i in range(2): execute_2.i = MethodInvoker(pbc.stepForward, Qt.QueuedConnection, "stream2") waitForSignal(pbc.playbackPaused) logger.info("stepForward[stream2]") execute_2.i = MethodInvoker(pbc.seekTime, Qt.QueuedConnection, (tbegin + tend) // 2) waitForSignal(pbc.currentTimestampChanged) logger.info("seekTime") execute_2.i = MethodInvoker(pbc.seekEnd, Qt.QueuedConnection) waitForSignal(pbc.currentTimestampChanged) logger.info("seekEnd") for i in range(10): execute_2.i = MethodInvoker(pbc.stepBackward, Qt.QueuedConnection, None) waitForSignal(pbc.playbackPaused) logger.info("stepBackward[None]") execute_2.i = MethodInvoker(pbc.seekTime, Qt.QueuedConnection, tbegin - 1000000000) waitForSignal(pbc.currentTimestampChanged) logger.info("seekTimeBegin") execute_2.i = MethodInvoker(pbc.seekTime, Qt.QueuedConnection, tend + 1000000000) waitForSignal(pbc.currentTimestampChanged) logger.info("seekTimeEnd") execute_2.i = MethodInvoker(QCoreApplication.quit, Qt.QueuedConnection) logger.info("execute_2:end")
def execute_1(): """ after 3 seconds, deactivate and re-activate the app after 3 seconds, re-open and re-activate the app after 3 more seconds, quit :return: """ logger.info("execute_1:begin") cfg = Services.getService("Configuration") rc = Services.getService("RecordingControl") logger.info("app activated") app = cfg.configuration().applicationByName("myApp") t = QTimer() t.setSingleShot(True) t.setInterval(3000) t.start() waitForSignal(t.timeout) execute_1.i = MethodInvoker(cfg.deactivate, Qt.QueuedConnection) waitForSignal(app.activeApplication.stateChanged, lambda s: s == FilterState.CONSTRUCTED) logger.info("app deactivated") execute_1.i = MethodInvoker(cfg.activate, Qt.QueuedConnection) waitForSignal(cfg.configuration().appActivated) if app.activeApplication.getState() != FilterState.ACTIVE: waitForSignal(app.activeApplication.stateChanged, lambda s: s == FilterState.ACTIVE) execute_1.i = MethodInvoker(rc.startRecording, Qt.QueuedConnection, ".") logger.info("app activated") t = QTimer() t.setSingleShot(True) t.setInterval(3000) t.start() waitForSignal(t.timeout) execute_1.i = MethodInvoker(cfg.deactivate, Qt.QueuedConnection) waitForSignal(app.activeApplication.stateChanged, lambda s: s == FilterState.CONSTRUCTED) logger.info("app deactivated") # re-open this application execute_1.i = MethodInvoker(cfg.loadConfig, Qt.QueuedConnection, "basicworkflow.json") waitForSignal(cfg.configuration().configNameChanged) logger.info("config loaded") # activate execute_1.i = MethodInvoker(cfg.changeActiveApp, Qt.QueuedConnection, "myApp") waitForSignal(cfg.configuration().appActivated) execute_1.i = MethodInvoker(cfg.activate, Qt.QueuedConnection) waitForSignal(cfg.configuration().appActivated) if app.activeApplication.getState() != FilterState.ACTIVE: waitForSignal(app.activeApplication.stateChanged, lambda s: s == FilterState.ACTIVE) logger.info("app activated") t = QTimer() t.setSingleShot(True) t.setInterval(3000) t.start() waitForSignal(t.timeout) execute_1.i = MethodInvoker(QCoreApplication.quit, Qt.QueuedConnection) logger.info("execute_1:end")
def _singleShotPlay(self): assertMainThread() pbsrv = Services.getService("PlaybackControl") MethodInvoker(pbsrv.startPlayback, Qt.QueuedConnection) self._disconnectSingleShotPlay()
def setupConnections(self, playbackDevice, nameFilters): """ Sets up signal/slot connections between this view/controller instance and the given playbackDevice. This function is thread safe and shall be called by a direct QT connection. It is intended, that this function is called in the onOpen(...) method of a filter. It expects playbackDevice to provide the following slots: - startPlayback() (starts generating DataSamples) - pausePlayback() (pause mode, stop generating DataSamples) - stepForward(QString stream) (optional; in case given, a single step operation shall be performed. if stream is not None, the playback shall stop when receiving the next data sample of stream; otherwise the playback shall proceed to the next data sample of any stream) - stepBackward(QString stream) (optional; see stepForward) - seekBeginning(QString stream) (optional; goes to the beginning of the sequence) - seekEnd() (optional; goes to the end of the stream) - seekTime(QString QDateTime) (optional; goes to the specified time stamp) - setSequence(QString) (optional; opens the given sequence) - setTimeFactor(float) (optional; sets the playback time factor, factor > 0) It expects playbackDevice to provide the following signals (all signals are optional): - sequenceOpened(QString filename, QDateTime begin, QDateTime end, QStringList streams) - currentTimestampChanged(QDateTime currentTime) - playbackStarted() - playbackPaused() - timeRatioChanged(float) :param playbackDevice: a QObject providing the aforementioned signals and slots :param nameFilters: a QStringList providing information about suported fileextensions (e.g. ["*.avi", "*.mp4"]) :return: """ with QMutexLocker(self._mutex): for devid in self._registeredDevices: if self._registeredDevices[devid]["object"] is playbackDevice: raise NexTRuntimeError("Trying to register a playbackDevice object twice.") if not self._startPlayback.connect(playbackDevice.startPlayback): raise NexTRuntimeError("cannot connect to slot startPlayback()") if not self._pausePlayback.connect(playbackDevice.pausePlayback): raise NexTRuntimeError("cannot connect to slot pausePlayback()") connections = [(self._startPlayback, playbackDevice.startPlayback), (self._pausePlayback, playbackDevice.pausePlayback)] featureset = set(["startPlayback", "pausePlayback"]) for feature in ["stepForward", "stepBackward", "seekTime", "seekBeginning", "seekEnd", "setTimeFactor"]: signal = getattr(self, "_" + feature) slot = getattr(playbackDevice, feature, None) if slot is not None and signal.connect(slot, Qt.UniqueConnection): featureset.add(feature) connections.append((signal, slot)) @handleException def setSequenceWrapper(filename): assertMainThread() if Application.activeApplication is None: return if Application.activeApplication.getState() not in [FilterState.ACTIVE, FilterState.OPENED]: return if QDir.match(nameFilters, pathlib.Path(filename).name): logger.debug("setSequence %s", filename) if Application.activeApplication.getState() == FilterState.ACTIVE: Application.activeApplication.stop() MethodInvoker(dict(object=playbackDevice, method="setSequence"), Qt.QueuedConnection, filename) Application.activeApplication.start() logger.debug("setSequence done") else: logger.debug("%s does not match filters: %s", filename, nameFilters) MethodInvoker(dict(object=playbackDevice, method="setSequence"), Qt.QueuedConnection, None) # setSequence is called only if filename matches the given filters if self._setSequence.connect(setSequenceWrapper, Qt.DirectConnection): featureset.add("setSequence") connections.append((self._setSequence, setSequenceWrapper)) for feature in ["sequenceOpened", "currentTimestampChanged", "playbackStarted", "playbackPaused", "timeRatioChanged"]: slot = getattr(self, "_" + feature) signal = getattr(playbackDevice, feature, None) if signal is not None and signal.connect(slot, Qt.UniqueConnection): featureset.add(feature) connections.append((signal, slot)) self._registeredDevices[self._deviceId] = dict(object=playbackDevice, featureset=featureset, nameFilters=nameFilters, connections=connections) self._deviceId += 1 MethodInvoker(dict(object=self, method="_updateFeatureSet", thread=mainThread()), Qt.QueuedConnection)