class AddonsPage(QWidget): def __init__(self, parent): super(AddonsPage, self).__init__() self.setMinimumSize(700, 500) self.main = parent.parent self.addonsManager = AddonsManager(self.main) self.addonsManager.loadaddons() self.widgets = [] self.layoutMain = QVBoxLayout(self) self.scroll = QScrollArea(self) self.scroll.setWidgetResizable(True) self.title = QLabel("Addons") self.title.setAlignment(Qt.AlignHCenter) self.layoutMain.addWidget(self.title) self.layoutMain.addWidget(self.scroll) self.container = QWidget() self.scroll.setWidget(self.container) self.layout = QVBoxLayout(self.container) self.label = QLabel("Pas de addons") self.label.setAlignment(Qt.AlignHCenter) self.layout.addWidget(self.label) for i in self.addonsManager.imported: self.addonW = AddonWidget(self.main, self, i.replace(".", "/").split("/")[-2]) self.widgets.append(self.addonW) self.layout.addWidget(self.addonW) self.layout.setAlignment(self.addonW, Qt.AlignTop) self.label.hide() def launchaddons(self, function, args=None): self.addonsManager.launchaddons(self.widgets, function, args)
class DownloadPage(QWidget): def __init__(self, parent): super(DownloadPage, self).__init__() self.setMinimumSize(500, 300) self.parent = parent self.nbDownload = 0 self.layoutMain = QVBoxLayout(self) self.scroll = QScrollArea(self) self.scroll.setWidgetResizable(True) self.title = QLabel("Téléchargements") self.title.setAlignment(Qt.AlignHCenter) self.layoutMain.addWidget(self.title) self.layoutMain.addWidget(self.scroll) self.container = QWidget() self.scroll.setWidget(self.container) self.layout = QVBoxLayout(self.container) self.label = QLabel("Pas de téléchargement") self.label.setAlignment(Qt.AlignHCenter) self.layout.addWidget(self.label) def downloadrequested(self, download): if download: if download.state() == QWebEngineDownloadItem.DownloadRequested: path = QFileDialog.getSaveFileName(self, "Sauver comme", download.path()) if path == "": return else: download.setPath(path[0]) download.accept() self.add( DownloadWidget( download, self.parent.parent.browserWidget.url().toString(), self.parent.parent)) self.show() else: QMessageBox.warning(self, "ERREUR", "Le téléchargement n'a pas été demandé.") else: QMessageBox.warning(self, "ERREUR", "Le téléchargement est nul.") def add(self, downloadwidget): downloadwidget.downloadSignal.removeClicked.connect(self.remove) self.layout.addWidget(downloadwidget) self.layout.setAlignment(downloadwidget, Qt.AlignTop) self.nbDownload += 1 if self.nbDownload >= 0: self.label.hide() def remove(self): downloadwidget = self.sender().parent self.layout.removeWidget(downloadwidget) downloadwidget.deleteLater() self.nbDownload -= 1 if self.nbDownload <= 0: self.label.show()
class MDIWidget(QMdiSubWindow): def __init__(self): super().__init__() self.w = 640 self.h_scale = .75 self.win_frame_offset = 25 self.mdi_w = self.w self.mdi_h = int(self.w * self.h_scale) + self.win_frame_offset self.image_w = int(self.w) self.image_h = int(self.w * self.h_scale) self.image_active = None self.resize(self.mdi_w, self.mdi_h) self.mid_text = QLabel("Nothing yet...", alignment=QtCore.Qt.AlignCenter) self.setWidget(self.mid_text) self.setWindowTitle("Video") self.setWindowIcon(self.create_icon_by_color(QColor("transparent"))) def display(self, image): image8 = image.astype(np.uint8) height, width, colors = image8.shape image = QImage(image8.data, width, height, 3 * width, QImage.Format_RGB888) self.image_active = QPixmap.fromImage(image.rgbSwapped()) self.rescale_active_image() def rescale_active_image(self): if self.image_active is not None: self.image_active = self.image_active.scaled(self.image_w, self.image_h) self.mid_text.setPixmap(self.image_active) def mousePressEvent(self, mousePressEvent): self.mid_text.hide() return super().mousePressEvent(mousePressEvent) def mouseReleaseEvent(self, mouseReleaseEvent): self.image_h = int(self.width() * self.h_scale) self.image_w = int(self.width()) self.rescale_active_image() self.mid_text.show() return super().mouseReleaseEvent(mouseReleaseEvent) def resizeEvent(self, resizeEvent): self.resize(self.width(), int(self.width() * self.h_scale) + self.win_frame_offset) return super().resizeEvent(resizeEvent) def create_icon_by_color(self, color): pixmap = QPixmap(512, 512) pixmap.fill(color) return QIcon(pixmap)
class IconStatusBar(QStatusBar): def __init__(self, parent): super().__init__(parent) # Icon self.iconWidget = QLabel(self) icons_base_dir = cvars.get("icons.path") error_icon_path = fs.path_helper(icons_base_dir) / 'error_devil2.png' self.error_pixmap = QPixmap(str(error_icon_path.resolve())) self.iconWidget.setScaledContents(True) self.iconWidget.setMaximumSize(self.height(), self.height()) self.iconWidget.setPixmap(self.error_pixmap) # Message self.messageWidget = QLabel("", self) # Insert icon and message self.insertWidget(0, self.iconWidget) self.insertWidget(1, self.messageWidget) self.iconWidget.hide() def show_error_icon(self): self.iconWidget.show() def hide_icon(self): self.iconWidget.hide() def set_message(self, message: str): self.messageWidget.setText(message) def display_status_bar_message(self, event=None, instruction=None): """ Display a message in the status bar. :param event: tuple of strings, (nature, content, details) :param instruction: 'erase' or None """ status_bar = self if instruction == 'erase': status_bar.set_message("") status_bar.hide_icon() elif event: nature, message, details = event if details: message += ": " + details if nature in ('error', 'lean_error'): status_bar.show_error_icon() else: status_bar.hide_icon() status_bar.set_message(message)
class _SettingsPanel(QWidget): def __init__(self, parent=None): super().__init__(parent) self.timeAxisAttributeCB = QComboBox(self) self.valuesTable = SearchableAttributeTableWidget( self, True, False, True, [Types.Numeric, Types.Ordinal]) self.indexTable = SearchableTableWidget(self) # self.meanCB = QCheckBox('Mean of selected', self) self.createButton = QPushButton('Create chart', self) sideLayout = QVBoxLayout(self) lab = QLabel('Select the column to use as time index') lab.setWordWrap(True) sideLayout.addWidget(lab) sideLayout.addWidget(self.timeAxisAttributeCB) lab = QLabel('Time axis format') self.timeAxisFormatCB = QComboBox(self) options = QStringListModel([ 'dd.MM.yyyy', 'yyyy-MM-dd', 'yyyy-MM', 'yyyy', 'MM', 'dd', 'hh:mm:ss', 'hh:mm', 'hh', 'yyyy-MM-dd HH:mm:ss', 'ddd MMM dd yyyy', 'ddd', 'MMM', 'MMM yyyy' ], parent=self) self.timeAxisFormatCB.setModel(options) self.timeAxisFormatCB.setCurrentIndex(0) sideLayout.addWidget(lab) sideLayout.addWidget(self.timeAxisFormatCB) lab = QLabel( 'Select any number of columns to show as a function of time') lab.setWordWrap(True) sideLayout.addSpacing(30) sideLayout.addWidget(lab) sideLayout.addWidget(self.valuesTable) lab = QLabel( 'Select the rows to show by index. An index attribute must be set') lab.setWordWrap(True) sideLayout.addSpacing(30) sideLayout.addWidget(lab) sideLayout.addWidget(self.indexTable) # sideLayout.addWidget(self.meanCB) sideLayout.addSpacing(30) sideLayout.addWidget(self.createButton) self.errorLabel = QLabel(self) self.errorLabel.setWordWrap(True) sideLayout.addWidget(self.errorLabel) self.errorLabel.hide()
class QLivePreview(QWidget): """ A custom class that can show an empty canvas with a message, or an image without a message. """ def __init__(self, placeholder: str = '(Nothing to preview)', *args, **kwargs): super().__init__(*args, **kwargs) self.window = QLabel() self.window.setFrameStyle(QFrame.Panel | QFrame.Sunken) self.window.setLineWidth(2) self.placeholder_text = QLabel(parent=self.window) self.placeholder_text.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) self.placeholder_text.setAlignment(Qt.AlignCenter) self.placeholder_text.setText(placeholder) # placeholder background when nothing is shown self.blank_background = QPixmap(290, 290) self.blank_background.fill(QColor(0, 0, 0, 32)) self.preview_layout = QVBoxLayout() self.preview_layout.addWidget(self.placeholder_text) # self.window.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed) self.window.setLayout(self.preview_layout) self.layout = QVBoxLayout() self.layout.addWidget(self.window) self.setLayout(self.layout) self.clearImage() def clearImage(self): self.window.setPixmap(self.blank_background) self.placeholder_text.show() def setImage(self, image: QPixmap): self.placeholder_text.hide() self.window.setPixmap(image)
class AccountModal(QDialog): def __init__(self, parent): QDialog.__init__(self) self.account_group_box = QGroupBox("Account") self.modal_error_label = QLabel( "Wrong address, account must be written in form : '0x...'.") self.account_group_layout = QFormLayout() self.new_addr_line = QLineEdit() self.modal_error_label.hide() self.account_group_layout.addRow(self.modal_error_label) self.parent = parent self.account_group_layout.addRow(QLabel("Write your private key:"), self.new_addr_line) self.modal_save = QPushButton("Save") self.modal_save.clicked.connect(self.modal_save_func) self.modal_cancel = QPushButton("Cancel") # self.modal_cancel.clicked.connect(self.modal_cancel_func) self.account_group_layout.addRow(self.modal_save, self.modal_cancel) self.modal_cancel.clicked.connect(self.modal_cancel_func) self.setLayout(self.account_group_layout) """ It is function to save private key for the current session (private key isn't stored, as it would take a lot of time to provide necessary security for such a task. """ def modal_save_func(self): new_addr = self.new_addr_line.displayText() if re.fullmatch("[0-9a-f]{64}", new_addr): self.modal_error_label.hide() self.hide() self.p_key = new_addr self.parent.update_pkey(self.p_key) else: self.new_addr_line.setText("") self.modal_error_label.setText( "Wrong address, account must be written in form : 'e91f...'(total length is 64 characters in hexadecimal form)." ) self.modal_error_label.show() """ This method is for the modal window, to return to the main screen from adding private key """ def modal_cancel_func(self): self.modal_error_label.hide() self.hide()
class ContractModal(QDialog): def __init__(self, parent): QDialog.__init__(self) # This is modal window to add new contract to work with self.modal_group_box = QGroupBox("Contract") self.modal_error_label = QLabel("Wrong address, account must be written in form : '0x...'.") self.modal_group_layout = QFormLayout() self.new_addr_line = QLineEdit() self.modal_error_label.hide() self.modal_group_layout.addRow(self.modal_error_label) self.modal_group_layout.addRow(QLabel("Write address of new contract:"), self.new_addr_line) self.modal_add = QPushButton("Add") self.modal_add.clicked.connect(self.modal_add_func) self.modal_cancel = QPushButton("Cancel") self.modal_cancel.clicked.connect(self.modal_cancel_func) self.modal_group_layout.addRow(self.modal_add, self.modal_cancel) self.parent = parent self.setLayout(self.modal_group_layout) """ This method adds address from textbox(in the modal window) to the list, and checks if it satisfies needed format """ def modal_add_func(self): new_addr = self.new_addr_line.displayText() if re.fullmatch("0x[0-9A-Fa-f]{40}", new_addr): self.modal_error_label.hide() self.new_addr_line.setText("") self.hide() self.parent.add_cont_address(new_addr) else: self.new_addr_line.setText("") self.modal_error_label.setText("Wrong address, account must be written in form : '0x...'.") self.modal_error_label.show() """ This method is for the modal window, to return to the main screen from adding new Registry smart contract """ def modal_cancel_func(self): self.modal_error_label.hide() self.hide()
class MainWindow(QMainWindow): def __init__(self): super(MainWindow, self).__init__() openmc.capi.init(['-c']) self.setWindowTitle('OpenMC Plot Explorer') self.restored = False self.pixmap = None self.zoom = 100 self.model = PlotModel() self.updateRelativeBases() self.restoreModelSettings() self.cellsModel = DomainTableModel(self.model.activeView.cells) self.materialsModel = DomainTableModel(self.model.activeView.materials) # Create viewing area self.frame = QScrollArea(self) self.frame.setAlignment(QtCore.Qt.AlignCenter) self.frame.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) self.setCentralWidget(self.frame) # Create plot image self.plotIm = PlotImage(self.model, self.frame, self) self.frame.setWidget(self.plotIm) # Dock self.dock = OptionsDock(self.model, FM, self) self.dock.setObjectName("OptionsDock") self.addDockWidget(QtCore.Qt.RightDockWidgetArea, self.dock) # Color Dialog self.colorDialog = ColorDialog(self.model, FM, self) self.colorDialog.hide() # Restore Window Settings self.restoreWindowSettings() # Create menubar self.createMenuBar() # Status Bar self.coord_label = QLabel() self.statusBar().addPermanentWidget(self.coord_label) self.coord_label.hide() # Load Plot self.statusBar().showMessage('Generating Plot...') self.dock.updateDock() self.colorDialog.updateDialogValues() if self.restored: self.showCurrentView() else: # Timer allows GUI to render before plot finishes loading QtCore.QTimer.singleShot(0, self.model.generatePlot) QtCore.QTimer.singleShot(0, self.showCurrentView) # Create and update menus: def createMenuBar(self): self.mainMenu = self.menuBar() # File Menu self.saveImageAction = QAction("&Save Image As...", self) self.saveImageAction.setShortcut("Ctrl+Shift+S") self.saveImageAction.setToolTip('Save plot image') self.saveImageAction.setStatusTip('Save plot image') self.saveImageAction.triggered.connect(self.saveImage) self.saveViewAction = QAction("Save &View Settings...", self) self.saveViewAction.setShortcut(QtGui.QKeySequence.Save) self.saveViewAction.setStatusTip('Save current view settings') self.saveViewAction.triggered.connect(self.saveView) self.openAction = QAction("&Open View Settings...", self) self.openAction.setShortcut(QtGui.QKeySequence.Open) self.openAction.setToolTip('Open saved view settings') self.openAction.setStatusTip('Open saved view settings') self.openAction.triggered.connect(self.openView) self.quitAction = QAction("&Quit", self) self.quitAction.setShortcut(QtGui.QKeySequence.Quit) self.quitAction.setToolTip('Quit OpenMC Plot Explorer') self.quitAction.setStatusTip('Quit OpenMC Plot Explorer') self.quitAction.triggered.connect(self.close) self.fileMenu = self.mainMenu.addMenu('&File') self.fileMenu.addAction(self.saveImageAction) self.fileMenu.addSeparator() self.fileMenu.addAction(self.saveViewAction) self.fileMenu.addAction(self.openAction) self.fileMenu.addSeparator() self.fileMenu.addAction(self.quitAction) # Edit Menu self.applyAction = QAction("&Apply Changes", self) self.applyAction.setShortcut("Shift+Return") self.applyAction.setToolTip('Generate new view with changes applied') self.applyAction.setStatusTip('Generate new view with changes applied') self.applyAction.triggered.connect(self.applyChanges) self.undoAction = QAction('&Undo', self) self.undoAction.setShortcut(QtGui.QKeySequence.Undo) self.undoAction.setToolTip('Undo') self.undoAction.setStatusTip('Undo last plot view change') self.undoAction.setDisabled(True) self.undoAction.triggered.connect(self.undo) self.redoAction = QAction('&Redo', self) self.redoAction.setDisabled(True) self.redoAction.setToolTip('Redo') self.redoAction.setStatusTip('Redo last plot view change') self.redoAction.setShortcut(QtGui.QKeySequence.Redo) self.redoAction.triggered.connect(self.redo) self.restoreAction = QAction("&Restore Default Plot", self) self.restoreAction.setShortcut("Ctrl+R") self.restoreAction.setToolTip('Restore to default plot view') self.restoreAction.setStatusTip('Restore to default plot view') self.restoreAction.triggered.connect(self.restoreDefault) self.editMenu = self.mainMenu.addMenu('&Edit') self.editMenu.addAction(self.applyAction) self.editMenu.addSeparator() self.editMenu.addAction(self.undoAction) self.editMenu.addAction(self.redoAction) self.editMenu.addSeparator() self.editMenu.addAction(self.restoreAction) self.editMenu.addSeparator() self.editMenu.aboutToShow.connect(self.updateEditMenu) # Edit -> Basis Menu self.xyAction = QAction('&xy ', self) self.xyAction.setCheckable(True) self.xyAction.setShortcut('Alt+X') self.xyAction.setToolTip('Change to xy basis') self.xyAction.setStatusTip('Change to xy basis') self.xyAction.triggered.connect( lambda: self.editBasis('xy', apply=True)) self.xzAction = QAction('x&z ', self) self.xzAction.setCheckable(True) self.xzAction.setShortcut('Alt+Z') self.xzAction.setToolTip('Change to xz basis') self.xzAction.setStatusTip('Change to xz basis') self.xzAction.triggered.connect( lambda: self.editBasis('xz', apply=True)) self.yzAction = QAction('&yz ', self) self.yzAction.setCheckable(True) self.yzAction.setShortcut('Alt+Y') self.yzAction.setToolTip('Change to yz basis') self.yzAction.setStatusTip('Change to yz basis') self.yzAction.triggered.connect( lambda: self.editBasis('yz', apply=True)) self.basisMenu = self.editMenu.addMenu('&Basis') self.basisMenu.addAction(self.xyAction) self.basisMenu.addAction(self.xzAction) self.basisMenu.addAction(self.yzAction) self.basisMenu.aboutToShow.connect(self.updateBasisMenu) # Edit -> Color By Menu self.cellAction = QAction('&Cell', self) self.cellAction.setCheckable(True) self.cellAction.setShortcut('Alt+C') self.cellAction.setToolTip('Color by cell') self.cellAction.setStatusTip('Color plot by cell') self.cellAction.triggered.connect( lambda: self.editColorBy('cell', apply=True)) self.materialAction = QAction('&Material', self) self.materialAction.setCheckable(True) self.materialAction.setShortcut('Alt+M') self.materialAction.setToolTip('Color by material') self.materialAction.setStatusTip('Color plot by material') self.materialAction.triggered.connect( lambda: self.editColorBy('material', apply=True)) self.colorbyMenu = self.editMenu.addMenu('&Color By') self.colorbyMenu.addAction(self.cellAction) self.colorbyMenu.addAction(self.materialAction) self.colorbyMenu.aboutToShow.connect(self.updateColorbyMenu) self.editMenu.addSeparator() self.maskingAction = QAction('Enable &Masking', self) self.maskingAction.setShortcut('Ctrl+M') self.maskingAction.setCheckable(True) self.maskingAction.setToolTip('Toggle masking') self.maskingAction.setStatusTip('Toggle whether masking is enabled') self.maskingAction.triggered[bool].connect( lambda bool=bool: self.toggleMasking(bool, apply=True)) self.editMenu.addAction(self.maskingAction) self.highlightingAct = QAction('Enable High&lighting', self) self.highlightingAct.setShortcut('Ctrl+L') self.highlightingAct.setCheckable(True) self.highlightingAct.setToolTip('Toggle highlighting') self.highlightingAct.setStatusTip( 'Toggle whether highlighting is enabled') self.highlightingAct.triggered[bool].connect( lambda bool=bool: self.toggleHighlighting(bool, apply=True)) self.editMenu.addAction(self.highlightingAct) # View Menu self.dockAction = QAction('Hide &Dock', self) self.dockAction.setShortcut("Ctrl+D") self.dockAction.setToolTip('Toggle dock visibility') self.dockAction.setStatusTip('Toggle dock visibility') self.dockAction.triggered.connect(self.toggleDockView) self.zoomAction = QAction('&Zoom...', self) self.zoomAction.setShortcut('Alt+Shift+Z') self.zoomAction.setToolTip('Edit zoom factor') self.zoomAction.setStatusTip('Edit zoom factor') self.zoomAction.triggered.connect(self.editZoomAct) self.viewMenu = self.mainMenu.addMenu('&View') self.viewMenu.addAction(self.dockAction) self.viewMenu.addSeparator() self.viewMenu.addAction(self.zoomAction) self.viewMenu.aboutToShow.connect(self.updateViewMenu) # Window Menu self.mainWindowAction = QAction('&Main Window', self) self.mainWindowAction.setShortcut('Alt+W') self.mainWindowAction.setCheckable(True) self.mainWindowAction.setToolTip('Bring main window to front') self.mainWindowAction.setStatusTip('Bring main window to front') self.mainWindowAction.triggered.connect(self.showMainWindow) self.colorDialogAction = QAction('Color &Options', self) self.colorDialogAction.setShortcut('Alt+D') self.colorDialogAction.setCheckable(True) self.colorDialogAction.setToolTip('Bring Color Dialog to front') self.colorDialogAction.setStatusTip('Bring Color Dialog to front') self.colorDialogAction.triggered.connect(self.showColorDialog) self.windowMenu = self.mainMenu.addMenu('&Window') self.windowMenu.addAction(self.mainWindowAction) self.windowMenu.addAction(self.colorDialogAction) self.windowMenu.aboutToShow.connect(self.updateWindowMenu) def updateEditMenu(self): changed = self.model.currentView != self.model.defaultView self.restoreAction.setDisabled(not changed) self.maskingAction.setChecked(self.model.currentView.masking) self.highlightingAct.setChecked(self.model.currentView.highlighting) self.undoAction.setText(f'&Undo ({len(self.model.previousViews)})') self.redoAction.setText(f'&Redo ({len(self.model.subsequentViews)})') def updateBasisMenu(self): self.xyAction.setChecked(self.model.currentView.basis == 'xy') self.xzAction.setChecked(self.model.currentView.basis == 'xz') self.yzAction.setChecked(self.model.currentView.basis == 'yz') def updateColorbyMenu(self): cv = self.model.currentView self.cellAction.setChecked(cv.colorby == 'cell') self.materialAction.setChecked(cv.colorby == 'material') def updateViewMenu(self): if self.dock.isVisible(): self.dockAction.setText('Hide &Dock') else: self.dockAction.setText('Show &Dock') def updateWindowMenu(self): self.colorDialogAction.setChecked(self.colorDialog.isActiveWindow()) self.mainWindowAction.setChecked(self.isActiveWindow()) # Menu and shared methods: def saveImage(self): filename, ext = QFileDialog.getSaveFileName(self, "Save Plot Image", "untitled", "Images (*.png *.ppm)") if filename: if "." not in filename: filename += ".png" self.plotIm.figure.savefig(filename, transparent=True) self.statusBar().showMessage('Plot Image Saved', 5000) def saveView(self): filename, ext = QFileDialog.getSaveFileName(self, "Save View Settings", "untitled", "View Settings (*.pltvw)") if filename: if "." not in filename: filename += ".pltvw" saved = { 'default': self.model.defaultView, 'current': self.model.currentView } with open(filename, 'wb') as file: pickle.dump(saved, file) def openView(self): filename, ext = QFileDialog.getOpenFileName(self, "Open View Settings", ".", "*.pltvw") if filename: try: with open(filename, 'rb') as file: saved = pickle.load(file) except Exception: message = 'Error loading plot settings' saved = {'default': None, 'current': None} if saved['default'] == self.model.defaultView: self.model.activeView = saved['current'] self.dock.updateDock() self.applyChanges() message = f'{filename} settings loaded' else: message = 'Error loading plot settings. Incompatible model.' self.statusBar().showMessage(message, 5000) def applyChanges(self): if self.model.activeView != self.model.currentView: self.statusBar().showMessage('Generating Plot...') QApplication.processEvents() self.model.storeCurrent() self.model.subsequentViews = [] self.model.generatePlot() self.resetModels() self.showCurrentView() else: self.statusBar().showMessage('No changes to apply.', 3000) def undo(self): self.statusBar().showMessage('Generating Plot...') QApplication.processEvents() self.model.undo() self.resetModels() self.showCurrentView() self.dock.updateDock() self.colorDialog.updateDialogValues() if not self.model.previousViews: self.undoAction.setDisabled(True) self.redoAction.setDisabled(False) def redo(self): self.statusBar().showMessage('Generating Plot...') QApplication.processEvents() self.model.redo() self.resetModels() self.showCurrentView() self.dock.updateDock() self.colorDialog.updateDialogValues() if not self.model.subsequentViews: self.redoAction.setDisabled(True) self.undoAction.setDisabled(False) def restoreDefault(self): if self.model.currentView != self.model.defaultView: self.statusBar().showMessage('Generating Plot...') QApplication.processEvents() self.model.storeCurrent() self.model.activeView = copy.deepcopy(self.model.defaultView) self.model.generatePlot() self.resetModels() self.showCurrentView() self.dock.updateDock() self.colorDialog.updateDialogValues() self.model.subsequentViews = [] def editBasis(self, basis, apply=False): self.model.activeView.basis = basis self.dock.updateBasis() if apply: self.applyChanges() def editColorBy(self, domain_kind, apply=False): self.model.activeView.colorby = domain_kind self.dock.updateColorBy() self.colorDialog.updateColorBy() if apply: self.applyChanges() def toggleMasking(self, state, apply=False): self.model.activeView.masking = bool(state) self.colorDialog.updateMasking() if apply: self.applyChanges() def toggleHighlighting(self, state, apply=False): self.model.activeView.highlighting = bool(state) self.colorDialog.updateHighlighting() if apply: self.applyChanges() def toggleDockView(self): if self.dock.isVisible(): self.dock.hide() if not self.isMaximized() and not self.dock.isFloating(): self.resize(self.width() - self.dock.width(), self.height()) else: self.dock.setVisible(True) if not self.isMaximized() and not self.dock.isFloating(): self.resize(self.width() + self.dock.width(), self.height()) self.resizePixmap() self.showMainWindow() def editZoomAct(self): percent, ok = QInputDialog.getInt(self, "Edit Zoom", "Zoom Percent:", self.dock.zoomBox.value(), 25, 2000) if ok: self.dock.zoomBox.setValue(percent) def editZoom(self, value): self.zoom = value self.resizePixmap() self.dock.zoomBox.setValue(value) def showMainWindow(self): self.raise_() self.activateWindow() def showColorDialog(self): self.colorDialog.show() self.colorDialog.raise_() self.colorDialog.activateWindow() # Dock methods: def editSingleOrigin(self, value, dimension): self.model.activeView.origin[dimension] = value def editPlotAlpha(self, value): self.model.activeView.plotAlpha = value self.dock.updatePlotAlpha() def editWidth(self, value): self.model.activeView.width = value self.onRatioChange() self.dock.updateWidth() def editHeight(self, value): self.model.activeView.height = value self.onRatioChange() self.dock.updateHeight() def toggleAspectLock(self, state): self.model.activeView.aspectLock = bool(state) self.onRatioChange() self.dock.updateAspectLock() def editVRes(self, value): self.model.activeView.v_res = value self.dock.updateVRes() def editHRes(self, value): self.model.activeView.h_res = value self.onRatioChange() self.dock.updateHRes() # Color dialog methods: def editMaskingColor(self): current_color = self.model.activeView.maskBackground dlg = QColorDialog(self) dlg.setCurrentColor(QtGui.QColor.fromRgb(*current_color)) if dlg.exec_(): new_color = dlg.currentColor().getRgb()[:3] self.model.activeView.maskBackground = new_color self.colorDialog.updateMaskingColor() def editHighlightColor(self): current_color = self.model.activeView.highlightBackground dlg = QColorDialog(self) dlg.setCurrentColor(QtGui.QColor.fromRgb(*current_color)) if dlg.exec_(): new_color = dlg.currentColor().getRgb()[:3] self.model.activeView.highlightBackground = new_color self.colorDialog.updateHighlightColor() def editAlpha(self, value): self.model.activeView.highlightAlpha = value def editSeed(self, value): self.model.activeView.highlightSeed = value def editBackgroundColor(self, apply=False): current_color = self.model.activeView.plotBackground dlg = QColorDialog(self) dlg.setCurrentColor(QtGui.QColor.fromRgb(*current_color)) if dlg.exec_(): new_color = dlg.currentColor().getRgb()[:3] self.model.activeView.plotBackground = new_color self.colorDialog.updateBackgroundColor() if apply: self.applyChanges() # Plot image methods def editPlotOrigin(self, xOr, yOr, zOr=None, apply=False): if zOr != None: self.model.activeView.origin = [xOr, yOr, zOr] else: origin = [None, None, None] origin[self.xBasis] = xOr origin[self.yBasis] = yOr origin[self.zBasis] = self.model.activeView.origin[self.zBasis] self.model.activeView.origin = origin self.dock.updateOrigin() if apply: self.applyChanges() def revertDockControls(self): self.dock.revertToCurrent() def editDomainColor(self, kind, id): if kind == 'Cell': domain = self.model.activeView.cells else: domain = self.model.activeView.materials current_color = domain[id].color dlg = QColorDialog(self) if isinstance(current_color, tuple): dlg.setCurrentColor(QtGui.QColor.fromRgb(*current_color)) elif isinstance(current_color, str): current_color = openmc.plots._SVG_COLORS[current_color] dlg.setCurrentColor(QtGui.QColor.fromRgb(*current_color)) if dlg.exec_(): new_color = dlg.currentColor().getRgb()[:3] domain[id].color = new_color self.applyChanges() def toggleDomainMask(self, state, kind, id): if kind == 'Cell': domain = self.model.activeView.cells else: domain = self.model.activeView.materials domain[id].masked = bool(state) self.applyChanges() def toggleDomainHighlight(self, state, kind, id): if kind == 'Cell': domain = self.model.activeView.cells else: domain = self.model.activeView.materials domain[id].highlighted = bool(state) self.applyChanges() # Helper methods: def restoreWindowSettings(self): settings = QtCore.QSettings() self.resize(settings.value("mainWindow/Size", QtCore.QSize(800, 600))) self.move( settings.value("mainWindow/Position", QtCore.QPoint(100, 100))) self.restoreState(settings.value("mainWindow/State")) self.colorDialog.resize( settings.value("colorDialog/Size", QtCore.QSize(400, 500))) self.colorDialog.move( settings.value("colorDialog/Position", QtCore.QPoint(600, 200))) self.colorDialog.setVisible( bool(settings.value("colorDialog/Visible", 0))) def restoreModelSettings(self): if os.path.isfile("plot_settings.pkl"): with open('plot_settings.pkl', 'rb') as file: model = pickle.load(file) if model.defaultView == self.model.defaultView: self.model.currentView = model.currentView self.model.activeView = copy.deepcopy(model.currentView) self.model.previousViews = model.previousViews self.model.subsequentViews = model.subsequentViews if os.path.isfile('plot_ids.binary') \ and os.path.isfile('plot.ppm'): self.restored = True def resetModels(self): self.cellsModel = DomainTableModel(self.model.activeView.cells) self.materialsModel = DomainTableModel(self.model.activeView.materials) self.cellsModel.beginResetModel() self.cellsModel.endResetModel() self.materialsModel.beginResetModel() self.materialsModel.endResetModel() self.colorDialog.updateDomainTabs() def showCurrentView(self): self.resizePixmap() self.updateScale() self.updateRelativeBases() if self.model.previousViews: self.undoAction.setDisabled(False) if self.model.subsequentViews: self.redoAction.setDisabled(False) else: self.redoAction.setDisabled(True) self.statusBar().showMessage('Done', 1000) self.adjustWindow() def updateScale(self): cv = self.model.currentView self.scale = (cv.h_res / cv.width, cv.v_res / cv.height) def updateRelativeBases(self): cv = self.model.currentView self.xBasis = 0 if cv.basis[0] == 'x' else 1 self.yBasis = 1 if cv.basis[1] == 'y' else 2 self.zBasis = 3 - (self.xBasis + self.yBasis) def adjustWindow(self): self.screen = app.desktop().screenGeometry() self.setMaximumSize(self.screen.width(), self.screen.height()) def onRatioChange(self): av = self.model.activeView if av.aspectLock: ratio = av.width / max(av.height, .001) av.v_res = int(av.h_res / ratio) self.dock.updateVRes() def showCoords(self, xPlotPos, yPlotPos): cv = self.model.currentView if cv.basis == 'xy': coords = (f"({round(xPlotPos, 2)}, {round(yPlotPos, 2)}, " f"{round(cv.origin[2], 2)})") elif cv.basis == 'xz': coords = (f"({round(xPlotPos, 2)}, {round(cv.origin[1], 2)}, " f"{round(yPlotPos, 2)})") else: coords = (f"({round(cv.origin[0], 2)}, {round(xPlotPos, 2)}, " f"{round(yPlotPos, 2)})") self.coord_label.setText(f'{coords}') def resizePixmap(self): z = self.zoom / 100. self.plotIm.setPixmap(self.frame.width() * z, self.frame.height() * z) self.plotIm.adjustSize() def moveEvent(self, event): self.adjustWindow() def resizeEvent(self, event): if self.pixmap: self.adjustWindow() self.updateScale() def closeEvent(self, event): settings = QtCore.QSettings() settings.setValue("mainWindow/Size", self.size()) settings.setValue("mainWindow/Position", self.pos()) settings.setValue("mainWindow/State", self.saveState()) settings.setValue("colorDialog/Size", self.colorDialog.size()) settings.setValue("colorDialog/Position", self.colorDialog.pos()) visible = int(self.colorDialog.isVisible()) settings.setValue("colorDialog/Visible", visible) if len(self.model.previousViews) > 10: self.model.previousViews = self.model.previousViews[-10:] if len(self.model.subsequentViews) > 10: self.model.subsequentViews = self.model.subsequentViews[-10:] with open('plot_settings.pkl', 'wb') as file: pickle.dump(self.model, file)
class App(QWidget): def __init__(self, bk, prefs): super().__init__() self.bk = bk self.prefs = prefs self.update = False # Install translator for the DOCXImport plugin dialog. # Use the Sigil language setting unless manually overridden. plugin_translator = QTranslator() if prefs['language_override'] is not None: print('Plugin preferences language override in effect') qmf = '{}_{}'.format(bk._w.plugin_name.lower(), prefs['language_override']) else: qmf = '{}_{}'.format(bk._w.plugin_name.lower(), bk.sigil_ui_lang) print( qmf, os.path.join(bk._w.plugin_dir, bk._w.plugin_name, 'translations')) plugin_translator.load( qmf, os.path.join(bk._w.plugin_dir, bk._w.plugin_name, 'translations')) print(QCoreApplication.instance().installTranslator(plugin_translator)) self._ok_to_close = False self.FTYPE_MAP = { 'smap': { 'title': _translate('App', 'Select custom style-map file'), 'defaultextension': '.txt', 'filetypes': 'Text Files (*.txt);;All files (*.*)', }, 'css': { 'title': _translate('App', 'Select custom CSS file'), 'defaultextension': '.css', 'filetypes': 'CSS Files (*.css)', }, 'docx': { 'title': _translate('App', 'Select DOCX file'), 'defaultextension': '.docx', 'filetypes': 'DOCX Files (*.docx)', }, } # Check online github files for newer version if self.prefs['check_for_updates']: self.update, self.newversion = self.check_for_update() self.initUI() def initUI(self): main_layout = QVBoxLayout(self) self.setWindowTitle('DOCXImport') self.upd_layout = QVBoxLayout() self.update_label = QLabel() self.update_label.setAlignment(Qt.AlignCenter) self.upd_layout.addWidget(self.update_label) self.get_update_button = QPushButton() self.get_update_button.clicked.connect(self.get_update) self.upd_layout.addWidget(self.get_update_button) main_layout.addLayout(self.upd_layout) if not self.update: self.update_label.hide() self.get_update_button.hide() self.details_grid = QGridLayout() self.epub2_select = QRadioButton() self.epub2_select.setText('EPUB2') self.epubType = QButtonGroup() self.epubType.addButton(self.epub2_select) self.details_grid.addWidget(self.epub2_select, 0, 0, 1, 1) self.checkbox_get_updates = QCheckBox() self.details_grid.addWidget(self.checkbox_get_updates, 0, 1, 1, 1) self.epub3_select = QRadioButton() self.epub3_select.setText('EPUB3') self.epubType.addButton(self.epub3_select) self.details_grid.addWidget(self.epub3_select, 1, 0, 1, 1) main_layout.addLayout(self.details_grid) self.checkbox_get_updates.setChecked(self.prefs['check_for_updates']) if self.prefs['epub_version'] == '2.0': self.epub2_select.setChecked(True) elif self.prefs['epub_version'] == '3.0': self.epub3_select.setChecked(True) else: self.epub2_select.setChecked(True) self.groupBox = QGroupBox() self.groupBox.setTitle('') self.verticalLayout_2 = QVBoxLayout(self.groupBox) self.docx_grid = QGridLayout() self.docx_label = QLabel() self.docx_grid.addWidget(self.docx_label, 0, 0, 1, 1) self.docx_path = QLineEdit() self.docx_grid.addWidget(self.docx_path, 1, 0, 1, 1) self.choose_docx_button = QPushButton() self.choose_docx_button.setText('...') self.docx_grid.addWidget(self.choose_docx_button, 1, 1, 1, 1) self.verticalLayout_2.addLayout(self.docx_grid) self.choose_docx_button.clicked.connect( lambda: self.fileChooser('docx', self.docx_path)) if len(self.prefs['lastDocxPath']): self.docx_path.setText(self.prefs['lastDocxPath']) self.docx_path.setEnabled(False) self.smap_grid = QGridLayout() self.checkbox_smap = QCheckBox(self.groupBox) self.smap_grid.addWidget(self.checkbox_smap, 0, 0, 1, 1) self.cust_smap_path = QLineEdit(self.groupBox) self.smap_grid.addWidget(self.cust_smap_path, 1, 0, 1, 1) self.choose_smap_button = QPushButton(self.groupBox) self.choose_smap_button.setText('...') self.smap_grid.addWidget(self.choose_smap_button, 1, 1, 1, 1) self.verticalLayout_2.addLayout(self.smap_grid) self.checkbox_smap.setChecked(self.prefs['useSmap']) self.checkbox_smap.stateChanged.connect(lambda: self.chkBoxActions( self.checkbox_smap, self.choose_smap_button)) self.choose_smap_button.clicked.connect( lambda: self.fileChooser('smap', self.cust_smap_path, self. checkbox_smap, self.choose_smap_button)) if len(self.prefs['useSmapPath']): self.cust_smap_path.setText(self.prefs['useSmapPath']) self.cust_smap_path.setEnabled(False) self.chkBoxActions(self.checkbox_smap, self.choose_smap_button) self.css_grid = QGridLayout() self.checkbox_css = QCheckBox(self.groupBox) self.css_grid.addWidget(self.checkbox_css, 0, 0, 1, 1) self.cust_css_path = QLineEdit(self.groupBox) self.css_grid.addWidget(self.cust_css_path, 1, 0, 1, 1) self.choose_css_button = QPushButton(self.groupBox) self.choose_css_button.setText('...') self.css_grid.addWidget(self.choose_css_button, 1, 1, 1, 1) self.verticalLayout_2.addLayout(self.css_grid) self.checkbox_css.setChecked(self.prefs['useCss']) self.checkbox_css.stateChanged.connect(lambda: self.chkBoxActions( self.checkbox_css, self.choose_css_button)) self.choose_css_button.clicked.connect( lambda: self.fileChooser('css', self.cust_css_path, self. checkbox_css, self.choose_css_button)) if len(self.prefs['useCssPath']): self.cust_css_path.setText(self.prefs['useCssPath']) self.cust_css_path.setEnabled(False) self.chkBoxActions(self.checkbox_css, self.choose_css_button) main_layout.addWidget(self.groupBox) self.checkbox_debug = QCheckBox() main_layout.addWidget(self.checkbox_debug) self.checkbox_debug.setChecked(self.prefs['debug']) spacerItem = QSpacerItem(20, 15, QSizePolicy.Minimum, QSizePolicy.Expanding) main_layout.addItem(spacerItem) button_box = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel) button_box.accepted.connect(self._ok_clicked) button_box.rejected.connect(self._cancel_clicked) main_layout.addWidget(button_box) self.retranslateUi(self) if self.prefs['qt_geometry'] is not None: try: self.restoreGeometry( QByteArray.fromHex( self.prefs['qt_geometry'].encode('ascii'))) except: pass self.show() def retranslateUi(self, App): self.update_label.setText(_translate('App', 'Plugin Update Available')) self.get_update_button.setText(_translate('App', 'Go to download page')) self.checkbox_get_updates.setText( _translate('App', 'Check for plugin updates')) self.docx_label.setText(_translate('App', 'DOCX File to import')) self.checkbox_smap.setText(_translate('App', 'Use Custom Style Map')) self.checkbox_css.setText(_translate('App', 'Use Custom CSS')) self.checkbox_debug.setText( _translate('App', 'Debug Mode (change takes effect next plugin run)')) def fileChooser(self, ftype, qlineedit, qcheck=None, qbutton=None): options = QFileDialog.Options() options |= QFileDialog.DontUseNativeDialog title = self.FTYPE_MAP[ftype]['title'] startfolder = self.prefs['lastDir'][ftype] ffilter = self.FTYPE_MAP[ftype]['filetypes'] inpath, _ = QFileDialog.getOpenFileName(self, title, startfolder, ffilter, options=options) if len(inpath): qlineedit.setEnabled(True) qlineedit.setText(os.path.normpath(inpath)) self.prefs['lastDir'][ftype] = os.path.dirname(inpath) qlineedit.setEnabled(False) else: if qcheck is not None: qcheck.setChecked(False) if qbutton is not None: qbutton.setEnabled(False) def chkBoxActions(self, chk, btn): btn.setEnabled(chk.isChecked()) def cmdDo(self): global _DETAILS self.prefs['qt_geometry'] = self.saveGeometry().toHex().data().decode( 'ascii') self.prefs['check_for_updates'] = self.checkbox_get_updates.isChecked() self.prefs['epub_version'] = self.epubType.checkedButton().text( )[-1] + '.0' self.prefs['debug'] = self.checkbox_debug.isChecked() _DETAILS['vers'] = self.epubType.checkedButton().text()[-1] + '.0' self.prefs['useSmap'] = self.checkbox_smap.isChecked() if self.checkbox_smap.isChecked(): if len(self.cust_smap_path.text()): self.prefs['useSmapPath'] = self.cust_smap_path.text() _DETAILS['smap'] = (self.checkbox_smap.isChecked(), self.cust_smap_path.text()) else: # Message box that no file is selected return self.prefs['useCss'] = self.checkbox_css.isChecked() if self.checkbox_css.isChecked(): if len(self.cust_css_path.text()): self.prefs['useCssPath'] = self.cust_css_path.text() _DETAILS['css'] = (self.checkbox_css.isChecked(), self.cust_css_path.text()) else: # Message box that no file is selected return if len(self.docx_path.text()): self.prefs['lastDocxPath'] = self.docx_path.text() _DETAILS['docx'] = self.docx_path.text() else: # Message box that no file is selected return def check_for_update(self): '''Use updatecheck.py to check for newer versions of the plugin''' chk = UpdateChecker(self.prefs['last_time_checked'], self.bk._w) update_available, online_version, time = chk.update_info() # update preferences with latest date/time/version self.prefs['last_time_checked'] = time if online_version is not None: self.prefs['last_online_version'] = online_version if update_available: return (True, online_version) return (False, online_version) def get_update(self): url = DOWNLOAD_PAGE if self.update: latest = '/tag/v{}'.format(self.newversion) url = url + latest webbrowser.open_new_tab(url) def _ok_clicked(self): self._ok_to_close = True self.cmdDo() self.bk.savePrefs(self.prefs) QCoreApplication.instance().quit() def _cancel_clicked(self): self._ok_to_close = True '''Close aborting any changes''' self.prefs['qt_geometry'] = self.saveGeometry().toHex().data().decode( 'ascii') self.prefs['check_for_updates'] = self.checkbox_get_updates.isChecked() self.prefs['debug'] = self.checkbox_debug.isChecked() self.bk.savePrefs(self.prefs) QCoreApplication.instance().quit() def closeEvent(self, event): if self._ok_to_close: event.accept() # let the window close else: self._cancel_clicked()
class QWidget_PlaceholderText(QWidget): """QTableView or Tree with palceholder text if empty. This class extends the `QWidget` class. """ __placeholder_text = "The table is empty." def __init__(self, placeholder_text: str = None): """ Args: placeholder_text (str): Placeholder text.. Defaults to None. """ self._placeholder_text = placeholder_text if placeholder_text else self.__placeholder_text self._placeholder_label = QLabel(self._placeholder_text, self) self.setup_placeholder_label() def setup_placeholder_label(self): """QComponents will be displayed here when you create them.""" self.update_placeholder_text() if not self.layout(): layout = QVBoxLayout() self.setLayout(layout) self.layout().addWidget(self._placeholder_label) def update_placeholder_text(self, text=None): """Update the placeholder text to the given string. Args: text (str): New placeholder text.. Defaults to None. """ if text: self._placeholder_text = text label = self._placeholder_label label.setText(self._placeholder_text) # Text label.setWordWrap(True) label.setAlignment(Qt.AlignHCenter | Qt.AlignVCenter) # transperant label.setAutoFillBackground(False) label.setAttribute(Qt.WA_TranslucentBackground) # color PlaceholderText palette = self.palette() # This enum value has been introduced in Qt 5.12 if hasattr(palette, 'PlaceholderText'): placeholder_color = palette.PlaceholderText else: placeholder_color = palette.WindowText color = palette.color(placeholder_color) palette.setColor(palette.Text, color) palette.setColor(palette.Text, color) label.setPalette(palette) def show_placeholder_text(self): """Show the placeholder text.""" self._placeholder_label.show() def hide_placeholder_text(self): """Hide the placeholder text.""" self._placeholder_label.hide()
class SequenceEditor(SchemeEditor): def __init__(self, parent=None): super().__init__(parent) self._sequences = [] """All sequences as a list of tuples (seq_as_graph, seq_as_list, radio_button)""" self.selector_placeholder = QLabel("(none)") self.selector_placeholder.setAlignment(Qt.AlignCenter) self.selector = None self.selector_button_group = None self.selector_scroll_area = QScrollArea() self.selector_dock = QDockWidget("Active Sequence", self) self.selector_dock.setWidget(self.selector_scroll_area) self.selector_dock.setAllowedAreas(Qt.LeftDockWidgetArea | Qt.RightDockWidgetArea) self.selector_dock.setFeatures(QDockWidget.DockWidgetMovable | QDockWidget.DockWidgetFloatable) self.addDockWidget(Qt.LeftDockWidgetArea, self.selector_dock) def setScheme(self, scheme): super().setScheme(scheme) self.scheme().graphChanged.connect(self._updateSelector) self._updateSelector() def sequences(self): """Return a list of tuples (sequence_as_graph, sequence_as_list, sequence_button).""" return self._sequences def selectedSequence(self): """Return a tuple (sequence_as_graph, sequence_as_list, sequence_button) of the selected sequence.""" checked = [s for s in self.sequences() if s[2].isChecked()] assert len(checked) <= 1 if len(checked) == 1: return checked[0] return None def _updateSelector(self): sequences = list(self._possibleSequences()) # Build a new widget with all the new options selector = QWidget() layout = QVBoxLayout() layout.setSizeConstraint(layout.SetMinAndMaxSize) layout.addWidget(self.selector_placeholder) selector.setLayout(layout) self.selector_scroll_area.setWidget(selector) self.selector_button_group = QButtonGroup() self.selector = selector if len(sequences) == 0: self.selector_placeholder.show() return self.selector_placeholder.hide() # Some black magic because of problems with scopes def selectSequence(seq): self.scheme().clearSelection() seq[0].selectAll() def makeSelectSequence(seq): return lambda: selectSequence(seq) # End of black magic self._sequences = [] for sgraph, slist in sequences: label = " → ".join([node.title() for node in slist]) button = QRadioButton(label) button.clicked.connect(makeSelectSequence((sgraph, slist))) self.selector_button_group.addButton(button) layout.addWidget(button) self._sequences.append((sgraph, slist, button)) # Now that the new widget was built, determine which point should be selected considering previous selection selected = self.selectedSequence() if selected is None: # If no previous selection, select the first one self._sequences[0][2].setChecked(True) return candidates = [] for sgraph, slist, button in self._sequences: # Analyze every new option if selected[0] <= sgraph: # If old selection is a subset of a new option, that means a new node was attached and current selection # should be expanded button.setChecked(True) return if sgraph <= selected[0]: # This option is a subset of previous option, which means that something was deleted. If an edge was # removed, there may be other parts of previous options, add them all to candidates for new selection. candidates.append((sgraph, slist, button)) if len(candidates) == 0: # If there are no candidates, select the first option. self._sequences[0][2].setChecked(True) return for node in selected[1]: # For each node in order in prev. selection, see if any candidates have it. Select the first mathcing one. for sgraph, slist, button in candidates: if node in sgraph: button.setChecked(True) return def _possibleSequences(self): """Return a list of tuples, where each tuple represents a possible experiment sequence. Tuple contains 2 items: a graph-view of the sequence, and a list of nodes of that sequence in order. """ g = self.scheme().graph for node in g.nodes: if len(node.inputs) == 0 or len(list(node.inputs[0].edges)) == 0: for sequence in self._possibleSequencesFrom(node): yield sequence def _possibleSequencesFrom(self, node): """Return a list of tuples, where each tuple represents a possible experiment sequence starting from this node. Tuple contains 2 items: a graph-view of the sequence, and a list of nodes of that sequence in order. """ has_sequences = False for out in node.outputs: for edge in out.edges: connected = edge.targetNode() if connected is None: # Filter for edges with no target nodes. These are usually fake edges that the user drags. # When they drop and become real, they will have a node. continue for sequence_graph, sequence_list in self._possibleSequencesFrom(connected): sequence_graph.add(node) sequence_graph.add(edge) sequence_list.insert(0, node) yield (sequence_graph, sequence_list) has_sequences = True if not has_sequences: # If no sequences could be built, this node is the final node. # In this case, just yield itself. result = (Graph(), list()) result[0].add(node) result[1].append(node) yield result
class ExerciseStatusBar(QStatusBar): # TODO: Docstring me and all methods def __init__(self, parent): super().__init__(parent) # Icon self.iconWidget = QLabel(self) icons_base_dir = cvars.get("icons.path") error_icon_path = fs.path_helper(icons_base_dir) / 'cancel.png' success_icon_path = fs.path_helper(icons_base_dir) / 'checked.png' self.error_pixmap = QPixmap(str(error_icon_path.resolve())) self.success_pixmap = QPixmap(str(success_icon_path.resolve())) self.iconWidget.setScaledContents(True) self.iconWidget.setMaximumSize(self.height(), self.height()) # Message self.messageWidget = QLabel("", self) # Insert icon and message self.insertWidget(0, self.iconWidget) self.insertWidget(1, self.messageWidget) self.show_success_icon() # Trick: the status bar adapts its height self.hide_icon() # Verbose mode self.display_success_msgs = cvars.get( 'display.display_success_messages', True) def show_error_icon(self): self.iconWidget.setPixmap(self.error_pixmap) self.iconWidget.show() def show_success_icon(self): self.iconWidget.setPixmap(self.success_pixmap) self.iconWidget.show() def hide_icon(self): self.iconWidget.hide() def set_message(self, msg: str): self.messageWidget.setText(msg) def erase(self): self.set_message("") self.hide_icon() @Slot() def show_normal_msg(self, msg): log.debug("StatusBar: show " + msg) self.hide_icon() self.set_message(msg) def manage_msgs(self, proof_step): """ Display a message in the status bar. Three kinds of messages are considered: new goal, error or success. - New goal msgs are normal msgs, i.e. they will remain in the status bar unless they are hidden temporarily by a temporary msg. e.g. "First case: we assume a in A". - success and error msgs are temporary msgs. """ #log.debug(f"Display msg: " # f"{proof_step.error_msg, proof_step.success_msg}") if proof_step.is_error(): tmp_msg = proof_step.error_msg else: tmp_msg = proof_step.success_msg # Capitalize first char but do not un-capitalize the remaining if tmp_msg: # Fixme: remove and capitalize msgs correctly! tmp_msg = tmp_msg[0].capitalize() + tmp_msg[1:] if proof_step.is_error(): log.debug("StatusBar: " + tmp_msg) self.show_error_icon() self.set_message(tmp_msg) elif proof_step.success_msg and self.display_success_msgs: log.debug("StatusBar: " + tmp_msg) self.show_success_icon() self.set_message(tmp_msg) else: self.hide_icon() tmp_msg = "" if proof_step.new_goals: new_goal = proof_step.new_goals[-1] if new_goal: # Set QTimer for normal msg if tmp_msg: log.debug("StatusBar timer " + new_goal.msg) timer = QTimer(self) func = partial(self.show_normal_msg, new_goal.msg) timer.singleShot(3000, func) # 3000 = 3sec else: # Show immediately self.show_normal_msg(new_goal.msg)
class fontWidget(QWidget): def __init__(self): super().__init__() self.fontName = '微软雅黑' self.fontSize = 60 self.fontBold = True self.fontItalic = False self.fontUnderline = False self.fontStrikeout = False fontBold = '粗体' if self.fontBold else '' fontItalic = '斜体' if self.fontItalic else '' fontUnderline = '下划线' if self.fontUnderline else '' fontStrikeOut = '删除线' if self.fontStrikeout else '' self.fontColor = '#ffffff' self.secondColor = '#000000' self.outlineColor = '#000000' self.shadowColor = '#696969' self.optionLayout = QGridLayout() self.setLayout(self.optionLayout) self.fontSelect = QPushButton( '%s%s号%s%s%s%s' % (self.fontName, self.fontSize, fontBold, fontItalic, fontUnderline, fontStrikeOut)) self.fontSelect.setFixedWidth(150) self.fontSelect.clicked.connect(self.getFont) self.optionLayout.addWidget(self.fontSelect, 0, 0, 1, 2) self.optionLayout.addWidget(QLabel(''), 0, 2, 1, 1) self.fontColorSelect = label() self.fontColorSelect.setAlignment(Qt.AlignCenter) self.fontColorSelect.setText(self.fontColor) self.fontColorSelect.setStyleSheet( 'background-color:%s;color:%s' % (self.fontColor, self.colorReverse(self.fontColor))) self.fontColorSelect.clicked.connect(self.getFontColor) self.optionLayout.addWidget(self.fontColorSelect, 0, 3, 1, 1) self.fontColorLabel = QLabel('字体颜色') self.optionLayout.addWidget(self.fontColorLabel, 0, 4, 1, 1) self.karaoke = QPushButton('卡拉OK模式') self.karaoke.setFixedWidth(150) self.karaokeStatus = False self.karaoke.clicked.connect(self.setKaraoke) self.optionLayout.addWidget(self.karaoke, 1, 0, 1, 2) self.secondColorSelect = label() self.secondColorSelect.setAlignment(Qt.AlignCenter) self.secondColorSelect.setText(self.secondColor) self.secondColorSelect.setStyleSheet( 'background-color:%s;color:%s' % (self.secondColor, self.colorReverse(self.secondColor))) self.secondColorSelect.clicked.connect(self.getSecondFontColor) self.secondColorSelect.hide() self.optionLayout.addWidget(self.secondColorSelect, 1, 3, 1, 1) self.secondColorLabel = QLabel('次要颜色') self.secondColorLabel.hide() self.optionLayout.addWidget(self.secondColorLabel, 1, 4, 1, 1) self.outlineSizeBox = QComboBox() self.outlineSizeBox.addItems(['0', '1', '2', '3', '4']) self.outlineSizeBox.setCurrentIndex(1) self.outlineSizeBox.setFixedWidth(100) self.optionLayout.addWidget(self.outlineSizeBox, 2, 0, 1, 1) self.outlineSizeLabel = QLabel('描边大小') self.optionLayout.addWidget(self.outlineSizeLabel, 2, 1, 1, 1) self.outlineColorSelect = label() self.outlineColorSelect.setAlignment(Qt.AlignCenter) self.outlineColorSelect.setText(self.outlineColor) self.outlineColorSelect.setStyleSheet( 'background-color:%s;color:%s' % (self.outlineColor, self.colorReverse(self.outlineColor))) self.outlineColorSelect.clicked.connect(self.getOutlineColor) self.optionLayout.addWidget(self.outlineColorSelect, 2, 3, 1, 1) self.outlineColorLabel = QLabel('描边颜色') self.optionLayout.addWidget(self.outlineColorLabel, 2, 4, 1, 1) self.shadowSizeBox = QComboBox() self.shadowSizeBox.addItems(['0', '1', '2', '3', '4']) self.shadowSizeBox.setCurrentIndex(1) self.shadowSizeBox.setFixedWidth(100) self.optionLayout.addWidget(self.shadowSizeBox, 3, 0, 1, 1) self.shadowSizeLabel = QLabel('阴影大小') self.optionLayout.addWidget(self.shadowSizeLabel, 3, 1, 1, 1) self.shadowColorSelect = label() self.shadowColorSelect.setAlignment(Qt.AlignCenter) self.shadowColorSelect.setText(self.shadowColor) self.shadowColorSelect.setStyleSheet( 'background-color:%s;color:%s' % (self.shadowColor, self.colorReverse(self.shadowColor))) self.shadowColorSelect.clicked.connect(self.getShadowColor) self.optionLayout.addWidget(self.shadowColorSelect, 3, 3, 1, 1) self.shadowColorLabel = QLabel('阴影颜色') self.optionLayout.addWidget(self.shadowColorLabel, 3, 4, 1, 1) self.align = QComboBox() self.align.addItems([ '1: 左下', '2: 中下', '3: 右下', '4: 中左', '5: 中间', '6: 中右', '7: 左上', '8: 中上', '9: 右上' ]) self.align.setCurrentIndex(1) self.align.setFixedWidth(100) self.optionLayout.addWidget(self.align, 4, 0, 1, 1) self.alignLabel = QLabel('对齐方式') self.optionLayout.addWidget(self.alignLabel, 4, 1, 1, 1) self.VAlignSlider = QSlider() self.VAlignSlider.setFixedWidth(100) self.VAlignSlider.setTickPosition(QSlider.TicksAbove) self.VAlignSlider.setSingleStep(10) self.VAlignSlider.setTickInterval(20) self.optionLayout.addWidget(self.VAlignSlider, 4, 3, 1, 1) self.VAlignSlider.setOrientation(Qt.Horizontal) self.VAlignLabel = QLabel('垂直边距') self.optionLayout.addWidget(self.VAlignLabel, 4, 4, 1, 1) self.LAlignSlider = QSlider() self.LAlignSlider.setFixedWidth(100) self.LAlignSlider.setTickPosition(QSlider.TicksAbove) self.LAlignSlider.setSingleStep(10) self.LAlignSlider.setTickInterval(20) self.optionLayout.addWidget(self.LAlignSlider, 5, 0, 1, 1) self.LAlignSlider.setOrientation(Qt.Horizontal) self.LAlignLabel = QLabel('左边距') self.optionLayout.addWidget(self.LAlignLabel, 5, 1, 1, 1) self.RAlignSlider = QSlider() self.RAlignSlider.setFixedWidth(100) self.RAlignSlider.setTickPosition(QSlider.TicksAbove) self.RAlignSlider.setSingleStep(10) self.RAlignSlider.setTickInterval(20) self.optionLayout.addWidget(self.RAlignSlider, 5, 3, 1, 1) self.RAlignSlider.setOrientation(Qt.Horizontal) self.RAlignLabel = QLabel('右边距') self.optionLayout.addWidget(self.RAlignLabel, 5, 4, 1, 1) def getFont(self): status, font = QFontDialog.getFont() if status: self.font = QFontInfo(font) self.fontName = self.font.family() self.fontSize = self.font.pointSize() self.fontBold = self.font.bold() self.fontItalic = self.font.italic() self.fontUnderline = self.font.underline() self.fontStrikeout = self.font.strikeOut() fontBold = '粗体' if self.fontBold else '' fontItalic = '斜体' if self.fontItalic else '' fontUnderline = '下划线' if self.fontUnderline else '' fontStrikeOut = '删除线' if self.fontStrikeout else '' self.fontSelect.setText('%s%s号%s%s%s%s' % (self.fontName, self.fontSize, fontBold, fontItalic, fontUnderline, fontStrikeOut)) def setKaraoke(self): self.karaokeStatus = not self.karaokeStatus if self.karaokeStatus: self.secondColorSelect.show() self.secondColorLabel.show() self.karaoke.setStyleSheet('background-color:#3daee9') else: self.secondColorSelect.hide() self.secondColorLabel.hide() self.karaoke.setStyleSheet('background-color:#31363b') def getFontColor(self): color = QColorDialog.getColor() if color.isValid(): self.fontColor = color.name() self.fontColorSelect.setText(self.fontColor) self.fontColorSelect.setStyleSheet( 'background-color:%s;color:%s' % (self.fontColor, self.colorReverse(self.fontColor))) def getSecondFontColor(self): color = QColorDialog.getColor() if color.isValid(): self.secondColor = color.name() self.secondColorSelect.setText(self.fontColor) self.secondColorSelect.setStyleSheet( 'background-color:%s;color:%s' % (self.secondColor, self.colorReverse(self.secondColor))) def getOutlineColor(self): color = QColorDialog.getColor() if color.isValid(): self.outlineColor = color.name() self.outlineColorSelect.setText(self.outlineColor) self.outlineColorSelect.setStyleSheet( 'background-color:%s;color:%s' % (self.outlineColor, self.colorReverse(self.outlineColor))) def getShadowColor(self): color = QColorDialog.getColor() if color.isValid(): self.shadowColor = color.name() self.shadowColorSelect.setText(self.shadowColor) self.shadowColorSelect.setStyleSheet( 'background-color:%s;color:%s' % (self.shadowColor, self.colorReverse(self.shadowColor))) def colorReverse(self, color): r = 255 - int(color[1:3], 16) g = 255 - int(color[3:5], 16) b = 255 - int(color[5:7], 16) return '#%s%s%s' % (hex(r)[2:], hex(g)[2:], hex(b)[2:])
class ResultWidget(QWidget): def __init__(self, route=None): super().__init__() layout = QVBoxLayout() self.route = route self.thread_pool = QThreadPool() layout_send = QHBoxLayout() self.send_button = QPushButton('Send') self.send_button.clicked.connect(self.make_request) self.search_line = QLineEdit() self.search_line.setPlaceholderText('Search') self.search_line.textChanged.connect(self.search_result_reset) self.search_line.returnPressed.connect(self.search_result) self.response_status_label = QLabel() self.response_status_label.setFont(FONT_ROUTE) self.response_status_label.hide() self.elapsed_time_label = QLabel() self.elapsed_time_label.setFont(FONT_ROUTE) self.elapsed_time_label.hide() self.search_summary_label = QLabel() self.search_summary_label.setFont(FONT_ROUTE) self.search_summary_label.hide() if route is not None: layout_send.addWidget(self.send_button) layout_send.addWidget(self.response_status_label) layout_send.addWidget(self.elapsed_time_label) layout_send.addStretch(1) layout_send.addWidget(self.search_summary_label) layout_send.addWidget(self.search_line) layout.addLayout(layout_send) self.result_text_edit = ResultTextEdit() self.result_text_edit.setReadOnly(True) self.result_text_edit.setFont(TEXT_FONT) self.result_text_edit.setContextMenuPolicy(Qt.NoContextMenu) self.result_text_edit.search.connect(self.focus) self.shortcut = QShortcut(QKeySequence("Ctrl+Return"), self, self.make_request) self.result_text_edit.setUndoRedoEnabled(False) self.highlighter = TextHighlighter(self.result_text_edit.document()) layout.addWidget(self.result_text_edit) if route is not None: saved_result = load_request_result(route) self.result_text_edit.setPlainText(saved_result) self.setLayout(layout) def goto(self, to): c = self.result_text_edit.textCursor() c.movePosition(to, QTextCursor.MoveAnchor, 1) self.result_text_edit.setTextCursor(c) def search_result_reset(self): self.goto(QTextCursor.Start) string_format = QTextCharFormat() string_format.setBackground(QColor('#668B8B')) extras = [] self.search_positions = [] while True: extra = QTextEdit.ExtraSelection() found = self.result_text_edit.find(self.search_line.text()) if not found: break extra.cursor = self.result_text_edit.textCursor() extra.format = string_format self.search_positions.append(extra.cursor.position()) extras.append(extra) self.result_text_edit.setExtraSelections(extras) self.goto(QTextCursor.Start) self.search_result() def search_result(self): p = self.result_text_edit.palette() p.setColor(QPalette.Highlight, QColor("#ee799f")) self.result_text_edit.setPalette(p) search_settings = QTextDocument.FindFlags() mod = QApplication.keyboardModifiers() if (mod & Qt.ShiftModifier) != 0: search_settings |= QTextDocument.FindBackward r = self.result_text_edit.find(self.search_line.text(), search_settings) if not r: if (mod & Qt.ShiftModifier) != 0: self.goto(QTextCursor.End) else: self.goto(QTextCursor.Start) self.result_text_edit.find(self.search_line.text(), search_settings) if self.search_line.text() == '': self.search_summary_label.hide() return current_position = self.result_text_edit.textCursor().position() try: current_index = self.search_positions.index(current_position) except ValueError: current_index = -1 self.search_summary_label.show() self.search_summary_label.setText( '%s/%s' % (current_index + 1, len(self.search_positions))) def focus(self): self.search_line.setFocus() self.search_line.selectAll() def make_request(self): self.response_status_label.hide() self.elapsed_time_label.hide() self.result_text_edit.setPlainText('Loading..') try: group_values, route_values = get_parameter_values_for_route( self.route) request = self.route.get_request(group_values, route_values) worker = RequestWorker( request.method, request.url, params=request.params, headers=request.headers, json=request.json, ) worker.signals.result.connect(self.set_result) self.thread_pool.start(worker) except CaribouException as e: self.result_text_edit.setPlainText(str(e)) except Exception: self.result_text_edit.setPlainText(traceback.format_exc()) def set_result(self, text, status_code, elapsed_time): if status_code == 0: self.response_status_label.hide() self.elapsed_time_label.hide() else: p = self.response_status_label.palette() if status_code == 200: self.response_status_label.setText(str(status_code) + ' OK') p.setColor(QPalette.WindowText, QColor('#1FDA9A')) else: self.response_status_label.setText(str(status_code) + ' ERROR') p.setColor(QPalette.WindowText, QColor('#DB3340')) self.response_status_label.setPalette(p) self.response_status_label.show() self.elapsed_time_label.setText('%s ms' % int(elapsed_time * 1000)) self.elapsed_time_label.show() self.result_text_edit.setUpdatesEnabled(False) self.result_text_edit.setPlainText(text) self.result_text_edit.setUpdatesEnabled(True) save_request_result(self.route, text) persist_storage()
class VqlEditorWidget(plugin.PluginWidget): """Exposed class to manage VQL/SQL queries from the mainwindow""" LOCATION = plugin.FOOTER_LOCATION ENABLE = True def __init__(self, parent=None): super().__init__(parent) self.setWindowTitle(self.tr("VQL Editor")) # Top toolbar self.top_bar = QToolBar() self.top_bar.setToolButtonStyle(Qt.ToolButtonTextBesideIcon) self.run_action = self.top_bar.addAction(FIcon(0xF040A), self.tr("Run"), self.run_vql) self.run_action.setShortcuts( [Qt.CTRL + Qt.Key_R, QKeySequence.Refresh]) self.run_action.setToolTip( self.tr("Run VQL query (%s)" % self.run_action.shortcut().toString())) # Syntax highlighter and autocompletion self.text_edit = CodeEdit() # Error handling self.log_edit = QLabel() self.log_edit.setMinimumHeight(40) self.log_edit.setStyleSheet( "QWidget{{background-color:'{}'; color:'{}'}}".format( style.WARNING_BACKGROUND_COLOR, style.WARNING_TEXT_COLOR)) self.log_edit.hide() self.log_edit.setFrameStyle(QFrame.StyledPanel | QFrame.Raised) main_layout = QVBoxLayout() main_layout.addWidget(self.top_bar) main_layout.addWidget(self.text_edit) main_layout.addWidget(self.log_edit) main_layout.setContentsMargins(0, 0, 0, 0) main_layout.setSpacing(0) self.setLayout(main_layout) def on_open_project(self, conn: sqlite3.Connection): """overrided from PluginWidget : Do not call this methods Args: conn (sqlite3.Connection): sqlite3 connection """ self.conn = conn self._fill_completer() self.on_refresh() def on_refresh(self): """overrided from PluginWidget""" vql_query = build_vql_query( self.mainwindow.state.fields, self.mainwindow.state.source, self.mainwindow.state.filters, self.mainwindow.state.group_by, self.mainwindow.state.having, ) self.set_vql(vql_query) def set_vql(self, text: str): """Set vql source code without executed Args: text (str): VQL query """ self.text_edit.blockSignals(True) self.text_edit.setPlainText(text) self.text_edit.blockSignals(False) def _fill_completer(self): """Create Completer with his model Fill the model with the SQL keywords and database fields """ # preload samples , selection and wordset samples = [i["name"] for i in sql.get_samples(self.conn)] selections = [i["name"] for i in sql.get_selections(self.conn)] wordsets = [i["name"] for i in sql.get_wordsets(self.conn)] # keywords = [] self.text_edit.completer.model.clear() self.text_edit.completer.model.beginResetModel() # register keywords for keyword in self.text_edit.syntax.sql_keywords: self.text_edit.completer.model.add_item(keyword, "VQL keywords", FIcon(0xF0169), "#f6ecf0") for selection in selections: self.text_edit.completer.model.add_item(selection, "Source table", FIcon(0xF04EB), "#f6ecf0") for wordset in wordsets: self.text_edit.completer.model.add_item(f"WORDSET['{wordset}']", "WORDSET", FIcon(0xF04EB), "#f6ecf0") for field in sql.get_fields(self.conn): name = field["name"] description = "<b>{}</b> ({}) from {} <br/><br/> {}".format( field["name"], field["type"], field["category"], field["description"]) color = style.FIELD_TYPE.get(field["type"], "str")["color"] icon = FIcon( style.FIELD_TYPE.get(field["type"], "str")["icon"], "white") if field["category"] == "variants" or field[ "category"] == "annotations": self.text_edit.completer.model.add_item( name, description, icon, color) if field["category"] == "samples": # Overwrite name for sample in samples: name = "sample['{}'].{}".format(sample, field["name"]) description = "<b>{}</b> ({}) from {} {} <br/><br/> {}".format( field["name"], field["type"], field["category"], sample, field["description"], ) self.text_edit.completer.model.add_item( name, description, icon, color) self.text_edit.completer.model.endResetModel() # if field["category"] == "samples": # for sample in samples: # keywords.append("sample['{}'].{}".format(sample, field["name"])) # else: # keywords.append(field["name"]) def check_vql(self) -> bool: """Check VQL statement; return True if OK, False when an error occurs Notes: This function also sets the error message to the bottom of the view. Returns: bool: Status of VQL query (True if valid, False otherwise). """ try: self.log_edit.hide() _ = [i for i in vql.parse_vql(self.text_edit.toPlainText())] except (TextXSyntaxError, VQLSyntaxError) as e: # Show the error message on the ui # Available attributes: e.message, e.line, e.col self.set_message("%s: %s, col: %d" % (e.__class__.__name__, e.message, e.col)) return False return True def run_vql(self): """Execute VQL code Suported commands and the plugins that need to be refreshed in consequence: - select_cmd: main ui (all plugins in fact) - count_cmd: *not supported* - drop_cmd: selections & wordsets - create_cmd: selections - set_cmd: selections - bed_cmd: selections - show_cmd: *not supported* - import_cmd: wordsets """ # Check VQL syntax first if not self.check_vql(): return for cmd in vql.parse_vql(self.text_edit.toPlainText()): LOGGER.debug("VQL command %s", cmd) cmd_type = cmd["cmd"] # If command is a select kind if cmd_type == "select_cmd": # => Command will be executed in different widgets (variant_view) # /!\ VQL Editor will not check SQL validity of the command # columns from variant table self.mainwindow.state.fields = cmd["fields"] # name of the variant selection self.mainwindow.state.source = cmd["source"] self.mainwindow.state.filters = cmd["filters"] self.mainwindow.state.group_by = cmd["group_by"] self.mainwindow.state.having = cmd["having"] # Refresh all plugins self.mainwindow.refresh_plugins(sender=self) continue try: # Check SQL validity of selections related commands command.create_command_from_obj(self.conn, cmd)() except (sqlite3.DatabaseError, VQLSyntaxError) as e: # Display errors in VQL editor self.set_message(str(e)) LOGGER.exception(e) continue # Selections related commands if cmd_type in ("create_cmd", "set_cmd", "bed_cmd"): # refresh source editor plugin for selections self.mainwindow.refresh_plugin("source_editor") continue if cmd_type == "drop_cmd": # refresh source editor plugin for selections self.mainwindow.refresh_plugin("source_editor") # refresh wordset plugin self.mainwindow.refresh_plugin("word_set") if cmd_type == "import_cmd": # refresh wordset plugin self.mainwindow.refresh_plugin("word_set") def set_message(self, message: str): """Show message error at the bottom of the view Args: message (str): Error message """ if self.log_edit.isHidden(): self.log_edit.show() icon_64 = FIcon(0xF0027, style.WARNING_TEXT_COLOR).to_base64(18, 18) self.log_edit.setText("""<div height=100%> <img src="data:image/png;base64,{}" align="left"/> <span> {} </span> </div>""".format(icon_64, message))
class Title(StyleWindow): class TitleControl(IntEnum): TitleLog = 1 << 1, TitleText = 1 << 2, TitleMinimized = 1 << 3, TitleMaximized = 1 << 4, TitleClose = 1 << 5 def __init__(self, parent=None): super(Title, self).__init__(parent) self._layout = None self._enabled_movie = False # 是否支持拖动 self._left_button_pressed = False self._pressed_pos = QPoint() self._log = None self._title = None self._maximize_btn = None self._minimize_btn = None self._close_btn = None self.mouse_drag_enabled = False self._init_def_layout() self.set_show_title_ctrls(self.TitleControl.TitleText | self.TitleControl.TitleMinimized | self.TitleControl.TitleMaximized | self.TitleControl.TitleClose) close = Signal() show_minimized = Signal() show_maximized = Signal() def _init_def_layout(self): self._layout = QHBoxLayout() self.set_layout(self._layout) self._layout.setContentsMargins(5, 0, 10, 5) self._layout.setSpacing(10) # self._log = QLabel() self._log.setObjectName("Title_LogLabel") self._layout.addWidget(self._log) self._log.hide() self._title = QLabel() self._title.setObjectName("Title_TitleLabel") self._layout.addWidget(self._title) # self._title.hide() self._layout.addSpacerItem( QSpacerItem(0, 0, QSizePolicy.Expanding, QSizePolicy.Fixed)) # close button self._close_btn = DImageButton() self._close_btn.setObjectName("Title_CloseButton") self._maximize_btn = DImageButton() self._maximize_btn.setObjectName("Title_MaximizeButton") self._minimize_btn = DImageButton() self._minimize_btn.setObjectName("Title_MinimizeButton") self._layout.addWidget(self._minimize_btn, 0, Qt.AlignRight) self._layout.addWidget(self._maximize_btn, 0, Qt.AlignRight) self._layout.addWidget(self._close_btn, 0, Qt.AlignRight) #self._close_btn.hide() self._maximize_btn.hide() # self._minimize_btn.hide() self._close_btn.clicked.connect(self.close) self._minimize_btn.clicked.connect(self.show_minimized) self._maximize_btn.clicked.connect(self.show_maximized) def log(self): return self._log def set_log(self, pix: QPixmap): self._log_label.setPixmap(pix) log_ = Property(QPixmap, fget=log, fset=set_log) @property def enabled_movie(self): return self._enabled_movie @enabled_movie.setter def enabled_movie(self, enabled): self._enabled_movie = enabled def mousePressEvent(self, event): self.set_calc_mouse_type( self._calc_cursor_pos(event.pos(), self._calc_cursor_col(event.pos()))) if self._mouse_drag_is_center_type(): if Qt.LeftButton == event.button() and self.parent() is not None: self._left_button_pressed = True self._pressed_pos = event.globalPos() - self.parent().pos() super(Title, self).mousePressEvent(event) def mouseReleaseEvent(self, event): self._left_button_pressed = False super(Title, self).mouseReleaseEvent(event) def mouseMoveEvent(self, event): if self._mouse_drag_is_center_type(): if self._left_button_pressed and self.parent() is not None: self.parent().move(event.globalPos() - self._pressed_pos) super(Title, self).mouseMoveEvent(event) @property def title_text(self): return self._title.text() @title_text.setter def title_text(self, text): self._title.setText(text) def set_show_title_ctrls(self, ctrls): """设置标题需要显示的控件""" self._log.setVisible(ctrls & self.TitleControl.TitleLog) self._title.setVisible(ctrls & self.TitleControl.TitleText) self._minimize_btn.setVisible(ctrls & self.TitleControl.TitleMinimized) self._maximize_btn.setVisible(ctrls & self.TitleControl.TitleMaximized) self._close_btn.setVisible(ctrls & self.TitleControl.TitleClose)
class _RotorsHandlerWidget(QFrame): """Coordinates all rotors and the settings button""" def __init__( self, master, position_plug, rotate_plug, rotate_ref_plug, enigma_api, refresh_plug, reflector_pos_plug, ): """ :param master: {Qt} Master qt object :param position_plug: {callable} Callable method for getting rotor positions :param rotate_plug: {callable} Temporary callable for getting rotor offset handlers :param rotate_ref_plug: {callable} Callable to change reflector position :param enigma_api: {EnigmaAPI} Shared EnigmaAPI instance :param refresh_plug: {callable} Used to refresh the GUI based on new rotor positions :param reflector_pos_plug: {callable} Used to get reflector position """ super().__init__(master) # QT WINDOW SETTINGS ================================================== self.setFrameStyle(QFrame.Panel | QFrame.Sunken) self.__layout = QHBoxLayout(self) self.__layout.setAlignment(Qt.AlignCenter) self.__rotor_indicators = [] self.__reflector_indicator = None # ATTRIBUTES ========================================================== self.__enigma_api = enigma_api self.__rotate_plug = rotate_plug self.__position_plug = position_plug self.__refresh_plug = refresh_plug self.__reflector_pos_plug = reflector_pos_plug # SETTINGS ICON ======================================================= self.__settings_button = QPushButton(QIcon(BASE_DIR + "settings.png"), "", self) self.__settings_button.setIconSize(QSize(50, 50)) self.__settings_button.setToolTip( "Edit Enigma rotor and reflector settings") self.__settings_button.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Expanding) self.__settings_button.clicked.connect(self.__open_settings) # GENERATE ROTORS AND REFLECTOR ======================================= self.__ukwd_indicator = QLabel("D") self.__ukwd_indicator.setStyleSheet( "QLabel{font-size: 20px; text-align: center; background-color: white; color: red}" ) self.__ukwd_indicator.setFrameStyle(QFrame.Panel | QFrame.Sunken) self.__ukwd_indicator.setAlignment(Qt.AlignCenter | Qt.AlignVCenter) self.__ukwd_indicator.setLineWidth(2) self.__ukwd_indicator.setFixedSize(40, 40) self.__ukwd_indicator.hide() self.__reflector_indicator = _RotorHandlerWidget( self, rotate_ref_plug(1, True), rotate_ref_plug(-1, True), self.set_positions, ) self.__reflector_indicator.setToolTip("Reflector position indicator") self.__reflector_indicator.hide() self._left_spacer = QSpacerItem(0, 0, QSizePolicy.Expanding, QSizePolicy.Expanding) self._right_spacer = QSpacerItem(0, 0, QSizePolicy.Expanding, QSizePolicy.Expanding) # REGENERATE =============================================== self.generate_rotors() self.set_positions() def generate_rotors(self): """Regenerates rotor and reflector views (with position indicators and and buttons to rotate them according to new EnigmaAPI settings""" self.__layout.removeItem(self._left_spacer) self.__layout.removeItem(self._right_spacer) self.__layout.removeWidget(self.__settings_button) self.__layout.removeWidget(self.__ukwd_indicator) self.__ukwd_indicator.hide() self.__reflector_indicator.hide() self.__layout.removeWidget(self.__reflector_indicator) self.__reflector_indicator.hide() for indicator in self.__rotor_indicators: indicator.deleteLater() self.__layout.addItem(self._left_spacer) self.__rotor_indicators = [] if self.__enigma_api.reflector() == "UKW-D": logging.info("UKW-D reflector detected, showing indicator...") self.__layout.addWidget(self.__ukwd_indicator) self.__ukwd_indicator.show() elif self.__enigma_api.reflector_rotatable(): logging.info("Rotatable reflector detected, showing indicator...") self.__layout.addWidget(self.__reflector_indicator) self.__reflector_indicator.show() # Create for i in range(len(self.__position_plug())): # Rotor controls indicator = _RotorHandlerWidget( self, self.__rotate_plug(i, 1), self.__rotate_plug(i, -1), self.set_positions, ) self.__layout.addWidget(indicator, alignment=Qt.AlignLeft) self.__rotor_indicators.append(indicator) self.__layout.addItem(self._right_spacer) self.__layout.addWidget(self.__settings_button) def __open_settings(self): """Opens settings and reloads indicators afterwards if changes are detected""" logging.info("Opening settings menu...") old_cfg = self.__enigma_api.get_config() settings = SettingsWindow(self, self.__enigma_api) settings.exec() # Exec gives focus to top window, unlike .show if old_cfg != self.__enigma_api.get_config(): logging.info("Settings changed, reloading GUI...") del settings self.generate_rotors() self.__refresh_plug() self.set_positions() else: logging.info("No changes to settings made...") def set_positions(self): """Refreshes position indicators to show new positions from EnigmaAPI""" if (self.__enigma_api.reflector_rotatable() and self.__enigma_api.reflector() != "UKW-D"): logging.info('Reflector indicator set to position "%s"', self.__reflector_pos_plug()) self.__reflector_indicator.set(self.__reflector_pos_plug()) logging.info('Rotor indicators set to positions "%s"', str(self.__position_plug())) for rotor, position in zip(self.__rotor_indicators, self.__position_plug()): rotor.set(position)
class ProgressUpdater(QWidget): def __init__(self, parent: QtCore.QObject = None, window: QtWidgets.QMainWindow = None, processingText: str = "Compressing...", clickToStartText: str = "Click compress to start.") -> None: super().__init__(parent=parent) self.parentWindow: QtWidgets.QMainWindow = window self.processingText = processingText self.clickToStartText = clickToStartText self.isLoading = True #self.setFixedHeight(30) self.setUpWidgets() def setUpWidgets(self) -> None: self.wheelLabel = QLabel(self) self.progressBar = QProgressBar(self) self.progressBar.setAlignment(QtCore.Qt.AlignHCenter | QtCore.Qt.AlignVCenter) self.progressBar.setSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding) self.infoLabel = QLabel(self) self.infoLabel.setSizePolicy(QtWidgets.QSizePolicy.Ignored, QtWidgets.QSizePolicy.Ignored) self.mainLayout = QHBoxLayout() self.mainLayout.addWidget(self.wheelLabel) self.mainLayout.addWidget(self.progressBar) self.mainLayout.addWidget(self.infoLabel, stretch=1) self.wheelMovie = QtGui.QMovie(self) self.wheelMovie.setFileName(getPath("load.gif")) self.wheelMovie.setScaledSize(QtCore.QSize(20, 20)) self.wheelMovie.start() self.wheelLabel.setMovie(self.wheelMovie) self.wheelLabel.hide() self.progressBar.setRange(0, 100) #self.progressBar.setFixedHeight(30) if (_platform == "win32"): self.parentWindow.taskbprogress.setRange(0, 100) self.progressBar.setValue(0) if (_platform == "win32"): self.parentWindow.taskbprogress.setValue(0) self.progressBar.setFixedSize(400, 20) self.progressBar.setFormat("Ready") self.infoLabel.setText(self.clickToStartText) self.mainLayout.setMargin(0) self.setLayout(self.mainLayout) def startLoading(self) -> None: self.isLoading = True if (_platform == "win32"): self.parentWindow.taskbprogress.show() if (_platform == "win32"): self.parentWindow.taskbprogress.setValue(0) self.progressBar.setValue(0) if (_platform == "win32"): self.parentWindow.taskbprogress.setRange(0, 0) self.progressBar.setRange(0, 0) self.infoLabel.setText(self.processingText) self.progressBar.setFormat("%p%") self.wheelLabel.show() def stopLoading(self) -> None: self.isLoading = False self.wheelLabel.hide() if (_platform == "win32"): self.parentWindow.taskbprogress.hide() if (_platform == "win32"): self.parentWindow.taskbprogress.setRange(0, 100) self.progressBar.setRange(0, 100) if (_platform == "win32"): self.parentWindow.taskbprogress.setValue(0) self.progressBar.setValue(0) self.progressBar.setFormat("Ready") self.infoLabel.setText(self.clickToStartText) def setText(self, text: str) -> None: self.infoLabel.setText(text) self.infoLabel.setToolTip(text) def setRange(self, min: int, max: int) -> None: self.progressBar.setRange(min, max) if (_platform == "win32"): self.parentWindow.taskbprogress.setRange(min, max) def setValue(self, value: int) -> None: self.progressBar.setValue(value) if (_platform == "win32"): self.parentWindow.taskbprogress.setValue(value)
class TreeWidget(QTreeWidget): def __init__(self, parent=None, emptyText="Hint"): super().__init__(parent=parent) self.backgroundLabel = QLabel(self) self.backgroundLabel.setAttribute( QtCore.Qt.WA_TransparentForMouseEvents) self.backgroundLabel.setWordWrap(True) self.backgroundLabel.show() self.openFileAction = self.doNothing self.backgroundLabel.setAlignment(QtCore.Qt.AlignCenter) self.backgroundLabel.setText(emptyText) self.backgroundLabel.resize(400, 200) self.opacity = QtWidgets.QGraphicsOpacityEffect(self) self.opacity.setOpacity(0.5) self.backgroundLabel.setGraphicsEffect(self.opacity) self.backgroundLabel.setStyleSheet( "background-color: transparent; font-size: 15pt; font-weight: light;" ) self.setColumnCount(5) self.setIconSize(QtCore.QSize(32, 32)) self.setAutoScroll(True) self.setVerticalScrollMode(QtWidgets.QTreeWidget.ScrollPerPixel) self.setHorizontalScrollMode(QtWidgets.QTreeWidget.ScrollPerPixel) self.setColumnWidth(0, 300) self.setSelectionMode( QtWidgets.QTreeView.SelectionMode.ContiguousSelection) self.setColumnWidth(3, 200) self.setHeaderLabels( ["File name", "Size", "Status", "File location", "Relative path"]) self.setDropIndicatorShown(True) self.setAcceptDrops(True) self.setSupportedDropActions = QtCore.Qt.CopyAction | QtCore.Qt.MoveAction def dragEnterEvent(self, e): e.accept() def dragMoveEvent(self, e): e.accept() def dropEvent(self, e): self.openFileAction(str(e.mimeData().text().replace("file://", ""))) def resizeEvent(self, event) -> None: eventResult = super().resizeEvent(event) w, h = self.width(), self.height() diffW = w - self.backgroundLabel.width() diffH = h - self.backgroundLabel.height() self.backgroundLabel.move(diffW // 2, diffH // 2) return eventResult def setEmptyText(self, text: str) -> None: self.backgroundLabel.setText(text) def connectFileDragEvent(self, func) -> None: self.openFileAction = func def doNothing(self, f: str) -> None: log("[ WARN ] File {f} dragged, but no actoin was defined for this event" ) def showHideLabel(self) -> None: if (self.topLevelItemCount() > 0): self.backgroundLabel.hide() else: self.backgroundLabel.show() def addTopLevelItem(self, item: QtWidgets.QTreeWidgetItem) -> None: super().addTopLevelItem(item) self.showHideLabel() def addTopLevelItems(self, items: typing.Sequence) -> None: super().addTopLevelItems(items) self.showHideLabel() def clear(self) -> None: super().clear() self.showHideLabel() def takeTopLevelItem(self, index: int) -> QtWidgets.QTreeWidgetItem: w = super().takeTopLevelItem(index) self.showHideLabel() return w def insertTopLevelItem(self, index: int, item: QtWidgets.QTreeWidgetItem) -> None: super().insertTopLevelItem(index, item) self.showHideLabel() def insertTopLevelItems(self, index: int, items: typing.Sequence) -> None: super().insertTopLevelItems(index, items) self.showHideLabel()
class Form(QDialog): def __init__(self, parent=None): self.round = 0 self.lcd = QLCDNumber(5) self.lcd2 = QLCDNumber(5) self.clock = QLCDNumber(5) super(Form, self).__init__(parent) self.setWindowTitle("Pomodoro") # Create widgets self.slider = QSlider(Qt.Horizontal) self.slider.setRange(1, 99) self.slider.setValue(25) self.slider2 = QSlider(Qt.Horizontal) self.slider2.setRange(1, 99) self.slider2.setValue(5) self.count = self.slider.value() * 60 self.rest = self.slider2.value() * 60 self.taskbar_count = 0 self.taskbar2_count = 0 self.text = QLabel("How long should the work period be?") self.text2 = QLabel("How long should the rest period be?") self.work = QLabel("WORK") self.pause = QLabel("REST") self.rounds = QLabel("Number of rounds: " + str(self.round)) self.work.setAlignment(Qt.AlignHCenter) self.work.setFont(QFont("Times", 18, QFont.Bold)) self.pause.setAlignment(Qt.AlignHCenter) self.pause.setFont(QFont("Times", 18, QFont.Bold)) self.button = QPushButton("Start timer") self.button2 = QPushButton("Stop timer") self.reset = QPushButton("Reset rounds") self.lcd.display("25:00") self.lcd2.display("05:00") mins = 25 secs = "00" self.clock.display(f"{mins}:{secs}") self.slider.valueChanged.connect(self.first_display) self.slider2.valueChanged.connect(self.second_display) self.slider.valueChanged.connect(self.clock_display) self.button2.hide() self.work.hide() self.pause.hide() self.clock.hide() # Create layout and add widgets layout = QVBoxLayout() layout.addWidget(self.text) layout.addWidget(self.lcd) layout.addWidget(self.slider) layout.addWidget(self.text2) layout.addWidget(self.lcd2) layout.addWidget(self.slider2) layout.addWidget(self.button) layout.addWidget(self.button2) layout.addWidget(self.work) layout.addWidget(self.pause) layout.addWidget(self.clock) layout.addWidget(self.rounds) layout.addWidget(self.reset) # Set dialog layout self.setLayout(layout) self.systemtray_icon = QSystemTrayIcon(QIcon("snake.png")) self.systemtray_icon.show() self.systemtray_icon.activated.connect(self.icon_activated) self.menu = QMenu(parent) self.exit_action = self.menu.addAction("Exit") self.systemtray_icon.setContextMenu(self.menu) self.exit_action.triggered.connect(self.slot_exit) # Add signals self.slider.valueChanged.connect(self.count_func) self.slider2.valueChanged.connect(self.count_func) self.button.clicked.connect(self.button_update) self.button.clicked.connect(self.timer_func) self.button.clicked.connect(self.round_count) self.button2.clicked.connect(self.stop) self.reset.clicked.connect(self.reset_rounds) def reset_rounds(self): self.round = 0 self.rounds.setText("Number of rounds: " + str(self.round)) def round_count(self): self.round += 1 self.rounds.setText("Number of rounds: " + str(self.round)) def icon_activated(self, reason): if reason in (QSystemTrayIcon.Trigger, QSystemTrayIcon.DoubleClick): self.show() def closeEvent(self, event): self.hide() event.ignore() def slot_exit(self): QApplication.exit(0) def first_display(self): minute = str(self.slider.sliderPosition()) second = ":00" leading_zero = "0" if self.slider.sliderPosition() >= 10: self.lcd.display(minute + second) else: self.lcd.display(leading_zero + minute + second) def second_display(self): minute = str(self.slider2.sliderPosition()) second = ":00" leading_zero = "0" if self.slider2.sliderPosition() >= 10: self.lcd2.display(minute + second) else: self.lcd2.display(leading_zero + minute + second) def clock_display(self): minute = str(self.slider.sliderPosition()) second = ":00" leading_zero = "0" if self.slider.sliderPosition() >= 10: self.clock.display(minute + second) else: self.clock.display(leading_zero + minute + second) def count_func(self): self.count = self.slider.sliderPosition() * 60 self.rest = self.slider2.sliderPosition() * 60 def countdown(self): minute, second = divmod(self.count, 60) zero = "0" show = self.work.show() if second < 10 and minute < 10: self.clock.display(zero + str(minute) + ":" + zero + str(second)) elif second < 10: self.clock.display(str(minute) + ":" + zero + str(second)) elif minute < 10: self.clock.display(zero + str(minute) + ":" + str(second)) else: self.clock.display(str(minute) + ":" + str(second)) self.count -= 1 if self.count < -1: self.work.hide() self.taskbar_rest() show = self.pause.show() minute, second = divmod(self.rest, 60) zero = "0" if self.rest == self.slider2.value() * 60: self.show() if second < 10 and minute < 10: self.clock.display(zero + str(minute) + ":" + zero + str(second)) elif second < 10: self.clock.display(str(minute) + ":" + zero + str(second)) elif minute < 10: self.clock.display(zero + str(minute) + ":" + str(second)) else: self.clock.display(str(minute) + ":" + str(second)) self.rest -= 1 if self.rest < -1: self.clock.display("00:00") self.taskbar_work() self.timer.stop() self.stop() show def timer_func(self): timer = QTimer() self.timer = timer self.timer.timeout.connect(self.countdown) self.timer.start(1000) def button_update(self): self.button.hide() self.text.hide() self.lcd.hide() self.slider.hide() self.text2.hide() self.lcd2.hide() self.slider2.hide() self.reset.hide() self.clock.show() self.button2.show() self.work.show() def taskbar_rest(self): if self.taskbar_count == 0: self.systemtray_icon.showMessage("PAUSE", "Time to rest!", QSystemTrayIcon.Information, 500000) self.taskbar_count = 1 def taskbar_work(self): if self.taskbar2_count == 0: self.systemtray_icon.showMessage("WORK", "Break over!", QSystemTrayIcon.Information, 500000) self.taskbar2_count = 1 def stop(self): self.timer.stop() self.button2.hide() self.work.hide() self.pause.hide() self.clock.hide() self.count = self.slider.value() * 60 self.rest = self.slider2.value() * 60 self.clock.display(str(self.slider.value()) + ":00") self.button.show() self.text.show() self.lcd.show() self.slider.show() self.text2.show() self.lcd2.show() self.slider2.show() self.reset.show() self.show() self.taskbar_count = 0 self.taskbar2_count = 0
class Changewall(QDialog): """ Parent of all the widgets """ def __init__(self, parent=None): super().__init__(parent) self.screen_width, self.screen_height = get_screen_res(screen) self.screen_res: str = f'{self.screen_width}x{self.screen_height}' self.payload: Dict[str, str] = { 'sorting': 'random', 'categories': '100', 'atleast': self.screen_res } self.sw = StackedWidget() self.progressbar = ProgressBar() self.progressbar.hide() self.prev_btn = Button('angle-left.svg', key='left') self.next_btn = Button('angle-right.svg', key='right') self.update_btn = Button('sync-alt.svg', ' Update', key='r') self.apply_btn = Button('check.svg', 'Apply') self.save_btn = Button('save.svg', 'Save') self.prev_btn.clicked.connect(self.prev) self.next_btn.clicked.connect(self.next) self.apply_btn.clicked.connect(self.apply) self.update_btn.clicked.connect(self.update_) self.save_btn.clicked.connect(self.save) self.saved_msg = QLabel('Saved') self.image_count = QLabel() self.image_res = QLabel() self.saved_msg.setStyleSheet(f'color: {INFO_COLOR}') self.image_count.setStyleSheet(f'color: {INFO_COLOR}') self.image_res.setStyleSheet(f'color: {INFO_COLOR}') self.info_layout = QHBoxLayout() self.info_layout.addWidget(self.progressbar) self.info_layout.addStretch() self.info_layout.addWidget(self.image_count) self.info_layout.addWidget(self.image_res) button_layout = QHBoxLayout() button_layout.addWidget(self.prev_btn) button_layout.addWidget(self.next_btn) button_layout.addWidget(self.update_btn) button_layout.addWidget(self.apply_btn) button_layout.addWidget(self.save_btn) self.main_layout = QVBoxLayout() self.main_layout.addLayout(self.info_layout) self.main_layout.addWidget(self.sw) self.main_layout.addLayout(button_layout) self.setLayout(self.main_layout) self.sw.added.connect(self.change_image_count) def prev(self) -> None: """ Show previous image in stacked widget """ current_index: int = self.sw.currentIndex() if current_index > 0: self.sw.setCurrentIndex(current_index - 1) logger.debug( f"Stacked widget's current index is {self.sw.currentIndex()}") self.change_info() def next(self) -> None: """ Show next image in stacked widget """ current_index: int = self.sw.currentIndex() self.sw.setCurrentIndex(current_index + 1) logger.debug( f"Stacked widget's current index is {self.sw.currentIndex()}") self.change_info() def update_(self) -> None: """ Download new json, new thumbnails and delete old ones with clearing stacked widget """ for file in THUMBS_DIR.iterdir(): logger.debug(f'Deleting {short_path(file)}') file.unlink() # Clear stacked widget for _ in range(self.sw.count()): widget = self.sw.widget(0) self.sw.removeWidget(widget) del widget download = Download(JSON_FILE, APP_DIR, SEARCH_URL, payload=self.payload) download.save() self.progressbar.show() self.download_thumbs() def apply(self) -> None: """ Set current image as wallpaper """ for file in CURRENT_DIR.iterdir(): file.unlink() logger.debug(f'Deleted {short_path(file)}') image_id: str = self.sw.current_image_id() info: Dict[str, str] = image_info(image_id) image: Path = Path(info['image_id'] + info['extension']) self.progressbar.show() download = Download(image, CURRENT_DIR, info['full_image_url'], stream=True) download.finished_chunk.connect(self.set_progressbar) download.finished_file.connect(set_wall) download.save() def save(self) -> None: """ Save image to CURRENT_DIR When image is saved, show Saved label """ image_id: str = self.sw.current_image_id() info: Dict[str, str] = image_info(image_id) image: Path = Path(info['image_id'] + info['extension']) self.progressbar.show() download = Download(image, SAVED_DIR, info['full_image_url'], stream=True) download.finished_chunk.connect(self.set_progressbar) download.save() # Show message "Saved" for 3 seconds in info layout self.info_layout.insertWidget(2, self.saved_msg) self.saved_msg.show() QTimer.singleShot(3000, self.hide_msg) save_msg: bool = config.getboolean('Program', 'show_save_message') def disable_save_msg(): config['Program']['show_save_message'] = 'no' config_save() logger.debug('Save message is now disabled') # Create and show "save message box" if it is set to True if save_msg: msgBox = QMessageBox(self) msgBox.setIcon(QMessageBox.Information) msgBox.setText('Saved') msgBox.setInformativeText( f'The image has been saved to \n{str(SAVED_DIR)}') msgBox.setStandardButtons(QMessageBox.Ok) dontshow_btn = msgBox.addButton("Don't show again", QMessageBox.ActionRole) dontshow_btn.clicked.connect(disable_save_msg) msgBox.exec_() def hide_msg(self) -> None: """ Remove save label from info layout and hide it """ self.info_layout.removeWidget(self.saved_msg) self.saved_msg.hide() def download_thumbs(self) -> None: """ Parse JSON_FILE then download thumbnails asynchronously. Each time thumbnail is downloaded, signals are emitted to stacked widget and progressbar """ with open(JSON_FILE, 'r') as f: data: Dict = json.load(f) for item in data['data']: url: str = item['thumbs']['large'] name: Path = Path(item['id'] + '.' + url[-3:]) dt = DownloadThread(name, THUMBS_DIR, url) dt.finished_file.connect(self.sw.add) dt.finished_file.connect(self.set_progressbar) QThreadPool.globalInstance().start(dt) def change_image_count(self) -> None: """ Update info of current image position in stacked widget and total number of images """ self.image_count.setText(self.sw.count_info()) def change_info(self) -> None: """ Everytime change_info is called it get info of current image to update label 'image_res' with image resolution and image position in stacked widget """ info: Dict[str, str] = image_info(self.sw.current_image_id()) self.change_image_count() if len(info) > 0: self.image_res.setText(info['resolution']) else: self.image_res.setText('Image info not found') def set_progressbar(self, _) -> None: """ Update progressbar and hide it when it reaches its maximum """ current: int = self.progressbar.value() self.progressbar.setValue(current + 1) if current == self.progressbar.maximum(): self.progressbar.hide() self.progressbar.setValue(0) def load(self) -> None: """ Download thumbnails if THUMBS_DIR is empty or JSON_FILE don't exist. Otherwise fill stacked widget with existing thumbnails """ create_dirs(THUMBS_DIR, CURRENT_DIR, SAVED_DIR) if JSON_FILE.exists(): if not is_dir_contains_images(THUMBS_DIR): logger.debug( f"{short_path(THUMBS_DIR)} is empty. Downloading new thumbnails" ) self.progressbar.show() self.download_thumbs() else: logger.debug('Filling stacked widget') self.sw.fill() self.change_info() else: logger.debug(f"{short_path(JSON_FILE)} doesn't exist. Updating") self.update_() def resize_move(self) -> None: """ Resize and move window using parameters from settings """ try: self.resize(*win_size) logger.debug(f'Resized window to {win_size}') except: logger.warning('Could not resize window') if win_pos: try: self.move(*win_pos) logger.debug(f'Moved window to {win_pos}') except: logger.warning('Could not move window to a new position') def closeEvent(self, event) -> None: """ Save window size and position before closing the window""" config['Program']['window_size'] = f'{self.width()}, {self.height()}' config['Program']['window_position'] = f'{self.x()}, {self.y()}' config_save()
class CameraPanel(QWidget): __obj = None def __init__(self, n_camera, app: QApplication, *args, **kwargs): if CameraPanel.__obj is not None: raise type( 'InstanceExists', (Exception, ), {})('A CameraPanel instance has already been constructed.') CameraPanel.__obj = self super().__init__(*args, **kwargs) self.cameras = [] self.box = QVBoxLayout() self.setLayout(self.box) Configuration(n_camera, self) TrafficMonitor(n_camera) FrameRateMonitor(n_camera) FrameDropMonitor(n_camera) self.connectButton = QPushButton("Connect") self.connectButton.clicked.connect(self.connectRemote) self.reconnecting = QLabel("Disconnected. Trying to reconnect") self.reconnecting.setWordWrap(True) self.total_traffic = QLabel("0.000 KB/s") self.restart_remote = QPushButton("Restart Remote") self.restart_remote.setSizePolicy(QSizePolicy.Maximum, QSizePolicy.Expanding) self.restart_remote.clicked.connect(self.restartRemote) self.scan = QPushButton("Scan") self.scan.setSizePolicy(QSizePolicy.Maximum, QSizePolicy.Expanding) self.scan.clicked.connect(Configuration().scan) self.top_frame = QFrame() self.top_frame.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Maximum) self.top_grid = QGridLayout() self.top_grid.setContentsMargins(0, 0, 0, 0) self.top_frame.setLayout(self.top_grid) self.top_grid.addWidget(self.connectButton, 0, 1) self.top_grid.addWidget(self.reconnecting, 0, 1) self.top_grid.addWidget(self.total_traffic, 0, 1) self.top_grid.addWidget(self.restart_remote, 0, 2) self.top_grid.addWidget(self.scan, 0, 0) self.total_traffic.hide() self.reconnecting.hide() self.box.addWidget(self.top_frame) self.timer = QTimer() self.timer.timeout.connect(self.updateTraffic) self.timer.start(100) for i in range(n_camera): self.cameras.append(Camera(i, app)) main_splitter = QSplitter(Qt.Horizontal) main_splitter.addWidget(self.cameras[0]) if n_camera == 2: main_splitter.addWidget(self.cameras[1]) elif n_camera == 3: sub_splitter = QSplitter(Qt.Horizontal) sub_splitter.addWidget(self.cameras[1]) sub_splitter.addWidget(self.cameras[2]) main_splitter.addWidget(sub_splitter) elif n_camera == 4: sub_splitter = QSplitter(Qt.Horizontal) sub_sub_splitter = QSplitter(Qt.Horizontal) sub_splitter.addWidget(sub_sub_splitter) sub_sub_splitter.addWidget(self.cameras[1]) sub_sub_splitter.addWidget(self.cameras[2]) sub_splitter.addWidget(self.cameras[3]) main_splitter.addWidget(sub_splitter) self.box.addWidget(main_splitter) def connectRemote(self): """ This function blocks until TCP connection succeeds. Call with caution """ if Configuration().connect(): self.connectButton.hide() self.total_traffic.show() for cam in self.cameras: cam.startReceiving() cam.mode_selection.setEnabled(True) cam.updateMode(cam.mode_selection.currentIndex()) def restartRemote(self): os.system( f'ssh root@{REMOTE_IP_ADDR} systemctl restart vision-server.service' ) if self.connectButton.isVisible(): self.connectRemote() else: Configuration().reconnect() def updateTraffic(self): self.total_traffic.setText('{:<4} KB/s'.format( round(TrafficMonitor().total / 1024, 2)))
class Game(QOpenGLWidget): selectedCoordinates: List[Positions] = [] SCREEN = 1 gameTitle = None playButtonLabel = None def __init__(self, QOpenGLWidget): super().__init__() ''' if turn equals to false, then cirlcle will be marked and if its equals to true, then X will be marked ''' self.turn = False self.initializeHomeScreen() self.matrix_game = [[0 for _ in range(3)] for _ in range(3)] self.players = ['X', 'O'] def initializeGL(self): self.resize(env.VIEW_WIDTH, env.VIEW_HEIGHT) glViewport(0, 0, env.VIEW_WIDTH, env.VIEW_HEIGHT) def resizeEvent(self, e: PySide2.QtGui.QResizeEvent): super().resizeEvent(e) width = self.SCREEN == 1 and 220 or 400 if self.gameTitle is not None: x = e.size().width() / 3 y = e.size().height() / 2 self.gameTitle.setGeometry(int(x), int(y) - 100, width, 50) if self.playButtonLabel is not None: x = e.size().width() / 3 y = e.size().height() / 3 self.playButtonLabel.setGeometry(int(x), int(y) + 50, width, 50) def initializeHomeScreen(self): self.gameTitle = QLabel("Tic Tac Toe", self) self.playButtonLabel = QLabel("Jogar", self) self.gameTitle.setStyleSheet(env.titleLabelProps) self.playButtonLabel.setStyleSheet(env.btnLabelProps) def initializeEndScreen(self, victoryMsg): self.gameTitle = QLabel(victoryMsg, self) self.playButtonLabel = QLabel("Jogar Novamente", self) self.gameTitle.setStyleSheet(env.titleLabelProps) self.playButtonLabel.setStyleSheet(env.btnLabelProps) print(self.geometry().x(), self.geometry().y()) self.gameTitle.setGeometry(int(self.geometry().x() * 9), int(self.geometry().y() * 9) - 100, 420, 50) self.playButtonLabel.setGeometry(int(self.geometry().x() * 9), int(self.geometry().y() * 10) + 50, 400, 50) def calculateCoordinates(self, x, y): coordinates = Positions() # get width and height in case of resizes # divides by 3 due to 9x9 matrix of tic tac toe w1 = self.geometry().width() / 3 w2 = w1 * 2 h1 = self.geometry().height() / 3 h2 = h1 * 2 # identifying x index if 0 <= x < w1: coordinates.x = 0 elif w1 <= x < w2: coordinates.x = 1 else: coordinates.x = 2 # identifying y index if 0 <= y < h1: coordinates.y = 0 elif h1 <= y < h2: coordinates.y = 1 else: coordinates.y = 2 return coordinates def switchButtonsState(self, activate): if activate: self.playButtonLabel.show() self.gameTitle.show() else: self.playButtonLabel.hide() self.gameTitle.hide() def calculateCoordinatesHome(self, x, y): print(x, y, 215 <= x < 426, 204 <= y < 280) btnGeometry = self.playButtonLabel.geometry() print(self.playButtonLabel.geometry().x(), self.playButtonLabel.geometry().width()) if btnGeometry.x() <= x < btnGeometry.x() + btnGeometry.width( ) and btnGeometry.y() <= y < btnGeometry.y() + btnGeometry.height(): self.switchButtonsState(False) return 2 return 1 def clearBoard(self): self.selectedCoordinates: List[Positions] = [] self.matrix_game = [[0 for _ in range(3)] for _ in range(3)] def checkPlayPosition(self, x, y): for coordinate in self.selectedCoordinates: if coordinate.x == x and coordinate.y == y: print("Esse campo já foi escolhido") return False return True def mouseReleaseEvent(self, QMouseEvent): if self.SCREEN == 1: self.SCREEN = self.calculateCoordinatesHome( QMouseEvent.x(), QMouseEvent.y()) elif self.SCREEN == 2: positions = self.calculateCoordinates(QMouseEvent.x(), QMouseEvent.y()) print("x: " + str(positions.x) + " Y: " + str(positions.y)) if self.checkPlayPosition(positions.x, positions.y): self.selectedCoordinates.append(positions) self.repaint() if self.turn: player = 'O' else: player = 'X' self.matrix_game[positions.x][positions.y] = player if self.hasFinished(positions.x, positions.y): self.initializeEndScreen("O Jogador '" + player + "' Ganhou!") print(player + ' Won!') self.clearBoard() self.SCREEN = 1 self.repaint() else: draw = True for line in range(3): for column in range(3): if self.matrix_game[line][ column] != 'X' and self.matrix_game[line][ column] != 'O': draw = False if draw: self.initializeEndScreen("Deu velha") print('Deu velha') self.clearBoard() self.SCREEN = 1 self.repaint() def repaint(self): if self.SCREEN == 1: self.switchButtonsState(True) elif self.SCREEN == 2: DRAWER.draw_grid() for coordinate in self.selectedCoordinates: if coordinate.turn == 0: if self.turn: DRAWER.draw_x(coordinate.x, coordinate.y) coordinate.turn = 1 else: DRAWER.draw_circle(coordinate.x, coordinate.y) coordinate.turn = 2 self.turn = not self.turn elif coordinate.turn == 1: DRAWER.draw_x(coordinate.x, coordinate.y) coordinate.turn = 1 elif coordinate.turn == 2: DRAWER.draw_circle(coordinate.x, coordinate.y) coordinate.turn = 2 self.update() def paintGL(self): glClear(GL_COLOR_BUFFER_BIT) glClearColor(5 / 255, 102 / 255, 141 / 255, 1.0) glPointSize(10) self.repaint() def hasFinished(self, x, y): # check if is vertical line if (self.matrix_game[0][y] in self.players) and self.matrix_game[0][y] == self.matrix_game[1][y] == \ self.matrix_game[2][y]: return True # check if is horizontal line if (self.matrix_game[x][0] in self.players) and self.matrix_game[x][0] == self.matrix_game[x][1] == \ self.matrix_game[x][2]: return True # check if is in first diagonal if (self.matrix_game[0][0] in self.players) and self.matrix_game[0][0] == self.matrix_game[1][1] == \ self.matrix_game[2][2]: return True # check if is in second diagonal if (self.matrix_game[0][2] in self.players) and self.matrix_game[0][2] == self.matrix_game[1][1] == \ self.matrix_game[2][0]: return True return False
class CamView(AbstractView): def __init__(self, name: str = "CAM_NONE", log_handlers: [StreamHandler] = None): self._logger = getLogger(__name__) if log_handlers: for h in log_handlers: self._logger.addHandler(h) self._logger.debug("Initializing") super().__init__(name) """ Min size for cam window """ self._subwindow_height = 222 self._subwindow_width = 518 self._initialization_bar_frame = EasyFrame() self._initialization_bar_frame.setMouseTracking(True) self._initialization_bar_frame.setMaximumHeight(70) self._initialization_bar_layout = QVBoxLayout( self._initialization_bar_frame) self._initialization_bar_label = QLabel(self._initialization_bar_frame) self._initialization_bar_label.setMouseTracking(True) self._initialization_bar = QProgressBar(self._initialization_bar_frame) self._initialization_bar.setMouseTracking(True) self._initialization_bar.setMaximumHeight(15) self._initialization_bar.setTextVisible(True) self._initialization_bar.setAlignment(Qt.AlignHCenter) self._initialization_bar_layout.addWidget( self._initialization_bar_label) self._initialization_bar_layout.addWidget(self._initialization_bar) self._cam_settings_frame = EasyFrame() self._cam_settings_layout = QGridLayout(self._cam_settings_frame) self._resolution_selector_label = QLabel(self._cam_settings_frame) self._resolution_selector_label.setAlignment(Qt.AlignLeft) self._resolution_selector = QComboBox(self._cam_settings_frame) self._resolution_selector.setMaximumHeight(22) self._cam_settings_layout.addWidget(self._resolution_selector_label, 0, 0) self._cam_settings_layout.addWidget(self._resolution_selector, 0, 1) self._fps_selector_label = QLabel(self._cam_settings_frame) self._fps_selector_label.setAlignment(Qt.AlignLeft) self._fps_selector = QComboBox(self._cam_settings_frame) self._fps_selector.setMaximumHeight(22) self._cam_settings_layout.addWidget(self._fps_selector_label, 1, 0) self._cam_settings_layout.addWidget(self._fps_selector, 1, 1) self._show_feed_checkbox_label = QLabel(self._cam_settings_frame) self._show_feed_checkbox_label.setAlignment(Qt.AlignLeft) self._show_feed_checkbox = QCheckBox() self._show_feed_checkbox.setChecked(True) self._show_feed_checkbox.setLayoutDirection(Qt.RightToLeft) self._cam_settings_layout.addWidget(self._show_feed_checkbox_label, 2, 0) self._cam_settings_layout.addWidget(self._show_feed_checkbox, 2, 1) self._use_cam_checkbox_label = QLabel(self._cam_settings_frame) self._use_cam_checkbox_label.setAlignment(Qt.AlignLeft) self._use_cam_checkbox = QCheckBox() self._use_cam_checkbox.setChecked(True) self._use_cam_checkbox.setLayoutDirection(Qt.RightToLeft) self._cam_settings_layout.addWidget(self._use_cam_checkbox_label, 3, 0) self._cam_settings_layout.addWidget(self._use_cam_checkbox, 3, 1) self._use_overlay_checkbox_label = QLabel(self._cam_settings_frame) self._use_overlay_checkbox_label.setAlignment(Qt.AlignLeft) self._use_overlay_checkbox = QCheckBox() self._use_overlay_checkbox.setChecked(True) self._use_overlay_checkbox.setLayoutDirection(Qt.RightToLeft) self._cam_settings_layout.addWidget(self._use_overlay_checkbox_label, 4, 0) self._cam_settings_layout.addWidget(self._use_overlay_checkbox, 4, 1) self._image_display_frame = EasyFrame() self._image_display_layout = QVBoxLayout(self._image_display_frame) self._image_display_label = QLabel(self._image_display_frame) self._image_display_label.setAlignment(Qt.AlignHCenter) self._image_display = QLabel(self._image_display_frame) self._image_display.setAlignment(Qt.AlignCenter) self._image_display.setMouseTracking(True) self._image_display_layout.addWidget(self._image_display_label) self._image_display_layout.addWidget(self._image_display) spacer = QSpacerItem(1, 1, vData=QSizePolicy.Expanding) self._dev_sets_frame = EasyFrame() self._dev_sets_layout = QVBoxLayout(self._dev_sets_frame) self.config_button = ClickAnimationButton() self.config_button.clicked.connect(self._config_button_handler) self.layout().addWidget(self.config_button, 0, 0, Qt.AlignTop | Qt.AlignRight) self.config_button.setFixedSize(30, 25) self.config_button.setStyleSheet( "background-color: rgba(200, 200, 200, 50%)") self._dev_sets_layout.addWidget(self._cam_settings_frame) self._dev_sets_layout.addItem(spacer) self.layout().addWidget(self._image_display, 0, 0) self.layout().addWidget(self._initialization_bar_frame, 0, 0) self._config_items = [ self._resolution_selector, self._fps_selector, self._use_cam_checkbox, self._show_feed_checkbox, self._use_overlay_checkbox, ] config_win_w = 350 config_win_h = len(self._config_items) * 30 self.config_button.raise_() self._config_win = ConfigPopUp() self._config_win.setLayout(self._dev_sets_layout) self._config_win.setFixedSize(config_win_w, config_win_h) self._rec_label = QLabel() self._rec_label.setStyleSheet( "background-color: rgba(0, 0, 0, 0%); color: red; font: 20px") self._rec_label.hide() self.layout().addWidget(self._rec_label, 0, 0, Qt.AlignBottom | Qt.AlignRight) self.layout().setMargin(0) self._window_changing = False self._showing_images = False self._hidden = False w = 320 self._aspect_ratio = 9 / 16 self.resize(w, self.heightForWidth(w)) self._strings = dict() self._lang_enum = LangEnum.ENG self.setMinimumSize(self._subwindow_width, self._subwindow_height) self.old_size = QSize(self.width(), self.height()) self._logger.debug("Initialized") def set_show_feed_button_handler(self, func) -> None: """ Add handler for show camera selector. :param func: The handler. :return None: """ self._logger.debug("running") self._show_feed_checkbox.toggled.connect(func) self._logger.debug("done") def set_resolution_selector_handler(self, func) -> None: """ Add handler for resolution selector. :param func: The handler. :return None: """ self._logger.debug("running") self._resolution_selector.activated.connect(func) self._logger.debug("done") def set_fps_selector_handler(self, func) -> None: """ Add handler for resolution selector. :param func: The handler. :return None: """ self._logger.debug("running") self._fps_selector.activated.connect(func) self._logger.debug("done") def set_use_cam_button_handler(self, func) -> None: """ Add handler for use camera selector. :param func: The handler. :return None: """ self._logger.debug("running") self._use_cam_checkbox.toggled.connect(func) self._logger.debug("done") def set_use_overlay_button_handler(self, func) -> None: """ Add handler for use camera selector. :param func: The handler. :return None: """ self._logger.debug("running") self._use_overlay_checkbox.toggled.connect(func) self._logger.debug("done") def _config_button_handler(self) -> None: """ Show config pop up. :return None: """ self._logger.debug("running") self.config_button.setStyleSheet( "background-color: rgba(200, 200, 200, 50%)") self._config_win.exec_() self._logger.debug("done") @property def language(self) -> LangEnum: """ :return: The current lang enum being used. """ return self._lang_enum @language.setter def language(self, lang: LangEnum) -> None: """ Set the language for this view object. :param lang: The language to use. :return None: """ self._logger.debug("running") self._lang_enum = lang self._strings = strings[lang] self._set_texts() self._set_tooltips() self._logger.debug("done") @property def resolution_list(self) -> list: """ Get list of resolutions. :return list: The list of resolutions. """ ret = list() return ret @resolution_list.setter def resolution_list(self, res_list: list) -> None: """ Set list of resolutions available to res_list. :param res_list: The list of available resolutions. :return None: """ self._logger.debug("running") self._resolution_selector.clear() for item in res_list: self._resolution_selector.addItem(str(item)) self._logger.debug("done") @property def resolution(self) -> str: """ Get the current resolution selection. :return str: The current resolution. """ return self._resolution_selector.currentText() @resolution.setter def resolution(self, res: str) -> None: """ Set the current resolution selection. :param res: The resolution to set to. :return None: """ self._resolution_selector.setCurrentIndex( self._resolution_selector.findText(res)) @property def fps_list(self) -> list: """ Get list of fps options. :return list: The list of fps options. """ ret = list() return ret @fps_list.setter def fps_list(self, fps_list: list) -> None: """ Set list of available fps to fps_list. :param fps_list: :return None: """ self._logger.debug("running") self._fps_selector.clear() for item in fps_list: self._fps_selector.addItem(str(item)) self._logger.debug("done") @property def fps(self) -> str: """ Get the current fps selection. :return str: The current fps selection. """ return self._fps_selector.currentText() @fps.setter def fps(self, fps: str) -> None: """ Set the current fps selection. :param fps: The fps to set to. :return None: """ self._logger.debug("running") self._fps_selector.setCurrentIndex(self._fps_selector.findText(fps)) self._logger.debug("done") @property def use_feed(self) -> bool: """ Get the current use_cam setting. :return bool: User selection for using cam. """ return self._show_feed_checkbox.isChecked() @use_feed.setter def use_feed(self, useable: bool) -> None: """ Set use_cam setting. :param useable: The setting to set to. :return None: """ self._logger.debug("running") self._show_feed_checkbox.setChecked(useable) self._logger.debug("Done") @property def use_cam(self) -> bool: """ Get the current use_cam setting. :return bool: User selection for using cam. """ return self._use_cam_checkbox.isChecked() @use_cam.setter def use_cam(self, useable: bool) -> None: """ Set use_cam setting. :param useable: The setting to set to. :return None: """ self._logger.debug("running") self._use_cam_checkbox.setChecked(useable) self._logger.debug("Done") @property def use_overlay(self) -> bool: """ Get the current use_overlay setting. :return bool: User selection for using overlay. """ return self._use_overlay_checkbox.isChecked() @use_overlay.setter def use_overlay(self, useable: bool) -> None: """ Set use_overlay setting. :param useable: The setting to set to. :return None: """ self._logger.debug("running") self._use_overlay_checkbox.setChecked(useable) self._logger.debug("Done") def heightForWidth(self, w: int) -> int: return int(w * self._aspect_ratio) def widthForHeight(self, h: int) -> int: return int(h / self._aspect_ratio) def hideEvent(self, hideEvent: QHideEvent): """ Track minimize event. :param hideEvent: :return None: """ self._hidden = True def showEvent(self, showEvent: QShowEvent): """ Track restore event. :param showEvent: :return None: """ self._hidden = False def update_image(self, image: QPixmap = None, msg: str = None) -> None: """ Update image viewer with new image. :param image: The new image to show. :param msg: The text to show if no image. :return None: """ self._logger.debug("running") if not self._window_changing: if image is not None: temp_image_w = image.scaledToWidth(self.width() - 15) temp_image_h = image.scaledToHeight(self.height() - 35) if temp_image_w.height() > self.height() - 35: self._image_display.setPixmap(temp_image_h) else: self._image_display.setPixmap(temp_image_w) elif msg is not None: self._image_display.setText(msg) self._logger.debug("done") def show_images(self) -> None: """ Show image display and hide initialization bar. :return None: """ self._logger.debug("running") geo = self.geometry() self._showing_images = True self._initialization_bar_frame.hide() self._image_display.show() self.config_button.show() self.setStyleSheet("background-color: black") self.setGeometry(geo) self._logger.debug("done") def show_initialization(self) -> None: """ Show initialization bar and hide image display. :return None: """ self._logger.debug("running") self._showing_images = False self._image_display.hide() self.config_button.hide() self._initialization_bar_frame.show() self._logger.debug("done") def update_init_bar(self, progress: int) -> None: """ set progress bar value to progress. :param progress: The value to set progress bar to. :return None: """ self._logger.debug("running") if progress > 100: progress = 100 elif progress < 0: progress = 0 self._initialization_bar.setValue(progress) self._logger.debug("done") def set_config_active(self, is_active: bool) -> None: """ Set whether this camera config options are usable. :param is_active: Usable. :return None: """ self._logger.debug("running") if self._showing_images: if is_active: self._rec_label.hide() elif self.use_cam: self._rec_label.show() for item in self._config_items: item.setEnabled(is_active) self._logger.debug("done") def _set_texts(self) -> None: """ Set the texts in this view object. :return None: """ self._logger.debug("running") self._initialization_bar_label.setText( self._strings[StringsEnum.INITIALIZATION_BAR_LABEL]) self._initialization_bar.setValue(0) self._image_display_label.setText( self._strings[StringsEnum.IMAGE_DISPLAY_LABEL]) self._image_display.setText(self._strings[StringsEnum.IMAGE_DISPLAY]) self._show_feed_checkbox_label.setText( self._strings[StringsEnum.SHOW_FEED_CHECKBOX_LABEL]) self._use_cam_checkbox_label.setText( self._strings[StringsEnum.USE_CAM_CHECKBOX_LABEL]) self._use_overlay_checkbox_label.setText( self._strings[StringsEnum.USE_OVERLAY_CHECKBOX_LABEL]) self._resolution_selector_label.setText( self._strings[StringsEnum.RESOLUTION_SELECTOR_LABEL]) self._fps_selector_label.setText( self._strings[StringsEnum.FPS_SELECTOR_LABEL]) self._config_win.setWindowTitle( self.get_name() + " " + self._strings[StringsEnum.CONFIG_TAB_LABEL]) self.config_button.setText("...") self._rec_label.setText("rec ●") self._logger.debug("done") def _set_tooltips(self) -> None: """ Set the tooltips in this view object. :return None: """ self._logger.debug("running") self._resolution_selector_label.setToolTip( self._strings[StringsEnum.RESOLUTION_SELECTOR_TOOLTIP]) self._resolution_selector.setToolTip( self._strings[StringsEnum.RESOLUTION_SELECTOR_TOOLTIP]) self._fps_selector_label.setToolTip( self._strings[StringsEnum.FPS_SELECTOR_TOOLTIP]) self._fps_selector.setToolTip( self._strings[StringsEnum.FPS_SELECTOR_TOOLTIP]) self._show_feed_checkbox_label.setToolTip( self._strings[StringsEnum.SHOW_FEED_CHECKBOX_TOOLTIP]) self._show_feed_checkbox.setToolTip( self._strings[StringsEnum.SHOW_FEED_CHECKBOX_TOOLTIP]) self._use_cam_checkbox_label.setToolTip( self._strings[StringsEnum.USE_CAM_CHECKBOX_TOOLTIP]) self._use_cam_checkbox.setToolTip( self._strings[StringsEnum.USE_CAM_CHECKBOX_TOOLTIP]) self._use_overlay_checkbox_label.setToolTip( self._strings[StringsEnum.USE_OVERLAY_TOOLTIP]) self._use_overlay_checkbox.setToolTip( self._strings[StringsEnum.USE_OVERLAY_TOOLTIP]) self._image_display.setToolTip( self._strings[StringsEnum.IMAGE_DISPLAY_TOOLTIP]) self._logger.debug("done")
class MyWidget(QWidget): def __init__(self, parent): super().__init__(parent) self.buttons_id_value = { 1: ('comma', ','), 2: ('space', '\b'), 3: ('tab', '\t'), 4: ('semicolon', ';') } self.separator = QButtonGroup() lab = QLabel() lab.setText('Choose a separator:') for bid, value in self.buttons_id_value.items(): self.separator.addButton(QRadioButton(value[0]), id=bid) self.separator.setExclusive(True) self.default_button = self.separator.button(1) button_layout = QHBoxLayout() for button in self.separator.buttons(): button_layout.addWidget(button) self.default_button.click() openFileChooser = QPushButton('Choose') fileChooser = QFileDialog(self, 'Open csv', str(os.getcwd()), 'Csv (*.csv *.tsv *.dat)') fileChooser.setFileMode(QFileDialog.ExistingFile) self.filePath = QLineEdit() openFileChooser.released.connect(fileChooser.show) fileChooser.fileSelected.connect(self.filePath.setText) self.filePath.textChanged.connect(self.checkFileExists) nameLabel = QLabel('Select a name:', self) self.nameField = QLineEdit(self) self.nameErrorLabel = QLabel(self) self.file_layout = QVBoxLayout() fileChooserLayout = QHBoxLayout() nameRowLayout = QHBoxLayout() fileChooserLayout.addWidget(openFileChooser) fileChooserLayout.addWidget(self.filePath) nameRowLayout.addWidget(nameLabel) nameRowLayout.addWidget(self.nameField) self.fileErrorLabel = QLabel(self) self.file_layout.addLayout(fileChooserLayout) self.file_layout.addWidget(self.fileErrorLabel) self.file_layout.addLayout(nameRowLayout) self.file_layout.addWidget(self.nameErrorLabel) self.fileErrorLabel.hide() self.nameErrorLabel.hide() self.tablePreview = SearchableAttributeTableWidget(self, True) self.tableSpinner = QtWaitingSpinner( self.tablePreview, centerOnParent=True, disableParentWhenSpinning=True) self.nameField.textEdited.connect(self.nameErrorLabel.hide) # Split file by row splitRowLayout = QHBoxLayout() self.checkSplit = QCheckBox('Split file by rows', self) self.numberRowsChunk = QLineEdit(self) self.numberRowsChunk.setPlaceholderText( 'Number of rows per chunk') self.numberRowsChunk.setValidator(QIntValidator(self)) splitRowLayout.addWidget(self.checkSplit) splitRowLayout.addWidget(self.numberRowsChunk) self.checkSplit.stateChanged.connect(self.toggleSplitRows) layout = QVBoxLayout() layout.addLayout(self.file_layout) layout.addWidget(lab) layout.addLayout(button_layout) layout.addLayout(splitRowLayout) layout.addWidget(QLabel('Preview')) layout.addWidget(self.tablePreview) self.setLayout(layout) self.filePath.textChanged.connect(self.loadPreview) self.separator.buttonClicked.connect(self.loadPreview) @Slot(object) def loadPreview(self) -> None: if not os.path.isfile(self.filePath.text()): return class WorkerThread(QThread): resultReady = Signal(Frame) def __init__(self, path: str, separ: str, parent=None): super().__init__(parent) self.__path = path self.__sep = separ def run(self): header = pd.read_csv(self.__path, sep=self.__sep, index_col=False, nrows=0) self.resultReady.emit(Frame(header)) sep: int = self.separator.checkedId() sep_s: str = self.buttons_id_value[sep][ 1] if sep != -1 else None assert sep_s is not None # Async call to load header worker = WorkerThread(path=self.filePath.text(), separ=sep_s, parent=self) worker.resultReady.connect(self.onPreviewComputed) worker.finished.connect(worker.deleteLater) self.tableSpinner.start() worker.start() @Slot(Frame) def onPreviewComputed(self, header: Frame): self.tablePreview.setSourceFrameModel(FrameModel(self, header)) self.tablePreview.model().setAllChecked(True) self.tableSpinner.stop() @Slot(str) def checkFileExists(self, path: str) -> None: file_exists = os.path.isfile(path) if not file_exists: self.fileErrorLabel.setText('File does not exists!') self.fileErrorLabel.setStyleSheet('color: red') self.filePath.setToolTip('File does not exists!') self.filePath.setStyleSheet('border: 1px solid red') # self.file_layout.addWidget(self.fileErrorLabel) self.parentWidget().disableOkButton() self.fileErrorLabel.show() else: # self.file_layout.removeWidget(self.fileErrorLabel) self.fileErrorLabel.hide() self.filePath.setStyleSheet('') self.parentWidget().enableOkButton() if not self.nameField.text(): name: str = os.path.splitext(os.path.basename(path))[0] self.nameField.setText(name) @Slot(Qt.CheckState) def toggleSplitRows(self, state: Qt.CheckState) -> None: if state == Qt.Checked: self.numberRowsChunk.setEnabled(True) else: self.numberRowsChunk.setDisabled(True) def showNameError(self, msg: str) -> None: self.nameErrorLabel.setText(msg) self.nameErrorLabel.setStyleSheet('color: red') self.nameErrorLabel.show()
class PropertyTreeDelegateEditor(QFrame): """A special editor that handles renaming blocks and groups.""" sizeHintChanged = Signal(object) """Emitted when the widget changes sizeHint. Delegate should update it's geometry.""" def __init__(self, parent=None): super().__init__(parent) layout = QVBoxLayout() self.setLayout(layout) self.setContentsMargins(0, 0, 0, 0) layout.setContentsMargins(0, 0, 0, 0) layout.setSpacing(0) self.setAutoFillBackground(True) self.setFocusPolicy(Qt.StrongFocus) self.setFrameStyle(QFrame.Panel | QFrame.Plain) self._fallback_text = None self._experiment = None self.line_edit = QLineEdit() self.line_edit.setFrame(False) self.message_label = QLabel() self.message_label.hide() # Divider line self.divider_line = QFrame() self.divider_line.setFrameShape(QFrame.HLine) self.divider_line.setFrameShadow(QFrame.Plain) self.divider_line.setLineWidth(1) layout.addWidget(self.line_edit) layout.addWidget(self.divider_line) layout.addWidget(self.message_label) self.line_edit.textChanged.connect(self._updateMessage) def experiment(self): return self._experiment def setExperiment(self, ex): self._experiment = ex def fallbackText(self): return self._fallback_text def text(self): """Get the current text value of the editor widget. If editing has not been finished succesfully, returns fallbackText(). """ (text_ok, _) = self.experiment().checkName(self.line_edit.text()) if text_ok: return self.line_edit.text() else: return self.fallbackText() def setFallbackText(self, text): """Set the fallback text value to be used if the editing was not succesfully finished. The text() method returns this value or the line_edit text. """ self._fallback_text = text def setText(self, text): """Set currently editable text. Equivalent to self.line_edit.setText.""" self.line_edit.setText(text) def _updateMessage(self): """Update the displayed message in response to changed text.""" if self.line_edit.text() == self.fallbackText(): # Perform no check text_ok = True else: (text_ok, reason) = self.experiment().checkName(self.line_edit.text()) if text_ok: self.message_label.hide() self.divider_line.hide() else: self.message_label.setText(reason) self.message_label.show() self.divider_line.show() self.sizeHintChanged.emit(self) def focusInEvent(self, event): super().focusInEvent(event) self.line_edit.setFocus()
class UpdateDialog(QDialog): def __init__(self, *args, **kwargs): super(UpdateDialog, self).__init__(*args, **kwargs) self.setAttribute(Qt.WA_DeleteOnClose) self.setWindowTitle("版本检查") self.setFixedSize(240, 120) layout = QVBoxLayout() self.current_version_message = QLabel("当前版本:", self) self.current_version_message.setAlignment(Qt.AlignCenter) layout.addWidget(self.current_version_message) self.last_version_message = QLabel("最新版本:检查中...", self) self.last_version_message.setAlignment(Qt.AlignCenter) layout.addWidget(self.last_version_message) # 启动更新的信息提示 self.run_message = QLabel("", self) self.run_message.setWordWrap(True) self.run_message.hide() self.run_message.setAlignment(Qt.AlignCenter) self.run_message.setStyleSheet("color:rgb(200,50,50)") layout.addWidget(self.run_message) opts_layout = QHBoxLayout() self.update_button = QPushButton("立即更新") self.update_button.clicked.connect(self.exit_for_updating) self.close_button = QPushButton("下次更新") self.close_button.clicked.connect(self.close) opts_layout.addWidget(self.update_button) opts_layout.addWidget(self.close_button) layout.addLayout(opts_layout) self.update_button.hide() self.setLayout(layout) # 检测最新版本 app = QApplication.instance() self.network_manager = getattr(app, "_network") self._get_last_version() def _get_last_version(self): """ 获取最新版本号 """ # 获取当前版本号 json_file = os.path.join(BASE_DIR, "classini/update_{}.json".format(SYS_BIT)) if not os.path.exists(json_file): self.current_version_message.setText("当前版本:检测失败.") self.last_version_message.setText("最新版本:检测失败.") self.close_button.setText("关闭") return with open(json_file, "r", encoding="utf-8") as jf: update_json = json.load(jf) self.current_version_message.setText("当前版本:{}".format(update_json["VERSION"])) url = SERVER + "check_version/?version={}&sys_bit={}".format(update_json["VERSION"], SYS_BIT) request = QNetworkRequest(url=QUrl(url)) reply = self.network_manager.get(request) reply.finished.connect(self.last_version_back) def last_version_back(self): """ 检测版本结果 """ reply = self.sender() if reply.error(): reply.deleteLater() self.last_version_message.setText("最新版本:检测失败.") return data = reply.readAll().data() u_data = json.loads(data.decode("utf-8")) if u_data["update_needed"]: for_update_file = os.path.join(BASE_DIR, "classini/for_update_{}.json".format(SYS_BIT)) # 写入待更新信息 f_data = { "VERSION": u_data["last_version"], "SERVER": u_data["file_server"], "FILES": u_data["update_files"] } with open(for_update_file, "w", encoding="utf-8") as f: json.dump(f_data, f, indent=4, ensure_ascii=False) self.update_button.show() else: self.update_button.hide() self.close_button.setText("关闭") self.last_version_message.setText("最新版本:{}".format(u_data["last_version"])) reply.deleteLater() def exit_for_updating(self): """ 退出当前程序,启动更新更新 """ script_file = os.path.join(BASE_DIR, "AutoUpdate.exe") if SYS_BIT == "admin": script_file = os.path.join(BASE_DIR, "AutoAdminUpdate.exe") is_close = True if os.path.exists(script_file): try: Popen(script_file, shell=False) except OSError as e: self.run_message.setText(str(e)) is_close = False else: self.run_message.setText("更新程序丢失...") is_close = False self.run_message.show() if is_close: sys.exit() def closeEvent(self, event): """ 下次更新 """ for_update_file = os.path.join(BASE_DIR, "classini/for_update_{}.json".format(SYS_BIT)) if os.path.exists(for_update_file): os.remove(for_update_file) super(UpdateDialog, self).closeEvent(event)
class OutputLayout(QGridLayout): ga_result = Signal( str ) # a signal that is emited so it can transfer resulting string to the output_layout class def __init__(self): super(OutputLayout, self).__init__() self.run_section = QWidget() self.run_section.setMaximumHeight(100) self.run_layout = QVBoxLayout() self.header_run = QLabel() self.run_comment = QLabel() self.btn_run = QPushButton("Run") self.btn_run.clicked.connect(self.run_button_clicked) self.progress = QProgressBar() self.console = QTextEdit() self.console.setText("This is output") self.init_console() self.init_run_section() self.addWidget(self.console, 1, 1, 1, 1) def print_output(self, output_text): self.console.setText(output_text) def append_output(self, text): self.console.append(text) def init_console(self): # new_font = QFont('courier new', 8) # self.console.setFont(new_font) self.console.setReadOnly(True) self.console.horizontalScrollBar().setEnabled(True) self.console.setLineWrapMode(QTextEdit.NoWrap) def init_run_section(self): font14 = QFont() font14.setPointSizeF(12) self.header_run.setFont(font14) self.header_run.setText("RUN") self.run_comment.setText("Iteration stopped in 128 iteration") self.run_comment.hide() font10 = QFont() font10.setPointSizeF(10) self.run_comment.setFont(font10) self.run_comment.setAlignment(Qt.AlignCenter) self.progress.show() self.addWidget(self.header_run, 2, 1, 1, 1) self.addWidget(self.run_comment, 3, 1, 1, 1) self.addWidget(self.progress, 4, 1, 1, 1) self.addWidget(self.btn_run, 5, 1, 1, 1) def run_button_clicked(self): self.clear_console() options = QApplication.instance().main_window.centerWidget.layout( ).inputs_layout.get_options() controller = QApplication.instance().controller self.progress.show() function = options["function"] num_var = options["num_var"] ga(function, int(num_var), options, controller) def set_progress_bar(self, value): self.progress.setValue(value) def set_status_text(self, text): self.run_comment.setText(text) def clear_console(self): self.console.clear() def set_run_comment(self, text): self.run_comment.setText(text) self.run_comment.show()
class UserSelect(QWidget): def __init__(self, kernel, parent=None): super(UserSelect, self).__init__(parent) self.kernel = kernel self.parent = parent self.initUI() def initUI(self): self.createElements() self.createLayout() self.createActions() self.hideNewUserForm() self.hideLoginForm() def createElements(self): self.userButtons = [ UserTile(u, self) for u in self.kernel.getAllUsers() ] for b, u in zip(self.userButtons, self.kernel.getAllUsers()): b.setProfilePicture(self.kernel.getUsersDir() + u + '/profile/profile_pic.png') b.createLayout() self.newUserButton = QPushButton('New User') self.nevermindButton = QPushButton("Nevermind") self.newUserNameField = QLineEdit("Username") self.newUserPasswordField = QLineEdit("Password") self.newUserPasswordField.setEchoMode(QLineEdit.Password) self.newUserConfirmPasswordField = QLineEdit("Confirm Password") self.newUserConfirmPasswordField.setEchoMode(QLineEdit.Password) self.submitNewUserButton = QPushButton('submit') self.newUserNameErrorLabel = QLabel('') self.newPasswordErrorLabel = QLabel('') self.existingUserLoginField = QLineEdit('Password') self.existingUserLoginField.setEchoMode(QLineEdit.Password) self.existingUserLoginButton = QPushButton('Login') self.existingUserLoginErrorLabel = QLabel('') self.selectedUser = None def createLayout(self): self.existingUsersLayout = QHBoxLayout() self.existingUsersLayout.addStretch() for ub in self.userButtons: ub.resize(100, 120) self.existingUsersLayout.addWidget(ub) self.existingUsersLayout.addStretch() self.formLayout = QVBoxLayout() self.formLayout.addStretch() self.formLayout.addWidget(self.newUserButton) self.formLayout.addWidget(self.nevermindButton) self.formLayout.addWidget(self.newUserNameField) self.formLayout.addWidget(self.newUserNameErrorLabel) self.formLayout.addWidget(self.newUserPasswordField) self.formLayout.addWidget(self.newUserConfirmPasswordField) self.formLayout.addWidget(self.newPasswordErrorLabel) self.formLayout.addWidget(self.submitNewUserButton) self.formLayout.addWidget(self.existingUserLoginField) self.formLayout.addWidget(self.existingUserLoginErrorLabel) self.formLayout.addWidget(self.existingUserLoginButton) self.formLayout.addStretch() self.lowLayout = QHBoxLayout() self.lowLayout.addStretch() self.lowLayout.addLayout(self.formLayout) self.lowLayout.addStretch() self.layout = QVBoxLayout(self) self.layout.addStretch() self.layout.addLayout(self.existingUsersLayout) self.layout.addLayout(self.lowLayout) self.layout.addStretch() def createActions(self): self.newUserButton.clicked.connect(self.revealNewUserForm) self.nevermindButton.clicked.connect(self.hideNewUserForm) self.submitNewUserButton.clicked.connect(self.submitNewUserRequest) self.existingUserLoginButton.clicked.connect(self.login) for btn in self.userButtons: btn.nameButton.clicked.connect( lambda: self.revealLoginForm(btn.nameButton.text())) def revealNewUserForm(self): self.hideLoginForm() self.newUserButton.hide() self.newUserNameField.show() self.newUserPasswordField.show() self.newUserConfirmPasswordField.show() self.submitNewUserButton.show() self.nevermindButton.show() def hideNewUserForm(self): self.newUserButton.show() self.newUserNameErrorLabel.hide() self.newPasswordErrorLabel.hide() self.newUserNameField.hide() self.newUserPasswordField.hide() self.newUserConfirmPasswordField.hide() self.submitNewUserButton.hide() self.nevermindButton.hide() def revealLoginForm(self, user): self.hideNewUserForm() self.selectedUser = user self.existingUserLoginButton.show() self.existingUserLoginErrorLabel.show() self.existingUserLoginField.show() def hideLoginForm(self): self.selectedUser = None self.existingUserLoginButton.hide() self.existingUserLoginErrorLabel.hide() self.existingUserLoginField.hide() def submitNewUserRequest(self): userName = self.newUserNameField.text() pwd = self.newUserPasswordField.text() conf_pwd = self.newUserConfirmPasswordField.text() err_msg = '' if pwd != conf_pwd: err_msg = 'Error: passwords do not match' self.newPasswordErrorLabel.setText(err_msg) self.newPasswordErrorLabel.show() else: self.newPasswordErrorLabel.hide() if self.kernel.userExists(userName): err_msg = 'Error: Username is already taken. Choose something else' self.newUserNameErrorLabel.setText(err_msg) self.newUserNameErrorLabel.show() else: self.newUserNameErrorLabel.hide() if err_msg != '': return if self.kernel.addUser(userName, pwd): self.parent.loadApplication() else: err = "Error: User Creation failed.\nPlease use your Robinhood Credentials" self.newPasswordErrorLabel.setText(err) self.newPasswordErrorLabel.show() def login(self): assert (self.selectedUser != None) pwd = self.existingUserLoginField.text() if not self.kernel.switchUser(self.selectedUser, pwd): self.existingUserLoginErrorLabel.setText(err) self.existingUserLoginErrorLabel.show() else: self.parent.loadApplication()