class QChatTab(QWidget): def __init__(self, chat_window, nick=None, just_accepted=False): QWidget.__init__(self) self.chat_window = chat_window self.nick = nick self.just_accepted = just_accepted self.unread_count = 0 self.widget_stack = QStackedWidget(self) self.widget_stack.addWidget(QNickInputWidget('new_chat.png', 150, self.connectClicked, parent=self)) self.widget_stack.addWidget(QConnectingWidget(parent=self)) self.widget_stack.addWidget(QChatWidget(self.chat_window, nick=nick, parent=self)) if self.nick is not None: self.widget_stack.setCurrentIndex(2) else: self.widget_stack.setCurrentIndex(0) layout = QHBoxLayout() layout.addWidget(self.widget_stack) self.setLayout(layout) def connectClicked(self, nick): if self.chat_window.isNickInTabs(nick): QMessageBox.warning(self, TITLE_ALREADY_CONNECTED, ALREADY_CONNECTED % (nick)) return self.nick = nick self.widget_stack.widget(1).setConnectingToNick(self.nick) self.widget_stack.setCurrentIndex(1) self.chat_window.client.openSession(self.nick) self.widget_stack.widget(2).setRemoteNick(self.nick) def appendMessage(self, message, source): self.widget_stack.widget(2).appendMessage(message, source) def showNowChattingMessage(self): self.widget_stack.setCurrentIndex(2) self.widget_stack.widget(2).showNowChattingMessage(self.nick) def enable(self): self.widget_stack.setCurrentIndex(2) self.widget_stack.widget(2).enable() def resetOrDisable(self): cur_widget_index = self.widget_stack.currentIndex() if cur_widget_index == 1: self.widget_stack.setCurrentIndex(0) elif cur_widget_index == 2: self.widget_stack.widget(2).disable()
class LoadFilesDialog(QDialog): def __init__(self): super().__init__() self.ui() def ui(self): vbox = QVBoxLayout() hbox1 = QHBoxLayout() hbox1Widget = QWidget(parent=self) self.arch_label = QLabel(parent=hbox1Widget) self.arch_label.setText("架构") self.combobox = QComboBox(parent=hbox1Widget) self.combobox.addItem("riscv64") self.combobox.setCurrentIndex(0) hbox1.addWidget(self.arch_label) hbox1.addStretch() hbox1.addWidget(self.combobox) hbox1Widget.setLayout(hbox1) vbox.addWidget(hbox1Widget) hbox2 = QHBoxLayout() hbox2Widget = QWidget(parent=self) self.guest_label = QLabel(parent=hbox2Widget) self.guest_label.setText("宿主程序") self.guest_combobox = QComboBox(parent=self) self.guest_combobox.addItems(["Linux", "Binary"]) self.guest_combobox.setCurrentIndex(0) self.guest_combobox.currentIndexChanged.connect(self.switchStack) hbox2.addWidget(self.guest_label) hbox2.addStretch() hbox2.addWidget(self.guest_combobox) hbox2Widget.setLayout(hbox2) vbox.addWidget(hbox2Widget) self.stack = QStackedWidget(parent=self) self.buildStackWidgets(self.stack) self.stack.setCurrentIndex(0) vbox.addWidget(self.stack) self.input_button = QPushButton(parent=self) self.input_button.setText("确认") self.input_button.clicked.connect(self.judge) vbox.addWidget(self.input_button) self.setLayout(vbox) self.setStyleSheet(dialog_stylesheet) self.resize(500, 300) self.setWindowTitle("选择加载文件") def buildStackWidgets(self, stack): linux_layout1 = QFormLayout() self.bbl_btn = QPushButton(parent=stack) self.bbl_btn.setText("加载") self.bbl_btn.clicked.connect(lambda: self.getFileName(self.bbl_btn)) self.kernel_btn = QPushButton(parent=stack) self.kernel_btn.setText("加载") self.kernel_btn.clicked.connect( lambda: self.getFileName(self.kernel_btn)) self.rootfs_btn = QPushButton(parent=stack) self.rootfs_btn.setText("加载") self.rootfs_btn.clicked.connect( lambda: self.getFileName(self.rootfs_btn)) self.cmdline_edit = QLineEdit(parent=stack) self.cmdline_edit.setText("console=hvc0 root=/dev/vda rw") linux_layout1.addRow("BBL", self.bbl_btn) linux_layout1.addRow("Kernel", self.kernel_btn) linux_layout1.addRow("RootFS", self.rootfs_btn) linux_layout1.addRow("cmdline", self.cmdline_edit) linux_widget = QWidget(parent=stack) linux_widget.setLayout(linux_layout1) stack.addWidget(linux_widget) bin_layout2 = QFormLayout() self.bin_btn = QPushButton(parent=stack) self.bin_btn.setText("加载") self.bin_btn.clicked.connect(lambda: self.getFileName(self.bin_btn)) bin_layout2.addRow("binary", self.bin_btn) bin_widget = QWidget(parent=stack) bin_widget.setLayout(bin_layout2) stack.addWidget(bin_widget) def switchStack(self, i): self.stack.setCurrentIndex(i) def getFileName(self, btn): filename = QFileDialog.getOpenFileName( self, caption="选取文件", filter="Binarys (*.bin);;AllFiles (*.*)") if (filename and filename[0] != ""): btn.setText(filename[0]) else: btn.setText("加载") def judge(self): global load_files if (self.stack.currentIndex() == 0): if ((self.bbl_btn.text() == "加载") or (self.kernel_btn.text() == "加载") or (self.rootfs_btn.text() == "加载")): QMessageBox.information(self, "提示", "请加载全部文件", QMessageBox.Ok, QMessageBox.Ok) return else: stopEmulator() load_files['type'] = self.guest_combobox.currentText() load_files["bbl"] = self.bbl_btn.text() load_files["kernel"] = self.kernel_btn.text() load_files["rootfs"] = self.rootfs_btn.text() load_files["cmdline"] = self.cmdline_edit.text() self.accept() else: if (self.bin_btn.text() == "加载"): QMessageBox.information(self, "提示", "请加载全部文件", QMessageBox.Ok, QMessageBox.Ok) return else: stopEmulator() load_files["type"] = self.guest_combobox.currentText() load_files["bin"] = self.bin_btn.text() self.accept()
class AboutDialog(QDialog): def __init__(self, parent=None): super().__init__( parent, Qt.MSWindowsFixedSizeDialogHint | Qt.WindowTitleHint | Qt.WindowSystemMenuHint | Qt.WindowCloseButtonHint) self.setAttribute(Qt.WA_DeleteOnClose) self.setWindowModality(Qt.ApplicationModal) self.setWindowTitle(self.tr("About")) app = QApplication.instance() name = app.applicationName() domain = app.organizationDomain() iconLabel = QLabel(self) iconLabel.setAlignment(Qt.AlignCenter) iconLabel.setMaximumWidth(250) iconLabel.setMinimumSize(250, 210) icon = self.windowIcon() size = icon.actualSize(QSize(186, 186)) iconLabel.setPixmap(icon.pixmap(size)) titleLabel = QLabel(self) titleLabel.setText( self. tr("<p style='color: #353535; font-size: 24pt; font-weight: 250'>" "TruFont Font Editor</p>" "<p style='font-size: 13pt; font-weight: 400'>{} Pristine Wax</p>" ).format(__version__)) textLabel = QLabel(self) text = self.tr( "<p>{n} is a free and open source font editor and scripting " "environment made by the developers of the {n} community.</p>" "<p>{n} is built upon the " "<a href='http://ts-defcon.readthedocs.org/en/ufo3/' " "style='color: #356FDE'>defcon</a> UFO library and exposes a " "<a href='http://robofab.com/' style='color: #356FDE'>robofab</a>" "-like API for scripting purposes.</p>" "<p>Running on Qt {} (PyQt {}).</p>" "<p>Version {} {} – Python {}.").format(QT_VERSION_STR, PYQT_VERSION_STR, __version__, gitShortHash, platform.python_version(), n=name) if domain: text += self.tr( "<br>See <a href='http://{d}' style='color: #356FDE'>{d}</a> " "for more information.</p>").format(d=domain) else: text += "</p>" textLabel.setText(text) textLabel.setOpenExternalLinks(True) textLabel.setWordWrap(True) authorsLabel = QTextBrowser(self) authorsLabel.setText("\n".join(self.authors())) licenseLabel = QTextBrowser(self) licenseLabel.setText(licenseText) thanksLabel = QTextBrowser(self) thanksLabel.setText(thanksText) self.stackWidget = QStackedWidget(self) self.stackWidget.addWidget(textLabel) self.stackWidget.addWidget(authorsLabel) self.stackWidget.addWidget(licenseLabel) self.stackWidget.addWidget(thanksLabel) textLayout = QVBoxLayout() textLayout.setContentsMargins(0, 14, 16, 16) textLayout.addWidget(titleLabel) textLayout.addWidget(self.stackWidget) spacer = QWidget() spacer.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Expanding) textLayout.addWidget(spacer) mainLayout = QHBoxLayout() mainLayout.addWidget(iconLabel) mainLayout.addLayout(textLayout) frame = QFrame() frame.setMinimumHeight(54) frame.setMaximumHeight(54) frame.setStyleSheet("background: rgb(230, 230, 230)") buttonsLayout = QHBoxLayout(frame) for index, text in enumerate(("Authors", "License", "Credits")): label = QLabel(text, self) label.setAlignment(Qt.AlignCenter) label.setCursor(Qt.PointingHandCursor) label.setProperty("index", index + 1) label.setStyleSheet("color: #356FDE; text-decoration: underline") label.installEventFilter(self) buttonsLayout.addWidget(label) layout = QVBoxLayout(self) layout.setContentsMargins(0, 0, 0, 0) layout.addLayout(mainLayout) layout.addWidget(frame) def authors(self): for line in gitShortLog.splitlines(): elem = line.split("\t")[1] if not elem or elem.startswith("=?") or elem.endswith("bot"): continue yield elem # ---------- # Qt methods # ---------- def eventFilter(self, obj, event): if event.type() == QEvent.MouseButtonPress: index = obj.property("index") if index is not None: if self.stackWidget.currentIndex() == index: index = 0 self.stackWidget.setCurrentIndex(index) return True return False return super().eventFilter(obj, event) def sizeHint(self): return QSize(600, 314)
class FlowDialog(QWidget): """ Class for controlling the order of screens in the experiment. """ def __init__(self, parent=None): super(FlowDialog, self).__init__(parent) self._n_steps = 1 # number of performed steps self.pages_widget = QStackedWidget() # create widgets self.experiment_setup = ExperimentSetup() self.instructions = InstructionsScreen("./instructions/he-informed-consent.txt") self.new_user_form = NewUserForm() self.data_widget = DataWidget() # add widgets to pages_widget self.pages_widget.addWidget(self.experiment_setup) self.pages_widget.addWidget(self.instructions) self.pages_widget.addWidget(self.new_user_form) self.pages_widget.addWidget(self.data_widget) # next button self.next_button = QPushButton("&Next") self.next_button_layout = QHBoxLayout() self.next_button_layout.addStretch(1) self.next_button_layout.addWidget(self.next_button) self.next_button.pressed.connect(self.nextPressed) main_layout = QGridLayout() # set screen shoulders main_layout.setRowMinimumHeight(0, 80) main_layout.setRowMinimumHeight(3, 80) main_layout.setColumnMinimumWidth(0, 80) main_layout.setColumnMinimumWidth(2, 80) main_layout.addWidget(self.pages_widget, 1, 1) main_layout.addLayout(self.next_button_layout, 2, 1) self.setLayout(main_layout) self.pages_widget.setCurrentIndex(0) def nextPressed(self): """ control the order and number of repetitions of the experiment """ self.data_widget.count_down_timer.restart_timer() # restart timer # if on setup screen if self.pages_widget.currentIndex() == 0: # get condition index number condition_index = self.experiment_setup.conditions_combo.currentIndex() # DEBUGGING: debug_condition = config.CONDITIONS['a'][condition_index] print(debug_condition) self.raise_index() # welcome screen elif self.pages_widget.currentIndex() == 1: self.raise_index() # if on new_user screen elif self.pages_widget.currentIndex() == 2: self.new_user_form.save_results() self.raise_index() # if on data screen elif self.pages_widget.currentIndex() == 3: if self._n_steps < config.NUM_STEPS: self.data_widget.add_log_row() self._n_steps += 1 print("Step number {}".format(self._n_steps)) # DEBUGGING else: self.raise_index() elif self.pages_widget.currentIndex() == 4: print("That's it") # DEBUGGING else: # If reached wrong index raise RuntimeError("No such index") def raise_index(self): current = self.pages_widget.currentIndex() self.pages_widget.setCurrentIndex(current + 1)
class OptionSettings(QDialog): def __init__(self, parent): QDialog.__init__(self, parent) line = QFrame() line.setFrameShape(QFrame.HLine) line.setFrameShadow(QFrame.Sunken) self.btnWin1 = OptionButtons('Win One', self.ShowWindow1) self.btnWin2 = OptionButtons('Win Two', self.ShowWindow2) # Vertical Box for Buttons ************************************* self.UpLeft = QVBoxLayout() self.UpLeft.addWidget(self.btnWin1) self.UpLeft.addWidget(self.btnWin2) self.UpLeft.addStretch(1) # Display Area on Right # Widget Flip Display ****************************************** self.UpRite = QHBoxLayout() self.Contents = QStackedWidget() self.Contents.addWidget(QTextEdit('Nothing Selected')) self.Contents.addWidget(Win1Disply(self)) self.Contents.addWidget(Win2Disply(self)) self.Contents.addWidget(QTextEdit('Settings Saved')) self.Contents.setCurrentIndex(0) self.UpRite.addWidget(self.Contents) # Button and Display Area on Top self.Upper = QHBoxLayout() self.Upper.addLayout(self.UpLeft) self.Upper.addLayout(self.UpRite) # Save and Cancel Area on Bottom self.btnSave = QPushButton("Save") self.btnSave.clicked.connect(self.SaveSettings) self.btnCncl = QPushButton("Cancel") self.btnCncl.clicked.connect(self.close) self.Lower = QHBoxLayout() self.Lower.addStretch(1) self.Lower.addWidget(self.btnSave) self.Lower.addWidget(self.btnCncl) # Entire Options Window Layout self.OuterBox = QVBoxLayout() self.OuterBox.addLayout(self.Upper) self.OuterBox.addLayout(self.Lower) self.setLayout(self.OuterBox) self.setWindowTitle('Settings') #Geometry(Left, Top, Width, Hight) self.setGeometry(250, 250, 550, 450) self.setModal(True) self.exec() def ShowWindow1(self): self.Contents.setCurrentIndex(1) def ShowWindow2(self): self.Contents.setCurrentIndex(2) def SaveSettings(self): self.Contents.setCurrentIndex(3) def RightArrow(self): if self.Contents.currentIndex() == 1: self.Contents.setCurrentIndex(2) else: self.Contents.setCurrentIndex(1)
class Slideshow(QMainWindow): def __init__(self, parent, gallery=list(), index=0): super(Slideshow, self).__init__() self.parent = parent self.configure_gui() self.create_widgets(gallery) self.create_menu() self.index = index self.move() self.showMaximized() self.activateWindow() def configure_gui(self): self.stack = QStackedWidget(self) self.setCentralWidget(self.stack) def create_widgets(self, gallery): self.model = Model(self, gallery) self.image = imageViewer(self) self.video = videoPlayer(self) self.stack.addWidget(self.image) self.stack.addWidget(self.video) self.setMouseTracking(True) self.timer = QTimer() self.timer.timeout.connect( lambda: self.setCursor(Qt.BlankCursor) ) def create_menu(self): self.menubar = self.menuBar() self.menubar.triggered.connect(self.menuPressEvent) # File file = self.menubar.addMenu('File') file.addAction('Copy Image to', lambda: copy_to(self, [self.model.index(self.index)]), shortcut='CTRL+SHIFT+C') file.addSeparator() file.addAction('Exit', self.close, shortcut='Ctrl+W') # View view = self.menubar.addMenu('View') view.addAction('Fullscreen', self.fullscreen, shortcut='F11') # Help help = self.menubar.addMenu('Help') menu = QMenu(self) # menu = create_menu( # self, # ) self.full = QAction( 'Fullscreen', menu, triggered=self.fullscreen ) menu.addAction(self.full) self.play_menu = QMenu('Slideshow') group1 = QActionGroup(self.play_menu) group1.setExclusive(True) self.play_menu.addActions([ QAction( 'Pause', self.play_menu, triggered=self.playEvent, checked=True ), QAction( 'Play', self.play_menu, triggered=self.playEvent ) ]) self.play_menu.addSeparator() group2 = QActionGroup(self.play_menu) group2.setExclusive(True) self.play_menu.addActions([ QAction( 'Speed - Slow', self.play_menu, triggered=self.playEvent ), QAction( 'Speed - Medium', self.play_menu, triggered=self.playEvent, checked=True ), QAction( 'Speed - Fast', self.play_menu, triggered=self.playEvent ) ]) # menu.addMenu(self.play_menu) menu.addSeparator() menu.addAction(QAction( 'Rotate right', menu, triggered=lambda: self.rotate(+1) )) menu.addAction(QAction( 'Rotate left', menu, triggered=lambda: self.rotate(-1) )) menu.addSeparator() menu.addAction(QAction( 'Copy', menu, triggered=self.copy )) menu.addAction(QAction( 'Delete', menu, triggered=self.delete )) menu.addSeparator() menu.addAction(QAction( 'Properties', menu, triggered=self.openEditor )) self.menu = menu def move(self, delta=0): if not self.model.gallery: self.image.no_image() return 0 self.index = (self.index + delta) % len(self.model.gallery) path = self.model.index(self.index).data(Qt.UserRole)[0] self.setWindowTitle(f'{Path(path).name} - Slideshow') if path is None: pixmap = QPixmap() else: if path.endswith(('.jpg', '.png')): image = QImage(path) path = None elif path.endswith(('.gif', '.mp4', '.webm')): image = get_frame(path) else: print(path) pixmap = QPixmap(image).scaled( self.size(), Qt.KeepAspectRatio, transformMode=Qt.SmoothTransformation ) self.image.update(pixmap) self.stack.setCurrentIndex(0) self.video.update(path) def fullscreen(self): if self.isFullScreen(): self.timer.stop() self.image.setStyleSheet('background: ') self.full.setText('Fullscreen') self.setCursor(Qt.ArrowCursor) self.showMaximized() self.menubar.show() else: self.image.setStyleSheet('background: black') self.full.setText('Exit fullscreen') self.setCursor(Qt.BlankCursor) self.menubar.hide() self.showFullScreen() def rotate(self, sign): path = self.model.index(self.index).data(Qt.UserRole)[0] if path.endswith(('jpg', 'png')): self.image.rotate(path, sign) else: self.video.rotate(path, sign) self.move() def copy(self): path = self.model.index(self.index).data(Qt.UserRole)[0] if path.endswith(('gif', '.mp4', '.webm')): return path = mktemp(suffix='.png') image = ImageGrab.grab( (0, 0, self.width(),self.height()) ) image.save(path) cb = QApplication.clipboard() cb.clear(mode=cb.Clipboard) cb.setText(path, mode=cb.Clipboard) def delete(self): if self.stack.currentIndex(): self.video.update(None) else: self.image.update(None) index = [self.model.index(self.index)] if self.parent.delete_records(index): del self.model.gallery[self.index] self.model.layoutChanged.emit() def openEditor(self): index = self.model.index(self.index) Properties(self.parent, [index.data(Qt.EditRole)]) def playEvent(self, event): match self.play_menu.activeAction().text: case 'Speed - Slow': pass case 'Speed - Medium': pass case 'Speed - Fast': pass def contextMenuEvent(self, event): self.menu.popup(event.globalPos()) def menuPressEvent(self, event=None): action = event.text() # if action == 'Copy Image to': # copy_to(self, [self.model.index(self.index)]) if action == 'Exit': self.close() def keyPressEvent(self, event): key_press = event.key() video = self.stack.currentIndex() alt = event.modifiers() == Qt.AltModifier ctrl = event.modifiers() == Qt.ControlModifier shift = event.modifiers() == Qt.ShiftModifier if key_press in (Qt.Key_Right, Qt.Key_Left): self.move(1 if key_press == Qt.Key_Right else -1) elif video and key_press in (Qt.Key_Home, Qt.Key_End): if key_press == Qt.Key_Home: self.video.position(0) else: self.video.position(0) elif video and key_press in (Qt.Key_Period, Qt.Key_Comma): sign = 1 if key_press == Qt.Key_Period else -1 if ctrl: self.video.position(sign * 50) else: self.video.position(sign * 5000) elif video and key_press in (Qt.Key_Up, Qt.Key_Down): sign = 1 if key_press == Qt.Key_Up else -1 if ctrl: self.video.volume(sign * 1) else: self.video.volume(sign * 10) elif video and key_press == Qt.Key_Space: self.video.pause() elif video and key_press == Qt.Key_M: self.video.mute() elif key_press == Qt.Key_Delete: self.delete() elif key_press == Qt.Key_F11: self.fullscreen() elif key_press == Qt.Key_Escape: if self.isFullScreen(): self.fullscreen() else: self.close() elif alt: if key_press in (Qt.Key_Return, Qt.Key_Enter): self.openEditor() elif ctrl: if shift and key_press == Qt.Key_C: opy_to(self, [self.model.index(self.index)]) elif key_press == Qt.Key_C: self.copy() elif key_press == Qt.Key_W: self.close() def mousePressEvent(self, event): if event.button() == Qt.MouseButton.LeftButton: if event.x() > (self.width() * .5): self.move(+1) elif event.x() < (self.width() * .5): self.move(-1) def mouseMoveEvent(self, event): if self.isFullScreen(): self.setCursor(Qt.ArrowCursor) self.timer.start(1500) def closeEvent(self, event): self.video.update(None)
class Ui_MainWindow(object): def setupUi(self, MainWindow): MainWindow.setObjectName("MainWindow") MainWindow.resize(800, 600) # window = QMainWindow() # window.resize(800, 600) # self.centralwidget = QtWidgets.QWidget(MainWindow) # self.centralwidget = QtWidgets.QWidget(MainWindow) self.stack = QStackedWidget(parent=MainWindow) self.centralwidget = QStackedWidget(parent=MainWindow) self.centralwidget.setObjectName("centralwidget") # self.matplotlibwidget_static = MatplotlibWidget(self.centralwidget) matplotlibwidget_static = MatplotlibWidget() matplotlibwidget_static.setGeometry(QtCore.QRect(10, 0, 611, 271)) matplotlibwidget_static.setObjectName("matplotlibwidget_static") self.stack.addWidget(matplotlibwidget_static) self.pushButton = QtWidgets.QPushButton(self.centralwidget) self.pushButton.setGeometry(QtCore.QRect(670, 80, 75, 23)) self.pushButton.setObjectName("pushButton") # self.matplotlibwidget_dynamic = MatplotlibWidget() # self.matplotlibwidget_dynamic.setEnabled(True) # self.matplotlibwidget_dynamic.setGeometry(QtCore.QRect(10, 270, 611, 291)) # self.matplotlibwidget_dynamic.setObjectName("matplotlibwidget_dynamic") matplotlibwidget_dynamic = MatplotlibWidget() matplotlibwidget_dynamic.setEnabled(True) matplotlibwidget_dynamic.setGeometry(QtCore.QRect(10, 0, 611, 271)) matplotlibwidget_dynamic.setObjectName("matplotlibwidget_dynamic") self.stack.addWidget(matplotlibwidget_dynamic) self.pushButton_2 = QtWidgets.QPushButton(self.centralwidget) self.pushButton_2.setGeometry(QtCore.QRect(670, 370, 75, 23)) self.pushButton_2.setObjectName("pushButton_2") MainWindow.setCentralWidget(self.centralwidget) self.menubar = QtWidgets.QMenuBar(MainWindow) self.menubar.setGeometry(QtCore.QRect(0, 0, 800, 23)) self.menubar.setObjectName("menubar") MainWindow.setMenuBar(self.menubar) self.statusbar = QtWidgets.QStatusBar(MainWindow) self.statusbar.setObjectName("statusbar") MainWindow.setStatusBar(self.statusbar) self.retranslateUi(MainWindow) QtCore.QMetaObject.connectSlotsByName(MainWindow) self.stack.show() # window.show() def retranslateUi(self, MainWindow): _translate = QtCore.QCoreApplication.translate MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow")) self.pushButton.setText(_translate("MainWindow", "显示静态图")) self.pushButton_2.setText(_translate("MainWindow", "显示动态图")) def next(self): self.stack.setCurrentIndex(self.stack.currentIndex() + 1) print('current', self.stack.currentIndex())
class MainWindow(QMainWindow): def __init__(self, *args, **kwargs): super(MainWindow, self).__init__(*args, **kwargs) self.setWindowTitle("") self.screens_config = [] self.current_screen = 0 self.sub_screens = {} self.button = {} self.central_widget = QStackedWidget() self.number_of_subs = 0 self.main_layout = QGridLayout() self.gui_element_builder = GuiElementsBuilder() def say_hello(self): print("Button clicked, Hello!") def set_central_widget2(self, widget: QWidget): source_button = self.sender() print("set_central_widget2 WidgetName: " + widget.getName()) index = self.central_widget.widget(widget) self.central_widget.setCurrentIndex(index) def set_central_widget(self): source_button = self.sender() for i in range(0, self.number_of_subs): if self.central_widget.widget(i).getName() == source_button.text(): self.central_widget.setCurrentIndex(i) def change_widget(self, widget: QWidget, direction: int): #print("len: " + str(len(self.screens_config["sub"]))) max_screen = len(self.screens_config["sub"]) if direction == 0: self.central_widget.setCurrentIndex( (self.central_widget.currentIndex() - 1) % max_screen) #self.current_screen = (self.current_screen + 1) % max_screen elif direction == 1: self.central_widget.setCurrentIndex( (self.central_widget.currentIndex() + 1) % max_screen) #self.current_screen = (self.current_screen - 1) % max_screen else: print("that not a valid direction") #Farbe sollte im Widget selbst geetzt werden bzw. schon bei Erstellung #for items in self.screens_config['sub']: #b = QColor(self.screens_config['sub'][self.current_screen]["Background"]) #b = QColor(self.screens_config['sub'][self.central_widget.currentIndex()]["Background"]) #p = self.central_widget.palette() #p.setColor(self.central_widget.backgroundRole(), b) #self.central_widget.setPalette(p) #self.central_widget.setAutoFillBackground(True) def init_with_config(self, config: dict): self.screens_config = config # TODO: put in delegation or inheritance #Set Title if 'name' not in config: title = str(config['main']['name']) self.setWindowTitle(title) # Set Resolution window_width = config['main']["resolution"][0] window_height = config['main']["resolution"][1] button_width = config['main']["button-size"][0] button_height = config['main']["button-size"][1] self.number_of_subs = len(config['sub']) else: title = str(config['name']) self.setWindowTitle(title) # Set Resolution window_width = config["resolution"][0] window_height = config["resolution"][1] button_width = config["button-size"][0] button_height = config["button-size"][1] self.number_of_subs = len(self.screens_config["sub"]) self.setFixedSize(window_width, window_height) main_widget = QWidget() main_widget.setLayout(self.main_layout) vbox_menu = QVBoxLayout() vbox_menu.setSizeConstraint(QLayout.SetFixedSize) system_color = "#ffd966" # Header self.main_layout.addWidget( self.gui_element_builder.get_svg_widget(Gui_Element.TOP_LEFT, 0, 0), 0, 0) self.main_layout.addWidget( self.gui_element_builder.get_svg_widget(Gui_Element.BUTTON, 30, 650), 0, 1, Qt.AlignTop) self.main_layout.addWidget( self.gui_element_builder.get_svg_widget(Gui_Element.END_RIGHT, 0, 0), 0, 3, Qt.AlignTop) # Menu self.main_layout.addLayout(vbox_menu, 1, 0) # Footer self.main_layout.addWidget( self.gui_element_builder.get_svg_widget(Gui_Element.BOTTOM_LEFT, 0, 0), 2, 0) self.main_layout.addWidget( self.gui_element_builder.get_svg_widget(Gui_Element.BUTTON, 30, 650), 2, 1, Qt.AlignBottom) self.main_layout.addWidget( self.gui_element_builder.get_svg_widget(Gui_Element.END_RIGHT, 0, 0), 2, 3, Qt.AlignBottom) # button_stylesheet = "background-color:" + system_color +"; border-width: 2px; border-radius: 36px; bordericolor: black; font: bold 14px; min-width: 10em; padding: 6px;" # button_up = QPushButton("\u1403") # button_up.setFixedSize(button_width * window_width/100, button_height * window_height/100) # button_up.setStyleSheet(button_stylesheet) # # button_down = QPushButton("\u1401") # button_down.setFixedSize(button_width * window_width / 100, button_height * window_height / 100) # button_down.setStyleSheet(button_stylesheet) button_ListWidget = QListWidget() button_ListWidget.setStyleSheet( """QListWidget{background: #f2eeed; border: 0px solid #f2eeed;}""" ) # Erstellen der rechten Button-Leiste ############## #vbox_menu.addWidget(button_up) button_width = button_width * window_width / 100 button_height = button_height * window_height / 100 background_color = "#f2eeed" button_size = QSize(button_width, button_height) for i in range(0, self.number_of_subs): subbutton_list_item = QListWidgetItem(button_ListWidget) placeholder_listItem = QListWidgetItem(button_ListWidget) placeholder_listItem.setSizeHint(QSize(button_width, 4)) flag = placeholder_listItem.flags() & Qt.ItemIsUserCheckable placeholder_listItem.setFlags(flag) #subbutton_listItem.setBackground(QColor("#f2eeed")) # Widgets ################################################################################################## if i == 0: self.central_widget.insertWidget(i, Clock()) else: self.central_widget.insertWidget( i, Placeholder(self.screens_config["sub"][i]["name"])) # Buttons ################################################################################################## button_color = self.screens_config['sub'][i]["Background"] self.button[i] = QPushButton(self.screens_config["sub"][i]["name"], self) self.button[i].setFixedSize(button_size) #Button with stylsheet #self.button[i].setStyleSheet("background-color:" +button_color +"; border-width: 2px; border-radius: 10px; bordericolor: black; font: bold 14px; min-width: 10em; padding: 6px;") path_button = Button_full.build_svg( Button_full( button_width, button_height, background_color, self.screens_config["sub"][i]["Background"] + "_button")) self.button[i].setStyleSheet("background-image: url(" + path_button + ");" "border:1px; background-color:" + button_color + ";") #print("Button: " + self.button[i].text() + "Screen: " + self.central_widget.widget(i).getName()) # signals ################################################################################################## #self.button[i].clicked.connect(lambda widget=self.central_widget.widget(i): self.set_central_widget2(self, widget)) self.button[i].clicked.connect( lambda widget=self.central_widget.widget( i): self.set_central_widget()) subbutton_list_item.setSizeHint(button_size) #print(self.button[i].size()) # button_ListWidget.addItem(placeholder_listItem) button_ListWidget.addItem(subbutton_list_item) button_ListWidget.setItemWidget(subbutton_list_item, self.button[i]) button_ListWidget.setMaximumWidth(1000) #vbox_menu.addWidget(self.button[i]) vbox_menu.addWidget(button_ListWidget) #vbox_menu.addWidget(button_down) #downbutton_listItem = QListWidgetItem(button_ListWidget) #downbutton_listItem.setSizeHint(button_down.size()) #button_ListWidget.addItem(downbutton_listItem) #button_ListWidget.setItemWidget(downbutton_listItem, button_down) button_ListWidget.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff) button_ListWidget.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff) button_ListWidget.setMaximumWidth( button_ListWidget.sizeHintForColumn(0)) ############################################# self.central_widget.setCurrentIndex(1) self.main_layout.addWidget(self.central_widget, 1, 1) self.setCentralWidget(main_widget)
class WindowOption(QWidget): def __init__(self): super(WindowOption, self).__init__() self.initUI() def initUI(self): """# Global """ self.setWindowTitle('Option') self.setFixedSize(800, 450) self.list_option = QListWidget(self) self.stack_window = QStackedWidget(self) self.style_list_option = "QListWidget{\ min-width: 120px;\ max-width: 120px;\ color: white;\ background: grey;}" self.style_groupbox = "QGroupBox{\ border: None;}" self.style_groupbox_font = "QGroupBox{\ font-family: MonoxRegular;\ font-size: 20px;}" layout_main = QHBoxLayout(spacing=0) layout_main.setContentsMargins(0, 0, 0, 0) layout_main.addWidget(self.list_option) layout_main.addWidget(self.stack_window) self.setLayout(layout_main) self.list_option.setStyleSheet(self.style_list_option) """# List Option""" self.list_option.setFrameShape(QListWidget.NoFrame) self.list_option.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff) self.list_option.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff) self.list_option.currentRowChanged.connect( self.stack_window.setCurrentIndex) font = QFont() font.setFamily('MonoxLight') font.setPointSize(20) item = QListWidgetItem() item.setFont(font) item.setText('DIY') item.setSizeHint(QSize(0, 60)) item.setTextAlignment(Qt.AlignCenter) self.list_option.addItem(item) item = QListWidgetItem() item.setFont(font) item.setText('PRO') item.setSizeHint(QSize(0, 60)) item.setTextAlignment(Qt.AlignCenter) self.list_option.addItem(item) """# Stack Window""" """# Page 0""" self.page0 = QWidget() self.tabwidget_page0 = QTabWidget(self.page0) self.tab0_page0 = QWidget(self.page0) self.tab1_page0 = QWidget(self.page0) self.tabwidget_page0.addTab(self.tab0_page0, 'Info') self.tabwidget_page0.addTab(self.tab1_page0, 'Option') self.page0tab0() self.page0tab1() self.page0global() self.page1tab0() self.page1tab1() self.page1global() def keyReleaseEvent(self, event): """DocString for pressKeyEwent""" #@todo: to be defined. if event.key() == Qt.Key_D: self.list_option.setCurrentRow(0) self.stack_window.setCurrentIndex(0) if event.key() == Qt.Key_P: self.list_option.setCurrentRow(1) self.stack_window.setCurrentIndex(1) if event.key() == Qt.Key_O: if self.stack_window.currentIndex() == 1: self.tabwidget_page1.setCurrentIndex(1) else: self.tabwidget_page0.setCurrentIndex(1) if event.key() == Qt.Key_I: if self.stack_window.currentIndex() == 1: self.tabwidget_page1.setCurrentIndex(0) else: self.tabwidget_page0.setCurrentIndex(0) if event.key() == Qt.Key_Return: if self.stack_window.currentIndex() == 1: self.pushbutton_ok_page1.click() else: self.pushbutton_ok_page0.click() if event.key() == Qt.Key_R: if self.stack_window.currentIndex() == 1: self.pushbutton_re_page1.click() else: self.pushbutton_re_page0.click() if event.key() == Qt.Key_Q: self.close() def page0tab0(self): """DocString for page0tab0""" #@todo: to be defined. font = QFont() font.setFamily('MonoxLight') font.setPointSize(12) label_date_page0 = QLabel('Date ') label_date_page0.setFont(font) label_time_page0 = QLabel('Time ') label_time_page0.setFont(font) label_loc_page0 = QLabel('Location ') label_loc_page0.setFont(font) label_users_name_page0 = QLabel('Name ') label_users_name_page0.setFont(font) font_spe = QFont() font_spe.setFamily('MonoxRegular Bold') font_spe.setPointSize(12) label_users_gender_page0 = QLabel('Gender') label_users_gender_page0.setFont(font_spe) label_note_page0 = QLabel('Note') label_note_page0.setFont(font) self.dateedit_date_page0 = QDateEdit(QDate.currentDate()) self.dateedit_date_page0.setDisplayFormat('yyyy/MM/dd') self.dateedit_date_page0.setCalendarPopup(True) self.dateedit_date_page0.setFont(font) self.timeedit_time_page0 = QTimeEdit(QTime.currentTime()) self.timeedit_time_page0.setDisplayFormat('HH : mm') self.timeedit_time_page0.setFont(font) self.lineedit_loc_page0 = QLineEdit('Mars') self.lineedit_loc_page0.setFont(font) self.lineedit_users_name_page0 = QLineEdit('Object') self.lineedit_users_name_page0.setFont(font) self.radiobutton_users_gender_male_page0 = QRadioButton('Male') self.radiobutton_users_gender_female_page0 = QRadioButton('Female') self.radiobutton_users_gender_secret_page0 = QRadioButton('Secret') self.textedit_note_page0 = QTextEdit('None') self.textedit_note_page0.setFont(font) line_split_page0 = QFrame() line_split_page0.setFrameShape(QFrame.VLine) line_split_page0.setFrameShadow(QFrame.Sunken) groupbox_radio_button = QGroupBox() groupbox_radio_button.setStyleSheet(self.style_groupbox) layout_groupbox_radio_button = QHBoxLayout() layout_groupbox_radio_button.addWidget( self.radiobutton_users_gender_male_page0) layout_groupbox_radio_button.addWidget( self.radiobutton_users_gender_female_page0) layout_groupbox_radio_button.addWidget( self.radiobutton_users_gender_secret_page0) groupbox_radio_button.setLayout(layout_groupbox_radio_button) layout_tab0_page0_global = QVBoxLayout(self.tab0_page0) layout_info_page0 = QHBoxLayout() layout_note_page0 = QVBoxLayout() layout_time_page0 = QVBoxLayout() layout_user_page0 = QVBoxLayout() layout_date_page0 = QHBoxLayout() layout_date_page0.setAlignment(Qt.AlignLeft) layout_clock_page0 = QHBoxLayout() layout_clock_page0.setAlignment(Qt.AlignLeft) layout_loc_page0 = QHBoxLayout() layout_loc_page0.setAlignment(Qt.AlignLeft) layout_name_page0 = QHBoxLayout() layout_gender_page0 = QVBoxLayout() layout_date_page0.addWidget(label_date_page0) layout_date_page0.addWidget(self.dateedit_date_page0) layout_clock_page0.addWidget(label_time_page0) layout_clock_page0.addWidget(self.timeedit_time_page0) layout_loc_page0.addWidget(label_loc_page0) layout_loc_page0.addWidget(self.lineedit_loc_page0) layout_name_page0.addWidget(label_users_name_page0) layout_name_page0.addWidget(self.lineedit_users_name_page0) layout_gender_page0.addWidget(label_users_gender_page0) layout_gender_page0.addWidget(groupbox_radio_button) layout_time_page0.addLayout(layout_date_page0) layout_time_page0.addLayout(layout_clock_page0) layout_time_page0.addLayout(layout_loc_page0) layout_user_page0.addLayout(layout_name_page0) layout_user_page0.addLayout(layout_gender_page0) layout_info_page0.addLayout(layout_time_page0) layout_info_page0.addWidget(line_split_page0) layout_info_page0.addLayout(layout_user_page0) layout_note_page0.addWidget(label_note_page0) layout_note_page0.addWidget(self.textedit_note_page0) layout_tab0_page0_global.addLayout(layout_info_page0) layout_tab0_page0_global.addLayout(layout_note_page0) self.tab0_page0.setLayout(layout_tab0_page0_global) def page0tab1(self): """DocString for page0tab1""" #@todo: to be defined. font = QFont() font.setFamily('MonoxLight') font.setPointSize(12) def page0global(self): """DocString for page0global""" #@todo: to be defined. self.pushbutton_ok_page0 = QPushButton('&Ok') self.pushbutton_re_page0 = QPushButton('&Reset') self.pushbutton_de_page0 = QPushButton('&Cancel') layout_page0_option = QHBoxLayout() layout_page0_option.addStretch(1) layout_page0_option.addWidget(self.pushbutton_ok_page0) layout_page0_option.addWidget(self.pushbutton_re_page0) layout_page0_option.addWidget(self.pushbutton_de_page0) layout_page0_global = QVBoxLayout(self.page0) layout_page0_global.addWidget(self.tabwidget_page0) layout_page0_global.addLayout(layout_page0_option) self.stack_window.addWidget(self.page0) self.page1 = QWidget() self.tabwidget_page1 = QTabWidget(self.page1) self.tab0_page1 = QWidget(self.page1) self.tab1_page1 = QWidget(self.page1) self.tabwidget_page1.addTab(self.tab0_page1, 'Info') self.tabwidget_page1.addTab(self.tab1_page1, 'Option') def page1tab0(self): """DocString for page1tab0""" #@todo: to be defined. font = QFont() font.setFamily('MonoxLight') font.setPointSize(12) label_date_page1 = QLabel('Date ') label_date_page1.setFont(font) label_time_page1 = QLabel('Time ') label_time_page1.setFont(font) label_loc_page1 = QLabel('Location ') label_loc_page1.setFont(font) label_users_name_page1 = QLabel('Name ') label_users_name_page1.setFont(font) label_users_gender_page1 = QLabel('Gender') label_users_gender_page1.setFont(font) label_note_page1 = QLabel('Note') label_note_page1.setFont(font) line_split_page1 = QFrame() line_split_page1.setFrameShape(QFrame.VLine) line_split_page1.setFrameShadow(QFrame.Sunken) self.dateedit_date_page1 = QDateEdit(QDate.currentDate()) self.dateedit_date_page1.setDisplayFormat('yyyy/MM/dd') self.dateedit_date_page1.setCalendarPopup(True) self.dateedit_date_page1.setFont(font) self.timeedit_time_page1 = QTimeEdit(QTime.currentTime()) self.timeedit_time_page1.setDisplayFormat('HH : mm') self.timeedit_time_page1.setFont(font) self.lineedit_loc_page1 = QLineEdit('Mars') self.lineedit_loc_page1.setFont(font) self.lineedit_users_name_page1 = QLineEdit('Object') self.lineedit_users_name_page1.setFont(font) self.radiobutton_users_gender_male_page1 = QRadioButton('Male') self.radiobutton_users_gender_female_page1 = QRadioButton('Female') self.radiobutton_users_gender_secret_page1 = QRadioButton('Secret') self.textedit_note_page1 = QTextEdit('None') self.textedit_note_page1.setFont(font) groupbox_radio_button = QGroupBox() groupbox_radio_button.setStyleSheet(self.style_groupbox) layout_groupbox_radio_button = QHBoxLayout() layout_groupbox_radio_button.addWidget( self.radiobutton_users_gender_male_page1) layout_groupbox_radio_button.addWidget( self.radiobutton_users_gender_female_page1) layout_groupbox_radio_button.addWidget( self.radiobutton_users_gender_secret_page1) groupbox_radio_button.setLayout(layout_groupbox_radio_button) layout_tab0_page1_global = QVBoxLayout(self.tab0_page1) layout_info_page1 = QHBoxLayout() layout_note_page1 = QVBoxLayout() layout_time_page1 = QVBoxLayout() layout_user_page1 = QVBoxLayout() layout_date_page1 = QHBoxLayout() layout_date_page1.setAlignment(Qt.AlignLeft) layout_clock_page1 = QHBoxLayout() layout_clock_page1.setAlignment(Qt.AlignLeft) layout_loc_page1 = QHBoxLayout() layout_loc_page1.setAlignment(Qt.AlignLeft) layout_name_page1 = QHBoxLayout() layout_gender_page1 = QVBoxLayout() layout_date_page1.addWidget(label_date_page1) layout_date_page1.addWidget(self.dateedit_date_page1) layout_clock_page1.addWidget(label_time_page1) layout_clock_page1.addWidget(self.timeedit_time_page1) layout_loc_page1.addWidget(label_loc_page1) layout_loc_page1.addWidget(self.lineedit_loc_page1) layout_name_page1.addWidget(label_users_name_page1) layout_name_page1.addWidget(self.lineedit_users_name_page1) layout_gender_page1.addWidget(label_users_gender_page1) layout_gender_page1.addWidget(groupbox_radio_button) layout_time_page1.addLayout(layout_date_page1) layout_time_page1.addLayout(layout_clock_page1) layout_time_page1.addLayout(layout_loc_page1) layout_user_page1.addLayout(layout_name_page1) layout_user_page1.addLayout(layout_gender_page1) layout_info_page1.addLayout(layout_time_page1) layout_info_page1.addWidget(line_split_page1) layout_info_page1.addLayout(layout_user_page1) layout_note_page1.addWidget(label_note_page1) layout_note_page1.addWidget(self.textedit_note_page1) layout_tab0_page1_global.addLayout(layout_info_page1) layout_tab0_page1_global.addLayout(layout_note_page1) self.tab0_page1.setLayout(layout_tab0_page1_global) def page1tab1(self): """DocString for page1tab1""" #@todo: to be defined. font = QFont() font.setFamily('MonoxLight') font.setPointSize(12) label_filter_or_not = QLabel('Filter') label_filter_or_not.setFont(font) label_filter_hz1 = QLabel('Hz') label_filter_hz1.setFont(font) label_filter_hz2 = QLabel('Hz') label_filter_hz2.setFont(font) label_filter_hz3 = QLabel('Hz') label_filter_hz3.setFont(font) label_filter_hz4 = QLabel('Hz') label_filter_hz4.setFont(font) label_sampling_freq = QLabel('Sampling Frequency') label_sampling_freq.setFont(font) label_notch_filter = QLabel('Notch Filter') label_notch_filter.setFont(font) label_bandpass_filter = QLabel('Bandpass Filter') label_bandpass_filter.setFont(font) label_bandpass_filter_to = QLabel('to') label_bandpass_filter_to.setFont(font) label_set_num = QLabel('Set Number') label_set_num.setFont(font) label_set_time = QLabel('Set Time') label_set_time.setFont(font) label_set_interval = QLabel('Auto Restart') label_set_interval.setFont(font) label_set_interval_s = QLabel('s') label_set_interval_s.setFont(font) label_tcp_address = QLabel('TCP Address') label_tcp_address.setFont(font) label_tcp_port = QLabel('TCP Port') label_tcp_port.setFont(font) label_filetype_save = QLabel('Filetype') label_filetype_save.setFont(font) label_channel_num = QLabel('Channel Number') label_channel_num.setFont(font) self.spinbox_set_num = QSpinBox() self.spinbox_set_time = QSpinBox() self.lineedit_tcp_address = QLineEdit() self.lineedit_tcp_address.setFont(font) self.lineedit_tcp_port = QLineEdit() self.lineedit_tcp_port.setFont(font) self.combobox_bandpass_high = QComboBox() self.combobox_bandpass_high.addItem('1') self.combobox_bandpass_high.addItem('5') self.combobox_bandpass_high.addItem('10') self.combobox_bandpass_high.addItem('20') self.combobox_bandpass_low = QComboBox() self.combobox_bandpass_low.addItem('50') self.combobox_bandpass_low.addItem('100') self.combobox_bandpass_low.addItem('200') self.combobox_bandpass_low.addItem('450') self.combobox_sampling_freq = QComboBox() self.combobox_sampling_freq.addItem('250') self.combobox_sampling_freq.addItem('500') self.combobox_sampling_freq.addItem('1000') self.combobox_sampling_freq.addItem('2000') self.combobox_notch_filter = QComboBox() self.combobox_notch_filter.addItem('50') self.combobox_notch_filter.addItem('60') self.combobox_filetype_save = QComboBox() self.combobox_filetype_save.addItem('csv') self.combobox_filetype_save.addItem('npy') self.combobox_channel_num = QComboBox() self.combobox_channel_num.addItem('64') self.combobox_channel_num.addItem('128') self.combobox_channel_num.addItem('192') self.checkbox_notch_filter = QCheckBox('Notch Filter') self.checkbox_bandpass_filter = QCheckBox('Bandpass Filter') self.radiobutton_restart_auto = QRadioButton('Auto Restart') self.radiobutton_restart_press = QRadioButton('Manual Restart') self.spinbox_restart_auto = QSpinBox() groupbox_filter_page1 = QGroupBox('Filter') groupbox_filter_page1.setStyleSheet(self.style_groupbox_font) groupbox_data_page1 = QGroupBox('Data') groupbox_data_page1.setStyleSheet(self.style_groupbox_font) groupbox_tcpip_page1 = QGroupBox('TCP/IP') groupbox_tcpip_page1.setStyleSheet(self.style_groupbox_font) layout_filter_or_not = QHBoxLayout() layout_filter_notch = QHBoxLayout() layout_filter_bandpass = QHBoxLayout() layout_sampling_freq = QHBoxLayout() layout_button_filter_reset = QHBoxLayout() layout_filter_or_not.addWidget(label_filter_or_not) layout_filter_or_not.addWidget(self.checkbox_notch_filter) layout_filter_or_not.addWidget(self.checkbox_bandpass_filter) layout_filter_notch.addWidget(label_notch_filter) layout_filter_notch.addWidget(self.combobox_notch_filter) layout_filter_notch.addWidget(label_filter_hz1) layout_filter_bandpass.addWidget(label_bandpass_filter) layout_filter_bandpass.addWidget(self.combobox_bandpass_high) layout_filter_bandpass.addWidget(label_filter_hz2) layout_filter_bandpass.addWidget(label_bandpass_filter_to) layout_filter_bandpass.addWidget(self.combobox_bandpass_low) layout_filter_bandpass.addWidget(label_filter_hz3) layout_sampling_freq.addWidget(label_sampling_freq) layout_sampling_freq.addWidget(self.combobox_sampling_freq) layout_sampling_freq.addWidget(label_filter_hz4) layout_data_channel_num = QHBoxLayout() layout_data_set = QHBoxLayout() layout_data_interval = QVBoxLayout() layout_data_filetype = QHBoxLayout() layout_button_data_reset = QHBoxLayout() layout_data_channel_num.addWidget(label_channel_num) layout_data_channel_num.addWidget(self.combobox_channel_num) layout_data_interval_auto = QHBoxLayout() layout_data_interval_press = QHBoxLayout() layout_data_interval_auto.addWidget(self.radiobutton_restart_auto) layout_data_interval_auto.addWidget(self.spinbox_restart_auto) layout_data_interval_auto.addWidget(label_set_interval_s) layout_data_interval_press.addWidget(self.radiobutton_restart_press) layout_data_interval.addLayout(layout_data_interval_auto) layout_data_interval.addLayout(layout_data_interval_press) layout_data_set.addWidget(label_set_num) layout_data_set.addWidget(self.spinbox_set_num) layout_data_set.addWidget(label_set_time) layout_data_set.addWidget(self.spinbox_set_time) layout_data_filetype.addWidget(label_filetype_save) layout_data_filetype.addWidget(self.combobox_filetype_save) layout_filter_page1 = QVBoxLayout() layout_filter_page1.addLayout(layout_filter_or_not) layout_filter_page1.addLayout(layout_filter_notch) layout_filter_page1.addLayout(layout_filter_bandpass) layout_filter_page1.addLayout(layout_sampling_freq) layout_data_page1 = QVBoxLayout() layout_data_page1.addLayout(layout_data_channel_num) layout_data_page1.addLayout(layout_data_set) layout_data_page1.addLayout(layout_data_interval) layout_data_page1.addLayout(layout_data_filetype) layout_tcpip_page1_global = QVBoxLayout() layout_tcpip_data = QHBoxLayout() layout_tcpip_button_reset = QHBoxLayout() layout_tcpip_data.addWidget(label_tcp_address) layout_tcpip_data.addWidget(self.lineedit_tcp_address) layout_tcpip_data.addWidget(label_tcp_port) layout_tcpip_data.addWidget(self.lineedit_tcp_port) layout_tcpip_page1_global.addLayout(layout_tcpip_data) groupbox_filter_page1.setLayout(layout_filter_page1) groupbox_data_page1.setLayout(layout_data_page1) groupbox_tcpip_page1.setLayout(layout_tcpip_page1_global) layout_tab1_page1_up = QHBoxLayout() layout_tab1_page1_down = QHBoxLayout() layout_tab1_page1_up.addWidget(groupbox_filter_page1) layout_tab1_page1_up.addWidget(groupbox_data_page1) layout_tab1_page1_down.addWidget(groupbox_tcpip_page1) layout_tab1_page1_global = QVBoxLayout() layout_tab1_page1_global.addLayout(layout_tab1_page1_up) layout_tab1_page1_global.addLayout(layout_tab1_page1_down) self.tab1_page1.setLayout(layout_tab1_page1_global) def page1global(self): """DocString for page1global""" #@todo: to be defined. self.pushbutton_ok_page1 = QPushButton('&Ok') self.pushbutton_de_page1 = QPushButton('&Cancel') self.pushbutton_re_page1 = QPushButton('&Reset') layout_page1_option = QHBoxLayout() layout_page1_option.addStretch(1) layout_page1_option.addWidget(self.pushbutton_ok_page1) layout_page1_option.addWidget(self.pushbutton_re_page1) layout_page1_option.addWidget(self.pushbutton_de_page1) layout_page1_global = QVBoxLayout() layout_page1_global.addWidget(self.tabwidget_page1) layout_page1_global.addLayout(layout_page1_option) self.page1.setLayout(layout_page1_global) self.stack_window.addWidget(self.page1)
class _ToolsDock(QWidget): __WIDGETS = {} __created = False __index = 0 # Signals executeFile = pyqtSignal() executeProject = pyqtSignal() executeSelection = pyqtSignal() stopApplication = pyqtSignal() def __init__(self, parent=None): super().__init__(parent) # Register signals connections layout = QHBoxLayout(self) layout.setContentsMargins(0, 0, 0, 0) layout.setSpacing(0) self.__buttons = [] self.__action_number = 1 self.__buttons_visibility = {} self.__current_widget = None self.__last_index = -1 self._stack_widgets = QStackedWidget() layout.addWidget(self._stack_widgets) # Buttons Widget self.buttons_widget = QWidget() self.buttons_widget.setObjectName("tools_dock") self.buttons_widget.setFixedHeight(26) self.buttons_widget.setLayout(QHBoxLayout()) self.buttons_widget.layout().setContentsMargins(2, 2, 5, 2) self.buttons_widget.layout().setSpacing(10) IDE.register_service("tools_dock", self) _ToolsDock.__created = True @classmethod def register_widget(cls, display_name, obj): """Register a widget providing the service name and the instance""" cls.__WIDGETS[cls.__index] = (obj, display_name) cls.__index += 1 def install(self): self._load_ui() ninjaide = IDE.get_service("ide") ninjaide.place_me_on("tools_dock", self, "central") ui_tools.install_shortcuts(self, actions.ACTIONS, ninjaide) ninjaide.goingDown.connect(self._save_settings) ninja_settings = IDE.ninja_settings() index = int(ninja_settings.value("tools_dock/widgetVisible", -1)) if index == -1: self.hide() else: self._show(index) def _load_ui(self): ninjaide = IDE.get_service("ide") shortcut_number = 1 for index, (obj, name) in _ToolsDock.__WIDGETS.items(): button = ToolButton(name, index + 1) button.setCheckable(True) button.clicked.connect(self.on_button_triggered) self.__buttons.append(button) self.buttons_widget.layout().addWidget(button) self.add_widget(name, obj) self.__buttons_visibility[button] = True # Shortcut action ksequence = self._get_shortcut(shortcut_number) short = QShortcut(ksequence, ninjaide) button.setToolTip( ui_tools.tooltip_with_shortcut(button._text, ksequence)) short.activated.connect(self._shortcut_triggered) shortcut_number += 1 self.buttons_widget.layout().addItem( QSpacerItem(0, 0, QSizePolicy.Expanding)) # Python Selector btn_selector = ui_tools.FancyButton("Loading...") btn_selector.setIcon(ui_tools.get_icon("python")) btn_selector.setCheckable(True) btn_selector.setEnabled(False) self.buttons_widget.layout().addWidget(btn_selector) # QML Interface self._python_selector = python_selector.PythonSelector(btn_selector) interpreter_srv = IDE.get_service("interpreter") interpreter_srv.foundInterpreters.connect( self._python_selector.add_model) btn_selector.toggled[bool].connect( lambda v: self._python_selector.setVisible(v)) # Popup for show/hide tools widget button_toggle_widgets = ToggleButton() self.buttons_widget.layout().addWidget(button_toggle_widgets) button_toggle_widgets.clicked.connect(self._show_menu) def _get_shortcut(self, short_number: int): """Return shortcut as ALT + number""" if short_number < 1 or short_number > 9: return QKeySequence() modifier = Qt.ALT if not settings.IS_MAC_OS else Qt.CTRL return QKeySequence(modifier + (Qt.Key_0 + short_number)) def _shortcut_triggered(self): short = self.sender() widget_index = int(short.key().toString()[-1]) - 1 widget = self.widget(widget_index) if widget.isVisible(): self._hide() else: self._show(widget_index) def _show_menu(self): menu = QMenu() for n, (obj, display_name) in _ToolsDock.__WIDGETS.items(): action = menu.addAction(display_name) action.setCheckable(True) action.setData(n) button = self.__buttons[n] visible = self.__buttons_visibility.get(button) action.setChecked(visible) result = menu.exec_(QCursor.pos()) if not result: return index = result.data() btn = self.__buttons[index] visible = self.__buttons_visibility.get(btn, False) self.__buttons_visibility[btn] = not visible if visible: btn.hide() else: btn.show() def get_widget_index_by_instance(self, instance): index = -1 for i, (obj, _) in self.__WIDGETS.items(): if instance == obj: index = i break return index def execute_file(self): run_widget = IDE.get_service("run_widget") index = self.get_widget_index_by_instance(run_widget) self._show(index) self.executeFile.emit() def execute_project(self): run_widget = IDE.get_service("run_widget") index = self.get_widget_index_by_instance(run_widget) self._show(index) self.executeProject.emit() def execute_selection(self): run_widget = IDE.get_service("run_widget") index = self.get_widget_index_by_instance(run_widget) self._show(index) self.executeSelection.emit() def kill_application(self): self.stopApplication.emit() def add_widget(self, display_name, obj): self._stack_widgets.addWidget(obj) func = getattr(obj, "install_widget", None) if isinstance(func, collections.Callable): func() def on_button_triggered(self): # Get button index button = self.sender() index = self.__buttons.index(button) if index == self.current_index() and self._is_current_visible(): self._hide() else: self._show(index) def widget(self, index): return self.__WIDGETS[index][0] def _hide(self): self.__current_widget.setVisible(False) index = self.current_index() self.__buttons[index].setChecked(False) self.widget(index).setVisible(False) self.hide() def hide_widget(self, obj): index = self.get_widget_index_by_instance(obj) self.set_current_index(index) self._hide() def _show(self, index): widget = self.widget(index) self.__current_widget = widget widget.setVisible(True) widget.setFocus() self.set_current_index(index) self.show() def set_current_index(self, index): if self.__last_index != -1: self.__buttons[self.__last_index].setChecked(False) self.__buttons[index].setChecked(True) if index != -1: self._stack_widgets.setCurrentIndex(index) widget = self.widget(index) widget.setVisible(True) self.__last_index = index def current_index(self): return self._stack_widgets.currentIndex() def _is_current_visible(self): return self.__current_widget and self.__current_widget.isVisible() def _save_settings(self): ninja_settings = IDE.ninja_settings() visible_widget = self.current_index() if not self.isVisible(): visible_widget = -1 ninja_settings.setValue("tools_dock/widgetVisible", visible_widget)
class _ToolsDock(QWidget): __WIDGETS = {} __created = False __index = 0 # Signals executeFile = pyqtSignal() executeProject = pyqtSignal() executeSelection = pyqtSignal() stopApplication = pyqtSignal() def __init__(self, parent=None): super().__init__(parent) # Register signals connections layout = QHBoxLayout(self) layout.setContentsMargins(0, 0, 0, 0) layout.setSpacing(0) self.__buttons = [] self.__action_number = 1 self.__buttons_visibility = {} self.__current_widget = None self.__last_index = -1 self._stack_widgets = QStackedWidget() layout.addWidget(self._stack_widgets) # Buttons Widget self.buttons_widget = QWidget() self.buttons_widget.setObjectName("tools_dock") self.buttons_widget.setFixedHeight(26) self.buttons_widget.setLayout(QHBoxLayout()) self.buttons_widget.layout().setContentsMargins(2, 2, 5, 2) self.buttons_widget.layout().setSpacing(10) IDE.register_service("tools_dock", self) _ToolsDock.__created = True @classmethod def register_widget(cls, display_name, obj): """Register a widget providing the service name and the instance""" cls.__WIDGETS[cls.__index] = (obj, display_name) cls.__index += 1 def install(self): self._load_ui() ninjaide = IDE.get_service("ide") ninjaide.place_me_on("tools_dock", self, "central") ui_tools.install_shortcuts(self, actions.ACTIONS, ninjaide) ninjaide.goingDown.connect(self._save_settings) ninja_settings = IDE.ninja_settings() index = int(ninja_settings.value("tools_dock/widgetVisible", -1)) if index == -1: self.hide() else: self._show(index) def _load_ui(self): ninjaide = IDE.get_service("ide") shortcut_number = 1 for index, (obj, name) in _ToolsDock.__WIDGETS.items(): button = ToolButton(name, index + 1) button.setCheckable(True) button.clicked.connect(self.on_button_triggered) self.__buttons.append(button) self.buttons_widget.layout().addWidget(button) self.add_widget(name, obj) self.__buttons_visibility[button] = True # Shortcut action ksequence = self._get_shortcut(shortcut_number) short = QShortcut(ksequence, ninjaide) button.setToolTip( ui_tools.tooltip_with_shortcut(button._text, ksequence)) short.activated.connect(self._shortcut_triggered) shortcut_number += 1 self.buttons_widget.layout().addItem( QSpacerItem(0, 0, QSizePolicy.Expanding)) # Python Selector btn_selector = ui_tools.FancyButton("Loading...") btn_selector.setIcon(ui_tools.get_icon("python")) btn_selector.setCheckable(True) btn_selector.setEnabled(False) self.buttons_widget.layout().addWidget(btn_selector) # QML Interface self._python_selector = python_selector.PythonSelector(btn_selector) interpreter_srv = IDE.get_service("interpreter") interpreter_srv.foundInterpreters.connect( self._python_selector.add_model) btn_selector.toggled[bool].connect( lambda v: self._python_selector.setVisible(v)) # Popup for show/hide tools widget button_toggle_widgets = ToggleButton() self.buttons_widget.layout().addWidget(button_toggle_widgets) button_toggle_widgets.clicked.connect(self._show_menu) def _get_shortcut(self, short_number: int): """Return shortcut as ALT + number""" if short_number < 1 or short_number > 9: return QKeySequence() modifier = Qt.ALT if not settings.IS_MAC_OS else Qt.CTRL return QKeySequence(modifier + (Qt.Key_0 + short_number)) def _shortcut_triggered(self): short = self.sender() widget_index = int(short.key().toString()[-1]) - 1 widget = self.widget(widget_index) if widget.isVisible(): self._hide() else: self._show(widget_index) def _show_menu(self): menu = QMenu() for n, (obj, display_name) in _ToolsDock.__WIDGETS.items(): action = menu.addAction(display_name) action.setCheckable(True) action.setData(n) button = self.__buttons[n] visible = self.__buttons_visibility.get(button) action.setChecked(visible) result = menu.exec_(QCursor.pos()) if not result: return index = result.data() btn = self.__buttons[index] visible = self.__buttons_visibility.get(btn, False) self.__buttons_visibility[btn] = not visible if visible: btn.hide() else: btn.show() def get_widget_index_by_instance(self, instance): index = -1 for i, (obj, _) in self.__WIDGETS.items(): if instance == obj: index = i break return index def execute_file(self): run_widget = IDE.get_service("run_widget") index = self.get_widget_index_by_instance(run_widget) self._show(index) self.executeFile.emit() def execute_project(self): run_widget = IDE.get_service("run_widget") index = self.get_widget_index_by_instance(run_widget) self._show(index) self.executeProject.emit() def execute_selection(self): run_widget = IDE.get_service("run_widget") index = self.get_widget_index_by_instance(run_widget) self._show(index) self.executeSelection.emit() def kill_application(self): self.stopApplication.emit() def add_widget(self, display_name, obj): self._stack_widgets.addWidget(obj) func = getattr(obj, "install_widget", None) if isinstance(func, collections.Callable): func() def on_button_triggered(self): # Get button index button = self.sender() index = self.__buttons.index(button) if index == self.current_index() and self._is_current_visible(): self._hide() else: self._show(index) def widget(self, index): return self.__WIDGETS[index][0] def _hide(self): self.__current_widget.setVisible(False) index = self.current_index() self.__buttons[index].setChecked(False) self.widget(index).setVisible(False) self.hide() def hide_widget(self, obj): index = self.get_widget_index_by_instance(obj) self.set_current_index(index) self._hide() def _show(self, index): widget = self.widget(index) self.__current_widget = widget widget.setVisible(True) widget.setFocus() self.set_current_index(index) self.show() def set_current_index(self, index): if self.__last_index != -1: self.__buttons[self.__last_index].setChecked(False) self.__buttons[index].setChecked(True) if index != -1: self._stack_widgets.setCurrentIndex(index) widget = self.widget(index) widget.setVisible(True) self.__last_index = index def current_index(self): return self._stack_widgets.currentIndex() def _is_current_visible(self): return self.__current_widget and self.__current_widget.isVisible() def _save_settings(self): ninja_settings = IDE.ninja_settings() visible_widget = self.current_index() if not self.isVisible(): visible_widget = -1 ninja_settings.setValue("tools_dock/widgetVisible", visible_widget)
class UiMainWindow(QWidget): """Main UI window of the application. Attributes: ---------- window_width: int Width of the window window_height: int Height of the window button_width: int Width of buttons button_height: int Height of buttons dist: int Distance to the edge of Widgets(Window/Button/Label...) model_selected: bool Shows whether a model is selected or not """ window_height = 650 window_width = 800 button_width = 180 button_height = 50 dist = 30 model_selected = False textbox_height = 25 small_button_width = 100 small_button_height = 30 debug_height = 200 debug_mode = False accepted_download = False current_city = "" def __init__(self, parent) -> None: super().__init__(parent) main_window.setObjectName("main_window") main_window.resize(self.window_width, self.window_height) self.centralwidget = QWidget(main_window) self.centralwidget.setObjectName("centralwidget") self.detector = Detection() self.Box_Stadt = QComboBox(self.centralwidget) self.Box_Stadt.setGeometry( QRect(self.dist, self.dist, self.button_width, self.button_height)) self.Box_Stadt.setObjectName("Box_Stadt") self.Box_Stadt.activated.connect(self.on_dropdown_selected) # dynamic city updates supported_cities_updater = Thread(target=update_dropdown, daemon=True, args=(self.Box_Stadt, )) supported_cities_updater.start() self.Text_City = QLineEdit(self.centralwidget) self.Text_City.setGeometry( QRect(self.dist + self.dist + self.button_width, self.dist + 10, self.button_width, self.textbox_height)) self.Text_City.setObjectName("Text_City") self.Text_City.setToolTip( 'Enter a city you wish to detect sights in that you cannot find in the dropdown on the left after updating.' ) self.Button_City = QPushButton(self.centralwidget) self.Button_City.setGeometry( QRect( int(2.3 * self.dist) + self.button_width + self.button_width, self.dist + 8, self.small_button_width, self.small_button_height)) self.Button_City.setObjectName("Button_City") self.Button_City.clicked.connect(self.request_city) self.Button_Detection = QPushButton(self.centralwidget) self.Button_Detection.setGeometry( QRect( self.window_width - (self.dist + self.button_width), self.window_height - (self.dist + self.button_height + 20), self.button_width, self.button_height, )) self.Button_Detection.setObjectName("Button_Detection") self.Button_Detection.clicked.connect(self.detect_sights) self.Button_Bild = QPushButton(self.centralwidget) self.Button_Bild.setGeometry( QRect( self.dist, self.window_height - (self.dist + self.button_height + 20), self.button_width, self.button_height, )) self.Button_Bild.setObjectName("Button_Bild") self.Button_Bild.clicked.connect(lambda: self.camera_viewfinder.hide()) self.Button_Bild.clicked.connect( lambda: self.Box_Camera_selector.setCurrentIndex(0)) self.Button_Bild.clicked.connect( lambda: self.stacked_widget.setCurrentIndex(0)) self.Button_Bild.clicked.connect(lambda: self.Label_Bild.show()) self.Button_Bild.clicked.connect(self.dragdrop) self.available_cameras = QCameraInfo.availableCameras() self.Box_Camera_selector = QComboBox(self.centralwidget) self.Box_Camera_selector.setGeometry( QRect( self.window_width - (self.dist + self.button_width), self.dist, self.button_width, self.button_height, )) self.Box_Camera_selector.setObjectName("Box_Camera_selector") self.Box_Camera_selector.addItem("") # self.Box_Camera_selector.addItems([camera.description() for camera in self.available_cameras]) self.Box_Camera_selector.addItems([ "Camera " + str(i) + ": " + str(self.available_cameras[i].description()) for i in range(len(self.available_cameras)) ]) self.Box_Camera_selector.currentIndexChanged.connect( self.select_camera) self.stacked_widget = QStackedWidget(self.centralwidget) label_height = (self.window_height - self.dist - self.button_height - self.dist) - (self.dist + self.button_height + self.dist) label_start_y = self.dist + self.button_height + self.dist self.stacked_widget.setGeometry( QRect( self.dist, label_start_y, self.window_width - (self.dist * 2), label_height, )) self.camera_viewfinder = QCameraViewfinder() self.Label_Bild = ImageLabel(self) self.Label_Bild.setGeometry( QRect(0, 0, self.window_width - (self.dist * 2), label_height)) self.checkBoxImprove = QCheckBox( "Help improving SightScan's detection quality", self.centralwidget) self.checkBoxImprove.setObjectName(u"improvement") self.checkBoxImprove.setGeometry(QRect(self.dist, 5, 350, 20)) self.checkBoxImprove.setChecked(False) self.checkBoxImprove.stateChanged.connect(self.set_improve_quality_var) self.checkBox = QCheckBox("Debug", self.centralwidget) self.checkBox.setObjectName(u"checkBox") self.checkBox.setGeometry( QRect(self.window_width - (self.dist + 50), self.window_height - (self.dist + 20), 70, 20)) self.checkBox.setChecked(False) self.checkBox.stateChanged.connect(self.debug_click) # Setup logging fn = "logs/" + datetime.now().strftime( '%d_%m_%Y__%H_%M_%S') + 'log.log' if not os.path.exists("logs"): os.mkdir("logs") f = '%(asctime)s :: %(levelname)s :: %(filename)s :: %(funcName)s :: %(lineno)d :: %(message)s' self.textDebug = QTextEditLogger(self.centralwidget) self.textDebug.setFormatter(logging.Formatter(f)) logging.basicConfig(filename=fn, format=f, level=logging.DEBUG) logging.getLogger().addHandler(self.textDebug) # Log Text Box in GUI self.textDebug.widget.setObjectName(u"textEdit") self.textDebug.widget.setEnabled(False) self.textDebug.widget.setGeometry( QRect(self.dist, self.window_height, self.window_width - 2 * self.dist, self.debug_height - self.dist)) size_policy = QSizePolicy(QSizePolicy.Fixed, QSizePolicy.Expanding) size_policy.setHorizontalStretch(0) size_policy.setVerticalStretch(0) size_policy.setHeightForWidth( self.textDebug.widget.sizePolicy().hasHeightForWidth()) self.textDebug.widget.setSizePolicy(size_policy) self.textDebug.widget.setReadOnly(True) self.stacked_widget.addWidget(self.Label_Bild) self.stacked_widget.addWidget(self.camera_viewfinder) main_window.setCentralWidget(self.centralwidget) self.menubar = QMenuBar(main_window) self.menubar.setGeometry(QRect(0, 0, 678, 21)) self.menubar.setObjectName("menubar") main_window.setMenuBar(self.menubar) self.statusbar = QStatusBar(main_window) self.statusbar.setObjectName("statusbar") main_window.setStatusBar(self.statusbar) main_window.setWindowIcon(QIcon(logo_without_text)) self.retranslateUi(main_window) QMetaObject.connectSlotsByName(main_window) def set_improve_quality_var(self): self.improve_checkbox_enabled = self.checkBoxImprove.isChecked() def retranslateUi(self, main_window: QMainWindow) -> None: """Set the text initially for all items. Parameters ---------- main_window: QMainWindow The instance of the prepared application window """ _translate = QCoreApplication.translate main_window.setWindowTitle(_translate(WINDOW, "SightScan")) self.Box_Stadt.addItems(['Choose City'] + initialize_cities()) self.Box_Camera_selector.setItemText( 0, _translate(WINDOW, "Choose Webcam")) self.Button_Detection.setText(_translate(WINDOW, START)) self.Button_Bild.setText(_translate(WINDOW, ENABLE)) self.Button_City.setText(_translate(WINDOW, "Add City")) def on_dropdown_selected(self) -> None: """Shows a pop-up for confirming the download of the selected city.""" city_pretty_print = self.Box_Stadt.currentText() city = self.Box_Stadt.currentText().replace(' ', '_').upper() if city != "CHOOSE_CITY": self.current_city = self.Box_Stadt.currentText() # if no connection to dos if get_supported_cities() == []: latest_version = "couldn't get the latest version" downloaded_version = "couldn't get the downloaded version" print('no connection to dos') # if connection to dos else: downloaded_version = -1 # initialization Path("weights").mkdir(mode=0o700, exist_ok=True) if not os.path.exists("weights/versions.txt"): with open('weights/versions.txt', 'w'): # creating a version file pass with open("weights/versions.txt", "r") as file: for line in file: elements = line.split("=") if elements[0].upper() == city: downloaded_version = int(elements[1]) break latest_version = get_dwh_model_version(city) if downloaded_version == -1: msg = QMessageBox() msg.setWindowTitle("Download City") msg.setWindowIcon(QIcon(logo_without_text)) msg.setText("Do you want to download " + city_pretty_print + "?") msg.setIcon(QMessageBox.Question) msg.setStandardButtons(QMessageBox.Cancel | QMessageBox.Ok) msg.setDefaultButton(QMessageBox.Ok) msg.setInformativeText("When downloaded, sights of " + city_pretty_print + " can be detected.") msg.buttonClicked.connect(self.handover_city) msg.exec_() elif latest_version > downloaded_version: update_msg = QMessageBox() update_msg.setWindowTitle("Update available") update_msg.setWindowIcon(QIcon(logo_without_text)) update_msg.setText( "Do you want to download an update for " + city + "?") update_msg.setIcon(QMessageBox.Question) update_msg.setStandardButtons(QMessageBox.Cancel | QMessageBox.Ok) update_msg.setDefaultButton(QMessageBox.Ok) update_msg.setInformativeText( "Updated cities can detect sights faster and more accurately. If you choose not to download, the " + "detection will still work.") update_msg.buttonClicked.connect(self.handover_city) update_msg.exec_() if self.accepted_download is True or latest_version == downloaded_version: self.accepted_download = False self.show_download_result() self.model_selected = True else: self.model_selected = False def handover_city(self, button) -> None: """Starts the download of the pre-trained model of the selected city. Parameters ---------- button: Pushed button inside the popup. """ if "OK" in button.text().upper(): city = self.Box_Stadt.currentText().replace(' ', '_').upper() self.model_selected = True model = get_downloaded_model(city) if model is not None: with open("weights/" + city + ".pt", "wb+") as file: file.write(model) self.accepted_download = True elif "CANCEL" in button.text().upper(): self.Box_Stadt.setCurrentIndex(0) def detect_sights(self) -> None: """Starts detection for the dropped image or shown webcam video with the downloaded model and displays the results in the label.""" city = self.Box_Stadt.currentText().replace(' ', '_').upper() print("Detection Status: " + str(self.detector.detection)) if self.model_selected is False: self.show_missing_model_popup() else: # start drag&drop image detection if self.stacked_widget.currentIndex() == 0 and self.Button_Bild.text() == DISABLE and \ self.Label_Bild.image != logo_with_text: print(f"Starting detection of {self.Label_Bild.image}") wipe_prediction_input_images(INPUT_PREDICTION_DIR) shutil.copy2(self.Label_Bild.image, INPUT_PREDICTION_DIR) self.detector.enable_detection() self.detector.detect(self, weights='weights/' + city + '.pt', debug=self.debug_mode) # stop video detection elif self.stacked_widget.currentIndex( ) == 0 and self.Button_Detection.text() == STOP: self.stop_video_detection() time.sleep(2) self.reactivate_cam() # if webcam activated elif self.stacked_widget.currentIndex() == 1: if self.Button_Detection.text() == START: self.Button_Detection.setText( QCoreApplication.translate(WINDOW, STOP)) self.Label_Bild.setStyleSheet(""" """) print("Video Detection Started") self.prep_video_detection() source = self.Box_Camera_selector.currentIndex() self.detector.enable_detection() self.detection_thread = Thread(target=self.detector.detect, args=(self, ), kwargs={ 'weights': 'weights/' + city + '.pt', 'source': str(source - 1), 'image_size': 704, 'debug': self.debug_mode }) self.detection_thread.start() else: print("Drop a File or select a Webcam!") def show_missing_model_popup(self) -> None: # Show Pop Up to choose a city emsg = QMessageBox() emsg.setWindowTitle("No city chosen") emsg.setWindowIcon(QIcon(logo_without_text)) emsg.setText( "You need to choose a city before the detection can start.") emsg.setIcon(QMessageBox.Warning) emsg.setStandardButtons(QMessageBox.Ok) emsg.setDefaultButton(QMessageBox.Ok) emsg.exec_() def show_download_result(self) -> None: # city_pretty_print = self.Box_Stadt.currentText() self.model_selected = True newest_vers_msg = QMessageBox() newest_vers_msg.setWindowTitle("Ready for Detection!") newest_vers_msg.setWindowIcon(QIcon(logo_without_text)) newest_vers_msg.setText("You can start detecting sights in " + self.current_city + "!") newest_vers_msg.setStandardButtons(QMessageBox.Ok) newest_vers_msg.setDefaultButton(QMessageBox.Ok) newest_vers_msg.exec_() def request_city(self) -> None: # Send entered city to dwh and show confirmation popup if the city name is known city_input = self.Text_City.text() city_request = city_input.upper() if len(filter_city(city_input)) == 1: send_city_request(city_request) cmsg = QMessageBox() cmsg.setWindowTitle("Request confirmed") cmsg.setWindowIcon(QIcon(logo_without_text)) cmsg.setText("Your request to add support for " + city_input + " has been sent to our backend.") cmsg.setStandardButtons(QMessageBox.Ok) cmsg.setDefaultButton(QMessageBox.Ok) cmsg.exec_() else: cmsg = QMessageBox() cmsg.setWindowTitle("Unknown city name") cmsg.setWindowIcon(QIcon(logo_without_text)) cmsg.setText( "The typed city name is not known. Please check the spelling.") cmsg.setIcon(QMessageBox.Warning) cmsg.setStandardButtons(QMessageBox.Ok) cmsg.setDefaultButton(QMessageBox.Ok) cmsg.exec_() def dragdrop(self) -> None: """Enables / disables Drag&Drop of images.""" if self.Button_Bild.text() == ENABLE: # stop video detection if active if self.Button_Detection.text() == STOP: self.Button_Detection.setText( QCoreApplication.translate(WINDOW, START)) self.detector.disable_detection() self.Label_Bild.setAcceptDrops(True) self.Label_Bild.setText("\n\n Drop Image here \n\n") self.Label_Bild.setStyleSheet(""" QLabel{ border: 4px dashed #aaa } """) self.Button_Bild.setText( QCoreApplication.translate(WINDOW, DISABLE)) elif self.Button_Bild.text() == DISABLE: self.Label_Bild.setAcceptDrops(False) self.Label_Bild.setText("") self.Label_Bild.setStyleSheet("") self.Label_Bild.image = logo_with_text self.Label_Bild.setPixmap(QPixmap(self.Label_Bild.image)) self.Button_Bild.setText(QCoreApplication.translate( WINDOW, ENABLE)) def select_camera(self, i): """Starts the selected camera. If "Choose webcam" is selected, it stops the camera. Parameters ---------- i: Index of the chosen camera. """ self.Label_Bild.image = logo_with_text self.Label_Bild.setPixmap(QPixmap(self.Label_Bild.image)) if i == 0: self.camera.stop() self.detector.disable_detection() self.Button_Detection.setText( QCoreApplication.translate(WINDOW, START)) self.stacked_widget.setCurrentIndex(0) self.camera_viewfinder.hide() self.Label_Bild.show() time.sleep(2) self.Label_Bild.image = logo_with_text self.Label_Bild.setPixmap(QPixmap(self.Label_Bild.image)) self.Label_Bild.setStyleSheet(""" """) else: self.camera_viewfinder.show() self.stacked_widget.setCurrentIndex(1) self.Label_Bild.hide() self.camera = QCamera(self.available_cameras[i - 1]) self.camera.setViewfinder(self.camera_viewfinder) self.camera.error.connect( lambda: self.alert(self.camera.errorString())) self.camera.start() self.Button_Bild.setText(QCoreApplication.translate( WINDOW, ENABLE)) def prep_video_detection(self) -> None: self.camera.stop() self.camera_viewfinder.hide() self.stacked_widget.setCurrentIndex(0) self.Label_Bild.image = loading_image self.Label_Bild.setPixmap(QPixmap(self.Label_Bild.image)) self.Label_Bild.show() def stop_video_detection(self) -> None: self.Button_Detection.setText(QCoreApplication.translate( WINDOW, START)) self.detector.disable_detection() self.stacked_widget.setCurrentIndex(1) self.Label_Bild.hide() self.camera_viewfinder.show() def debug_click(self, state): self.debug_mode = bool(state) if state: main_window.resize(self.window_width, self.window_height + self.debug_height) self.textDebug.widget.setEnabled(True) else: main_window.resize(self.window_width, self.window_height) self.textDebug.widget.setEnabled(False) def reactivate_cam(self) -> None: self.Label_Bild.image = logo_with_text self.Label_Bild.setPixmap(QPixmap(self.Label_Bild.image)) self.camera.start() def close_all(self) -> None: if self.Button_Detection.text() == STOP: self.detector.disable_detection() self.stop_video_detection()
class ComprehensionTestApp(QMainWindow): """ @modifies self.symbol_test, self.question1, self.question2 @effects makes a new ComprehensionTestApp """ def __init__(self): super().__init__() self.symbol_tests = QStackedWidget() self.question1 = "Exactly what do you think this symbol means?" self.question2 = "What action would you take in response to this symbol?" self.init_gui() """ @modifies self @effects sets up the main window """ def init_gui(self): exitAct = QAction('&Exit', self) exitAct.setShortcut('Ctrl+Q') exitAct.setStatusTip('Exit application') exitAct.triggered.connect(self.quit) saveAct = QAction('&Save as PDF', self) saveAct.setShortcut('Ctrl+S') saveAct.setStatusTip('Saves as PDF') saveAct.triggered.connect(self.save_as_pdf) importAct = QAction('&Import symbols', self) importAct.setShortcut('Ctrl+I') importAct.setStatusTip( 'Imports all the specified images on to their own test page') importAct.triggered.connect(self.import_symbols) menu_bar = self.menuBar() filemenu = menu_bar.addMenu('&File') filemenu.addAction(importAct) filemenu.addAction(saveAct) filemenu.addAction(exitAct) goToAct = QAction('&Go to page', self) goToAct.setStatusTip('Go to the specified page') goToAct.setShortcut('Ctrl+G') goToAct.triggered.connect(self.go_to_page) newQuestAct = QAction('&Change default questions', self) newQuestAct.setStatusTip( 'Changes the default questions asked about the symbol') newQuestAct.setShortcut('Ctrl+H') newQuestAct.triggered.connect(self.change_questions) moveLeftAct = QAction('&Move current page left', self) moveLeftAct.setShortcut('Ctrl+,') moveLeftAct.setStatusTip('Moves the current page one page to the left') moveLeftAct.triggered.connect(self.move_test_left) moveRightAct = QAction('&Move current page right', self) moveRightAct.setShortcut('Ctrl+.') moveRightAct.setStatusTip( 'Moves the current page one page to the right') moveRightAct.triggered.connect(self.move_test_right) moveToAct = QAction('Move current page to', self) moveToAct.setShortcut('Ctrl+M') moveToAct.setStatusTip('Moves the current page to the specified page') moveToAct.triggered.connect(self.move_to_page) delPagesAct = QAction('Delete pages', self) delPagesAct.setShortcut('Ctrl+D') delPagesAct.setStatusTip('Deletes the specified pages') delPagesAct.triggered.connect(self.delete_pages) editmenu = menu_bar.addMenu('&Edit') editmenu.addAction(delPagesAct) editmenu.addAction(newQuestAct) editmenu.addAction(goToAct) editmenu.addAction(moveLeftAct) editmenu.addAction(moveRightAct) editmenu.addAction(moveToAct) nextAct = QAction(QIcon("next.png"), 'Next page (Ctrl+P)', self) nextAct.setShortcut('Ctrl+P') nextAct.setStatusTip('Goes to the next page') nextAct.triggered.connect(self.next_page) prevAct = QAction(QIcon("prev.png"), "Previous page (Ctrl+O)", self) prevAct.setShortcut('Ctrl+O') prevAct.setStatusTip('Goes to the previous page') prevAct.triggered.connect(self.prev_page) newAct = QAction(QIcon("add.png"), "Add a new page (Ctrl+N)", self) newAct.setShortcut('Ctrl+N') newAct.setStatusTip('Creates a new page') newAct.triggered.connect(self.add_blank_test) remAct = QAction(QIcon("remove.png"), "Delete page (Ctrl+R)", self) remAct.setShortcut('Ctrl+R') remAct.setStatusTip('Deletes the current page') remAct.triggered.connect(self.delete_test) tool_bar = self.addToolBar("Next Page") tool_bar.addAction(prevAct) tool_bar.addAction(nextAct) tool_bar.addAction(newAct) tool_bar.addAction(remAct) self.setWindowTitle("Open Comprehension Test Generator") self.setWindowIcon(QIcon("rubber ducky.png")) self.setGeometry(500, 500, 500, 450) self.add_blank_test() self.setCentralWidget(self.symbol_tests) self.show() """ @modifies none @effects confirms if the user wishes to exit """ def closeEvent(self, event): msg = QMessageBox.question(self, 'Message', "Really quit?", QMessageBox.Yes | QMessageBox.No, QMessageBox.No) if msg == QMessageBox.Yes: event.accept() else: event.ignore() """ @modifies none @effects confirms if the user wishes to exit """ def quit(self): msg = QMessageBox.question(self, 'Message', "Really quit?", QMessageBox.Yes | QMessageBox.No, QMessageBox.No) if msg == QMessageBox.Yes: qApp.quit() """ @modifies self.symbol_tests @effects adds a blank symbol_test to the end of symbol_tests sets the current symbol_test to the new one """ def add_blank_test(self): page = self.symbol_tests.count() + 1 test = SymbolTestWidget( self, comp_questions=[self.question1, self.question2], pageNumber=page, pageTotal=page) test.set_visual_context("blank image.png") self.symbol_tests.addWidget(test) self.symbol_tests.setCurrentIndex(page - 1) for i in range(0, self.symbol_tests.count() - 1): self.symbol_tests.widget(i).set_numPages(self.symbol_tests.count()) """ @modifies self.symbol_tests @effects changes the current symbol_test to the next one """ def next_page(self): if self.symbol_tests.currentIndex() < self.symbol_tests.count() - 1: self.symbol_tests.setCurrentIndex( self.symbol_tests.currentIndex() + 1) """ @modifies self.symbol_test @effects changes the current symbol_test to the previous one """ def prev_page(self): if self.symbol_tests.currentIndex() > 0: self.symbol_tests.setCurrentIndex( self.symbol_tests.currentIndex() - 1) """ @modifies none @effects saves all the symbol_tests as seperate pages on a PDF document """ def save_as_pdf(self): filename = QFileDialog.getSaveFileName(self, 'Save to PDF', 'c:\\', "*.pdf") if filename != ('', ''): if os.path.exists(filename[0]): try: infile = PdfFileReader(filename[0], 'rb') except: error = QMessageBox() error.setIcon(QMessageBox.Warning) error.setStandardButtons(QMessageBox.Ok) error.setText( "File could not be written to. If the file is currently open, try closing it" ) error.exec_() return if infile.getNumPages() == 0: print("HERE!") doc = QTextDocument() doc.print(printer) printer = QPrinter() printer.setPageSize(QPrinter.A4) printer.setOutputFormat(QPrinter.PdfFormat) printer.setOutputFileName(filename[0]) painter = QPainter() font = QFont("times") font.setPointSize(12) x = painter.begin(printer) if x == False: error = QMessageBox() error.setIcon(QMessageBox.Warning) error.setStandardButtons(QMessageBox.Ok) error.setText( "File could not be saved. If the file is currently open, try closing it" ) error.exec_() return painter.setFont(font) for i in range(0, self.symbol_tests.count()): cur_symbol_test = self.symbol_tests.widget(i) if cur_symbol_test.print_visual_context() == False: pixmap = cur_symbol_test.get_symbol() pixmap = pixmap.scaled(350, 350) painter.drawPixmap(30, 100, pixmap) painter.drawText(750, 20, cur_symbol_test.get_page()) painter.drawText( 420, 200, 350, 400, QtCore.Qt.TextWordWrap, "Context: " + cur_symbol_test.get_context()) painter.drawText(30, 600, cur_symbol_test.get_question1()) painter.drawText(30, 830, cur_symbol_test.get_question2()) cur_pen = painter.pen() line_pen = QPen() line_pen.setWidth(2) painter.setPen(line_pen) painter.drawLine(70, 656, 600, 656) painter.drawLine(70, 712, 600, 712) painter.drawLine(70, 768, 600, 768) painter.drawLine(70, 886, 600, 886) painter.drawLine(70, 942, 600, 942) painter.drawLine(70, 998, 600, 998) painter.setPen(cur_pen) else: pixmap = cur_symbol_test.get_visual_context() pixmap = pixmap.scaled(300, 300) painter.drawPixmap(200, 10, pixmap) pixmap = cur_symbol_test.get_symbol() pixmap = pixmap.scaled(250, 250) painter.drawPixmap(225, 320, pixmap) painter.drawText(750, 20, cur_symbol_test.get_page()) #painter.drawText(420, 200, 350, 400, QtCore.Qt.TextWordWrap, "Context: " + cur_symbol_test.get_context()) painter.drawText(30, 600, cur_symbol_test.get_question1()) painter.drawText(30, 830, cur_symbol_test.get_question2()) cur_pen = painter.pen() line_pen = QPen() line_pen.setWidth(2) painter.setPen(line_pen) painter.drawLine(70, 656, 600, 656) painter.drawLine(70, 712, 600, 712) painter.drawLine(70, 768, 600, 768) painter.drawLine(70, 886, 600, 886) painter.drawLine(70, 942, 600, 942) painter.drawLine(70, 998, 600, 998) painter.setPen(cur_pen) if (i < self.symbol_tests.count() - 1): printer.newPage() painter.end() """ @modifies self.symbol_tests @effects adds all the specified symbols to their own symbol_test and adds those tests to self.symbol_tests """ def import_symbols(self): fnames = QFileDialog.getOpenFileNames(self, 'Open file', 'c:\\', "Image files (*.jpg *.png)") if fnames != ([], ''): for i in range(0, len(fnames[0])): page = self.symbol_tests.count() + 1 test = SymbolTestWidget( self, comp_questions=[self.question1, self.question2], pageNumber=page, pageTotal=page) test.set_symbol(fnames[0][i]) test.set_visual_context("blank image.png") self.symbol_tests.addWidget(test) self.symbol_tests.setCurrentIndex(page - 1) for i in range(0, self.symbol_tests.count()): self.symbol_tests.widget(i).set_numPages( self.symbol_tests.count()) """ @modifies self.symbol_tests @effects removes the current test from self.symbol_tests """ def delete_test(self): msg = QMessageBox.question(self, 'Message', "Delete test?", QMessageBox.Yes | QMessageBox.No, QMessageBox.No) if msg == QMessageBox.No: return if self.symbol_tests.currentIndex() != 0: cur_idx = self.symbol_tests.currentIndex() self.symbol_tests.setCurrentIndex(cur_idx - 1) self.symbol_tests.removeWidget(self.symbol_tests.widget(cur_idx)) for i in range(0, self.symbol_tests.count()): cur_symbol_test = self.symbol_tests.widget(i) cur_symbol_test.set_numPages(self.symbol_tests.count()) if i >= cur_idx: cur_symbol_test.set_page(i + 1) elif self.symbol_tests.currentIndex( ) == 0 and self.symbol_tests.count() > 1: self.symbol_tests.setCurrentIndex(1) self.symbol_tests.removeWidget(self.symbol_tests.widget(0)) for i in range(0, self.symbol_tests.count()): cur_symbol_test = self.symbol_tests.widget(i) cur_symbol_test.set_numPages(self.symbol_tests.count()) cur_symbol_test.set_page(i + 1) elif self.symbol_tests.currentIndex() == 0 and self.symbol_tests.count( ) == 1: page = 1 test = SymbolTestWidget( self, comp_questions=[self.question1, self.question2], pageNumber=page, pageTotal=page) self.symbol_tests.addWidget(test) self.symbol_tests.setCurrentIndex(1) self.symbol_tests.removeWidget(self.symbol_tests.widget(0)) """ @modifies self.symbol_tests @effects decreases the current symbol_test's index in self.symbol_tests by 1, moves it down 1 postion """ def move_test_left(self): cur_idx = self.symbol_tests.currentIndex() if cur_idx > 0: cur_widget = self.symbol_tests.widget(cur_idx) prev_widget = self.symbol_tests.widget(cur_idx - 1) cur_widget.set_page(int(cur_widget.get_page()) - 1) prev_widget.set_page(int(prev_widget.get_page()) + 1) self.symbol_tests.removeWidget(prev_widget) self.symbol_tests.insertWidget( self.symbol_tests.indexOf(cur_widget) + 1, prev_widget) """ @modifies self.symbol_tests @effects increases the current symbol_test's index in self.symbol_tests by 1, moves it up 1 postion """ def move_test_right(self): cur_idx = self.symbol_tests.currentIndex() if cur_idx < self.symbol_tests.count() - 1: cur_widget = self.symbol_tests.widget(cur_idx) next_widget = self.symbol_tests.widget(cur_idx + 1) cur_widget.set_page(int(cur_widget.get_page()) + 1) next_widget.set_page(int(next_widget.get_page()) - 1) self.symbol_tests.removeWidget(next_widget) self.symbol_tests.insertWidget( self.symbol_tests.indexOf(cur_widget), next_widget) """ @modifies self.symbol_tests @effects moves the current symbol_test to a specified position in self.symbol_tests """ def move_to_page(self): text = QInputDialog.getText(self, "Move Page", "Move current page to: ", QLineEdit.Normal) if text[1] != False: try: x = int(text[0]) if x <= 0 or x > self.symbol_tests.count(): error = QMessageBox() error.setIcon(QMessageBox.Warning) error.setStandardButtons(QMessageBox.Ok) error.setText("Page must be between 1 and " + str(self.symbol_tests.count())) error.exec_() return cur_idx = self.symbol_tests.currentIndex() cur_widget = self.symbol_tests.widget(cur_idx) cur_widget.set_page(x - 1) self.symbol_tests.removeWidget(cur_widget) self.symbol_tests.insertWidget(x - 1, cur_widget) self.symbol_tests.setCurrentIndex(x - 1) for i in range(0, self.symbol_tests.count()): loop_widget = self.symbol_tests.widget(i) loop_widget.set_page(i + 1) except ValueError: error = QMessageBox() error.setIcon(QMessageBox.Warning) error.setStandardButtons(QMessageBox.Ok) error.setText("Can only enter an integer between 1 and " + str(self.symbol_tests.count())) error.exec_() """ @modifies self.symbol_tests @effects sets the surrent symbol_test equal to the one at the specified page """ def go_to_page(self): text = QInputDialog.getText(self, "Go to", "Go to page: ", QLineEdit.Normal) if text[1] != False: try: x = int(text[0]) if x <= 0 or x > self.symbol_tests.count(): error = QMessageBox() error.setIcon(QMessageBox.Warning) error.setStandardButtons(QMessageBox.Ok) error.setText("Page must be between 1 and " + str(self.symbol_tests.count())) error.exec_() return self.symbol_tests.setCurrentIndex(x - 1) except ValueError: error = QMessageBox() error.setIcon(QMessageBox.Warning) error.setStandardButtons(QMessageBox.Ok) error.setText("Can only enter an integer between 1 and " + str(self.symbol_tests.count())) error.exec_() """ @modifies self.question1, self.question2 @effects changes the default questions """ def change_questions(self): text = QInputDialog.getText(self, "Question 1 Input", "Question 1: ", QLineEdit.Normal, self.question1) if str(text[0]) != '': self.question1 = str(text[0]) text = QInputDialog.getText(self, "Question 2 Input", "Question 2: ", QLineEdit.Normal, self.question2) if str(text[0]) != '': self.question2 = str(text[0]) def delete_pages(self): text = QInputDialog.getText( self, "Delete pages", "Enter page numbers seperated by a comma:", QLineEdit.Normal) error = QMessageBox() error.setIcon(QMessageBox.Warning) error.setStandardButtons(QMessageBox.Ok) if text[1] != False: pages = text[0].split(",") del_widgets = [] for page in pages: try: x = int(page) except: error.setText("All page numbers must be integers") error.exec_() return if int(page) > self.symbol_tests.count() or int(page) < 1: error.setText("Page " + page + " does not exist") error.exec_() return del_widgets.append(self.symbol_tests.widget(int(page) - 1)) confirm = QMessageBox.question( self, 'Message', "Really delete pages: " + text[0] + "?", QMessageBox.Yes | QMessageBox.No, QMessageBox.No) for del_widget in del_widgets: del_idx = self.symbol_tests.indexOf(del_widget) if del_idx != self.symbol_tests.currentIndex(): self.symbol_tests.removeWidget(del_widget) else: #Removing the only test if self.symbol_tests.count() == 1: self.add_blank_test() self.symbol_tests.removeWidget(del_widget) self.symbol_tests.currentWidget().set_page(1) self.symbol_tests.currentWidget().set_numPages(1) return else: if del_idx == self.symbol_tests.count() - 1: self.symbol_tests.setCurrentIndex(del_idx - 1) self.symbol_tests.removeWidget(del_widget) else: self.symbol_tests.setCurrentIndex(del_idx + 1) self.symbol_tests.removeWidget(del_widget) count = 1 for i in range(0, self.symbol_tests.count()): cur_widget = self.symbol_tests.widget(i) cur_widget.set_page(count) cur_widget.set_numPages(self.symbol_tests.count()) count += 1
class DAT_GUI(QtWidgets.QMainWindow): def __init__(self, parent=None): super().__init__(parent) self.initUI() def initUI(self): screen = QApplication.desktop().screenNumber( QApplication.desktop().cursor().pos()) centerPoint = QApplication.desktop().screenGeometry(screen).center() # should fit in 1024x768 (old computer screens) window_width = 900 window_height = 700 self.setGeometry( QtCore.QRect( centerPoint.x() - int(window_width / 2), centerPoint.y() - int(window_height / 2), window_width, window_height)) # should I rather center on the screen # zoom parameters self.scale = 1.0 self.min_scaling_factor = 0.1 self.max_scaling_factor = 20 self.zoom_increment = 0.05 self.setWindowTitle(__NAME__ + ' v' + str(__VERSION__)) self.paint = Createpaintwidget() # initiate 2D image for 2D display self.img = None self.list = QListWidget( self) # a list that contains files to read or play with self.list.setSelectionMode(QAbstractItemView.ExtendedSelection) self.list.selectionModel().selectionChanged.connect( self.selectionChanged) # connect it to sel change self.scrollArea = QScrollArea() self.scrollArea.setBackgroundRole(QPalette.Dark) self.scrollArea.setWidget(self.paint) self.paint.scrollArea = self.scrollArea self.table_widget = QWidget() table_widget_layout = QVBoxLayout() # Initialize tab screen self.tabs = QTabWidget(self) self.tab1 = QWidget() self.tab2 = QWidget() self.tab3 = QWidget() # Add tabs self.tabs.addTab(self.tab1, "Mask neuron") self.tabs.addTab(self.tab2, "Mask cell body") self.tabs.addTab(self.tab3, "Segment dendrites") # listen to tab changes self.tabs.currentChanged.connect(self._onTabChange) # Create first tab self.tab1.layout = QGridLayout() self.tab1.layout.setAlignment(Qt.AlignTop) self.tab1.layout.setHorizontalSpacing(3) self.tab1.layout.setVerticalSpacing(3) self.tab1.layout.setContentsMargins(0, 0, 0, 0) label1_tab1 = QLabel('Step 1:') self.tab1.layout.addWidget(label1_tab1, 0, 0) self.local_threshold = QPushButton("Local threshold") self.local_threshold.clicked.connect(self.run_threshold_neuron) self.tab1.layout.addWidget(self.local_threshold, 0, 1) self.global_threshold = QPushButton("Global threshold") self.global_threshold.clicked.connect(self.run_threshold_neuron) self.tab1.layout.addWidget(self.global_threshold, 0, 2) self.local_n_global_threshold = QPushButton( "Local AND Global threshold") self.local_n_global_threshold.clicked.connect( self.run_threshold_neuron) self.tab1.layout.addWidget(self.local_n_global_threshold, 0, 3) self.extra_value_for_threshold = QSpinBox() self.extra_value_for_threshold.setSingleStep(1) self.extra_value_for_threshold.setRange(0, 1_000_000) self.extra_value_for_threshold.setValue(6) self.tab1.layout.addWidget(self.extra_value_for_threshold, 0, 4) self.threshold_method = QComboBox() self.threshold_method.addItem('Mean') self.threshold_method.addItem('Median') self.tab1.layout.addWidget(self.threshold_method, 0, 5) label2_tab1 = QLabel('Step 2 (optional):') self.tab1.layout.addWidget(label2_tab1, 1, 0) self.remove_pixel_blobs_smaller_or_equal = QPushButton( "Remove pixel blobs smaller or equal to") self.remove_pixel_blobs_smaller_or_equal.clicked.connect( self.remove_blobs) self.tab1.layout.addWidget(self.remove_pixel_blobs_smaller_or_equal, 1, 1) self.remove_blobs_size = QSpinBox() self.remove_blobs_size.setSingleStep(1) self.remove_blobs_size.setRange(0, 1_000_000) self.remove_blobs_size.setValue(1) self.tab1.layout.addWidget(self.remove_blobs_size, 1, 2) label3_tab1 = QLabel('Step 3: Save') self.tab1.layout.addWidget(label3_tab1, 2, 0) self.tab1.setLayout(self.tab1.layout) self.tab2.layout = QGridLayout() self.tab2.layout.setAlignment(Qt.AlignTop) self.tab2.layout.setHorizontalSpacing(3) self.tab2.layout.setVerticalSpacing(3) self.tab2.layout.setContentsMargins(0, 0, 0, 0) label1_tab2 = QLabel('Step 1:') self.tab2.layout.addWidget(label1_tab2, 0, 0) self.detect_cell_body = QPushButton("Detect cell body") self.detect_cell_body.clicked.connect(self.detect_neuronal_cell_body) self.tab2.layout.addWidget(self.detect_cell_body, 0, 1) self.extraCutOff_cell_body = QSpinBox() self.extraCutOff_cell_body.setSingleStep(1) self.extraCutOff_cell_body.setRange(0, 1_000_000) self.extraCutOff_cell_body.setValue(5) self.tab2.layout.addWidget(self.extraCutOff_cell_body, 0, 2) erosion_label = QLabel('erosion rounds') self.tab2.layout.addWidget(erosion_label, 0, 3) self.nb_erosion_cellbody = QSpinBox() self.nb_erosion_cellbody.setSingleStep(1) self.nb_erosion_cellbody.setRange(0, 1_000_000) self.nb_erosion_cellbody.setValue(2) self.tab2.layout.addWidget(self.nb_erosion_cellbody, 0, 4) min_object_size_label = QLabel('minimum object size') self.tab2.layout.addWidget(min_object_size_label, 0, 5) self.min_obj_size_px = QSpinBox() self.min_obj_size_px.setSingleStep(1) self.min_obj_size_px.setRange(0, 1_000_000) self.min_obj_size_px.setValue(600) self.tab2.layout.addWidget(self.min_obj_size_px, 0, 6) fill_label = QLabel('fill up to') self.tab2.layout.addWidget(fill_label, 0, 7) self.fill_holes_up_to = QSpinBox() self.fill_holes_up_to.setSingleStep(1) self.fill_holes_up_to.setRange(0, 1_000_000) self.fill_holes_up_to.setValue(600) self.tab2.layout.addWidget(self.fill_holes_up_to, 0, 8) nb_dilation_cell_body_label = QLabel('nb dilation cell body') self.tab2.layout.addWidget(nb_dilation_cell_body_label, 0, 9) self.nb_dilation_cellbody = QSpinBox() self.nb_dilation_cellbody.setSingleStep(1) self.nb_dilation_cellbody.setRange(0, 1_000_000) self.nb_dilation_cellbody.setValue(2) self.tab2.layout.addWidget(self.nb_dilation_cellbody, 0, 10) label2_tab2 = QLabel('Step 2: Save') self.tab2.layout.addWidget(label2_tab2, 6, 0) self.tab2.setLayout(self.tab2.layout) self.tab3.layout = QGridLayout() self.tab3.layout.setAlignment(Qt.AlignTop) self.tab3.layout.setHorizontalSpacing(3) self.tab3.layout.setVerticalSpacing(3) self.tab3.layout.setContentsMargins(0, 0, 0, 0) label1_tab3 = QLabel('Step 1:') self.tab3.layout.addWidget(label1_tab3, 0, 0) self.wshed = QPushButton("Watershed") self.wshed.clicked.connect(self.watershed_segment_the_neuron) self.tab3.layout.addWidget(self.wshed, 0, 1) self.whsed_big_blur = QDoubleSpinBox() self.whsed_big_blur.setSingleStep(0.1) self.whsed_big_blur.setRange(0, 100) self.whsed_big_blur.setValue(2.1) self.tab3.layout.addWidget(self.whsed_big_blur, 0, 2) self.whsed_small_blur = QDoubleSpinBox() self.whsed_small_blur.setSingleStep(0.1) self.whsed_small_blur.setRange(0, 100) self.whsed_small_blur.setValue(1.4) self.tab3.layout.addWidget(self.whsed_small_blur, 0, 3) self.wshed_rm_small_cells = QSpinBox() self.wshed_rm_small_cells.setSingleStep(1) self.wshed_rm_small_cells.setRange(0, 1_000_000) self.wshed_rm_small_cells.setValue(10) self.tab3.layout.addWidget(self.wshed_rm_small_cells, 0, 4) self.jSpinner11 = QSpinBox() self.jSpinner11.setSingleStep(1) self.jSpinner11.setRange(0, 1_000_000) self.jSpinner11.setValue(10) self.tab3.layout.addWidget(self.jSpinner11, 0, 5) label1_bis_tab3 = QLabel('Alternative Step 1:') self.tab3.layout.addWidget(label1_bis_tab3, 1, 0) self.skel = QPushButton("Skeletonize") self.skel.clicked.connect(self.skeletonize_mask) self.tab3.layout.addWidget(self.skel, 1, 1) label2_tab3 = QLabel('Step 2:') self.tab3.layout.addWidget(label2_tab3, 2, 0) self.apply_cell_body = QPushButton("Apply cell body") self.apply_cell_body.clicked.connect( self.apply_cell_body_to_skeletonized_mask) self.tab3.layout.addWidget(self.apply_cell_body, 2, 1) label3_tab3 = QLabel('Step 3 (Optional):') self.tab3.layout.addWidget(label3_tab3, 3, 0) self.prune = QPushButton("Prune") self.prune.clicked.connect(self.prune_dendrites) self.tab3.layout.addWidget(self.prune, 3, 1) self.prune_length = QSpinBox() self.prune_length.setSingleStep(1) self.prune_length.setRange(0, 1_000_000) self.prune_length.setValue(3) self.tab3.layout.addWidget(self.prune_length, 3, 2) label4_tab3 = QLabel('Step 4 (Optional):') self.tab3.layout.addWidget(label4_tab3, 4, 0) self.find_neurons = QPushButton("Find neurons") self.find_neurons.clicked.connect(self.find_neurons_in_mask) self.tab3.layout.addWidget(self.find_neurons, 4, 1) self.find_neurons_min_size = QSpinBox() self.find_neurons_min_size.setSingleStep(1) self.find_neurons_min_size.setRange(0, 1_000_000) self.find_neurons_min_size.setValue(45) self.tab3.layout.addWidget(self.find_neurons_min_size, 4, 2) self.prune_unconnected_segments = QPushButton( "Prune unconnected segments (run 'Find neurons' first)") self.prune_unconnected_segments.clicked.connect( self.prune_neuron_unconnected_segments) self.tab3.layout.addWidget(self.prune_unconnected_segments, 4, 3) label6_tab3 = QLabel('Step 5: Save') self.tab3.layout.addWidget(label6_tab3, 5, 0) label5_tab3 = QLabel('Step 6:') self.tab3.layout.addWidget(label5_tab3, 6, 0) self.create_n_save_bonds = QPushButton("Segment dendrites") self.create_n_save_bonds.clicked.connect(self.save_segmented_bonds) self.tab3.layout.addWidget(self.create_n_save_bonds, 6, 1) self.tab3.setLayout(self.tab3.layout) # Add tabs to widget table_widget_layout.addWidget(self.tabs) self.table_widget.setLayout(table_widget_layout) self.Stack = QStackedWidget(self) self.Stack.addWidget(self.scrollArea) # create a grid that will contain all the GUI interface self.grid = QGridLayout() self.grid.addWidget(self.Stack, 0, 0) self.grid.addWidget(self.list, 0, 1) # The first parameter of the rowStretch method is the row number, the second is the stretch factor. So you need two calls to rowStretch, like this: --> below the first row is occupying 80% and the second 20% self.grid.setRowStretch(0, 75) self.grid.setRowStretch(2, 25) # first col 75% second col 25% of total width self.grid.setColumnStretch(0, 75) self.grid.setColumnStretch(1, 25) # void QGridLayout::addLayout(QLayout * layout, int row, int column, int rowSpan, int columnSpan, Qt::Alignment alignment = 0) self.grid.addWidget(self.table_widget, 2, 0, 1, 2) # spans over one row and 2 columns # BEGIN TOOLBAR # pen spin box and connect self.penSize = QSpinBox() self.penSize.setSingleStep(1) self.penSize.setRange(1, 256) self.penSize.setValue(3) self.penSize.valueChanged.connect(self.penSizechange) self.channels = QComboBox() self.channels.addItem("merge") self.channels.currentIndexChanged.connect(self.channelChange) tb_drawing_pane = QToolBar() save_button = QToolButton() save_button.setText("Save") save_button.clicked.connect(self.save_current_mask) tb_drawing_pane.addWidget(save_button) tb_drawing_pane.addWidget(QLabel("Channels")) tb_drawing_pane.addWidget(self.channels) # tb.addAction("Save") # tb_drawing_pane.addWidget(QLabel("Pen size")) tb_drawing_pane.addWidget(self.penSize) self.grid.addWidget(tb_drawing_pane, 1, 0) # END toolbar # toolbar for the list tb_list = QToolBar() del_button = QToolButton() del_button.setText("Delete selection from list") del_button.clicked.connect(self.delete_from_list) tb_list.addWidget(del_button) self.grid.addWidget(tb_list, 1, 1) # self.setCentralWidget(self.scrollArea) self.setCentralWidget(QFrame()) self.centralWidget().setLayout(self.grid) # self.statusBar().showMessage('Ready') statusBar = self.statusBar( ) # sets an empty status bar --> then can add messages in it self.paint.statusBar = statusBar # add progress bar to status bar self.progress = QProgressBar(self) self.progress.setGeometry(200, 80, 250, 20) statusBar.addWidget(self.progress) # Set up menu bar self.mainMenu = self.menuBar() self.zoomInAct = QAction( "Zoom &In (25%)", self, # shortcut="Ctrl++", enabled=True, triggered=self.zoomIn) self.zoomOutAct = QAction( "Zoom &Out (25%)", self, # shortcut="Ctrl+-", enabled=True, triggered=self.zoomOut) self.normalSizeAct = QAction( "&Normal Size", self, # shortcut="Ctrl+S", enabled=True, triggered=self.defaultSize) self.viewMenu = QMenu("&View", self) self.viewMenu.addAction(self.zoomInAct) self.viewMenu.addAction(self.zoomOutAct) self.viewMenu.addAction(self.normalSizeAct) self.menuBar().addMenu(self.viewMenu) self.setMenuBar(self.mainMenu) # set drawing window fullscreen fullScreenShortcut = QtWidgets.QShortcut( QtGui.QKeySequence(QtCore.Qt.Key_F), self) fullScreenShortcut.activated.connect(self.fullScreen) fullScreenShortcut.setContext(QtCore.Qt.ApplicationShortcut) escapeShortcut = QtWidgets.QShortcut( QtGui.QKeySequence(QtCore.Qt.Key_Escape), self) escapeShortcut.activated.connect(self.escape) escapeShortcut.setContext(QtCore.Qt.ApplicationShortcut) # Show/Hide the mask escapeShortcut = QtWidgets.QShortcut( QtGui.QKeySequence(QtCore.Qt.Key_H), self) escapeShortcut.activated.connect(self.showHideMask) escapeShortcut.setContext(QtCore.Qt.ApplicationShortcut) zoomPlus = QtWidgets.QShortcut("Ctrl+Shift+=", self) zoomPlus.activated.connect(self.zoomIn) zoomPlus.setContext(QtCore.Qt.ApplicationShortcut) zoomPlus2 = QtWidgets.QShortcut("Ctrl++", self) zoomPlus2.activated.connect(self.zoomIn) zoomPlus2.setContext(QtCore.Qt.ApplicationShortcut) zoomMinus = QtWidgets.QShortcut("Ctrl+Shift+-", self) zoomMinus.activated.connect(self.zoomOut) zoomMinus.setContext(QtCore.Qt.ApplicationShortcut) zoomMinus2 = QtWidgets.QShortcut("Ctrl+-", self) zoomMinus2.activated.connect(self.zoomOut) zoomMinus2.setContext(QtCore.Qt.ApplicationShortcut) spaceShortcut = QtWidgets.QShortcut( QtGui.QKeySequence(QtCore.Qt.Key_Space), self) spaceShortcut.activated.connect(self.nextFrame) spaceShortcut.setContext(QtCore.Qt.ApplicationShortcut) backspaceShortcut = QtWidgets.QShortcut( QtGui.QKeySequence(QtCore.Qt.Key_Backspace), self) backspaceShortcut.activated.connect(self.prevFrame) backspaceShortcut.setContext(QtCore.Qt.ApplicationShortcut) # connect enter keys to edit dendrites enterShortcut = QtWidgets.QShortcut( QtGui.QKeySequence(QtCore.Qt.Key_Return), self) enterShortcut.activated.connect(self.runSkel) enterShortcut.setContext(QtCore.Qt.ApplicationShortcut) enter2Shortcut = QtWidgets.QShortcut( QtGui.QKeySequence(QtCore.Qt.Key_Enter), self) enter2Shortcut.activated.connect(self.runSkel) enter2Shortcut.setContext(QtCore.Qt.ApplicationShortcut) #Qt.Key_Enter is the Enter located on the keypad: #Qt::Key_Return 0x01000004 #Qt::Key_Enter 0x01000005 Typically located on the keypad. self.setAcceptDrops(True) # KEEP IMPORTANT def __get_mask_img_from_overlay(self): if self.paint.imageDraw: channels_count = 4 s = self.paint.imageDraw.bits().asstring( self.img.shape[0] * self.img.shape[1] * channels_count) arr = np.frombuffer(s, dtype=np.uint8).reshape( (self.img.shape[0], self.img.shape[1], channels_count)) return Img(arr[..., 2].copy(), dimensions='hw') else: return None def __get_output_folder(self): selected_items = self.list.selectedItems() if selected_items: filename = selected_items[0].toolTip() filename0_without_ext = os.path.splitext(filename)[0] return filename0_without_ext else: return None def delete_from_list(self): list_items = self.list.selectedItems() # empty list --> nothing to do if not list_items: return for item in list_items: self.list.takeItem(self.list.row(item)) def save_current_mask(self): output_folder = self.__get_output_folder() if output_folder is None: logger.error('No image is selected --> nothing to save') return mask = self.__get_mask_img_from_overlay() if mask is None: logger.error('No mask/overlay detected --> nothing to save') return if self.tabs.currentIndex() == 0: print('saving', os.path.join(output_folder, 'mask.tif')) mask.save(os.path.join(output_folder, 'mask.tif')) elif self.tabs.currentIndex() == 1: print('saving', os.path.join(output_folder, 'cellBodyMask.tif')) mask.save(os.path.join(output_folder, 'cellBodyMask.tif')) else: print('saving', os.path.join(output_folder, 'handCorrection.tif')) mask.save(os.path.join(output_folder, 'handCorrection.tif')) def detect_neuronal_cell_body(self): try: # get image and detect cell body mask = detect_cell_body( self.img, fillHoles=self.fill_holes_up_to.value(), denoise=self.min_obj_size_px.value(), nbOfErosions=self.nb_erosion_cellbody.value(), nbOfDilatations=self.nb_dilation_cellbody.value(), extraCutOff=self.extraCutOff_cell_body.value(), channel=self.channels.currentText()) if mask is not None: self.paint.imageDraw = Img(self.createRGBA(mask), dimensions='hwc').getQimage() self.paint.update() else: logger.error('Cell body could not be detected') except: traceback.print_exc() def __get_neuronal_mask(self, warn=True): output_folder = self.__get_output_folder() if output_folder is None: if warn: logger.error('No image selected --> nothing to do') return None if os.path.exists(os.path.join(output_folder, 'mask.tif')): # NB should I check the nb of channels --> no I don't want to handle externally created files and want people to rely fully on my stuff that has return Img(os.path.join(output_folder, 'mask.tif')) else: if warn: logger.error( 'Neuronal mask not found --> Please create one in the "Mask neuron" tab first' ) return None def __get_corrected_mask(self, warn=True): output_folder = self.__get_output_folder() if output_folder is None: if warn: logger.error('No image selected --> nothing to do') return None if os.path.exists(os.path.join(output_folder, 'handCorrection.tif')): return Img(os.path.join(output_folder, 'handCorrection.tif')) elif os.path.exists(os.path.join(output_folder, 'mask.tif')) and not warn: return Img(os.path.join(output_folder, 'mask.tif')) return None def __get_cellbody_mask(self, warn=True): output_folder = self.__get_output_folder() if output_folder is None: if warn: logger.error('No image selected --> nothing to do') return None if os.path.exists(os.path.join(output_folder, 'cellBodyMask.tif')): # NB should I check the nb of channels --> no I don't want to handle externally created files and want people to rely fully on my stuff that has return Img(os.path.join(output_folder, 'cellBodyMask.tif')) else: if warn: logger.error( 'Cell body mask not found --> Please create one in the "Mask cell body" tab first' ) return None # seems ok for now def watershed_segment_the_neuron(self): try: # get raw image and segment it using the watershed algorithm # make it load the neuronal mask neuronal_mask = self.__get_neuronal_mask() if neuronal_mask is None: return # TODO should I add autoskel or not mask = watershed_segment_neuron( self.img, neuronal_mask, fillSize=self.jSpinner11.value(), autoSkel=True, first_blur=self.whsed_big_blur.value(), second_blur=self.whsed_small_blur.value(), min_size=self.wshed_rm_small_cells.value(), channel=self.channels.currentText()) if mask is not None: self.paint.imageDraw = Img(self.createRGBA(mask), dimensions='hwc').getQimage() self.paint.update() else: logger.error( 'Something went wrong, the neuron could not be segmented, sorry...' ) except: traceback.print_exc() def save_segmented_bonds(self): output_folder = self.__get_output_folder() if output_folder is None: logger.error('No image is selected --> nothing to save') return # get mask the find neurons mask = self.__get_mask_img_from_overlay() if mask is None: logger.error('No mask/overlay detected --> nothing to do') return mask = detect_cell_bonds(mask) if mask is None: logger.error( 'Could not find dendrites, are you sure a mask is overlayed over the neuron' ) return # code for conversion of 24 bits numpy array to an RGB one --> keep and store in Img at some point cause useful # convert 24 bits array to RGB RGB_mask = np.zeros(shape=(*mask.shape, 3), dtype=np.uint8) RGB_mask[..., 2] = mask & 255 RGB_mask[..., 1] = (mask >> 8) & 255 RGB_mask[..., 0] = (mask >> 16) & 255 Img(RGB_mask, dimensions='hwc').save(os.path.join(output_folder, 'bonds.tif')) def prune_neuron_unconnected_segments(self): # get mask the find neurons mask = self.__get_mask_img_from_overlay() if mask is None: logger.error('No mask/overlay detected --> nothing to do') return mask = find_neurons( mask, neuron_minimum_size_threshold=self.find_neurons_min_size.value(), return_unconnected=True) if mask is None: logger.error( 'Could not find neurons, are you sure a mask is overlayed over the neuron' ) return self.paint.imageDraw = Img(self.createRGBA(mask), dimensions='hwc').getQimage() self.paint.update() def apply_cell_body_to_skeletonized_mask(self): mask = self.__get_mask_img_from_overlay() if mask is None: logger.error('No mask/overlay detected --> nothing to do') return cell_body_mask = self.__get_cellbody_mask() if cell_body_mask is None: return cell_body_outline = get_cell_body_outline(cell_body_mask, mask) if cell_body_outline is None: logger.error( 'Error could not add cell body outline to the neuronal mask...' ) return self.paint.imageDraw = Img(self.createRGBA(cell_body_outline), dimensions='hwc').getQimage() self.paint.update() def find_neurons_in_mask(self): # get mask the find neurons mask = self.__get_mask_img_from_overlay() if mask is None: logger.error('No mask/overlay detected --> nothing to do') return mask_copy = mask.copy() mask = find_neurons( mask, neuron_minimum_size_threshold=self.find_neurons_min_size.value()) if mask is None: logger.error( 'Could not find neurons, are you sure a mask is overlayed over the neuron' ) return # we set the red channel, the blue one, the alpha transparency (channel 4) and finally we only allow alpha channel in the two masks regions to keep the rest of the stuff final_overlay = np.zeros(shape=(*mask_copy.shape, 4), dtype=np.uint8) final_overlay[..., 0] = np.logical_xor(mask, mask_copy).astype( np.uint8) * 255 # blue channel final_overlay[mask == 0, 0] = 0 final_overlay[..., 1] = final_overlay[ ..., 0] # green channel # copy the channel to make the stuff appear cyan final_overlay[..., 2] = mask_copy # red channel final_overlay[np.logical_or(mask, mask_copy) != 0, 3] = 255 # --> need set alpha transparency of the image self.paint.imageDraw = Img(final_overlay, dimensions='hwc').getQimage() self.paint.update() def prune_dendrites(self): prune_lgth = self.prune_length.value() if prune_lgth <= 0: logger.info('prune length is 0 --> nothing to do') return # get the mask from displayed image mask = self.__get_mask_img_from_overlay() if mask is None: logger.error('No mask/overlay detected --> nothing to do') return # see how to get the stuff ???? mask = prune_dendrites(mask, prune_below=prune_lgth) if mask is None: logger.error( 'Could not prune dendrites, are you sure there is a mask ovrlayed on the neuron' ) return self.paint.imageDraw = Img(self.createRGBA(mask), dimensions='hwc').getQimage() self.paint.update() def skeletonize_mask(self): # get mask then skeletonize it then return it --> see exactly try: # get raw image and segment it using the skeletonize algorithm # make it load the neuronal mask neuronal_mask = self.__get_neuronal_mask() if neuronal_mask is None: return mask = skel_segment_neuronal_mask(neuronal_mask) if mask is not None: self.paint.imageDraw = Img(self.createRGBA(mask), dimensions='hwc').getQimage() self.paint.update() else: logger.error( 'Something went wrong, the neuron could not be sekeletonized, sorry...' ) except: traceback.print_exc() def _onTabChange(self): # if tab is changed --> do stuff # load files or warn... if self.tabs.currentIndex() == 0: mask = self.__get_neuronal_mask(warn=False) if mask is not None: self.paint.imageDraw = Img(self.createRGBA(mask), dimensions='hwc').getQimage() self.paint.update() elif self.tabs.currentIndex() == 1: mask = self.__get_cellbody_mask(warn=False) if mask is not None: self.paint.imageDraw = Img(self.createRGBA(mask), dimensions='hwc').getQimage() self.paint.update() elif self.tabs.currentIndex() == 2: mask = self.__get_corrected_mask(warn=False) if mask is not None: self.paint.imageDraw = Img(self.createRGBA(mask), dimensions='hwc').getQimage() self.paint.update() def run_threshold_neuron(self): try: local_or_global = 'global' if self.sender() == self.local_threshold: local_or_global = 'local' elif self.sender() == self.local_n_global_threshold: local_or_global = 'local+global' mask = threshold_neuron( self.img, mode=local_or_global, blur_method=self.threshold_method.currentText(), spin_value=self.extra_value_for_threshold.value(), channel=self.channels.currentText()) if mask is not None: self.paint.imageDraw = Img(self.createRGBA(mask), dimensions='hwc').getQimage() self.paint.update() except: traceback.print_exc() def channelChange(self, i): if self.Stack.currentIndex() == 0: if i == 0: self.paint.setImage(self.img) else: channel_img = self.img.imCopy(c=i - 1) self.paint.setImage(channel_img) self.paint.update() def penSizechange(self): self.paint.brushSize = self.penSize.value() def selectionChanged(self): self.paint.maskVisible = True selected_items = self.list.selectedItems() if selected_items: start = timer() if self.img is not None: # make sure we don't load the image twice if selected_items[0].toolTip() != self.img.metadata['path']: self.img = Img(selected_items[0].toolTip()) logger.debug("took " + str(timer() - start) + " secs to load image") else: logger.debug("image already loaded --> ignoring") else: self.img = Img(selected_items[0].toolTip()) logger.debug("took " + str(timer() - start) + " secs to load image") if self.img is not None: selection = self.channels.currentIndex() self.channels.disconnect() self.channels.clear() comboData = ['merge'] if self.img.has_c(): for i in range(self.img.get_dimension('c')): comboData.append(str(i)) logger.debug('channels found ' + str(comboData)) self.channels.addItems(comboData) if selection != -1 and selection < self.channels.count(): self.channels.setCurrentIndex(selection) else: self.channels.setCurrentIndex(0) self.channels.currentIndexChanged.connect(self.channelChange) if selected_items: self.statusBar().showMessage('Loading ' + selected_items[0].toolTip()) selection = self.channels.currentIndex() if selection == 0: self.paint.setImage(self.img) else: self.paint.setImage(self.img.imCopy(c=selection - 1)) self.scaleImage(0) self.update() self.paint.update() if self.list.currentItem() and self.list.currentItem().icon( ).isNull(): logger.debug('Updating icon') icon = QIcon(QPixmap.fromImage(self.paint.image)) pixmap = icon.pixmap(24, 24) icon = QIcon(pixmap) self.list.currentItem().setIcon(icon) else: logger.debug("Empty selection") self.paint.image = None self.scaleImage(0) self.update() self.paint.update() self.img = None # try update also the masks if they are available try: self._onTabChange() except: pass def clearlayout(self, layout): for i in reversed(range(layout.count())): layout.itemAt(i).widget().setParent(None) def showHideMask(self): self.paint.maskVisible = not self.paint.maskVisible self.paint.update() def escape(self): if self.Stack.isFullScreen(): self.fullScreen() def fullScreen(self): if not self.Stack.isFullScreen(): self.Stack.setWindowFlags( QtCore.Qt.Window | QtCore.Qt.CustomizeWindowHint | # QtCore.Qt.WindowTitleHint | # QtCore.Qt.WindowCloseButtonHint | QtCore.Qt.WindowStaysOnTopHint) self.Stack.showFullScreen() else: self.Stack.setWindowFlags(QtCore.Qt.Widget) self.grid.addWidget(self.Stack, 0, 0) # dirty hack to make it repaint properly --> obviously not all lines below are required but some are --> need test, the last line is key though self.grid.update() self.Stack.update() self.Stack.show() self.centralWidget().setLayout(self.grid) self.centralWidget().update() self.update() self.show() self.repaint() self.Stack.update() self.Stack.repaint() self.centralWidget().repaint() def nextFrame(self): idx = self.list.model().index(self.list.currentRow() + 1, 0) if idx.isValid(): self.list.selectionModel().setCurrentIndex( idx, QItemSelectionModel.ClearAndSelect) # SelectCurrent def remove_blobs(self): blob_size = self.remove_blobs_size.value() if blob_size <= 0: logger.info('blob size is 0 --> nothing to do') return # get the mask from displayed image mask = self.__get_mask_img_from_overlay() if mask is None: logger.error('No mask/overlay detected --> nothing to save') return mask = remove_small_objects(mask.astype(np.bool), min_size=blob_size, connectivity=2, in_place=False) # then place back pixels in the mask # now set the mask back # plt.imshow(mask) # plt.show() self.paint.imageDraw = Img(self.createRGBA(mask), dimensions='hwc').getQimage() self.paint.update() def runSkel(self): # only allow that for tab 3 if self.tabs.currentIndex() == 2: mask = self.__get_mask_img_from_overlay() if mask is None: logger.error('No mask/overlay detected --> nothing to do') return # just skeletonize the image mask = skel_segment_neuronal_mask( mask, fill_holes=0) # should I put it to 0 or other things ??? if mask is None: logger.error('Could not skeletonize user edited mask...') return self.paint.imageDraw = Img(self.createRGBA(mask), dimensions='hwc').getQimage() self.paint.update() def createRGBA(self, handCorrection): # use pen color to display the mask # in fact I need to put the real color RGBA = np.zeros((handCorrection.shape[0], handCorrection.shape[1], 4), dtype=np.uint8) red = self.paint.drawColor.red() green = self.paint.drawColor.green() blue = self.paint.drawColor.blue() # bug somewhere --> fix it some day --> due to bgra instead of RGBA RGBA[handCorrection != 0, 0] = blue # b RGBA[handCorrection != 0, 1] = green # g RGBA[handCorrection != 0, 2] = red # r RGBA[..., 3] = 255 # alpha --> indeed alpha RGBA[handCorrection == 0, 3] = 0 # very complex fix some day return RGBA def prevFrame(self): idx = self.list.model().index(self.list.currentRow() - 1, 0) if idx.isValid(): self.list.selectionModel().setCurrentIndex( idx, QItemSelectionModel.ClearAndSelect) def zoomIn(self): self.statusBar().showMessage('Zooming in', msecs=200) if self.Stack.currentIndex() == 0: self.scaleImage(self.zoom_increment) def zoomOut(self): self.statusBar().showMessage('Zooming out', msecs=200) if self.Stack.currentIndex() == 0: self.scaleImage(-self.zoom_increment) def defaultSize(self): self.paint.adjustSize() self.scale = 1.0 self.scaleImage(0) def scaleImage(self, factor): self.scale += factor if self.paint.image is not None: self.paint.resize(self.scale * self.paint.image.size()) else: # no image set size to 0, 0 --> scroll pane will auto adjust self.paint.resize(QSize(0, 0)) self.scale -= factor # reset zoom self.paint.scale = self.scale # self.paint.vdp.scale = self.scale self.zoomInAct.setEnabled(self.scale < self.max_scaling_factor) self.zoomOutAct.setEnabled(self.scale > self.min_scaling_factor) # allow DND def dragEnterEvent(self, event): if event.mimeData().hasUrls: event.accept() else: event.ignore() def dragMoveEvent(self, event): if event.mimeData().hasUrls: event.setDropAction(QtCore.Qt.CopyAction) event.accept() else: event.ignore() # handle DND on drop def dropEvent(self, event): if event.mimeData().hasUrls: event.setDropAction(QtCore.Qt.CopyAction) event.accept() urls = [] for url in event.mimeData().urls(): urls.append(url.toLocalFile()) for url in urls: import os item = QListWidgetItem(os.path.basename(url), self.list) item.setToolTip(url) self.list.addItem(item) else: event.ignore()
class E5SideBar(QWidget): """ Class implementing a sidebar with a widget area, that is hidden or shown, if the current tab is clicked again. """ Version = 2 North = 0 East = 1 South = 2 West = 3 def __init__(self, orientation=None, delay=200, parent=None): """ Constructor @param orientation orientation of the sidebar widget (North, East, South, West) @param delay value for the expand/shrink delay in milliseconds (integer) @param parent parent widget (QWidget) """ super(E5SideBar, self).__init__(parent) self.__tabBar = QTabBar() self.__tabBar.setDrawBase(True) self.__tabBar.setShape(QTabBar.RoundedNorth) self.__tabBar.setUsesScrollButtons(True) self.__tabBar.setDrawBase(False) self.__stackedWidget = QStackedWidget(self) self.__stackedWidget.setContentsMargins(0, 0, 0, 0) self.__autoHideButton = QToolButton() self.__autoHideButton.setCheckable(True) self.__autoHideButton.setIcon( UI.PixmapCache.getIcon("autoHideOff.png")) self.__autoHideButton.setChecked(True) self.__autoHideButton.setToolTip( self.tr("Deselect to activate automatic collapsing")) self.barLayout = QBoxLayout(QBoxLayout.LeftToRight) self.barLayout.setContentsMargins(0, 0, 0, 0) self.layout = QBoxLayout(QBoxLayout.TopToBottom) self.layout.setContentsMargins(0, 0, 0, 0) self.layout.setSpacing(0) self.barLayout.addWidget(self.__autoHideButton) self.barLayout.addWidget(self.__tabBar) self.layout.addLayout(self.barLayout) self.layout.addWidget(self.__stackedWidget) self.setLayout(self.layout) # initialize the delay timer self.__actionMethod = None self.__delayTimer = QTimer(self) self.__delayTimer.setSingleShot(True) self.__delayTimer.setInterval(delay) self.__delayTimer.timeout.connect(self.__delayedAction) self.__minimized = False self.__minSize = 0 self.__maxSize = 0 self.__bigSize = QSize() self.splitter = None self.splitterSizes = [] self.__hasFocus = False # flag storing if this widget or any child has the focus self.__autoHide = False self.__tabBar.installEventFilter(self) self.__orientation = E5SideBar.North if orientation is None: orientation = E5SideBar.North self.setOrientation(orientation) self.__tabBar.currentChanged[int].connect( self.__stackedWidget.setCurrentIndex) e5App().focusChanged.connect(self.__appFocusChanged) self.__autoHideButton.toggled[bool].connect(self.__autoHideToggled) def setSplitter(self, splitter): """ Public method to set the splitter managing the sidebar. @param splitter reference to the splitter (QSplitter) """ self.splitter = splitter self.splitter.splitterMoved.connect(self.__splitterMoved) self.splitter.setChildrenCollapsible(False) index = self.splitter.indexOf(self) self.splitter.setCollapsible(index, False) def __splitterMoved(self, pos, index): """ Private slot to react on splitter moves. @param pos new position of the splitter handle (integer) @param index index of the splitter handle (integer) """ if self.splitter: self.splitterSizes = self.splitter.sizes() def __delayedAction(self): """ Private slot to handle the firing of the delay timer. """ if self.__actionMethod is not None: self.__actionMethod() def setDelay(self, delay): """ Public method to set the delay value for the expand/shrink delay in milliseconds. @param delay value for the expand/shrink delay in milliseconds (integer) """ self.__delayTimer.setInterval(delay) def delay(self): """ Public method to get the delay value for the expand/shrink delay in milliseconds. @return value for the expand/shrink delay in milliseconds (integer) """ return self.__delayTimer.interval() def __cancelDelayTimer(self): """ Private method to cancel the current delay timer. """ self.__delayTimer.stop() self.__actionMethod = None def shrink(self): """ Public method to record a shrink request. """ self.__delayTimer.stop() self.__actionMethod = self.__shrinkIt self.__delayTimer.start() def __shrinkIt(self): """ Private method to shrink the sidebar. """ self.__minimized = True self.__bigSize = self.size() if self.__orientation in [E5SideBar.North, E5SideBar.South]: self.__minSize = self.minimumSizeHint().height() self.__maxSize = self.maximumHeight() else: self.__minSize = self.minimumSizeHint().width() self.__maxSize = self.maximumWidth() if self.splitter: self.splitterSizes = self.splitter.sizes() self.__stackedWidget.hide() if self.__orientation in [E5SideBar.North, E5SideBar.South]: self.setFixedHeight(self.__tabBar.minimumSizeHint().height()) else: self.setFixedWidth(self.__tabBar.minimumSizeHint().width()) self.__actionMethod = None def expand(self): """ Public method to record a expand request. """ self.__delayTimer.stop() self.__actionMethod = self.__expandIt self.__delayTimer.start() def __expandIt(self): """ Private method to expand the sidebar. """ self.__minimized = False self.__stackedWidget.show() self.resize(self.__bigSize) if self.__orientation in [E5SideBar.North, E5SideBar.South]: minSize = max(self.__minSize, self.minimumSizeHint().height()) self.setMinimumHeight(minSize) self.setMaximumHeight(self.__maxSize) else: minSize = max(self.__minSize, self.minimumSizeHint().width()) self.setMinimumWidth(minSize) self.setMaximumWidth(self.__maxSize) if self.splitter: self.splitter.setSizes(self.splitterSizes) self.__actionMethod = None def isMinimized(self): """ Public method to check the minimized state. @return flag indicating the minimized state (boolean) """ return self.__minimized def isAutoHiding(self): """ Public method to check, if the auto hide function is active. @return flag indicating the state of auto hiding (boolean) """ return self.__autoHide def eventFilter(self, obj, evt): """ Public method to handle some events for the tabbar. @param obj reference to the object (QObject) @param evt reference to the event object (QEvent) @return flag indicating, if the event was handled (boolean) """ if obj == self.__tabBar: if evt.type() == QEvent.MouseButtonPress: pos = evt.pos() for i in range(self.__tabBar.count()): if self.__tabBar.tabRect(i).contains(pos): break if i == self.__tabBar.currentIndex(): if self.isMinimized(): self.expand() else: self.shrink() return True elif self.isMinimized(): self.expand() elif evt.type() == QEvent.Wheel: delta = evt.angleDelta().y() if delta > 0: self.prevTab() else: self.nextTab() return True return QWidget.eventFilter(self, obj, evt) def addTab(self, widget, iconOrLabel, label=None): """ Public method to add a tab to the sidebar. @param widget reference to the widget to add (QWidget) @param iconOrLabel reference to the icon or the label text of the tab (QIcon, string) @param label the labeltext of the tab (string) (only to be used, if the second parameter is a QIcon) """ if label: index = self.__tabBar.addTab(iconOrLabel, label) self.__tabBar.setTabToolTip(index, label) else: index = self.__tabBar.addTab(iconOrLabel) self.__tabBar.setTabToolTip(index, iconOrLabel) self.__stackedWidget.addWidget(widget) if self.__orientation in [E5SideBar.North, E5SideBar.South]: self.__minSize = self.minimumSizeHint().height() else: self.__minSize = self.minimumSizeHint().width() def insertTab(self, index, widget, iconOrLabel, label=None): """ Public method to insert a tab into the sidebar. @param index the index to insert the tab at (integer) @param widget reference to the widget to insert (QWidget) @param iconOrLabel reference to the icon or the labeltext of the tab (QIcon, string) @param label the labeltext of the tab (string) (only to be used, if the second parameter is a QIcon) """ if label: index = self.__tabBar.insertTab(index, iconOrLabel, label) self.__tabBar.setTabToolTip(index, label) else: index = self.__tabBar.insertTab(index, iconOrLabel) self.__tabBar.setTabToolTip(index, iconOrLabel) self.__stackedWidget.insertWidget(index, widget) if self.__orientation in [E5SideBar.North, E5SideBar.South]: self.__minSize = self.minimumSizeHint().height() else: self.__minSize = self.minimumSizeHint().width() def removeTab(self, index): """ Public method to remove a tab. @param index the index of the tab to remove (integer) """ self.__stackedWidget.removeWidget(self.__stackedWidget.widget(index)) self.__tabBar.removeTab(index) if self.__orientation in [E5SideBar.North, E5SideBar.South]: self.__minSize = self.minimumSizeHint().height() else: self.__minSize = self.minimumSizeHint().width() def clear(self): """ Public method to remove all tabs. """ while self.count() > 0: self.removeTab(0) def prevTab(self): """ Public slot used to show the previous tab. """ ind = self.currentIndex() - 1 if ind == -1: ind = self.count() - 1 self.setCurrentIndex(ind) self.currentWidget().setFocus() def nextTab(self): """ Public slot used to show the next tab. """ ind = self.currentIndex() + 1 if ind == self.count(): ind = 0 self.setCurrentIndex(ind) self.currentWidget().setFocus() def count(self): """ Public method to get the number of tabs. @return number of tabs in the sidebar (integer) """ return self.__tabBar.count() def currentIndex(self): """ Public method to get the index of the current tab. @return index of the current tab (integer) """ return self.__stackedWidget.currentIndex() def setCurrentIndex(self, index): """ Public slot to set the current index. @param index the index to set as the current index (integer) """ self.__tabBar.setCurrentIndex(index) self.__stackedWidget.setCurrentIndex(index) if self.isMinimized(): self.expand() def currentWidget(self): """ Public method to get a reference to the current widget. @return reference to the current widget (QWidget) """ return self.__stackedWidget.currentWidget() def setCurrentWidget(self, widget): """ Public slot to set the current widget. @param widget reference to the widget to become the current widget (QWidget) """ self.__stackedWidget.setCurrentWidget(widget) self.__tabBar.setCurrentIndex(self.__stackedWidget.currentIndex()) if self.isMinimized(): self.expand() def indexOf(self, widget): """ Public method to get the index of the given widget. @param widget reference to the widget to get the index of (QWidget) @return index of the given widget (integer) """ return self.__stackedWidget.indexOf(widget) def isTabEnabled(self, index): """ Public method to check, if a tab is enabled. @param index index of the tab to check (integer) @return flag indicating the enabled state (boolean) """ return self.__tabBar.isTabEnabled(index) def setTabEnabled(self, index, enabled): """ Public method to set the enabled state of a tab. @param index index of the tab to set (integer) @param enabled enabled state to set (boolean) """ self.__tabBar.setTabEnabled(index, enabled) def orientation(self): """ Public method to get the orientation of the sidebar. @return orientation of the sidebar (North, East, South, West) """ return self.__orientation def setOrientation(self, orient): """ Public method to set the orientation of the sidebar. @param orient orientation of the sidebar (North, East, South, West) """ if orient == E5SideBar.North: self.__tabBar.setShape(QTabBar.RoundedNorth) self.__tabBar.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Preferred) self.barLayout.setDirection(QBoxLayout.LeftToRight) self.layout.setDirection(QBoxLayout.TopToBottom) self.layout.setAlignment(self.barLayout, Qt.AlignLeft) elif orient == E5SideBar.East: self.__tabBar.setShape(QTabBar.RoundedEast) self.__tabBar.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Expanding) self.barLayout.setDirection(QBoxLayout.TopToBottom) self.layout.setDirection(QBoxLayout.RightToLeft) self.layout.setAlignment(self.barLayout, Qt.AlignTop) elif orient == E5SideBar.South: self.__tabBar.setShape(QTabBar.RoundedSouth) self.__tabBar.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Preferred) self.barLayout.setDirection(QBoxLayout.LeftToRight) self.layout.setDirection(QBoxLayout.BottomToTop) self.layout.setAlignment(self.barLayout, Qt.AlignLeft) elif orient == E5SideBar.West: self.__tabBar.setShape(QTabBar.RoundedWest) self.__tabBar.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Expanding) self.barLayout.setDirection(QBoxLayout.TopToBottom) self.layout.setDirection(QBoxLayout.LeftToRight) self.layout.setAlignment(self.barLayout, Qt.AlignTop) self.__orientation = orient def tabIcon(self, index): """ Public method to get the icon of a tab. @param index index of the tab (integer) @return icon of the tab (QIcon) """ return self.__tabBar.tabIcon(index) def setTabIcon(self, index, icon): """ Public method to set the icon of a tab. @param index index of the tab (integer) @param icon icon to be set (QIcon) """ self.__tabBar.setTabIcon(index, icon) def tabText(self, index): """ Public method to get the text of a tab. @param index index of the tab (integer) @return text of the tab (string) """ return self.__tabBar.tabText(index) def setTabText(self, index, text): """ Public method to set the text of a tab. @param index index of the tab (integer) @param text text to set (string) """ self.__tabBar.setTabText(index, text) def tabToolTip(self, index): """ Public method to get the tooltip text of a tab. @param index index of the tab (integer) @return tooltip text of the tab (string) """ return self.__tabBar.tabToolTip(index) def setTabToolTip(self, index, tip): """ Public method to set the tooltip text of a tab. @param index index of the tab (integer) @param tip tooltip text to set (string) """ self.__tabBar.setTabToolTip(index, tip) def tabWhatsThis(self, index): """ Public method to get the WhatsThis text of a tab. @param index index of the tab (integer) @return WhatsThis text of the tab (string) """ return self.__tabBar.tabWhatsThis(index) def setTabWhatsThis(self, index, text): """ Public method to set the WhatsThis text of a tab. @param index index of the tab (integer) @param text WhatsThis text to set (string) """ self.__tabBar.setTabWhatsThis(index, text) def widget(self, index): """ Public method to get a reference to the widget associated with a tab. @param index index of the tab (integer) @return reference to the widget (QWidget) """ return self.__stackedWidget.widget(index) def saveState(self): """ Public method to save the state of the sidebar. @return saved state as a byte array (QByteArray) """ if len(self.splitterSizes) == 0: if self.splitter: self.splitterSizes = self.splitter.sizes() self.__bigSize = self.size() if self.__orientation in [E5SideBar.North, E5SideBar.South]: self.__minSize = self.minimumSizeHint().height() self.__maxSize = self.maximumHeight() else: self.__minSize = self.minimumSizeHint().width() self.__maxSize = self.maximumWidth() data = QByteArray() stream = QDataStream(data, QIODevice.WriteOnly) stream.setVersion(QDataStream.Qt_4_6) stream.writeUInt16(self.Version) stream.writeBool(self.__minimized) stream << self.__bigSize stream.writeUInt32(self.__minSize) stream.writeUInt32(self.__maxSize) stream.writeUInt32(len(self.splitterSizes)) for size in self.splitterSizes: stream.writeUInt32(size) stream.writeBool(self.__autoHide) return data def restoreState(self, state): """ Public method to restore the state of the sidebar. @param state byte array containing the saved state (QByteArray) @return flag indicating success (boolean) """ if state.isEmpty(): return False if self.__orientation in [E5SideBar.North, E5SideBar.South]: minSize = self.layout.minimumSize().height() maxSize = self.maximumHeight() else: minSize = self.layout.minimumSize().width() maxSize = self.maximumWidth() data = QByteArray(state) stream = QDataStream(data, QIODevice.ReadOnly) stream.setVersion(QDataStream.Qt_4_6) version = stream.readUInt16() # version minimized = stream.readBool() if minimized and not self.__minimized: self.shrink() stream >> self.__bigSize if version == 1: self.__minSize = max(stream.readUInt16(), minSize) self.__maxSize = max(stream.readUInt16(), maxSize) count = stream.readUInt16() self.splitterSizes = [] for _ in range(count): self.splitterSizes.append(stream.readUInt16()) elif version == 2: self.__minSize = max(stream.readUInt32(), minSize) self.__maxSize = max(stream.readUInt32(), maxSize) count = stream.readUInt32() self.splitterSizes = [] for _ in range(count): self.splitterSizes.append(stream.readUInt32()) self.__autoHide = stream.readBool() self.__autoHideButton.setChecked(not self.__autoHide) if not minimized: self.expand() return True ####################################################################### ## methods below implement the autohide functionality ####################################################################### def __autoHideToggled(self, checked): """ Private slot to handle the toggling of the autohide button. @param checked flag indicating the checked state of the button (boolean) """ self.__autoHide = not checked if self.__autoHide: self.__autoHideButton.setIcon( UI.PixmapCache.getIcon("autoHideOn.png")) else: self.__autoHideButton.setIcon( UI.PixmapCache.getIcon("autoHideOff.png")) def __appFocusChanged(self, old, now): """ Private slot to handle a change of the focus. @param old reference to the widget, that lost focus (QWidget or None) @param now reference to the widget having the focus (QWidget or None) """ if isinstance(now, QWidget): self.__hasFocus = self.isAncestorOf(now) if (self.__autoHide and not self.__hasFocus and not self.isMinimized()): self.shrink() elif self.__autoHide and self.__hasFocus and self.isMinimized(): self.expand() def enterEvent(self, event): """ Protected method to handle the mouse entering this widget. @param event reference to the event (QEvent) """ if self.__autoHide and self.isMinimized(): self.expand() else: self.__cancelDelayTimer() def leaveEvent(self, event): """ Protected method to handle the mouse leaving this widget. @param event reference to the event (QEvent) """ if self.__autoHide and not self.__hasFocus and not self.isMinimized(): self.shrink() else: self.__cancelDelayTimer() def shutdown(self): """ Public method to shut down the object. This method does some preparations so the object can be deleted properly. It disconnects from the focusChanged signal in order to avoid trouble later on. """ e5App().focusChanged.disconnect(self.__appFocusChanged)
class FontWindow(BaseWindow): def __init__(self, font, parent=None): super().__init__(parent) self._font = None self._infoWindow = None self._featuresWindow = None self._groupsWindow = None self._kerningWindow = None self._metricsWindow = None self.toolBar = ToolBar(self) self.toolBar.setTools( t() for t in QApplication.instance().drawingTools()) self.glyphCellView = GlyphCellView(self) self.glyphCellView.glyphActivated.connect(self.openGlyphTab) self.glyphCellView.glyphsDropped.connect(self._orderChanged) self.glyphCellView.selectionChanged.connect(self._selectionChanged) self.glyphCellView.setAcceptDrops(True) self.glyphCellView.setCellRepresentationName("TruFont.GlyphCell") self.glyphCellView.setFrameShape(self.glyphCellView.NoFrame) self.glyphCellView.setFocus() self.tabWidget = TabWidget(self) self.tabWidget.setAutoHide(True) self.tabWidget.setHeroFirstTab(True) self.tabWidget.addTab(self.tr("Font")) self.stackWidget = QStackedWidget(self) self.stackWidget.addWidget(self.glyphCellView) self.tabWidget.currentTabChanged.connect(self._tabChanged) self.tabWidget.tabRemoved.connect( lambda index: self.stackWidget.removeWidget( self.stackWidget.widget(index))) self.stackWidget.currentChanged.connect(self._widgetChanged) self.propertiesView = PropertiesView(font, self) self.propertiesView.hide() self.statusBar = StatusBar(self) self.statusBar.setMinimumSize(32) self.statusBar.setMaximumSize(128) self.statusBar.sizeChanged.connect(self._sizeChanged) self.setFont_(font) app = QApplication.instance() app.dispatcher.addObserver( self, "_drawingToolRegistered", "drawingToolRegistered") app.dispatcher.addObserver( self, "_drawingToolUnregistered", "drawingToolUnregistered") app.dispatcher.addObserver( self, "_glyphViewGlyphsChanged", "glyphViewGlyphsChanged") layout = QHBoxLayout(self) layout.addWidget(self.toolBar) vLayout = QVBoxLayout() vLayout.addWidget(self.tabWidget) pageWidget = PageWidget() pageWidget.addWidget(self.stackWidget) pageWidget.addWidget(self.statusBar) vLayout.addWidget(pageWidget) layout.addLayout(vLayout) layout.addWidget(self.propertiesView) layout.setContentsMargins(0, 2, 0, 0) layout.setSpacing(2) elements = [ ("Ctrl+D", self.deselect), (platformSpecific.closeKeySequence(), self.closeGlyphTab), # XXX: does this really not warrant widget focus? (QKeySequence.Delete, self.delete), ("Shift+" + QKeySequence( QKeySequence.Delete).toString(), self.delete), ("Z", lambda: self.zoom(1)), ("X", lambda: self.zoom(-1)), ] e = platformSpecific.altDeleteSequence() if e is not None: elements.append((e, self.delete)) e = platformSpecific.altRedoSequence() if e is not None: elements.append((e, self.redo)) for keys, callback in elements: shortcut = QShortcut(QKeySequence(keys), self) shortcut.activated.connect(callback) self.installEventFilter(PreviewEventFilter(self)) self.readSettings() self.propertiesView.activeLayerModified.connect( self._activeLayerModified) self.statusBar.sizeChanged.connect(self.writeSettings) def readSettings(self): geometry = settings.fontWindowGeometry() if geometry: self.restoreGeometry(geometry) cellSize = settings.glyphCellSize() self.statusBar.setSize(cellSize) hidden = settings.propertiesHidden() if not hidden: self.properties() def writeSettings(self): settings.setFontWindowGeometry(self.saveGeometry()) settings.setGlyphCellSize(self.glyphCellView.cellSize()[0]) settings.setPropertiesHidden(self.propertiesView.isHidden()) def menuBar(self): return self.layout().menuBar() def setMenuBar(self, menuBar): self.layout().setMenuBar(menuBar) def setupMenu(self, menuBar): app = QApplication.instance() fileMenu = menuBar.fetchMenu(Entries.File) fileMenu.fetchAction(Entries.File_New) fileMenu.fetchAction(Entries.File_Open) fileMenu.fetchMenu(Entries.File_Open_Recent) if not platformSpecific.mergeOpenAndImport(): fileMenu.fetchAction(Entries.File_Import) fileMenu.addSeparator() fileMenu.fetchAction(Entries.File_Save, self.saveFile) fileMenu.fetchAction(Entries.File_Save_As, self.saveFileAs) fileMenu.fetchAction(Entries.File_Save_All) fileMenu.fetchAction(Entries.File_Reload, self.reloadFile) fileMenu.addSeparator() fileMenu.fetchAction(Entries.File_Export, self.exportFile) fileMenu.fetchAction(Entries.File_Exit) editMenu = menuBar.fetchMenu(Entries.Edit) self._undoAction = editMenu.fetchAction(Entries.Edit_Undo, self.undo) self._redoAction = editMenu.fetchAction(Entries.Edit_Redo, self.redo) editMenu.addSeparator() cut = editMenu.fetchAction(Entries.Edit_Cut, self.cut) copy = editMenu.fetchAction(Entries.Edit_Copy, self.copy) copyComponent = editMenu.fetchAction( Entries.Edit_Copy_As_Component, self.copyAsComponent) paste = editMenu.fetchAction(Entries.Edit_Paste, self.paste) self._clipboardActions = (cut, copy, copyComponent, paste) editMenu.fetchAction(Entries.Edit_Select_All, self.selectAll) # editMenu.fetchAction(Entries.Edit_Deselect, self.deselect) editMenu.fetchAction(Entries.Edit_Find, self.findGlyph) editMenu.addSeparator() editMenu.fetchAction(Entries.Edit_Settings) viewMenu = menuBar.fetchMenu(Entries.View) viewMenu.fetchAction(Entries.View_Zoom_In, lambda: self.zoom(1)) viewMenu.fetchAction(Entries.View_Zoom_Out, lambda: self.zoom(-1)) viewMenu.fetchAction(Entries.View_Reset_Zoom, self.resetZoom) viewMenu.addSeparator() viewMenu.fetchAction( Entries.View_Next_Tab, lambda: self.tabOffset(1)) viewMenu.fetchAction( Entries.View_Previous_Tab, lambda: self.tabOffset(-1)) viewMenu.fetchAction( Entries.View_Next_Glyph, lambda: self.glyphOffset(1)) viewMenu.fetchAction( Entries.View_Previous_Glyph, lambda: self.glyphOffset(-1)) viewMenu.fetchAction( Entries.View_Layer_Up, lambda: self.layerOffset(-1)) viewMenu.fetchAction( Entries.View_Layer_Down, lambda: self.layerOffset(1)) viewMenu.addSeparator() viewMenu.fetchAction(Entries.View_Show_Points) viewMenu.fetchAction(Entries.View_Show_Metrics) viewMenu.fetchAction(Entries.View_Show_Images) viewMenu.fetchAction(Entries.View_Show_Guidelines) fontMenu = menuBar.fetchMenu(Entries.Font) fontMenu.fetchAction(Entries.Font_Font_Info, self.fontInfo) fontMenu.fetchAction(Entries.Font_Font_Features, self.fontFeatures) fontMenu.addSeparator() fontMenu.fetchAction(Entries.Font_Add_Glyphs, self.addGlyphs) fontMenu.fetchAction(Entries.Font_Sort, self.sortGlyphs) # glyphMenu = menuBar.fetchMenu(self.tr("&Glyph")) # self._layerAction = glyphMenu.fetchAction( # self.tr("&Layer Actions…"), self.layerActions, "L") menuBar.fetchMenu(Entries.Scripts) windowMenu = menuBar.fetchMenu(Entries.Window) windowMenu.fetchAction(Entries.Window_Groups, self.groups) windowMenu.fetchAction(Entries.Window_Kerning, self.kerning) windowMenu.fetchAction(Entries.Window_Metrics, self.metrics) windowMenu.fetchAction(Entries.Window_Scripting) windowMenu.fetchAction(Entries.Window_Properties, self.properties) windowMenu.addSeparator() action = windowMenu.fetchAction(Entries.Window_Output) action.setEnabled(app.outputWindow is not None) helpMenu = menuBar.fetchMenu(Entries.Help) helpMenu.fetchAction(Entries.Help_Documentation) helpMenu.fetchAction(Entries.Help_Report_An_Issue) helpMenu.addSeparator() helpMenu.fetchAction(Entries.Help_About) self._updateGlyphActions() # -------------- # Custom methods # -------------- def font_(self): return self._font def setFont_(self, font): if self._font is not None: self._font.removeObserver(self, "Font.Changed") self._font.removeObserver(self, "Font.GlyphOrderChanged") self._font.removeObserver(self, "Font.SortDescriptorChanged") self._font = font self.setWindowTitle(self.fontTitle()) if font is None: return self._updateGlyphsFromGlyphOrder() font.addObserver(self, "_fontChanged", "Font.Changed") font.addObserver( self, "_glyphOrderChanged", "Font.GlyphOrderChanged") font.addObserver( self, "_sortDescriptorChanged", "Font.SortDescriptorChanged") def fontTitle(self): if self._font is None: return None path = self._font.path or self._font.binaryPath if path is not None: return os.path.basename(path.rstrip(os.sep)) return self.tr("Untitled") def isGlyphTab(self): return bool(self.stackWidget.currentIndex()) def openGlyphTab(self, glyph): # if a tab with this glyph exists already, switch to it for index in range(self.stackWidget.count()): if not index: continue view = self.stackWidget.widget(index) if list(view.glyphs()) == [glyph]: self.tabWidget.setCurrentTab(index) return # spawn widget = GlyphCanvasView(self) widget.setInputNames([glyph.name]) widget.activeGlyphChanged.connect(self._selectionChanged) widget.glyphNamesChanged.connect(self._namesChanged) widget.pointSizeModified.connect(self.statusBar.setSize) widget.toolModified.connect(self.toolBar.setCurrentTool) # add self.tabWidget.addTab(_textForGlyphs([glyph])) self.stackWidget.addWidget(widget) # activate self.tabWidget.setCurrentTab(-1) def closeGlyphTab(self): index = self.stackWidget.currentIndex() if index: self.tabWidget.removeTab(index) def maybeSaveBeforeExit(self): if self._font.dirty: ret = CloseMessageBox.getCloseDocument(self, self.fontTitle()) if ret == QMessageBox.Save: self.saveFile() return True elif ret == QMessageBox.Discard: return True return False return True # ------------- # Notifications # ------------- # app def _drawingToolRegistered(self, notification): toolClass = notification.data["tool"] index = self.stackWidget.currentIndex() parent = self.stackWidget.currentWidget() if index else None self.toolBar.addTool(toolClass(parent=parent)) def _drawingToolUnregistered(self, notification): toolClass = notification.data["tool"] for tool in self.toolBar.tools(): if isinstance(tool, toolClass): self.toolBar.removeTool(tool) return raise ValueError( "couldn't find tool to unregister: {}".format(toolClass)) def _glyphViewGlyphsChanged(self, notification): self._updateGlyphActions() # widgets def _activeLayerModified(self): if self.isGlyphTab(): widget = self.stackWidget.currentWidget() index = self.sender().currentIndex().row() layers = self._font.layers layer = layers[layers.layerOrder[index]] currentGlyph = widget.activeGlyph() # XXX: adjust TLayer.get and use it if currentGlyph.name in layer: glyph = layer[currentGlyph.name] else: glyph = layer.newGlyph(currentGlyph.name) widget.setActiveGlyph(glyph) def _namesChanged(self): sender = self.sender() index = self.stackWidget.indexOf(sender) self.tabWidget.setTabName(index, _textForGlyphs(sender.glyphs())) def _sizeChanged(self): size = self.statusBar.size() if self.isGlyphTab(): widget = self.stackWidget.currentWidget() widget.setPointSize(size) else: self.glyphCellView.setCellSize(size) def _tabChanged(self, index): self.statusBar.setShouldPropagateSize(not index) # we need to hide, then setParent, then show self.stackWidget.currentWidget().hide() newWidget = self.stackWidget.widget(index) if index: for tool in self.toolBar.tools(): tool.setParent(newWidget) self.stackWidget.setCurrentIndex(index) newWidget.setFocus(Qt.OtherFocusReason) def _toolChanged(self, tool): widget = self.stackWidget.currentWidget() ok = widget.setCurrentTool(tool) # the glyph view NAKed the change (in mouseDown) # set back the current tool in the toolbar if not ok: self.toolBar.setCurrentTool(widget.currentTool()) def _widgetChanged(self, index): # update current glyph self._updateCurrentGlyph() # update undo/redo self._updateGlyphActions() # update slider if self.isGlyphTab(): lo, hi, unit = 0, 900000, " pt" widget = self.stackWidget.currentWidget() size = widget.pointSize() else: lo, hi, unit = 32, 128, None size = self.glyphCellView.cellSize()[0] self.statusBar.setMinimumSize(lo) self.statusBar.setMaximumSize(hi) self.statusBar.setSize(size) self.statusBar.setUnit(unit) self.statusBar.setTextVisible(not self.isGlyphTab()) # update and connect setCurrentTool try: self.toolBar.currentToolChanged.disconnect() except TypeError: pass if not index: return widget = self.stackWidget.currentWidget() widget.setCurrentTool(self.toolBar.currentTool()) self.toolBar.currentToolChanged.connect(self._toolChanged) def _orderChanged(self): # TODO: reimplement when we start showing glyph subsets glyphs = self.glyphCellView.glyphs() self._font.glyphOrder = [glyph.name for glyph in glyphs] def _selectionChanged(self): if self.isGlyphTab(): activeGlyph = self.stackWidget.currentWidget().activeGlyph() else: activeGlyph = self.glyphCellView.lastSelectedGlyph() # selection text # TODO: this should probably be internal to the label selection = self.glyphCellView.selection() if selection is not None: count = len(selection) if count == 1: glyph = self.glyphCellView.glyphsForIndexes(selection)[0] text = "%s " % glyph.name else: text = "" if count: text = self.tr("{0}(%n selected)".format(text), n=count) else: text = "" self.statusBar.setText(text) # currentGlyph app = QApplication.instance() app.setCurrentGlyph(activeGlyph) # actions self._updateGlyphActions() # defcon def _fontChanged(self, notification): font = notification.object self.setWindowModified(font.dirty) def _glyphOrderChanged(self, notification): self._updateGlyphsFromGlyphOrder() def _updateGlyphsFromGlyphOrder(self): font = self._font glyphOrder = font.glyphOrder if glyphOrder: glyphCount = 0 glyphs = [] for glyphName in glyphOrder: if glyphName in font: glyph = font[glyphName] glyphCount += 1 else: glyph = font.get(glyphName, asTemplate=True) glyphs.append(glyph) if glyphCount < len(font): # if some glyphs in the font are not present in the glyph # order, loop again to add them at the end for glyph in font: if glyph not in glyphs: glyphs.append(glyph) font.disableNotifications(observer=self) font.glyphOrder = [glyph.name for glyph in glyphs] font.enableNotifications(observer=self) else: glyphs = list(font) font.disableNotifications(observer=self) font.glyphOrder = [glyph.name for glyph in glyphs] font.enableNotifications(observer=self) self.glyphCellView.setGlyphs(glyphs) def _sortDescriptorChanged(self, notification): font = notification.object descriptors = notification.data["newValue"] if descriptors is None: return if descriptors[0]["type"] == "glyphSet": glyphNames = descriptors[0]["glyphs"] else: glyphNames = font.unicodeData.sortGlyphNames( font.keys(), descriptors) font.glyphOrder = glyphNames # ------------ # Menu methods # ------------ # File def saveFile(self, path=None, ufoFormatVersion=3): if path is None and self._font.path is None: self.saveFileAs() else: if path is None: path = self._font.path self._font.save(path, ufoFormatVersion) def saveFileAs(self): fileFormats = OrderedDict([ (self.tr("UFO Font version 3 {}").format("(*.ufo)"), 3), (self.tr("UFO Font version 2 {}").format("(*.ufo)"), 2), ]) state = settings.saveFileDialogState() path = self._font.path or self._font.binaryPath if path: directory = os.path.dirname(path) else: directory = None if state else QStandardPaths.standardLocations( QStandardPaths.DocumentsLocation)[0] # TODO: switch to directory dlg on platforms that need it dialog = QFileDialog( self, self.tr("Save File"), directory, ";;".join(fileFormats.keys())) if state: dialog.restoreState(state) dialog.setAcceptMode(QFileDialog.AcceptSave) if directory: dialog.setDirectory(directory) ok = dialog.exec_() settings.setSaveFileDialogState(dialog.saveState()) if ok: nameFilter = dialog.selectedNameFilter() path = dialog.selectedFiles()[0] if not os.path.basename(path).endswith(".ufo"): path += ".ufo" self.saveFile(path, fileFormats[nameFilter]) app = QApplication.instance() app.setCurrentFile(self._font.path) self.setWindowTitle(self.fontTitle()) # return ok def reloadFile(self): font = self._font path = font.path or font.binaryPath if not font.dirty or path is None: return if not ReloadMessageBox.getReloadDocument(self, self.fontTitle()): return if font.path is not None: font.reloadInfo() font.reloadKerning() font.reloadGroups() font.reloadFeatures() font.reloadLib() font.reloadGlyphs(font.keys()) font.dirty = False else: # TODO: we should do this in-place font_ = font.__class__().new() font_.extract(font.binaryPath) self.setFont_(font_) def exportFile(self): params, ok = ExportDialog.getExportParameters(self, self._font) if not ok: return baseName = params['baseName'] directory = params['exportDirectory'] compression = set(map(str.lower, params['compression'])) for format in map(str.lower, params['formats']): fileName = "{}.{}".format(baseName, format) path = os.path.join(directory, fileName) try: self._font.export(path, format, compression=compression) except Exception as e: msg = self.tr( "This font’s feature file contains an error." ) if isinstance(e, FeatureLibError) else None errorReports.showCriticalException(e, message=msg) # Edit def undo(self): widget = self.stackWidget.currentWidget() if self.isGlyphTab(): glyph = widget.activeGlyph() else: glyph = widget.lastSelectedGlyph() glyph.undo() def redo(self): widget = self.stackWidget.currentWidget() if self.isGlyphTab(): glyph = widget.activeGlyph() else: glyph = widget.lastSelectedGlyph() glyph.redo() def cut(self): self.copy() widget = self.stackWidget.currentWidget() if self.isGlyphTab(): glyph = widget.activeGlyph() deleteUISelection(glyph) else: glyphs = widget.glyphs() for index in widget.selection(): glyph = glyphs[index] glyph.clear() def copy(self): font = self._font widget = self.stackWidget.currentWidget() clipboard = QApplication.clipboard() mimeData = QMimeData() if self.isGlyphTab(): glyph = widget.activeGlyph() copyGlyph = glyph.getRepresentation("TruFont.FilterSelection") packGlyphs = (copyGlyph,) else: glyphs = self.glyphCellView.glyphs() packGlyphs = ( glyphs[index] for index in sorted( self.glyphCellView.selection())) svgGlyphs = [] pickled = [] for i, glyph in enumerate(packGlyphs): pickled.append(glyph.serialize( blacklist=("name", "unicodes") )) pen = SVGPathPen(font) glyph.draw(pen) col = i % 5 row = i // 5 g = '<g transform="matrix(1,0,0,-1,%f,%f)"><path d="%s"/></g>' % ( font.info.unitsPerEm * col, font.info.unitsPerEm * row, pen.getCommands()) svgGlyphs.append(g) mimeData.setData("application/x-trufont-glyph-data", pickle.dumps(pickled)) svg = """\ <?xml version="1.0" standalone="no"?> <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN" "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd"> <svg version="1.0" xmlns="http://www.w3.org/2000/svg"> %s </svg> """ % "\n".join(svgGlyphs) mimeData.setData("image/svg+xml", svg.encode("utf-8")) clipboard.setMimeData(mimeData) def copyAsComponent(self): if self.isGlyphTab(): pass else: glyphs = self.glyphCellView.glyphs() pickled = [] for index in self.glyphCellView.selection(): glyph = glyphs[index] componentGlyph = glyph.__class__() componentGlyph.width = glyph.width component = componentGlyph.instantiateComponent() component.baseGlyph = glyph.name pickled.append(componentGlyph.serialize()) clipboard = QApplication.clipboard() mimeData = QMimeData() mimeData.setData("application/x-trufont-glyph-data", pickle.dumps(pickled)) clipboard.setMimeData(mimeData) def paste(self): isGlyphTab = self.isGlyphTab() widget = self.stackWidget.currentWidget() if isGlyphTab: glyphs = (widget.activeGlyph(),) else: selection = self.glyphCellView.selection() glyphs = widget.glyphsForIndexes(selection) clipboard = QApplication.clipboard() mimeData = clipboard.mimeData() if mimeData.hasFormat("application/x-trufont-glyph-data"): data = pickle.loads(mimeData.data( "application/x-trufont-glyph-data")) if len(data) == len(glyphs): for pickled, glyph in zip(data, glyphs): if isGlyphTab: pasteGlyph = glyph.__class__() pasteGlyph.deserialize(pickled) # TODO: if we serialize selected state, we don't need # to do this pasteGlyph.selected = True if len(pasteGlyph) or len(pasteGlyph.components) or \ len(pasteGlyph.anchors): glyph.beginUndoGroup() glyph.holdNotifications() count = len(glyph) pen = glyph.getPointPen() # contours, components pasteGlyph.drawPoints(pen) for contour in glyph[count:]: contour.selected = True # anchors for anchor in pasteGlyph.anchors: glyph.appendAnchor(dict(anchor)) # guidelines for guideline in pasteGlyph.guidelines: glyph.appendGuideline(dict(guideline)) glyph.releaseHeldNotifications() glyph.endUndoGroup() else: glyph.deserialize(pickled) return if mimeData.hasFormat("image/svg+xml"): if len(glyphs) == 1: glyph = glyphs[0] try: svgPath = SVGPath.fromstring( mimeData.data("image/svg+xml")) except: pass else: glyph.beginUndoGroup() if not isGlyphTab: glyph.clear() svgPath.draw(glyph.getPen()) glyph.endUndoGroup() return if mimeData.hasText(): if len(glyphs) == 1: glyph = glyphs[0] otherGlyph = glyph.__class__() text = mimeData.text() try: readGlyphFromString( text, otherGlyph, otherGlyph.getPointPen()) except: try: svgPath = SVGPath.fromstring(text) svgPath.draw(otherGlyph.getPen()) except: return glyph.beginUndoGroup() if not isGlyphTab: glyph.clear() otherGlyph.drawPoints(glyph.getPointPen()) glyph.endUndoGroup() def selectAll(self): widget = self.stackWidget.currentWidget() if self.isGlyphTab(): glyph = widget.activeGlyph() if glyph.selected: for anchor in glyph.anchors: anchor.selected = True for component in glyph.components: component.selected = True else: glyph.selected = True else: widget.selectAll() def deselect(self): widget = self.stackWidget.currentWidget() if self.isGlyphTab(): glyph = widget.activeGlyph() for anchor in glyph.anchors: anchor.selected = False for component in glyph.components: component.selected = False glyph.selected = False else: widget.setSelection(set()) def delete(self): modifiers = QApplication.keyboardModifiers() widget = self.stackWidget.currentWidget() if self.isGlyphTab(): glyph = widget.activeGlyph() # TODO: fuse more the two methods, they're similar and delete is # Cut except not putting in the clipboard if modifiers & Qt.AltModifier: deleteUISelection(glyph) else: preserveShape = not modifiers & Qt.ShiftModifier removeUIGlyphElements(glyph, preserveShape) else: erase = modifiers & Qt.ShiftModifier if self._proceedWithDeletion(erase): glyphs = widget.glyphsForIndexes(widget.selection()) for glyph in glyphs: font = glyph.font for layer in font.layers: if glyph.name in layer: defaultLayer = layer[glyph.name] == glyph if defaultLayer and not erase: # TODO: clear in glyph.template setter? glyph.clear() glyph.template = True else: del layer[glyph.name] def findGlyph(self): widget = self.stackWidget.currentWidget() if self.isGlyphTab(): glyph = widget.activeGlyph() newGlyph, ok = FindDialog.getNewGlyph(self, glyph) if ok and newGlyph is not None: widget.setActiveGlyph(newGlyph) else: pass # XXX # View def zoom(self, step): if self.isGlyphTab(): widget = self.stackWidget.currentWidget() newScale = widget.scale() * pow(1.2, step) widget.zoom(newScale) self.statusBar.setSize(widget.pointSize()) else: value = self.statusBar.size() newValue = value + 10 * step self.statusBar.setSize(newValue) def resetZoom(self): widget = self.stackWidget.currentWidget() if self.isGlyphTab(): widget.fitScaleBBox() else: settings.removeGlyphCellSize() cellSize = settings.glyphCellSize() self.statusBar.setSize(cellSize) def tabOffset(self, value): tab = self.tabWidget.currentTab() newTab = (tab + value) % len(self.tabWidget.tabs()) self.tabWidget.setCurrentTab(newTab) def glyphOffset(self, value): widget = self.stackWidget.currentWidget() if self.isGlyphTab(): currentGlyph = widget.activeGlyph() font = currentGlyph.font glyphOrder = font.glyphOrder # should be enforced in fontView already if not (glyphOrder and len(glyphOrder)): return index = glyphOrder.index(currentGlyph.name) newIndex = (index + value) % len(glyphOrder) glyph = font[glyphOrder[newIndex]] widget.setActiveGlyph(glyph) else: lastSelectedCell = widget.lastSelectedCell() if lastSelectedCell is None: return newIndex = lastSelectedCell + value if newIndex < 0 or newIndex >= len(widget.glyphs()): return widget.setSelection({newIndex}) def layerOffset(self, value): widget = self.stackWidget.currentWidget() if self.isGlyphTab(): currentGlyph = widget.activeGlyph() layerSet, layer = currentGlyph.layerSet, currentGlyph.layer if None in (layerSet, layer): return index = layerSet.layerOrder.index(layer.name) newIndex = (index + value) % len(layerSet) layer_ = layerSet[layerSet.layerOrder[newIndex]] if layer_ == layer: return # XXX: fix get # glyph = layer_.get(currentGlyph.name) if currentGlyph.name in layer_: glyph = layer_[currentGlyph.name] else: glyph = layer_.newGlyph(currentGlyph.name) widget.setActiveGlyph(glyph) # Font def fontInfo(self): # If a window is already opened, bring it to the front, else spawn one. # TODO: see about using widget.setAttribute(Qt.WA_DeleteOnClose) # otherwise it seems we're just leaking memory after each close... # (both raise_ and show allocate memory instead of using the hidden # widget it seems) if self._infoWindow is not None and self._infoWindow.isVisible(): self._infoWindow.raise_() else: self._infoWindow = FontInfoWindow(self._font, self) self._infoWindow.show() def fontFeatures(self): # TODO: see up here if self._featuresWindow is not None and self._featuresWindow.isVisible( ): self._featuresWindow.raise_() else: self._featuresWindow = FontFeaturesWindow(self._font, self) self._featuresWindow.show() def addGlyphs(self): glyphs = self.glyphCellView.glyphs() newGlyphNames, params, ok = AddGlyphsDialog.getNewGlyphNames( self, glyphs) if ok: sortFont = params.pop("sortFont") for name in newGlyphNames: glyph = self._font.get(name, **params) if glyph is not None: glyphs.append(glyph) self.glyphCellView.setGlyphs(glyphs) if sortFont: # TODO: when the user add chars from a glyphSet and no others, # should we try to sort according to that glyphSet? # The above would probably warrant some rearchitecturing. # kick-in the sort mechanism self._font.sortDescriptor = self._font.sortDescriptor def sortGlyphs(self): sortDescriptor, ok = SortDialog.getDescriptor( self, self._font.sortDescriptor) if ok: self._font.sortDescriptor = sortDescriptor # Window def groups(self): # TODO: see up here if self._groupsWindow is not None and self._groupsWindow.isVisible(): self._groupsWindow.raise_() else: self._groupsWindow = GroupsWindow(self._font, self) self._groupsWindow.show() def kerning(self): # TODO: see up here if self._kerningWindow is not None and self._kerningWindow.isVisible(): self._kerningWindow.raise_() else: self._kerningWindow = KerningWindow(self._font, self) self._kerningWindow.show() def metrics(self): # TODO: see up here if self._metricsWindow is not None and self._metricsWindow.isVisible(): self._metricsWindow.raise_() else: self._metricsWindow = MetricsWindow(self._font) # XXX: need proper, fast windowForFont API! self._metricsWindow._fontWindow = self self.destroyed.connect(self._metricsWindow.close) self._metricsWindow.show() # TODO: default string kicks-in on the window before this. Figure out # how to make a clean interface selection = self.glyphCellView.selection() if selection: glyphs = self.glyphCellView.glyphsForIndexes(selection) self._metricsWindow.setGlyphs(glyphs) def properties(self): shouldBeVisible = self.propertiesView.isHidden() self.propertiesView.setVisible(shouldBeVisible) self.writeSettings() # update methods def _setGlyphPreview(self, value): index = self.stackWidget.currentIndex() if index: widget = self.stackWidget.currentWidget() widget.setPreviewEnabled(value) def _updateCurrentGlyph(self): # TODO: refactor this pattern... widget = self.stackWidget.currentWidget() if self.isGlyphTab(): glyph = widget.activeGlyph() else: glyph = widget.lastSelectedGlyph() if glyph is not None: app = QApplication.instance() app.setCurrentGlyph(glyph) def _updateGlyphActions(self): if not hasattr(self, "_undoAction"): return widget = self.stackWidget.currentWidget() if self.isGlyphTab(): currentGlyph = widget.activeGlyph() else: currentGlyph = widget.lastSelectedGlyph() # disconnect eventual signal of previous glyph objects = ( (self._undoAction, self.undo), (self._redoAction, self.redo), ) for action, slot in objects: try: action.disconnect() except TypeError: pass action.triggered.connect(slot) # now update status if currentGlyph is None: self._undoAction.setEnabled(False) self._redoAction.setEnabled(False) else: undoManager = currentGlyph.undoManager self._undoAction.setEnabled(currentGlyph.canUndo()) undoManager.canUndoChanged.connect(self._undoAction.setEnabled) self._redoAction.setEnabled(currentGlyph.canRedo()) undoManager.canRedoChanged.connect(self._redoAction.setEnabled) # and other actions for action in self._clipboardActions: action.setEnabled(currentGlyph is not None) # helper def _proceedWithDeletion(self, erase=False): if not self.glyphCellView.selection(): return tr = self.tr("Delete") if erase else self.tr("Clear") text = self.tr("Do you want to %s selected glyphs?") % tr.lower() closeDialog = QMessageBox( QMessageBox.Question, "", self.tr("%s glyphs") % tr, QMessageBox.Yes | QMessageBox.No, self) closeDialog.setInformativeText(text) closeDialog.setModal(True) ret = closeDialog.exec_() if ret == QMessageBox.Yes: return True return False # ---------- # Qt methods # ---------- def setWindowTitle(self, title): if platformSpecific.appNameInTitle(): title += " – TruFont" super().setWindowTitle("[*]{}".format(title)) def sizeHint(self): return QSize(1270, 800) def moveEvent(self, event): self.writeSettings() resizeEvent = moveEvent def showEvent(self, event): app = QApplication.instance() data = dict( font=self._font, window=self, ) app.postNotification("fontWindowWillOpen", data) super().showEvent(event) app.postNotification("fontWindowOpened", data) def closeEvent(self, event): ok = self.maybeSaveBeforeExit() if ok: app = QApplication.instance() data = dict( font=self._font, window=self, ) app.postNotification("fontWindowWillClose", data) self._font.removeObserver(self, "Font.Changed") app = QApplication.instance() app.dispatcher.removeObserver(self, "drawingToolRegistered") app.dispatcher.removeObserver(self, "drawingToolUnregistered") app.dispatcher.removeObserver(self, "glyphViewGlyphsChanged") event.accept() else: event.ignore() def event(self, event): if event.type() == QEvent.WindowActivate: app = QApplication.instance() app.setCurrentFontWindow(self) self._updateCurrentGlyph() return super().event(event) def paintEvent(self, event): painter = QPainter(self) painter.fillRect(event.rect(), QColor(212, 212, 212))
class MainWindow(QtWidgets.QMainWindow): def __init__(self, parent=None): super(MainWindow, self).__init__(parent) self.initUI() self.stacked_widget.currentChanged.connect(self.set_button_state) self.next_button.clicked.connect(self.next_page) self.prev_button.clicked.connect(self.prev_page) def initUI(self): self.next_button = QPushButton('Next') self.prev_button = QPushButton('Previous') self.next_button.setEnabled(False) self.prev_button.setEnabled(False) self.stacked_widget = QStackedWidget() widget = QtWidgets.QWidget() self.setCentralWidget(widget) hbox = QHBoxLayout() hbox.addStretch(1) hbox.addWidget(self.prev_button) hbox.addWidget(self.next_button) vbox = QVBoxLayout() vbox.addWidget(self.stacked_widget) vbox.addLayout(hbox) h = HomeScreen(self) self.insert_page(h.label1) navigation = Navigation(sample, self) self.insert_page(navigation.horizontalGroupBox) #add the toolbar to the main window self.toolbar = navigation.toolbar self.addToolBar(self.toolbar) #start with the toolbar hidden self.toolbar.toggleViewAction().setChecked(True) self.toolbar.toggleViewAction().trigger() # create main layout widget.setLayout(vbox) def set_button_state(self, index): self.prev_button.setEnabled(index > 0) n_pages = len(self.stacked_widget) self.next_button.setEnabled( index % n_pages < n_pages - 1) def insert_page(self, widget, index=-1): self.stacked_widget.insertWidget(index, widget) self.set_button_state(self.stacked_widget.currentIndex()) def next_page(self): new_index = self.stacked_widget.currentIndex()+1 if new_index < len(self.stacked_widget): self.stacked_widget.setCurrentIndex(new_index) self.toolbar.toggleViewAction().trigger() def prev_page(self): new_index = self.stacked_widget.currentIndex()-1 if new_index >= 0: self.stacked_widget.setCurrentIndex(new_index) self.toolbar.toggleViewAction().trigger()
class MainWin(QMainWindow): def __init__(self): super().__init__() self.wait_times = [10, 30, 60] self.push_wait = [] # list of wait buttons self.displaystat_hdr = [] # TODO replace with OrderedDict? self.displaystat_val = [] self.statlabels = OrderedDict() # statname: (namelabel, vallabel) # create widgets w = QWidget(self) self.central_widget = w self.time_lbl = QLabel(w) self.date_lbl = QLabel(w) self.location_lbl = QLabel(w) self.energy_bar = QProgressBar(w) self.arousal_bar = QProgressBar(w) self.widget_stack = QStackedWidget(self) self.location_view = LocationView(self) self.school_management = SchoolManagement(self) # create layout self.retranslateUi() grid = QGridLayout(self.central_widget) grid.setContentsMargins(0, 0, 0, 0) grid.addWidget(self.time_lbl, 0, 5, 1, 2) grid.addWidget(self.date_lbl, 0, 3, 1, 2) grid.addWidget(self.energy_bar, 0, 7, 1, 5) grid.addWidget(self.arousal_bar, 1, 7, 1, 5) grid.addWidget(self.location_lbl, 2, 0, 2, 3) for i, stat in enumerate(self.displaystat_hdr): val = self.displaystat_val[i] statlabel = QLabel(stat, w) vallabel = QLabel(val, w) self.statlabels[stat] = (statlabel, vallabel) grid.addWidget(statlabel, 2, 3 + i, 1, 1) grid.addWidget(vallabel, 3, 3 + i, 1, 1) # TODO: style # displaystat_hdr[i].setPalette(HHStyle::hdr_text); # displaystat_hdr[i].setAlignment(Qt::AlignCenter); # displaystat_val[i].setPalette(HHStyle::white_text); # displaystat_val[i].setAlignment(Qt::AlignCenter); self.widget_stack.addWidget(self.location_view) self.widget_stack.addWidget(self.school_management) grid.addWidget(self.widget_stack, 4, 0, 50, 12) self.setCentralWidget(w) # configure window self.setWindowTitle("pyprinciple") geom = QDesktopWidget().availableGeometry() self.setGeometry(100, 50, 800, 600) # configure widgets w.setContentsMargins(0, 0, 0, 0) w.setObjectName("central_widget") geom.setHeight(geom.height() * 0.98) self.time_lbl.setObjectName("text") # use text style from style sheet self.time_lbl.setFont(style.big_font) self.time_lbl.setAlignment(Qt.AlignCenter) self.date_lbl.setObjectName("text") self.date_lbl.setFont(style.big_font) self.date_lbl.setAlignment(Qt.AlignRight) self.location_lbl.setObjectName("text") self.location_lbl.setFont(style.location_font) # self.gridW.setContentsMargins(0, 0, 0, 0) # gridW.setGeometry(geom) self.energy_bar.setValue(60) self.energy_bar.setObjectName("energy") self.arousal_bar.setValue(48) self.arousal_bar.setObjectName("arousal") geom.setHeight(geom.height() * 10 / 11) # removes space for header self.location_lbl.setGeometry(geom) self.school_management.setGeometry(geom) self.widget_stack.setCurrentIndex(0) # TODO only shown if window contains locationview for i in range(0, len(self.wait_times)): pw = QPushButton("Wait %d min" % self.wait_times[i]) self.push_wait.append(pw) grid.addWidget(pw, 0, i, 2, 1) for name in world.peopleAt("Your Home"): self.location_view.addPerson(Person(name)) self.retranslateUi() self.show() def retranslateUi(self): tra = QApplication.translate ctxt = "MainWin" self.setWindowTitle(tra(ctxt, "Main window")) # TODO: use ordered dict instead displaystat = [ "Education", "Happiness", "Loyalty", "Inhibition", "Lust", "Corruption", "Reputation", "Students", "Money" ] self.displaystat_hdr = [tra(ctxt, stat) for stat in displaystat] self.displaystat_val = [tra(ctxt, "30.1") for stat in displaystat[:-2]] self.displaystat_val.append(tra(ctxt, "91")) self.displaystat_val.append(tra(ctxt, "$10,000")) for waittime, pushbu in zip(self.wait_times, self.push_wait): pushbu.setText(tra(ctxt, "Wait %s min" % waittime)) # TODO: translating dates and times is easier via datetime objects self.time_lbl.setText(tra(ctxt, "8:00")) self.date_lbl.setText(tra(ctxt, "Monday (1/1/2017)")) self.location_lbl.setText(tra(ctxt, "Home")) @pyqtSlot() def toggle_school_management(self): if self.widget_stack.currentIndex() is 1: self.widget_stack.setCurrentIndex(0) else: self.widget_stack.setCurrentIndex(1)
class MainWindow(QMainWindow): def __init__(self): super(MainWindow, self).__init__() self.setMinimumSize( int(QApplication.primaryScreen().size().width() * 0.1), int(QApplication.primaryScreen().size().height() * 0.2)) self.resize(int(QApplication.primaryScreen().size().width() * 0.3), int(QApplication.primaryScreen().size().height() * 0.5)) window_width = int(QApplication.primaryScreen().size().width() * 0.3) id = QFontDatabase.addApplicationFont(variables.FONT_FILE) family = QFontDatabase.applicationFontFamilies(id)[0] variables.font = QFont(family, variables.FONT_SIZE) variables.font_small = QFont(family, variables.FONT_SIZE_SMALL) variables.nw = network.network() variables.signals = variables.DialogSignals() variables.signals.create_dialog.connect(self.createDialog) variables.signals.open_dialog.connect(self.openDialog) variables.signals.close_dialog.connect(self.closeDialog) variables.signals.message_sent.connect(self.messageSent) variables.nw.received.connect(self.receiveMessage) variables.nw.undelivered.connect(self.undelivered) variables.nw.delivered.connect(self.delivered) variables.nw.read.connect(self.read) variables.nw.reconnect.connect(self.clientReconnected) self.dialogs = [] self.dialog_menu = DialogList() self.main_widget = QStackedWidget() self.main_widget.addWidget(self.dialog_menu) self.setCentralWidget(self.main_widget) self.setWindowTitle(variables.APP_NAME) self.setWindowIcon(QIcon(variables.LOGO_IMG)) self.dialog_menu.ip_input.setFocus() def resizeEvent(self, event): super(MainWindow, self).resizeEvent(event) variables.window_width = self.size().width() for i in range(len(self.dialogs)): self.dialogs[i].width_changed.emit() def createDialog(self, ip): self.dialogs.append(Dialog(ip)) def openDialog(self, id): self.main_widget.addWidget(self.dialogs[id]) self.main_widget.setCurrentIndex(1) self.dialog_menu.model.set_status(id, variables.STATUS_READ) self.dialogs[id].send_input.setFocus() self.dialogs[id].model.send_read() def closeDialog(self): self.main_widget.setCurrentIndex(0) self.main_widget.removeWidget(self.main_widget.widget(1)) def receiveMessage(self, msg, ip): for i in range(len(self.dialogs)): if self.dialogs[i].ip == ip: self.dialogs[i].message_from(msg) self.dialog_menu.model.last_msg(i, msg, variables.USER_THEM) if self.main_widget.currentIndex( ) == 1 and self.main_widget.currentWidget().ip == ip: self.dialogs[i].model.send_read() else: self.dialog_menu.model.set_status(i, variables.STATUS_NEW) def messageSent(self, msg, ip): for i in range(len(self.dialogs)): if self.dialogs[i].ip == ip: self.dialog_menu.model.last_msg(i, msg, variables.USER_THEM) def undelivered(self, id, ip): for i in range(len(self.dialogs)): if self.dialogs[i].ip == ip: self.dialogs[i].model.setStatus(id, variables.STATUS_UNDELIVERED) self.dialog_menu.model.set_status(i, variables.STATUS_UNDELIVERED) def delivered(self, id, ip): for i in range(len(self.dialogs)): if self.dialogs[i].ip == ip: self.dialogs[i].model.setStatus(id, variables.STATUS_UNREAD) self.dialog_menu.model.set_status(i, variables.STATUS_UNREAD) def read(self, id, ip): for i in range(len(self.dialogs)): if self.dialogs[i].ip == ip: self.dialogs[i].model.setStatus(id, variables.STATUS_READ) self.dialog_menu.model.set_status(i, variables.STATUS_READ) def clientReconnected(self, ip): for i in range(len(self.dialogs)): if self.dialogs[i].ip == ip: self.dialogs[i].model.new_base()
class _ToolsDock(ui_tools.StyledBar): def __init__(self, parent=None): super().__init__(parent) # Register signals connections # connections = ( # { # "target": "main_container", # "signal_name": "runFile", # "slot": self.execute_file # }, # ) # Buttons Widget self._buttons_widget = ui_tools.StyledBar() self._buttons_widget.setProperty("border", True) self._buttons_widget.setFixedHeight(26) self._buttons_widget.setLayout(QHBoxLayout()) self._buttons_widget.layout().setContentsMargins(2, 2, 0, 2) self._buttons_widget.layout().setSpacing(10) IDE.register_service('tools_dock', self) def install(self): self.setup_ui() ide = IDE.get_service('ide') ide.place_me_on('tools_dock', self, 'central') ui_tools.install_shortcuts(self, actions.ACTIONS, ide) ide.goingDown.connect(self._save_settings) settings = IDE.ninja_settings() index = int(settings.value("tools_dock/tool_visible", -1)) if index == -1: self.hide() else: self.set_current_index(index) def setup_ui(self): self._stack = QStackedWidget() self.__current_widget = None self.__last_index = -1 self.__buttons = [] main_layout = QVBoxLayout(self) main_layout.setContentsMargins(0, 0, 0, 0) main_layout.setSpacing(0) # toolbar = StyledBar() tool_layout = QVBoxLayout() tool_layout.setContentsMargins(0, 0, 0, 0) tool_layout.setSpacing(0) self._run_widget = run_widget.RunWidget() main_layout.addLayout(tool_layout) self._tool_stack = QStackedWidget() self._tool_stack.setMaximumHeight(22) # self._tool_stack.setMaximumWidth(22) tool_layout.addWidget(self._tool_stack) # FIXME: poner en stack # clear_btn = QToolButton() # clear_btn.setIcon(QIcon(self.style().standardIcon(QStyle.SP_LineEditClearButton))) # clear_btn.clicked.connect(self._run_widget.output.clear) # tool_layout.addWidget(clear_btn) # tool_layout.addItem(QSpacerItem(0, 0, QSizePolicy.Expanding)) # tool_layout.addWidget(close_button) # main_layout.addWidget(toolbar) main_layout.addWidget(self._stack) # Widgets # errors_tree = IDE.get_service('errors_tree') from ninja_ide.gui.tools_dock import find_in_files self.widgets = [ self._run_widget, console_widget.ConsoleWidget(), find_in_files.FindInFilesWidget(), # errors_tree ] # Intall widgets number = 1 for wi in self.widgets: if wi is None: continue btn = ToolButton(number, wi.display_name()) btn.setCheckable(True) # Action # action = QAction(wi.display_name(), self) # action.triggered.connect(self._triggered) number += 1 self.__buttons.append(btn) self._buttons_widget.layout().addWidget(btn) self._stack.addWidget(wi) btn.clicked.connect(self._button_triggered) # Toolbar buttons container = QWidget(self._tool_stack) tool_buttons_layout = QHBoxLayout() tool_buttons_layout.setContentsMargins(0, 0, 0, 0) tool_buttons_layout.setSpacing(0) for b in wi.button_widgets(): tool_buttons_layout.addWidget(b) tool_buttons_layout.addStretch(5) container.setLayout(tool_buttons_layout) self._tool_stack.addWidget(container) self._buttons_widget.layout().addItem( QSpacerItem(0, 0, QSizePolicy.Expanding)) self.__current_widget = self._stack.currentWidget() self.set_current_index(0) def execute_file(self): """Execute the current file""" main_container = IDE.get_service("main_container") editor_widget = main_container.get_current_editor() if editor_widget is not None and editor_widget.is_modified or \ editor_widget.file_path: main_container.save_file(editor_widget) # FIXME: Emit a signal for plugin! # self.fileExecuted.emit(editor_widget.file_path) file_path = editor_widget.file_path extension = file_manager.get_file_extension(file_path) # TODO: Remove the IF statment and use Handlers if extension == "py": self._run_application(file_path) def _run_application(self, filename): """Execute the process to run the application""" self._show(0) # Show widget in index = 0 self._run_widget.start_process(filename) self._run_widget.input.setFocus() @pyqtSlot() def _button_triggered(self): # Get ToolButton index button = self.sender() index = self.__buttons.index(button) if index == self.current_index() and self._is_current_visible(): self._hide() else: self._show(index) def _show(self, index): self.show() self.widgets[index].setVisible(True) self.__current_widget = self.widgets[index] self.set_current_index(index) def _hide(self): self.__current_widget.setVisible(False) index = self.current_index() self.__buttons[index].setChecked(False) self.widgets[index].setVisible(False) self.hide() def showEvent(self, event): super().showEvent(event) self._stack.currentWidget().setFocus() def set_current_index(self, index): if self.__last_index != -1: self.__buttons[self.__last_index].setChecked(False) self.__buttons[index].setChecked(True) if index != -1: self._stack.setCurrentIndex(index) self._tool_stack.setCurrentIndex(index) tool = self.widgets[index] tool.setVisible(True) self.__last_index = index def current_index(self): return self._stack.currentIndex() def _is_current_visible(self): return self.__current_widget and self.__current_widget.isVisible() def _save_settings(self): settings = IDE.ninja_settings() visible_index = self.current_index() if not self.isVisible(): visible_index = -1 settings.setValue("tools_dock/tool_visible", visible_index)
class TabBarWindow(TabWindow): """Implementation which uses a separate QTabBar and QStackedWidget. The Tab bar is placed next to the menu bar to save real estate.""" def __init__(self, app, **kwargs): super().__init__(app, **kwargs) def _setupUi(self): self.setWindowTitle(self.app.NAME) self.resize(640, 480) self.tabBar = QTabBar() self.verticalLayout = QVBoxLayout() self.verticalLayout.setContentsMargins(0, 0, 0, 0) self._setupActions() self._setupMenu() self.centralWidget = QWidget(self) self.setCentralWidget(self.centralWidget) self.stackedWidget = QStackedWidget() self.centralWidget.setLayout(self.verticalLayout) self.horizontalLayout = QHBoxLayout() self.horizontalLayout.addWidget(self.menubar, 0, Qt.AlignTop) self.horizontalLayout.addWidget(self.tabBar, 0, Qt.AlignTop) self.verticalLayout.addLayout(self.horizontalLayout) self.verticalLayout.addWidget(self.stackedWidget) self.tabBar.currentChanged.connect(self.showTabIndex) self.tabBar.tabCloseRequested.connect(self.onTabCloseRequested) self.stackedWidget.currentChanged.connect(self.updateMenuBar) self.stackedWidget.widgetRemoved.connect(self.onRemovedWidget) self.tabBar.setTabsClosable(True) self.restoreGeometry() def addTab(self, page, title, switch=True): stack_index = self.stackedWidget.addWidget(page) self.tabBar.insertTab(stack_index, title) if isinstance(page, DirectoriesDialog): self.tabBar.setTabButton( stack_index, QTabBar.RightSide, None) if switch: # switch to the added tab immediately upon creation self.setTabIndex(stack_index) return stack_index @pyqtSlot(int) def showTabIndex(self, index): # The tab bar's indices should be aligned with the stackwidget's if index >= 0 and index <= self.stackedWidget.count(): self.stackedWidget.setCurrentIndex(index) def indexOfWidget(self, widget): # Warning: this may return -1 if widget is not a child of stackedwidget return self.stackedWidget.indexOf(widget) def setCurrentIndex(self, tab_index): self.setTabIndex(tab_index) # The signal will handle switching the stackwidget's widget # self.stackedWidget.setCurrentWidget(self.stackedWidget.widget(tab_index)) def setCurrentWidget(self, widget): """Sets the current Tab on TabBar for this widget.""" self.tabBar.setCurrentIndex(self.indexOfWidget(widget)) @pyqtSlot(int) def setTabIndex(self, index): if index is None: return self.tabBar.setCurrentIndex(index) @pyqtSlot(int) def onRemovedWidget(self, index): self.removeTab(index) @pyqtSlot(int) def removeTab(self, index): """Remove the tab, but not the widget (it should already be removed)""" return self.tabBar.removeTab(index) @pyqtSlot(int) def removeWidget(self, widget): return self.stackedWidget.removeWidget(widget) def isTabVisible(self, index): return self.tabBar.isTabVisible(index) def getCurrentIndex(self): return self.stackedWidget.currentIndex() def getWidgetAtIndex(self, index): return self.stackedWidget.widget(index) def getCount(self): return self.stackedWidget.count() @pyqtSlot() def toggleTabBar(self): value = self.sender().isChecked() self.actionToggleTabs.setChecked(value) self.tabBar.setVisible(value) @pyqtSlot(int) def onTabCloseRequested(self, index): target_widget = self.getWidgetAtIndex(index) if isinstance(target_widget, DirectoriesDialog): # On MacOS, the tab has a close button even though we explicitely # set it to None in order to hide it. This should prevent # the "Directories" tab from closing by mistake. return # target_widget.close() # seems unnecessary # Removing the widget should trigger tab removal via the signal self.removeWidget(self.getWidgetAtIndex(index)) @pyqtSlot() def onDialogAccepted(self): """Remove tabbed dialog when Accepted/Done (close button clicked).""" widget = self.sender() self.removeWidget(widget)
class PyMultiPageWidget(QWidget): currentIndexChanged = pyqtSignal(int) pageTitleChanged = pyqtSignal(str) def __init__(self, parent=None): super(PyMultiPageWidget, self).__init__(parent) self.comboBox = QComboBox() self.comboBox.setObjectName('__qt__passive_comboBox') self.stackWidget = QStackedWidget() self.comboBox.activated.connect(self.setCurrentIndex) self.layout = QVBoxLayout() self.layout.addWidget(self.comboBox) self.layout.addWidget(self.stackWidget) self.setLayout(self.layout) def sizeHint(self): return QSize(200, 150) def count(self): return self.stackWidget.count() def widget(self, index): return self.stackWidget.widget(index) @pyqtSlot(QWidget) def addPage(self, page): self.insertPage(self.count(), page) @pyqtSlot(int, QWidget) def insertPage(self, index, page): page.setParent(self.stackWidget) self.stackWidget.insertWidget(index, page) title = page.windowTitle() if title == "": title = "Page %d" % (self.comboBox.count() + 1) page.setWindowTitle(title) self.comboBox.insertItem(index, title) @pyqtSlot(int) def removePage(self, index): widget = self.stackWidget.widget(index) self.stackWidget.removeWidget(widget) self.comboBox.removeItem(index) def getPageTitle(self): cw = self.stackWidget.currentWidget() return cw.windowTitle() if cw is not None else '' @pyqtSlot(str) def setPageTitle(self, newTitle): cw = self.stackWidget.currentWidget() if cw is not None: self.comboBox.setItemText(self.getCurrentIndex(), newTitle) cw.setWindowTitle(newTitle) self.pageTitleChanged.emit(newTitle) def getCurrentIndex(self): return self.stackWidget.currentIndex() @pyqtSlot(int) def setCurrentIndex(self, index): if index != self.getCurrentIndex(): self.stackWidget.setCurrentIndex(index) self.comboBox.setCurrentIndex(index) self.currentIndexChanged.emit(index) pageTitle = pyqtProperty(str, fget=getPageTitle, fset=setPageTitle, stored=False) currentIndex = pyqtProperty(int, fget=getCurrentIndex, fset=setCurrentIndex)
class PyMultiPageWidget(QWidget): currentIndexChanged = pyqtSignal(int) pageTitleChanged = pyqtSignal(str) def __init__(self, parent=None): super(PyMultiPageWidget, self).__init__(parent) self.comboBox = QComboBox() # MAGIC # It is important that the combo box has an object name beginning # with '__qt__passive_', otherwise, it is inactive in the form editor # of the designer and you can't change the current page via the # combo box. # MAGIC self.comboBox.setObjectName('__qt__passive_comboBox') self.stackWidget = QStackedWidget() self.comboBox.activated.connect(self.setCurrentIndex) self.layout = QVBoxLayout() self.layout.addWidget(self.comboBox) self.layout.addWidget(self.stackWidget) self.setLayout(self.layout) def sizeHint(self): return QSize(200, 150) def count(self): return self.stackWidget.count() def widget(self, index): return self.stackWidget.widget(index) @pyqtSlot(QWidget) def addPage(self, page): self.insertPage(self.count(), page) @pyqtSlot(int, QWidget) def insertPage(self, index, page): page.setParent(self.stackWidget) self.stackWidget.insertWidget(index, page) title = page.windowTitle() if title == "": title = "Page %d" % (self.comboBox.count() + 1) page.setWindowTitle(title) self.comboBox.insertItem(index, title) @pyqtSlot(int) def removePage(self, index): widget = self.stackWidget.widget(index) self.stackWidget.removeWidget(widget) self.comboBox.removeItem(index) def getPageTitle(self): cw = self.stackWidget.currentWidget() return cw.windowTitle() if cw is not None else '' @pyqtSlot(str) def setPageTitle(self, newTitle): cw = self.stackWidget.currentWidget() if cw is not None: self.comboBox.setItemText(self.getCurrentIndex(), newTitle) cw.setWindowTitle(newTitle) self.pageTitleChanged.emit(newTitle) def getCurrentIndex(self): return self.stackWidget.currentIndex() @pyqtSlot(int) def setCurrentIndex(self, index): if index != self.getCurrentIndex(): self.stackWidget.setCurrentIndex(index) self.comboBox.setCurrentIndex(index) self.currentIndexChanged.emit(index) pageTitle = pyqtProperty(str, fget=getPageTitle, fset=setPageTitle, stored=False) currentIndex = pyqtProperty(int, fget=getCurrentIndex, fset=setCurrentIndex)
class CityWidget(QWidget): def __init__(self, name: str, units: UnitSystem = UnitSystem.metric, foreground_color="#ffffff", font_name=""): super(CityWidget, self).__init__() self.city_name = name self.units = units self.foreground_color = foreground_color self.font_name = font_name self.label_style = "QLabel { color : " + self.foreground_color + "; }" self.high_label_style = "QLabel { color : #ffba26; }" self.button_style = "QPushButton { background-color: " + self.foreground_color + "; border: 2px; " \ "border-radius: 20px; " \ "border-style: outset;}" self.line_style = "QLabel { background-color: " + self.foreground_color + "; border: 2px; border-radius: 4px;}" self.font = QFont(font_name, 30, QFont.Bold) self.font_small = QFont(font_name, 20, QFont.Bold) self.weather_data = {} self.main_layout = QVBoxLayout() self.view_stack = QStackedWidget() self.main_layout.addWidget(self.view_stack) self.temp_view = SimpleTempViewWidget(self, units, self.foreground_color, self.font_name) self.view_stack.addWidget(self.temp_view) self.wind_view = SimpleWindViewWidget(self, units, self.foreground_color, self.font_name) self.view_stack.addWidget(self.wind_view) self.separator_line = QHBoxLayout() self.main_layout.addLayout(self.separator_line) self.line = QLabel() self.line.setStyleSheet(self.line_style) self.line.setFixedSize(640, 10) self.separator_line.addStretch(1) self.separator_line.addWidget(self.line) self.separator_line.addStretch(1) self.bottom_line = QHBoxLayout() self.main_layout.addLayout(self.bottom_line) self.left_button = QPushButton("<") self.left_button.setFixedSize(40, 80) self.left_button.setStyleSheet(self.button_style) self.bottom_widget_name_backward = QLabel() self.set_label_style(self.bottom_widget_name_backward, self.font_small, self.label_style) self.bottom_widget_name_current = QLabel() self.set_label_style(self.bottom_widget_name_current, self.font, self.high_label_style) self.bottom_widget_name_foreward = QLabel() self.set_label_style(self.bottom_widget_name_foreward, self.font_small, self.label_style) self.right_button = QPushButton(">") self.right_button.setFixedSize(40, 80) self.right_button.setStyleSheet(self.button_style) self.bottom_line.addWidget(self.left_button) self.bottom_line.addStretch(1) self.bottom_line.addWidget(self.bottom_widget_name_backward) self.bottom_line.addSpacing(30) self.bottom_line.addWidget(self.bottom_widget_name_current) self.bottom_line.addSpacing(30) self.bottom_line.addWidget(self.bottom_widget_name_foreward) self.bottom_line.addStretch(1) self.bottom_line.addWidget(self.right_button) self.setLayout(self.main_layout) self.set_bottom_line() # Timer self.switch_timer = QTimer(self) self.switch_timer.setInterval(10000) # 10 seconds self.switch_timer.timeout.connect(lambda: self.toggle_main_widget(+1)) self.switch_timer.start() self.left_button.clicked.connect( lambda state: self.toggle_main_widget(-1)) self.right_button.clicked.connect( lambda state: self.toggle_main_widget(+1)) def set_units(self, units: UnitSystem): self.units = units def get_units(self) -> UnitSystem: return self.units.name def switch_view(self, index: int = 0): max_value = self.self.view_stack.count() self.self.view_stack.setCurrentIndex(index % max_value) def set_data(self, data: dict): for i in range(0, self.view_stack.count()): self.view_stack.widget(i).set_data(data) def get_name(self) -> str: return self.city_name def set_label_style(self, label: QLabel, font: QFont, style: str): label.setStyleSheet(style) label.setFont(font) def set_bottom_line(self): max_value = self.view_stack.count() current = self.view_stack.currentIndex() if max_value == 1: self.bottom_widget_name_backward.setText("") self.bottom_widget_name_current.setText( self.view_stack.widget(current).get_name()) self.bottom_widget_name_foreward.setText("") return self.bottom_widget_name_backward.setText( self.view_stack.widget((current - 1) % max_value).get_name()) self.bottom_widget_name_current.setText( self.view_stack.widget(current).get_name()) self.bottom_widget_name_foreward.setText( self.view_stack.widget((current + 1) % max_value).get_name()) def toggle_main_widget(self, index: int): max_value = self.view_stack.count() current = self.view_stack.currentIndex() self.view_stack.setCurrentIndex((current + index) % max_value) self.set_bottom_line()
class E5SideBar(QWidget): """ Class implementing a sidebar with a widget area, that is hidden or shown, if the current tab is clicked again. """ Version = 1 North = 0 East = 1 South = 2 West = 3 def __init__(self, orientation=None, delay=200, parent=None): """ Constructor @param orientation orientation of the sidebar widget (North, East, South, West) @param delay value for the expand/shrink delay in milliseconds (integer) @param parent parent widget (QWidget) """ super(E5SideBar, self).__init__(parent) self.__tabBar = QTabBar() self.__tabBar.setDrawBase(True) self.__tabBar.setShape(QTabBar.RoundedNorth) self.__tabBar.setUsesScrollButtons(True) self.__tabBar.setDrawBase(False) self.__stackedWidget = QStackedWidget(self) self.__stackedWidget.setContentsMargins(0, 0, 0, 0) self.__autoHideButton = QToolButton() self.__autoHideButton.setCheckable(True) self.__autoHideButton.setIcon( UI.PixmapCache.getIcon("autoHideOff.png")) self.__autoHideButton.setChecked(True) self.__autoHideButton.setToolTip( self.tr("Deselect to activate automatic collapsing")) self.barLayout = QBoxLayout(QBoxLayout.LeftToRight) self.barLayout.setContentsMargins(0, 0, 0, 0) self.layout = QBoxLayout(QBoxLayout.TopToBottom) self.layout.setContentsMargins(0, 0, 0, 0) self.layout.setSpacing(0) self.barLayout.addWidget(self.__autoHideButton) self.barLayout.addWidget(self.__tabBar) self.layout.addLayout(self.barLayout) self.layout.addWidget(self.__stackedWidget) self.setLayout(self.layout) # initialize the delay timer self.__actionMethod = None self.__delayTimer = QTimer(self) self.__delayTimer.setSingleShot(True) self.__delayTimer.setInterval(delay) self.__delayTimer.timeout.connect(self.__delayedAction) self.__minimized = False self.__minSize = 0 self.__maxSize = 0 self.__bigSize = QSize() self.splitter = None self.splitterSizes = [] self.__hasFocus = False # flag storing if this widget or any child has the focus self.__autoHide = False self.__tabBar.installEventFilter(self) self.__orientation = E5SideBar.North if orientation is None: orientation = E5SideBar.North self.setOrientation(orientation) self.__tabBar.currentChanged[int].connect( self.__stackedWidget.setCurrentIndex) e5App().focusChanged[QWidget, QWidget].connect(self.__appFocusChanged) self.__autoHideButton.toggled[bool].connect(self.__autoHideToggled) def setSplitter(self, splitter): """ Public method to set the splitter managing the sidebar. @param splitter reference to the splitter (QSplitter) """ self.splitter = splitter self.splitter.splitterMoved.connect(self.__splitterMoved) self.splitter.setChildrenCollapsible(False) index = self.splitter.indexOf(self) self.splitter.setCollapsible(index, False) def __splitterMoved(self, pos, index): """ Private slot to react on splitter moves. @param pos new position of the splitter handle (integer) @param index index of the splitter handle (integer) """ if self.splitter: self.splitterSizes = self.splitter.sizes() def __delayedAction(self): """ Private slot to handle the firing of the delay timer. """ if self.__actionMethod is not None: self.__actionMethod() def setDelay(self, delay): """ Public method to set the delay value for the expand/shrink delay in milliseconds. @param delay value for the expand/shrink delay in milliseconds (integer) """ self.__delayTimer.setInterval(delay) def delay(self): """ Public method to get the delay value for the expand/shrink delay in milliseconds. @return value for the expand/shrink delay in milliseconds (integer) """ return self.__delayTimer.interval() def __cancelDelayTimer(self): """ Private method to cancel the current delay timer. """ self.__delayTimer.stop() self.__actionMethod = None def shrink(self): """ Public method to record a shrink request. """ self.__delayTimer.stop() self.__actionMethod = self.__shrinkIt self.__delayTimer.start() def __shrinkIt(self): """ Private method to shrink the sidebar. """ self.__minimized = True self.__bigSize = self.size() if self.__orientation in [E5SideBar.North, E5SideBar.South]: self.__minSize = self.minimumSizeHint().height() self.__maxSize = self.maximumHeight() else: self.__minSize = self.minimumSizeHint().width() self.__maxSize = self.maximumWidth() if self.splitter: self.splitterSizes = self.splitter.sizes() self.__stackedWidget.hide() if self.__orientation in [E5SideBar.North, E5SideBar.South]: self.setFixedHeight(self.__tabBar.minimumSizeHint().height()) else: self.setFixedWidth(self.__tabBar.minimumSizeHint().width()) self.__actionMethod = None def expand(self): """ Public method to record a expand request. """ self.__delayTimer.stop() self.__actionMethod = self.__expandIt self.__delayTimer.start() def __expandIt(self): """ Private method to expand the sidebar. """ self.__minimized = False self.__stackedWidget.show() self.resize(self.__bigSize) if self.__orientation in [E5SideBar.North, E5SideBar.South]: minSize = max(self.__minSize, self.minimumSizeHint().height()) self.setMinimumHeight(minSize) self.setMaximumHeight(self.__maxSize) else: minSize = max(self.__minSize, self.minimumSizeHint().width()) self.setMinimumWidth(minSize) self.setMaximumWidth(self.__maxSize) if self.splitter: self.splitter.setSizes(self.splitterSizes) self.__actionMethod = None def isMinimized(self): """ Public method to check the minimized state. @return flag indicating the minimized state (boolean) """ return self.__minimized def isAutoHiding(self): """ Public method to check, if the auto hide function is active. @return flag indicating the state of auto hiding (boolean) """ return self.__autoHide def eventFilter(self, obj, evt): """ Public method to handle some events for the tabbar. @param obj reference to the object (QObject) @param evt reference to the event object (QEvent) @return flag indicating, if the event was handled (boolean) """ if obj == self.__tabBar: if evt.type() == QEvent.MouseButtonPress: pos = evt.pos() for i in range(self.__tabBar.count()): if self.__tabBar.tabRect(i).contains(pos): break if i == self.__tabBar.currentIndex(): if self.isMinimized(): self.expand() else: self.shrink() return True elif self.isMinimized(): self.expand() elif evt.type() == QEvent.Wheel: if qVersion() >= "5.0.0": delta = evt.angleDelta().y() else: delta = evt.delta() if delta > 0: self.prevTab() else: self.nextTab() return True return QWidget.eventFilter(self, obj, evt) def addTab(self, widget, iconOrLabel, label=None): """ Public method to add a tab to the sidebar. @param widget reference to the widget to add (QWidget) @param iconOrLabel reference to the icon or the label text of the tab (QIcon, string) @param label the labeltext of the tab (string) (only to be used, if the second parameter is a QIcon) """ if label: index = self.__tabBar.addTab(iconOrLabel, label) self.__tabBar.setTabToolTip(index, label) else: index = self.__tabBar.addTab(iconOrLabel) self.__tabBar.setTabToolTip(index, iconOrLabel) self.__stackedWidget.addWidget(widget) if self.__orientation in [E5SideBar.North, E5SideBar.South]: self.__minSize = self.minimumSizeHint().height() else: self.__minSize = self.minimumSizeHint().width() def insertTab(self, index, widget, iconOrLabel, label=None): """ Public method to insert a tab into the sidebar. @param index the index to insert the tab at (integer) @param widget reference to the widget to insert (QWidget) @param iconOrLabel reference to the icon or the labeltext of the tab (QIcon, string) @param label the labeltext of the tab (string) (only to be used, if the second parameter is a QIcon) """ if label: index = self.__tabBar.insertTab(index, iconOrLabel, label) self.__tabBar.setTabToolTip(index, label) else: index = self.__tabBar.insertTab(index, iconOrLabel) self.__tabBar.setTabToolTip(index, iconOrLabel) self.__stackedWidget.insertWidget(index, widget) if self.__orientation in [E5SideBar.North, E5SideBar.South]: self.__minSize = self.minimumSizeHint().height() else: self.__minSize = self.minimumSizeHint().width() def removeTab(self, index): """ Public method to remove a tab. @param index the index of the tab to remove (integer) """ self.__stackedWidget.removeWidget(self.__stackedWidget.widget(index)) self.__tabBar.removeTab(index) if self.__orientation in [E5SideBar.North, E5SideBar.South]: self.__minSize = self.minimumSizeHint().height() else: self.__minSize = self.minimumSizeHint().width() def clear(self): """ Public method to remove all tabs. """ while self.count() > 0: self.removeTab(0) def prevTab(self): """ Public slot used to show the previous tab. """ ind = self.currentIndex() - 1 if ind == -1: ind = self.count() - 1 self.setCurrentIndex(ind) self.currentWidget().setFocus() def nextTab(self): """ Public slot used to show the next tab. """ ind = self.currentIndex() + 1 if ind == self.count(): ind = 0 self.setCurrentIndex(ind) self.currentWidget().setFocus() def count(self): """ Public method to get the number of tabs. @return number of tabs in the sidebar (integer) """ return self.__tabBar.count() def currentIndex(self): """ Public method to get the index of the current tab. @return index of the current tab (integer) """ return self.__stackedWidget.currentIndex() def setCurrentIndex(self, index): """ Public slot to set the current index. @param index the index to set as the current index (integer) """ self.__tabBar.setCurrentIndex(index) self.__stackedWidget.setCurrentIndex(index) if self.isMinimized(): self.expand() def currentWidget(self): """ Public method to get a reference to the current widget. @return reference to the current widget (QWidget) """ return self.__stackedWidget.currentWidget() def setCurrentWidget(self, widget): """ Public slot to set the current widget. @param widget reference to the widget to become the current widget (QWidget) """ self.__stackedWidget.setCurrentWidget(widget) self.__tabBar.setCurrentIndex(self.__stackedWidget.currentIndex()) if self.isMinimized(): self.expand() def indexOf(self, widget): """ Public method to get the index of the given widget. @param widget reference to the widget to get the index of (QWidget) @return index of the given widget (integer) """ return self.__stackedWidget.indexOf(widget) def isTabEnabled(self, index): """ Public method to check, if a tab is enabled. @param index index of the tab to check (integer) @return flag indicating the enabled state (boolean) """ return self.__tabBar.isTabEnabled(index) def setTabEnabled(self, index, enabled): """ Public method to set the enabled state of a tab. @param index index of the tab to set (integer) @param enabled enabled state to set (boolean) """ self.__tabBar.setTabEnabled(index, enabled) def orientation(self): """ Public method to get the orientation of the sidebar. @return orientation of the sidebar (North, East, South, West) """ return self.__orientation def setOrientation(self, orient): """ Public method to set the orientation of the sidebar. @param orient orientation of the sidebar (North, East, South, West) """ if orient == E5SideBar.North: self.__tabBar.setShape(QTabBar.RoundedNorth) self.__tabBar.setSizePolicy( QSizePolicy.Expanding, QSizePolicy.Preferred) self.barLayout.setDirection(QBoxLayout.LeftToRight) self.layout.setDirection(QBoxLayout.TopToBottom) self.layout.setAlignment(self.barLayout, Qt.AlignLeft) elif orient == E5SideBar.East: self.__tabBar.setShape(QTabBar.RoundedEast) self.__tabBar.setSizePolicy( QSizePolicy.Preferred, QSizePolicy.Expanding) self.barLayout.setDirection(QBoxLayout.TopToBottom) self.layout.setDirection(QBoxLayout.RightToLeft) self.layout.setAlignment(self.barLayout, Qt.AlignTop) elif orient == E5SideBar.South: self.__tabBar.setShape(QTabBar.RoundedSouth) self.__tabBar.setSizePolicy( QSizePolicy.Expanding, QSizePolicy.Preferred) self.barLayout.setDirection(QBoxLayout.LeftToRight) self.layout.setDirection(QBoxLayout.BottomToTop) self.layout.setAlignment(self.barLayout, Qt.AlignLeft) elif orient == E5SideBar.West: self.__tabBar.setShape(QTabBar.RoundedWest) self.__tabBar.setSizePolicy( QSizePolicy.Preferred, QSizePolicy.Expanding) self.barLayout.setDirection(QBoxLayout.TopToBottom) self.layout.setDirection(QBoxLayout.LeftToRight) self.layout.setAlignment(self.barLayout, Qt.AlignTop) self.__orientation = orient def tabIcon(self, index): """ Public method to get the icon of a tab. @param index index of the tab (integer) @return icon of the tab (QIcon) """ return self.__tabBar.tabIcon(index) def setTabIcon(self, index, icon): """ Public method to set the icon of a tab. @param index index of the tab (integer) @param icon icon to be set (QIcon) """ self.__tabBar.setTabIcon(index, icon) def tabText(self, index): """ Public method to get the text of a tab. @param index index of the tab (integer) @return text of the tab (string) """ return self.__tabBar.tabText(index) def setTabText(self, index, text): """ Public method to set the text of a tab. @param index index of the tab (integer) @param text text to set (string) """ self.__tabBar.setTabText(index, text) def tabToolTip(self, index): """ Public method to get the tooltip text of a tab. @param index index of the tab (integer) @return tooltip text of the tab (string) """ return self.__tabBar.tabToolTip(index) def setTabToolTip(self, index, tip): """ Public method to set the tooltip text of a tab. @param index index of the tab (integer) @param tip tooltip text to set (string) """ self.__tabBar.setTabToolTip(index, tip) def tabWhatsThis(self, index): """ Public method to get the WhatsThis text of a tab. @param index index of the tab (integer) @return WhatsThis text of the tab (string) """ return self.__tabBar.tabWhatsThis(index) def setTabWhatsThis(self, index, text): """ Public method to set the WhatsThis text of a tab. @param index index of the tab (integer) @param text WhatsThis text to set (string) """ self.__tabBar.setTabWhatsThis(index, text) def widget(self, index): """ Public method to get a reference to the widget associated with a tab. @param index index of the tab (integer) @return reference to the widget (QWidget) """ return self.__stackedWidget.widget(index) def saveState(self): """ Public method to save the state of the sidebar. @return saved state as a byte array (QByteArray) """ if len(self.splitterSizes) == 0: if self.splitter: self.splitterSizes = self.splitter.sizes() self.__bigSize = self.size() if self.__orientation in [E5SideBar.North, E5SideBar.South]: self.__minSize = self.minimumSizeHint().height() self.__maxSize = self.maximumHeight() else: self.__minSize = self.minimumSizeHint().width() self.__maxSize = self.maximumWidth() data = QByteArray() stream = QDataStream(data, QIODevice.WriteOnly) stream.setVersion(QDataStream.Qt_4_6) stream.writeUInt16(self.Version) stream.writeBool(self.__minimized) stream << self.__bigSize stream.writeUInt16(self.__minSize) stream.writeUInt16(self.__maxSize) stream.writeUInt16(len(self.splitterSizes)) for size in self.splitterSizes: stream.writeUInt16(size) stream.writeBool(self.__autoHide) return data def restoreState(self, state): """ Public method to restore the state of the sidebar. @param state byte array containing the saved state (QByteArray) @return flag indicating success (boolean) """ if state.isEmpty(): return False if self.__orientation in [E5SideBar.North, E5SideBar.South]: minSize = self.layout.minimumSize().height() maxSize = self.maximumHeight() else: minSize = self.layout.minimumSize().width() maxSize = self.maximumWidth() data = QByteArray(state) stream = QDataStream(data, QIODevice.ReadOnly) stream.setVersion(QDataStream.Qt_4_6) stream.readUInt16() # version minimized = stream.readBool() if minimized and not self.__minimized: self.shrink() stream >> self.__bigSize self.__minSize = max(stream.readUInt16(), minSize) self.__maxSize = max(stream.readUInt16(), maxSize) count = stream.readUInt16() self.splitterSizes = [] for i in range(count): self.splitterSizes.append(stream.readUInt16()) self.__autoHide = stream.readBool() self.__autoHideButton.setChecked(not self.__autoHide) if not minimized: self.expand() return True ####################################################################### ## methods below implement the autohide functionality ####################################################################### def __autoHideToggled(self, checked): """ Private slot to handle the toggling of the autohide button. @param checked flag indicating the checked state of the button (boolean) """ self.__autoHide = not checked if self.__autoHide: self.__autoHideButton.setIcon( UI.PixmapCache.getIcon("autoHideOn.png")) else: self.__autoHideButton.setIcon( UI.PixmapCache.getIcon("autoHideOff.png")) def __appFocusChanged(self, old, now): """ Private slot to handle a change of the focus. @param old reference to the widget, that lost focus (QWidget or None) @param now reference to the widget having the focus (QWidget or None) """ self.__hasFocus = self.isAncestorOf(now) if self.__autoHide and not self.__hasFocus and not self.isMinimized(): self.shrink() elif self.__autoHide and self.__hasFocus and self.isMinimized(): self.expand() def enterEvent(self, event): """ Protected method to handle the mouse entering this widget. @param event reference to the event (QEvent) """ if self.__autoHide and self.isMinimized(): self.expand() else: self.__cancelDelayTimer() def leaveEvent(self, event): """ Protected method to handle the mouse leaving this widget. @param event reference to the event (QEvent) """ if self.__autoHide and not self.__hasFocus and not self.isMinimized(): self.shrink() else: self.__cancelDelayTimer() def shutdown(self): """ Public method to shut down the object. This method does some preparations so the object can be deleted properly. It disconnects from the focusChanged signal in order to avoid trouble later on. """ e5App().focusChanged[QWidget, QWidget].disconnect( self.__appFocusChanged)
class StartWidget(QWidget): def __init__(self, parent, foreground_color="#ffffff", font_name=""): super(StartWidget, self).__init__() self.foreground_color = foreground_color self.font_name = font_name self.parent = parent self.key = "" self.language = "de" self.font = QFont(font_name, 40, QFont.Bold) self.font_small = QFont(font_name, 20, QFont.Bold) self.high_label_style = "QLabel { color : #ffba26; }" self.label_style = "QLabel { color : " + self.foreground_color + "; }" style = "QPushButton { background-color: " + self.foreground_color + "; border: 2px; border-radius: 20px; border-style: outset;}" self.line_style = "QLabel { background-color: " + self.foreground_color + "; border: 2px; border-radius: 4px;}" self.view_stack = QStackedWidget() self.view_stack.addWidget(SetupWidget(self, self.foreground_color)) self.main_layout = QVBoxLayout() self.head_line = QHBoxLayout() self.separator_line = QHBoxLayout() self.main_line = QHBoxLayout() self.head_widget_name_backward = QLabel() self.set_label_style(self.head_widget_name_backward, self.font_small, self.label_style) self.head_widget_name_current = QLabel() self.set_label_style(self.head_widget_name_current, self.font, self.high_label_style) self.head_widget_name_foreward = QLabel() self.set_label_style(self.head_widget_name_foreward, self.font_small, self.label_style) self.line = QLabel() self.line.setStyleSheet(self.line_style) self.line.setFixedSize(640, 10) self.separator_line.addStretch(1) self.separator_line.addWidget(self.line) self.separator_line.addStretch(1) self.left_button = QPushButton("<") self.left_button.setFixedSize(40, 80) self.left_button.setStyleSheet(style) self.right_button = QPushButton(">") self.right_button.setFixedSize(40, 80) self.right_button.setStyleSheet(style) self.head_line.addWidget(self.left_button) self.head_line.addStretch(1) self.head_line.addWidget(self.head_widget_name_backward) self.head_line.addSpacing(30) self.head_line.addWidget(self.head_widget_name_current) self.head_line.addSpacing(30) self.head_line.addWidget(self.head_widget_name_foreward) self.head_line.addStretch(1) self.head_line.addWidget(self.right_button) self.main_line.addWidget(self.view_stack) self.left_button.clicked.connect( lambda state: self.toggle_main_widget(-1)) self.right_button.clicked.connect( lambda state: self.toggle_main_widget(+1)) self.main_layout.addLayout(self.head_line) self.main_layout.addLayout(self.separator_line) self.main_layout.addLayout(self.main_line) self.set_headline() self.setLayout(self.main_layout) def set_headline(self): max_value = self.view_stack.count() current = self.view_stack.currentIndex() if max_value == 1: self.head_widget_name_backward.setText("") self.head_widget_name_current.setText( self.view_stack.widget(current).get_name()) self.head_widget_name_foreward.setText("") return self.head_widget_name_backward.setText( self.view_stack.widget((current - 1) % max_value).get_name()) self.head_widget_name_current.setText( self.view_stack.widget(current).get_name()) self.head_widget_name_foreward.setText( self.view_stack.widget((current + 1) % max_value).get_name()) def toggle_main_widget(self, index: int): max_value = self.view_stack.count() current = self.view_stack.currentIndex() self.view_stack.setCurrentIndex((current + index) % max_value) self.set_headline() def create_new_city(self, city_name: str, unit: UnitSystem): city_widget = CityWidget(city_name, unit, self.foreground_color, self.font_name) call_parameter = { "city": city_name, "units": unit, "language": self.language, "key": self.key } data = self.parent.get_data(call_parameter) city_widget.set_data(data) self.view_stack.addWidget(city_widget) current = self.view_stack.currentIndex() self.toggle_main_widget(current + 1) local = QDateTime(QDateTime.currentDateTime()) print("UTC:", local.toTime_t()) self.save_config() def set_label_style(self, label: QLabel, font: QFont, style: str): label.setStyleSheet(style) label.setFont(font) def update_data(self, config: dict) -> dict: call_parameter = { "city": config["city"], "units": config["unit"], "language": self.language, "key": self.key } # "key": "36dcf663b6964439a18574709e1d6eef"} data = self.parent.get_data(call_parameter) def set_key(self, key: str): self.key = key def set_language(self, language: str): self.language = language def save_city_config(self) -> list: if self.view_stack.count() < 2: return [] cities = [] for pos in range(1, self.view_stack.count()): city = {"name": "", "last_update": "", "unit_system": ""} widget = self.view_stack.widget(pos) city["unit_system"] = str(widget.get_units()) city["name"] = widget.get_name() cities.append(city) return cities def save_config(self): config = { "language": self.language, "key": self.key, "cities": self.save_city_config() } self.parent.save_config(config)
class PyMultiPageWidget(QWidget): currentIndexChanged = pyqtSignal(int) pageTitleChanged = pyqtSignal(str) def __init__(self, parent=None): super(PyMultiPageWidget, self).__init__(parent) self.comboBox = QComboBox() # MAGIC # It is important that the combo box has an object name beginning # with '__qt__passive_', otherwise, it is inactive in the form editor # of the designer and you can't change the current page via the # combo box. # MAGIC self.comboBox.setObjectName('__qt__passive_comboBox') self.stackWidget = QStackedWidget() self.comboBox.activated.connect(self.setCurrentIndex) self.layout = QVBoxLayout() self.layout.addWidget(self.comboBox) self.layout.addWidget(self.stackWidget) self.setLayout(self.layout) def sizeHint(self): return QSize(200, 150) def count(self): return self.stackWidget.count() def widget(self, index): return self.stackWidget.widget(index) @pyqtSlot(QWidget) def addPage(self, page): self.insertPage(self.count(), page) @pyqtSlot(int, QWidget) def insertPage(self, index, page): page.setParent(self.stackWidget) self.stackWidget.insertWidget(index, page) title = page.windowTitle() if title == "": title = "Page %d" % (self.comboBox.count() + 1) page.setWindowTitle(title) self.comboBox.insertItem(index, title) @pyqtSlot(int) def removePage(self, index): widget = self.stackWidget.widget(index) self.stackWidget.removeWidget(widget) self.comboBox.removeItem(index) def getPageTitle(self): return self.stackWidget.currentWidget().windowTitle() @pyqtSlot(str) def setPageTitle(self, newTitle): self.comboBox.setItemText(self.getCurrentIndex(), newTitle) self.stackWidget.currentWidget().setWindowTitle(newTitle) self.pageTitleChanged.emit(newTitle) def getCurrentIndex(self): return self.stackWidget.currentIndex() @pyqtSlot(int) def setCurrentIndex(self, index): if index != self.getCurrentIndex(): self.stackWidget.setCurrentIndex(index) self.comboBox.setCurrentIndex(index) self.currentIndexChanged.emit(index) pageTitle = pyqtProperty(str, fget=getPageTitle, fset=setPageTitle, stored=False) currentIndex = pyqtProperty(int, fget=getCurrentIndex, fset=setCurrentIndex)
class MainWindow(QtWidgets.QMainWindow): def __init__(self, parent=None): super(MainWindow, self).__init__(parent) self.setWindowTitle('Code Size Visualizer') self.stacked_widget = QStackedWidget() #to show different pages self.stacked_widget.currentChanged.connect(self.set_button_state) widget = QtWidgets.QWidget() self.setCentralWidget(widget) vbox = QVBoxLayout() vbox.addWidget(self.stacked_widget) #add the to the main window self.toolbar = QToolBar("Edit", self) #on file upload chart button gets enables and upon pressing it goes to next page h = HomeScreen(self) #calling this first self.chartBtn = h.viewChartBtn self.lineEdit = h.lineEdit self.chartBtn.clicked.connect(self.next_page) h.pushButton.clicked.connect(self.file_open) self.insert_page(h.centralwidget) self.addToolBar(self.toolbar) #start with the toolbar hidden self.toolbar.toggleViewAction().setChecked(True) self.toolbar.toggleViewAction().trigger() # create main layout widget.setLayout(vbox) #FILE OPEN def file_open(self): name, _ = QtWidgets.QFileDialog.getOpenFileName(None, 'Open File', "", "*.map",options=QtWidgets.QFileDialog.DontUseNativeDialog) self.lineEdit.setText(name) chartData, navData = FileOpen.openFile(name) navigation = Navigation(chartData, navData , self) self.addToolBar(navigation.toolbar) self.insert_page(navigation.horizontalGroupBox) def set_button_state(self, index): n_pages = len(self.stacked_widget) self.chartBtn.setEnabled( index % n_pages < n_pages - 1) def insert_page(self, widget, index=-1): self.stacked_widget.insertWidget(index, widget) self.set_button_state(self.stacked_widget.currentIndex()) def next_page(self): new_index = self.stacked_widget.currentIndex()+1 if new_index < len(self.stacked_widget): self.stacked_widget.setCurrentIndex(new_index) self.toolbar.toggleViewAction().trigger()