class MainWindow(QMainWindow): writeToPDFSignal = Signal(str, str) def __init__(self, parent = None): super(MainWindow, self).__init__(parent) self.mainWindow = getUiFromFile(os.path.join(ROOT_DIR,"UI/mainwindow.ui")) self.setCentralWidget(self.mainWindow) # Visualization Part # accessing Motivo self.fileDialog = QFileDialog() self.userPreferencesDialog = UserPreferencesDialog() self.svgCache = ImageProvider(100) self.errorMessage = QMessageBox() self.csvFilePath = None self.writeToPDFThread = QThread(self) self.writeToPDF = ResultsExporter(self.svgCache) self.writeToPDF.moveToThread(self.writeToPDFThread) # TO DO: Creating a queue for motivo sessions # hide Layout self.mainWindow.writeToPdfProgressBar.hide() self.mainWindow.cancelWritingToPDFButton.hide() self.mainWindow.writeToPdfLabel.hide() # Creating the dialogs for menu bar actions self.basicDialog = BasicDialog() self.advancedDialog = AdvancedDialog() # Creating connections between menu bar actions and dialogs # File self.mainWindow.actionExit.triggered.connect(self.exit) # Motivo self.mainWindow.actionBasic.triggered.connect(self.openBasicDialog) self.mainWindow.actionAdvanced.triggered.connect(self.openAdvancedDialog) self.mainWindow.actionSaveAsPDF.triggered.connect(self.startWriteToPDFThread) self.mainWindow.actionOpenCSV.triggered.connect(self.openSelectCsv) self.writeToPDFSignal.connect(self.writeToPDF.setData) # Options self.mainWindow.actionUserPreferences.triggered.connect(self.openUserPreferencesDialog) # visualization self.basicDialog.outputReady.connect(self.visualizeFromBasicDialog) self.advancedDialog.outputReady.connect(self.visualizeFromAdvancedDialog) self.writeToPDF.progress.connect(self.updateWriteToPDFProgress) self.writeToPDF.finished.connect(self.writeToPdfComplete) self.mainWindow.cancelWritingToPDFButton.clicked.connect(self.cancelWritingToPDF) # functions called through menuBar actions def exit(self): self.close() def openBasicDialog(self): self.basicDialog.show() def openAdvancedDialog(self): self.advancedDialog.show() def openUserPreferencesDialog(self): self.userPreferencesDialog.show() def checkMotivoDirectory(self): if MotivoAccess.motivoDirectory == '': motivoDirError = QMessageBox() motivoDirError.critical(self.mainWindow, "motivo", "Motivo directory not found, please set the directory in User Preferences") def openSelectCsv(self): filePathAndName = self.fileDialog.getOpenFileName()[0] if filePathAndName[filePathAndName.rfind('.'):] == '.csv': self.csvFilePath = filePathAndName self.visualizeFromOpenCSV() elif filePathAndName != '': self.errorMessage.critical(self.mainWindow, "Visualize from a csv file", "Incorrect file format") def visualizeFromOpenCSV(self): self.updateMainGraphletWidget(self.csvFilePath) def visualizeFromBasicDialog(self): self.csvFilePath = self.basicDialog.getOutputFile() + '.csv' self.updateMainGraphletWidget(self.csvFilePath) def visualizeFromAdvancedDialog(self): self.csvFilePath = self.advancedDialog.getOutputFile() + '.csv' self.updateMainGraphletWidget(self.csvFilePath) def updateMainGraphletWidget(self,outputCsvFilePath): # Reset the widgets self.mainWindow.resultsView.clearSpans() for i in reversed(range(self.mainWindow.plotLayout.count())): self.mainWindow.plotLayout.itemAt(i).widget().setParent(None) try: with open(outputCsvFilePath) as fh: csvreader = csv.reader(fh) headers = next(csvreader) headers.insert(1,"Graph") data = list(csvreader) except FileNotFoundError: print("Results file not found") results = ResultVisualizer(self.svgCache,data,headers,self.mainWindow.resultsView,self.mainWindow.plotLayout) results.visualizeMotif() results.visualizeChart() def startWriteToPDFThread(self): if self.csvFilePath == None: self.errorMessage.about(self.mainWindow, 'Save As PDF', 'Nothing to save') return pdfFileName = self.fileDialog.getSaveFileName()[0] if pdfFileName == '': return #self.writeToPDFThread.setTerminationEnabled(True) self.writeToPDFThread.start() self.mainWindow.writeToPdfProgressBar.setValue(0) self.showWriteToPdfProgressBar() self.writeToPDFSignal.emit(self.csvFilePath, pdfFileName) def updateWriteToPDFProgress(self, progress): self.mainWindow.writeToPdfProgressBar.setValue(progress) def writeToPdfComplete(self): self.mainWindow.writeToPdfProgressBar.setValue(100) self.hideWriteToPdfProgressBar() self.writeToPDFThread.quit() def showWriteToPdfProgressBar(self): self.mainWindow.writeToPdfProgressBar.show() self.mainWindow.cancelWritingToPDFButton.show() self.mainWindow.writeToPdfLabel.show() def hideWriteToPdfProgressBar(self): self.mainWindow.writeToPdfProgressBar.hide() self.mainWindow.cancelWritingToPDFButton.hide() self.mainWindow.writeToPdfLabel.hide() def cancelWritingToPDF(self): self.hideWriteToPdfProgressBar() self.writeToPDFThread.requestInterruption() self.writeToPDFThread.quit()
class X11GameManager(GameManager): connection: xcffib.Connection _instances: Dict[int, X11GameInstance] _atom: Dict[bytes, int] _shm: List[Tuple[int, sysv_ipc.SharedMemory]] def __init__(self, **kwargs): super().__init__(*kwargs) self._instances = {} self._atom = {} self._shm = [] self.logger = logging.getLogger(__name__ + "." + self.__class__.__name__) self.connection = xcffib.Connection() self.screen = self.connection.get_screen_pointers()[self.connection.pref_screen] self.xcomposite = self.connection(xcffib.composite.key) self.xshm = self.connection(xcffib.shm.key) self.xinput = self.connection(xcffib.xinput.key) self._setup_composite() self._setup_overlay() self.event_thread = QThread(self) self.event_worker = X11EventWorker(self) self.event_worker.moveToThread(self.event_thread) self.event_thread.started.connect(self.event_worker.run) self.event_thread.finished.connect(self.event_worker.deleteLater) self.event_worker.create_signal.connect(self.on_game_opened) self.event_worker.destroy_signal.connect(self.on_game_closed) self.event_thread.start() self.get_instances() def stop(self): self.event_thread.requestInterruption() self.event_thread.quit() self.event_thread.wait() def get_instances(self) -> List[GameInstance]: def visit(wid: int): if self.is_game(wid): if wid not in self._instances: self.logger.info("Found game instance %d", wid) instance = X11GameInstance(self, wid, parent=self) self._instances[wid] = instance try: query = self.connection.core.QueryTree(wid).reply() for child in query.children: visit(child) except xcffib.xproto.WindowError: return visit(self.screen.root) return list(self._instances.values()) def get_active_instance(self) -> Union[GameInstance, None]: for instance in self._instances.values(): if instance.is_focused(): return instance return None def is_game(self, wid: int) -> bool: geom_req = self.connection.core.GetGeometry(wid) try: wm_class = self.get_property(wid, xcffib.xproto.Atom.WM_CLASS) except xcffib.xproto.WindowError: geom_req.discard_reply() return False geom = geom_req.reply() if geom.width == 32 and geom.height == 32: # OpenGL test window return False if not wm_class: return False instance_name, app_name = wm_class.split("\00") return app_name == WM_APP_NAME def get_active_window(self) -> int: return self.get_property(self.screen.root, "_NET_ACTIVE_WINDOW") def _setup_overlay(self): self.overlay = DesktopWideOverlay() self.overlay.show() self.overlay.check_compatibility() def _setup_composite(self): self.xcomposite.QueryVersion(0, 4, is_checked=True) def get_property( self, wid: int, name: str, type_=xcffib.xproto.GetPropertyType.Any, index=0, max_values=1000, ): reply = self.connection.core.GetProperty( False, wid, self.get_atom(name), type_, index, max_values, ).reply() if reply.type == xcffib.xproto.Atom.STRING: return reply.value.to_string()[:-1] elif reply.type in (xcffib.xproto.Atom.WINDOW, xcffib.xproto.Atom.CARDINAL): return struct.unpack("=I", reply.value.buf()[:4])[0] return reply.value def get_atom(self, atom: str) -> int: if isinstance(atom, int): return atom if hasattr(xcffib.xproto.Atom, atom): return getattr(xcffib.xproto.Atom, atom) atom = atom.encode("ascii") if atom in self._atom: return self._atom[atom] out = self.connection.core.InternAtom(False, len(atom), atom).reply().atom self._atom[atom] = out return out def get_shm(self, size: int) -> Tuple[int, sysv_ipc.SharedMemory]: for item in self._shm: if item[1].size >= size: self._shm.remove(item) return item shm = sysv_ipc.SharedMemory(None, flags=sysv_ipc.IPC_CREX, size=size) xid = self.connection.generate_id() self.xshm.Attach(xid, shm.id, False, is_checked=True) return xid, shm def free_shm(self, shm: Tuple[int, sysv_ipc.SharedMemory]): self._shm.append(shm) self.gc_shm() def gc_shm(self): while len(self._shm) > MAX_SHM: xid, shm = self._shm.pop(0) self.xshm.Detach(xid) shm.detach() shm.remove() @Slot(xcffib.Event) def on_game_opened(self, evt: xcffib.xproto.CreateNotifyEvent): self.logger.info("New game window opened %d", evt.window) self._instances[evt.window] = X11GameInstance(self, evt.window, parent=self) @Slot(int) def on_game_closed(self, wid: int): if wid not in self._instances: return self.logger.info("Game window %d closed", wid) instance = self._instances[wid] del self._instances[wid] self.instance_removed.emit(instance) self.instance_changed.emit()
class X11GameManager(GameManager): connection: xcffib.Connection _instances: Dict[int, X11GameInstance] _atom: Dict[bytes, int] _shm: List[Tuple[int, sysv_ipc.SharedMemory]] def __init__(self, **kwargs): super().__init__(*kwargs) self._instances = {} self._atom = {} self._shm = [] self.connection = xcffib.Connection() self.screen = self.connection.get_screen_pointers()[self.connection.pref_screen] self.xcomposite = self.connection(xcffib.composite.key) self.xshm = self.connection(xcffib.shm.key) self.xinput = self.connection(xcffib.xinput.key) self._setup_composite() self._setup_overlay() self.event_thread = QThread(self) self.event_worker = X11EventWorker(self) self.event_worker.moveToThread(self.event_thread) self.event_thread.started.connect(self.event_worker.run) self.event_thread.finished.connect(self.event_worker.deleteLater) self.event_thread.start() def stop(self): self.event_thread.requestInterruption() self.event_thread.quit() self.event_thread.wait() def get_instances(self) -> List[GameInstance]: def visit(wid: int): wm_class = self.get_property(wid, xcffib.xproto.Atom.WM_CLASS) if wm_class: instance_name, app_name = wm_class.split("\00") if app_name == WM_APP_NAME: if wid not in self._instances: instance = X11GameInstance(self, wid, parent=self) self._instances[wid] = instance query = self.connection.core.QueryTree(wid).reply() for child in query.children: visit(child) visit(self.screen.root) return list(self._instances.values()) def get_active_window(self) -> int: return self.get_property(self.screen.root, "_NET_ACTIVE_WINDOW") def _setup_overlay(self): self.overlay = DesktopWideOverlay() self.overlay.show() def _setup_composite(self): self.xcomposite.QueryVersion(0, 4, is_checked=True) def get_property( self, wid: int, name: str, type_=xcffib.xproto.GetPropertyType.Any, index=0, max_values=1000, ): reply = self.connection.core.GetProperty( False, wid, self.get_atom(name), type_, index, max_values, ).reply() if reply.type == xcffib.xproto.Atom.STRING: return reply.value.to_string()[:-1] elif reply.type in (xcffib.xproto.Atom.WINDOW, xcffib.xproto.Atom.CARDINAL): return struct.unpack("=I", reply.value.buf()[:4])[0] return reply.value def get_atom(self, atom: str) -> int: if isinstance(atom, int): return atom if hasattr(xcffib.xproto.Atom, atom): return getattr(xcffib.xproto.Atom, atom) atom = atom.encode("ascii") if atom in self._atom: return self._atom[atom] out = self.connection.core.InternAtom(False, len(atom), atom).reply().atom self._atom[atom] = out return out def get_shm(self, size: int) -> Tuple[int, sysv_ipc.SharedMemory]: for item in self._shm: if item[1].size >= size: self._shm.remove(item) return item shm = sysv_ipc.SharedMemory(None, flags=sysv_ipc.IPC_CREX, size=size) xid = self.connection.generate_id() self.xshm.Attach(xid, shm.id, False, is_checked=True) return xid, shm def free_shm(self, shm: Tuple[int, sysv_ipc.SharedMemory]): self._shm.append(shm) self.gc_shm() def gc_shm(self): while len(self._shm) > MAX_SHM: xid, shm = self._shm.pop(0) self.xshm.Detach(xid) shm.detach() shm.remove()