class MainWindow(QMainWindow): def __init__(self): QMainWindow.__init__(self) self.setWindowTitle("Piano Manager") self.setFixedSize(800, 480) # self.showFullScreen() # Create Page Objects Here LogInPage = LogIn() SignUpPage = SignUp() InUsePage = InUse() # Connect pages to switch each others LogInPage.ui.ButtonRegister.clicked.connect(partial( self.switchPage, 1)) LogInPage.ui.DButtonYes.clicked.connect(partial(self.switchPage, 2)) SignUpPage.ui.buttonHome.clicked.connect(partial(self.switchPage, 0)) InUsePage.ui.ButtonQuit.clicked.connect(partial(self.switchPage, 0)) # Insert pages into QStackedWidget self.centralWidgets = QStackedWidget() self.centralWidgets.addWidget(LogInPage) # index: 0 self.centralWidgets.addWidget(SignUpPage) # index: 1 self.centralWidgets.addWidget(InUsePage) # index: 2 self.setCentralWidget(self.centralWidgets) def getCurWidget(self): return self.centralWidgets.currentWidget() def switchPage(self, idx): self.getCurWidget().clearPage() self.centralWidgets.setCurrentIndex(idx) self.getCurWidget().setPage()
class DatatypeSelector(QGroupBox): def __init__(self, title: str, datatype_to_widget, parent: "QWidget" = None): super().__init__(title, parent) # Maps a datatype with its respective widget. The widget is optional self._datatype_to_widget = datatype_to_widget self._datatype_combobox = QComboBox() self._stacked_widgets = QStackedWidget() for (i, (name, datatype_factory)) in enumerate( DataTypeContainer.providers.items()): datatype_instance = datatype_factory() self._datatype_combobox.addItem(name, datatype_instance) if datatype_factory in self._datatype_to_widget: self._stacked_widgets.insertWidget( i, self._datatype_to_widget[datatype_factory]( datatype_instance)) self._main_layout = QVBoxLayout() self._main_layout.addWidget(self._datatype_combobox) self._main_layout.addWidget(self._stacked_widgets) self.setLayout(self._main_layout) self._datatype_combobox.currentIndexChanged[int].connect( self._change_active_widget) @property def selected_datatype(self): return self._datatype_combobox.currentData() def change_current_datatype(self, new_datatype_dict: dict): index = self._datatype_combobox.findText(new_datatype_dict["class"]) if index != -1: self._datatype_combobox.setCurrentIndex(index) self._datatype_combobox.currentData().from_dict(new_datatype_dict) self._stacked_widgets.currentWidget().reload() def _change_active_widget(self, index): self._stacked_widgets.setCurrentIndex(index) # Hide the `stacked_widgets` when the current datatype doesn't needs to display # a widget if self._stacked_widgets.currentIndex() != index: self._stacked_widgets.setVisible(False) else: self._stacked_widgets.setVisible(True) def to_dict(self): return self.selected_datatype.to_dict()
class stackedExample(QWidget): def __init__(self): super(stackedExample, self).__init__() self.leftList = QListWidget() self.leftList.insertItem(0, 'Contact') self.leftList.insertItem(1, 'Personal') self.leftList.insertItem(2, 'Educational') self.stack_1 = QWidget() self.stack_2 = QWidget() self.stack_3 = QWidget() self.stack_1_UI() self.stack_2_UI() self.stack_3_UI() self.stack = QStackedWidget() self.stack.addWidget(self.stack_1) self.stack.addWidget(self.stack_2) self.stack.addWidget(self.stack_3) hbox = QHBoxLayout() hbox.addWidget(self.leftList) hbox.addWidget(self.stack) self.setLayout(hbox) self.leftList.currentRowChanged.connect(self.display) self.setGeometry(300, 50, 10, 10) self.setWindowTitle("Stack Widget Demo") self.show() def stack_1_UI(self): layout = QFormLayout() layout.addRow("Name", QLineEdit()) layout.addRow("Address", QLineEdit()) #self.setTabText(0,"Contact Details") self.stack_1.setLayout(layout) def stack_2_UI(self): layout = QFormLayout() sex = QHBoxLayout() sex.addWidget(QRadioButton("Male")) sex.addWidget(QRadioButton("Female")) layout.addRow(QLabel("Sex"), sex) layout.addRow("Date of Birth", QLineEdit()) self.stack_2.setLayout(layout) def stack_3_UI(self): layout = QHBoxLayout() layout.addWidget(QLabel("subjects")) layout.addWidget(QCheckBox("Physics")) layout.addWidget(QCheckBox("Maths")) self.stack_3.setLayout(layout) def display(self, i): self.stack.setCurrentIndex(i)
class ControlWidget(QWidget): def __init__(self, status_bar): super().__init__() self.statusBar = status_bar self.initUI() def initUI(self): """ Init app """ self._stacked_widget = QStackedWidget() signal_controller = SignalSender() signal_controller.signal_newWindowIndx2MainWindow.connect(self.set_widget_by_index) signal_controller.signal_goto_settings.connect(self.set_widget_settings) self.grid = QGridLayout() self.grid.addWidget(self._stacked_widget) # Create log in widget self._log_in_widget = LogInWidget(self.statusBar, signal_controller) self._log_in_widget.set_index(self._stacked_widget.addWidget(self._log_in_widget)) # Create file receiver self._file_receiver = FileReceiverWidget(self, signal_controller) self._file_receiver.set_index(self._stacked_widget.addWidget(self._file_receiver)) # Create file sender self._file_sender = FileSenderWidget(self, signal_controller) self._file_sender.set_index(self._stacked_widget.addWidget(self._file_sender)) # Create settings self._config_widget= ConfigWidget(self) self._config_widget.set_index(self._stacked_widget.addWidget(self._config_widget)) self.setLayout(self.grid) self._stacked_widget.setCurrentIndex(self._log_in_widget.get_index()) self.setWindowTitle("Main menu") def set_widget_settings(self): self.set_widget_by_index(self._config_widget.get_index()) def set_widget_by_index(self, index: int): self._stacked_widget.setCurrentIndex(index) def get_status_bar(self): return self.statusBar
class MainWindow(QMainWindow): def __init__(self): QMainWindow.__init__(self) self.setWindowTitle("음취헌 Piano Manager") self.setFixedSize(800, 480) page_in_use = InUse() page_log_in = LogIn() page_sign_up = SignUp() self.widget = QStackedWidget() self.widget.addWidget(page_log_in) # index 0 self.widget.addWidget(page_in_use) # index 1 self.widget.addWidget(page_sign_up) # index 2 self.setCentralWidget(self.widget) page_in_use.ui.button_quit.clicked.connect(partial( self.switch_page, 0)) page_log_in.ui.button_register.clicked.connect( partial(self.switch_page, 2)) page_log_in.ui.dialog_true.button_yes.clicked.connect( partial(self.switch_page, 1)) page_sign_up.widget(4).ui.dialog_true.button_ok.clicked.connect( partial(self.switch_page, 0)) for i in range(5): page_sign_up.widget(i).ui.button_home.clicked.connect( partial(self.switch_page, 0)) def switch_page(self, idx): if idx == 1: self.widget.widget(1).set_page(self.widget.widget(0).get_contact()) self.widget.currentWidget().clear_page() self.widget.setCurrentIndex(idx) else: self.widget.currentWidget().clear_page() self.widget.setCurrentIndex(idx) self.widget.currentWidget().set_page()
class PageAllegroOptions(QWidget): def __init__(self, parent=None): QWidget.__init__(self) self.parent = parent self.parent.addWidget(self) self.auto_login, self.auto_pwd, self.auto_email, self.auto_time = data.get_autofill() self.gridLayout = QGridLayout(self) self.gridLayout.setContentsMargins(0, 0, 0, 0) self.gridLayout.setSpacing(0) self.gridLayout.setColumnStretch(0, 3) self.gridLayout.setColumnStretch(1, 7) self.pushButton_auto = QPushButton(self) self.pushButton_auto.setText("Autofill") self.pushButton_auto.setStyleSheet(styles.btn_allegro_ops_auto) self.pushButton_auto.setCheckable(True) self.pushButton_auto.setChecked(True) self.pushButton_auto.clicked.connect(lambda: self.on_auto()) self.gridLayout.addWidget(self.pushButton_auto, 0, 0, 1, 1) self.pushButton_help = QPushButton(self) self.pushButton_help.setText("Help") self.pushButton_help.setStyleSheet(styles.btn_allegro_ops_auto) self.pushButton_help.setCheckable(True) self.pushButton_help.setChecked(False) self.pushButton_help.clicked.connect(lambda: self.on_help()) self.gridLayout.addWidget(self.pushButton_help, 1, 0, 1, 1) self.spacer_btn_d = QSpacerItem(40, 20, QSizePolicy.Expanding) self.gridLayout.addItem(self.spacer_btn_d, 2, 0, 1, 1) self.stackedWidget = QStackedWidget(self) self.stackedWidget.setStyleSheet("""QStackedWidget{background-color: #fff;}""") self.gridLayout.addWidget(self.stackedWidget, 0, 1, 3, 1) self.widget_auto = QWidget(self.stackedWidget) self.widget_auto.setStyleSheet("""QWidget{background-color: #fff;}""") self.stackedWidget.addWidget(self.widget_auto) self.widget_help = QWidget(self.stackedWidget) self.widget_help.setStyleSheet("""QWidget{background-color: #fff;}""") self.stackedWidget.addWidget(self.widget_help) self.gridLayout_help = QVBoxLayout(self.widget_help) self.gridLayout_help.setContentsMargins(50, 50, 50, 50) self.gridLayout_help.setSpacing(50) self.label_help = QLabel(self.widget_help) self.label_help.setStyleSheet(styles.label_lineEdit) self.label_help.setWordWrap(True) self.label_help.setText(styles.help_text) self.gridLayout_help.addWidget(self.label_help) self.spacer_help = QSpacerItem(40, 20, QSizePolicy.Expanding) self.gridLayout_help.addItem(self.spacer_help) self.gridLayout_auto = QGridLayout(self.widget_auto) self.gridLayout_auto.setContentsMargins(0, 0, 0, 0) self.gridLayout_auto.setSpacing(20) self.gridLayout_auto.setColumnStretch(0, 1) self.gridLayout_auto.setColumnStretch(1, 1) self.gridLayout_auto.setColumnStretch(2, 1) self.gridLayout_auto.setColumnStretch(3, 1) self.gridLayout_auto.setRowStretch(0, 3) self.gridLayout_auto.setRowStretch(1, 1) self.gridLayout_auto.setRowStretch(2, 1) self.gridLayout_auto.setRowStretch(3, 1) self.gridLayout_auto.setRowStretch(4, 1) self.gridLayout_auto.setRowStretch(5, 1) self.gridLayout_auto.setRowStretch(6, 3) self.lineEdit_login = QLineEdit(self.widget_auto) self.lineEdit_login.setMinimumSize(QSize(0, 60)) self.lineEdit_login.setSizeIncrement(QSize(40, 40)) self.lineEdit_login.setStyleSheet(styles.lineEdit_opt) self.gridLayout_auto.addWidget(self.lineEdit_login, 1, 2, 1, 1) self.lineEdit_login.setPlaceholderText("login or email of your account") self.lineEdit_login.setText(self.auto_login) self.lineEdit_password = QLineEdit(self.widget_auto) self.lineEdit_password.setMinimumSize(QSize(20, 60)) self.lineEdit_password.setStyleSheet(styles.lineEdit_opt) self.lineEdit_password.setEchoMode(QLineEdit.Password) self.lineEdit_password.setReadOnly(False) self.gridLayout_auto.addWidget(self.lineEdit_password, 2, 2, 1, 1) self.lineEdit_password.setPlaceholderText("password of your account") self.lineEdit_password.setText(self.auto_pwd) self.lineEdit_email = QLineEdit(self.widget_auto) self.lineEdit_email.setMinimumSize(QSize(0, 60)) self.lineEdit_email.setStyleSheet(styles.lineEdit_opt) self.lineEdit_email.setFrame(True) self.lineEdit_email.setEchoMode(QLineEdit.Normal) self.gridLayout_auto.addWidget(self.lineEdit_email, 3, 2, 1, 1) self.lineEdit_email.setPlaceholderText("email to which the notification will be sent") self.lineEdit_email.setText(self.auto_email) self.lineEdit_time = QLineEdit(self.widget_auto) self.lineEdit_time.setMinimumSize(QSize(0, 60)) self.lineEdit_time.setStyleSheet(styles.lineEdit) self.gridLayout_auto.addWidget(self.lineEdit_time, 4, 2, 1, 1) self.lineEdit_time.setPlaceholderText("interval between refreshes") self.lineEdit_time.setText(str(self.auto_time)) self.label_login = QLabel("Allegro login", self) self.label_login.setStyleSheet(styles.label_lineEdit) self.gridLayout_auto.addWidget(self.label_login, 1, 1, 1, 1) self.label_password = QLabel("Allegro password", self) self.label_password.setStyleSheet(styles.label_lineEdit) self.gridLayout_auto.addWidget(self.label_password, 2, 1, 1, 1) self.label_email = QLabel("Email to notificate", self) self.label_email.setStyleSheet(styles.label_lineEdit) self.gridLayout_auto.addWidget(self.label_email, 3, 1, 1, 1) self.label_time = QLabel("Refresh time[s]", self) self.label_time.setStyleSheet(styles.label_lineEdit) self.gridLayout_auto.addWidget(self.label_time, 4, 1, 1, 1) self.pushButton_set = QPushButton("Set", self.widget_auto) self.pushButton_set.clicked.connect(lambda: self.on_set()) self.pushButton_set.setMinimumSize(QSize(0, 40)) self.pushButton_set.setStyleSheet(styles.btn_dark) self.gridLayout_auto.addWidget(self.pushButton_set, 5, 2, 1, 1) self.spacer_auto_l = QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum) self.gridLayout_auto.addItem(self.spacer_auto_l, 1, 1, 1, 1) self.spacer_auto_r = QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum) self.gridLayout_auto.addItem(self.spacer_auto_r, 1, 3, 1, 1) self.spacer_auto_t = QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum) self.gridLayout_auto.addItem(self.spacer_auto_t, 0, 0, 1, 1) self.spacer_auto_b = QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum) self.gridLayout_auto.addItem(self.spacer_auto_b, 6, 0, 1, 1) def on_set(self): data.add_autofill(self.lineEdit_login.text(), self.lineEdit_password.text(), self.lineEdit_email.text(), int(self.lineEdit_time.text())) auto_login, auto_pwd, auto_email, auto_time = data.get_autofill() self.parent.pageAllegroAdd.lineEdit_login.setText(auto_login) self.parent.pageAllegroAdd.lineEdit_password.setText(auto_pwd) self.parent.pageAllegroAdd.lineEdit_email.setText(auto_email) self.parent.pageAllegroAdd.lineEdit_time.setText(str(auto_time)) def on_auto(self): self.pushButton_auto.setChecked(True) self.pushButton_help.setChecked(False) self.stackedWidget.setCurrentIndex(0) def on_help(self): self.pushButton_auto.setChecked(False) self.pushButton_help.setChecked(True) self.stackedWidget.setCurrentIndex(1)
class _ViewSwitcherWidget(QWidget): """Object that handles displaying of Enigma model wikis and images""" def __init__(self, master, regen_plug): """ :param master: Qt parent object :param regen_plug: {callable} Regenerates settings view to new contents """ super().__init__(master) layout = QHBoxLayout() self.setLayout(layout) # LIST OF AVAILABLE MODELS ============================================= self.model_list = QListWidget() self.model_list.setMaximumWidth(150) self.model_list.setMinimumWidth(150) self.model_list.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) self.model_list.currentRowChanged.connect(self.select_model) self.regen_plug = regen_plug # STACKED MODEL VIEWS ================================================== self.stacked_wikis = QStackedWidget() for i, model in enumerate(VIEW_DATA): self.model_list.insertItem(i, model) description = VIEW_DATA[model]["description"] self.stacked_wikis.addWidget( _EnigmaViewWidget(self, model, description)) self.total_models = len(VIEW_DATA) layout.addWidget(self.model_list) layout.addWidget(self.stacked_wikis) # Sets initially viewed self.currently_selected = None def select_model(self, i): """Called when the "Use this model" button is pressed :param i: {str} Newly selected model index """ logging.info('Changing settings view to model "%s"' % list(VIEW_DATA.keys())[i]) model = list(VIEW_DATA.keys())[i] self.regen_plug(model) self.stacked_wikis.setCurrentIndex(i) self.model_list.blockSignals(True) self.model_list.setCurrentRow(i) self.model_list.blockSignals(False) self.currently_selected = model def highlight(self, i): """Highlights an option with Blue color, indicating that it is currently selected in EnigmaAPI :param i: {int} Index from list options """ for index in range(self.total_models): item = self.model_list.item(index) item.setForeground(Qt.black) item.setToolTip(None) selected = self.model_list.item(i) selected.setBackground(Qt.gray) # .setForeground(Qt.blue) selected.setToolTip("Currently used Enigma model")
def setCurrentIndex(self, index): self.fader_widget = FaderWidget(self.currentWidget(), self.widget(index)) QStackedWidget.setCurrentIndex(self, index)
class appConfigScreen(QWidget): keybind_signal = Signal("QObject") update_signal = Signal("QObject") def __init__(self, appName, parms={}): super().__init__() self.dbg = False self.level = 'user' exePath = sys.argv[0] if os.path.islink(sys.argv[0]): exePath = os.path.realpath(sys.argv[0]) baseDir = os.path.abspath(os.path.dirname(exePath)) os.chdir(baseDir) self.rsrc = "%s/rsrc" % baseDir self.parms = parms self.modules = [] self.appName = appName self.textDomain = self.appName.lower().replace(" ", "_") gettext.textdomain('{}'.format(self.textDomain)) _ = gettext.gettext self.wikiPage = appName self.background = "%s/background.png" % self.rsrc self.banner = "%s/%s" % (self.rsrc, "banner.png") self.last_index = 0 self.stacks = {0: {'name': _("Options"), 'icon': 'icon'}} self.appConfig = appConfig() self.config = {} #def init def _debug(self, msg): if self.dbg: print("%s" % msg) #def _debug def setWiki(self, url): self.wikiPage = url #def setWiki def setTextDomain(self, textDomain): self.textDomain = textDomain #def setTextDomain def setRsrcPath(self, rsrc): if os.path.isdir(rsrc): self.rsrc = rsrc else: self._debug("%s doesn't exists") self._debug("Resources dir: %s" % self.rsrc) #def setRsrcPath def setIcon(self, icon): self._debug("Icon: %s" % icon) icn = icon if not os.path.isfile(icon): sw_ko = False self._debug("%s not found" % icon) if QtGui.QIcon.fromTheme(icon): icn = QtGui.QIcon.fromTheme(icon) if icn.name() == "": self._debug("%s not present at theme" % icon) sw_ko = True else: self._debug("%s found at theme" % icon) self._debug("Name: %s found at theme" % icn.name()) elif os.path.isfile("%s/%s" % (self.rsrc, icon)): icon = "%s/%s" % (self.rsrc, icon) self._debug("%s found at rsrc folder" % icon) icn = QtGui.QIcon(icon) else: icn = QtGui.QIcon.fromTheme("application-menu") self._debug("Icon not found at %s" % self.rsrc) if sw_ko: icn = QtGui.QIcon.fromTheme("application-menu") self._debug("Icon %s not found at theme" % icon) self.setWindowIcon(icn) #def setIcon def setBanner(self, banner): if not os.path.isfile(banner): if os.path.isfile("%s/%s" % (self.rsrc, banner)): banner = "%s/%s" % (self.rsrc, banner) else: banner = "" self._debug("Banner not found at %s" % self.rsrc) self.banner = banner #def setBanner def setBackgroundImage(self, background): if not os.path.isfile(background): if os.path.isfile("%s/%s" % (self.rsrc, background)): background = "%s/%s" % (self.rsrc, background) else: background = "" self._debug("Background not found at %s" % self.rsrc) self.background = background #def setBanner def setConfig(self, confDirs, confFile): self.appConfig.set_baseDirs(confDirs) self.appConfig.set_configFile(confFile) #def setConfig(self,confDirs,confFile): def _searchWiki(self): url = "" baseUrl = "https://wiki.edu.gva.es/lliurex/tiki-index.php?page=" if self.wikiPage.startswith("http"): url = self.wikiPage else: url = "%s%s" % (baseUrl, self.wikiPage) #try: # req=Request(url) # content=urlopen(req).read() #except: # self._debug("Wiki not found at %s"%url) # url="" return (url) #def _searchWiki def _get_default_config(self): data = {} data = self.appConfig.getConfig('system') self.level = data['system'].get('config', 'user') if self.level != 'system': data = self.appConfig.getConfig(self.level) level = data[self.level].get('config', 'n4d') if level != self.level: self.level = level data = self.appConfig.getConfig(level) data[self.level]['config'] = self.level self._debug("Read level from config: %s" % self.level) return (data) #def _get_default_config(self,level): def getConfig(self, level=None, exclude=[]): data = self._get_default_config() if not level: level = self.level if level != 'system': data = {} data = self.appConfig.getConfig(level, exclude) self.config = data.copy() self._debug("Read level from config: %s" % level) return (data) #def getConfig(self,level): def Show(self): if self.config == {}: self.getConfig() self.setStyleSheet(self._define_css()) if os.path.isdir("stacks"): for mod in os.listdir("stacks"): if mod.endswith(".py"): mod_name = mod.split(".")[0] mod_import = "from stacks.%s import *" % mod_name try: exec(mod_import) self.modules.append(mod_name) self._debug("Load stack %s" % mod_name) except Exception as e: # self._debug("Unable to load %s: %s"%(mod_name,e)) print("Unable to load %s: %s" % (mod_name, e)) idx = 1 for mod_name in self.modules: try: mod = eval("%s(self)" % mod_name) except Exception as e: # self._debug("Import failed for %s: %s"%(mod_name,e)) print("Import failed for %s: %s" % (mod_name, e)) continue if type(mod.index) == type(0): if mod.index > 0: idx = mod.index try: if mod.enabled == False: continue except: pass while idx in self.stacks.keys(): idx += 1 self._debug("New idx for %s: %s" % (mod_name, idx)) if 'parm' in mod.__dict__.keys(): try: if mod.parm: self._debug("Setting parms for %s" % mod_name) self._debug("self.parms['%s']" % mod.parm) mod.apply_parms(eval("self.parms['%s']" % mod.parm)) except Exception as e: self._debug("Failed to pass parm %s to %s: %s" % (mod.parm, mod_name, e)) try: mod.setTextDomain(self.textDomain) except Exception as e: print("Can't set textdomain for %s: %s" % (mod_name, e)) try: mod.setAppConfig(self.appConfig) except Exception as e: print("Can't set appConfig for %s: %s" % (mod_name, e)) try: if mod.visible == False: visible = False else: visible = True except: visible = True self.stacks[idx] = { 'name': mod.description, 'icon': mod.icon, 'tooltip': mod.tooltip, 'module': mod, 'visible': visible } try: mod.message.connect(self._show_message) except: pass self._render_gui() return (False) def _render_gui(self): self.getConfig() box = QGridLayout() img_banner = QLabel() if os.path.isfile(self.banner): img = QtGui.QPixmap(self.banner) img_banner.setPixmap(img) img_banner.setAlignment(Qt.AlignCenter) img_banner.setObjectName("banner") box.addWidget(img_banner, 0, 0, 1, 2) self.lst_options = QListWidget() self.stk_widget = QStackedWidget() idx = 0 if len(self.stacks) > 2: l_panel = self._left_panel() box.addWidget(l_panel, 1, 0, 1, 1, Qt.Alignment(1) | Qt.AlignTop) # self.stk_widget.setCurrentIndex(0) else: idx = 1 # self.stk_widget.setCurrentIndex(1) r_panel = self._right_panel() self.stk_widget.setCurrentIndex(idx) #self.gotoStack(idx,"") box.addWidget(r_panel, 1, 1, 1, 1) self.setLayout(box) self.show() #def _render_gui def _left_panel(self): panel = QWidget() box = QVBoxLayout() btn_menu = QPushButton() icn = QtGui.QIcon.fromTheme("application-menu") btn_menu.setIcon(icn) btn_menu.setIconSize(QSize(BTN_MENU_SIZE, BTN_MENU_SIZE)) btn_menu.setMaximumWidth(BTN_MENU_SIZE) btn_menu.setMaximumHeight(BTN_MENU_SIZE) btn_menu.setToolTip(_("Options")) btn_menu.setObjectName("menuButton") # box.addWidget(btn_menu,Qt.Alignment(1)) indexes = [] for index, option in self.stacks.items(): idx = index lst_widget = QListWidgetItem() lst_widget.setText(option['name']) mod = option.get('module', None) if mod: try: idx = mod.index except: pass if idx > 0: icn = QtGui.QIcon.fromTheme(option['icon']) lst_widget.setIcon(icn) if 'tooltip' in option.keys(): lst_widget.setToolTip(option['tooltip']) while idx in indexes: idx += 1 indexes.append(index) self.stacks[idx]['widget'] = lst_widget orderedStacks = {} orderedStacks[0] = self.stacks[0] #self.lst_options.addItem(orderedStacks[0]['widget']) cont = 0 indexes.sort() for index in indexes: if index: orderedStacks[cont] = self.stacks[index].copy() if self.stacks[index].get('visible', True) == True: self.lst_options.addItem(orderedStacks[cont]['widget']) cont += 1 self.stacks = orderedStacks.copy() box.addWidget(self.lst_options) self.lst_options.currentRowChanged.connect(self._show_stack) self.lst_options.setCurrentIndex(QModelIndex()) self.last_index = None panel.setLayout(box) self.resize(self.size().width() + box.sizeHint().width(), self.size().height() + box.sizeHint().height() / 2) self.lst_options.setFixedSize( self.lst_options.sizeHintForColumn(0) + 2 * (self.lst_options.frameWidth() + 15), self.height() ) #self.lst_options.sizeHintForRow(0) * self.lst_options.count() + 2 * (self.lst_options.frameWidth()+15)) return (panel) #def _left_panel def _right_panel(self): panel = QWidget() box = QVBoxLayout() idx = 0 text = [ _("Welcome to the configuration of ") + self.appName, _("From here you can:<br>") ] orderIdx = list(self.stacks.keys()) for idx in orderIdx: data = self.stacks[idx] stack = data.get('module', None) if stack: stack.setLevel(self.level) stack.setConfig(self.config) stack._load_screen() if self.stacks[idx].get('visible', True) == True: text.append( " * <a href=\"appconf://%s\"><span style=\"font-weight:bold;text-decoration:none\">%s</span></a>" % (idx + 1, stack.menu_description)) try: self.stk_widget.insertWidget(idx, stack) except: self.stk_widget.insertWidget(idx, stack.init_stack()) stack = QWidget() stack.setObjectName("panel") s_box = QVBoxLayout() lbl_txt = QLabel() lbl_txt.setTextFormat(Qt.RichText) lbl_txt.setText("<br>".join(text)) lbl_txt.linkActivated.connect(self._linkStack) lbl_txt.setObjectName("desc") lbl_txt.setAlignment(Qt.AlignTop) lbl_txt.setTextInteractionFlags(Qt.TextBrowserInteraction) s_box.addWidget(lbl_txt, 1) #Get wiki page url = self._searchWiki() if url: desc = _("Wiki help") lbl_wiki = QLabel( "<a href=\"%s\"><span style=\"text-align: right;\">%s</span></a>" % (url, desc)) lbl_wiki.setOpenExternalLinks(True) s_box.addWidget(lbl_wiki, 0, Qt.AlignRight) stack.setLayout(s_box) self.stk_widget.insertWidget(0, stack) #self.stacks[0]['module']=stack box.addWidget(self.stk_widget) panel.setLayout(box) return (panel) #def _right_panel def _linkStack(self, *args): stack = args[0].split('/')[-1] self.gotoStack(int(stack), '') def gotoStack(self, idx, parms): self._show_stack(idx=idx, parms=parms) def _show_stack(self, item=None, idx=None, parms=None): if (self.last_index == abs(self.lst_options.currentRow()) and (idx == self.last_index or isinstance(item, int))): # or self.last_index==None)): return if isinstance( self.stacks.get(self.last_index, {}).get('module', None), appConfigStack) == True: if self.stacks[self.last_index]['module'].getChanges(): if self._save_changes(self.stacks[self.last_index] ['module']) == QMessageBox.Cancel: self.lst_options.setCurrentRow(self.last_index) return else: self.stacks[self.last_index]['module'].setChanged(False) self.stacks[self.last_index]['module'].initScreen() if self.stacks[self.last_index]['module'].refresh: self._debug("Refresh config") self.getConfig() else: self._debug(self.stacks.get(self.last_index, {}).get('module')) self.last_index = 0 if isinstance(idx, int) == False: idx = self.lst_options.currentRow() + 1 self.last_index = idx - 1 try: self.stacks[idx]['module'].setConfig(self.config) except: pass self.stk_widget.setCurrentIndex(idx) # self.statusBar.hide() if parms: self.stacks[idx]['module'].setParms(parms) #def _show_stack def closeEvent(self, event): try: if self.stacks[self.last_index]['module'].getChanges(): if self._save_changes(self.stacks[self.last_index] ['module']) == QMessageBox.Cancel: event.ignore() except: pass def _show_message(self, msg, status=None): return # self.statusBar.setText(msg) # if status: # self.statusBar.show(status) # else: # self.statusBar.show(status) #def _show_message def _save_changes(self, module): dia = QMessageBox( QMessageBox.Question, _("Apply changes"), _("There're changes not saved at current screen.\nDiscard them and continue?" ), QMessageBox.Discard | QMessageBox.Cancel, self) return (dia.exec_()) def _define_css(self): css = """ QPushButton{ padding: 6px; margin:6px; font: 14px Roboto; } QPushButton#menu:active{ background:none; } QStatusBar{ background:red; color:white; font: 14px Roboto bold; } QLabel{ padding:6px; margin:6px; } #dlgLabel{ font:12px Roboto; margin:0px; border:0px; padding:3px; } QLineEdit{ border:0px; border-bottom:1px solid grey; padding:1px; font:14px Roboto; margin-right:6px; } #panel{ background-image:url("%s"); background-size:stretch; background-repeat:no-repeat; background-position:center; } #desc{ background-color:rgba(255,255,255,0.7); color:black; } #banner{ padding:1px; margin:1px; border:0px; } """ % self.background return (css)
class UpdatePrompt(QDialog): def __init__(self): super().__init__() self.makeView() return def makeView(self): layout = QVBoxLayout() btnLayout = QHBoxLayout() self.centStack = QStackedWidget() self.updateButton = QPushButton('Update') self.cancelButton = QPushButton('Cancel') notifyLabel = QLabel('There are upgrades scheduled') self.inputBox = QLineEdit() self.outputBox = QTextBrowser() #refreshIcon = QIcon.fromTheme('process-working') self.refreshIcon = QMovie('assets/spin3.gif') refreshAnimation = QLabel() layout.addWidget(notifyLabel) layout.addWidget(self.centStack) layout.addWidget(self.inputBox) layout.addLayout(btnLayout) btnLayout.addWidget(self.cancelButton) btnLayout.addWidget(self.updateButton) self.centStack.addWidget(refreshAnimation) self.centStack.addWidget(self.outputBox) refreshAnimation.setMovie(self.refreshIcon) refreshAnimation.setAlignment(Qt.AlignCenter) self.refreshIcon.start() self.inputBox.setEchoMode(QLineEdit.Password) self.inputBox.setFocus() self.inputBox.returnPressed.connect(self.pkgUpdates) self.updateButton.clicked.connect(self.pkgUpdates) self.cancelButton.clicked.connect(self.cancelUpdates) self.updateButton.setDefault(True) self.centStack.setCurrentIndex(1) notifyLabel.setAlignment(Qt.AlignTop) self.outputBox.setReadOnly(True) #self.outputBox.setAlignment(Qt.AlignTop) self.setWindowTitle('Package Upgrades') self.setLayout(layout) self.resize(450, 250) return async def asetup(self, password): async with trio.open_nursery() as nursery: finishedState = trio.Event() nursery.start_soon(self.upProc, password, 'update', finishedState) #nursery.start_soon(self.KEAlive, finishedState) return async def upProc(self, password, cmd, finishedState): proc = await trio.open_process(['sudo', '-S', 'apt-get', cmd, '-y'], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) await proc.stdin.send_all((password + '\n').encode()) while (proc.poll() == None): QCoreApplication.processEvents() await trio.sleep(0.1) result = '' result = await self.pullOutput(proc) self.appendToOutput(result) proc.terminate() if (cmd == 'update'): await self.upProc(password, 'upgrade', finishedState) finishedState.set() return async def pullOutput(self, proc): x = await proc.stdout.receive_some() x = x.decode() result = '' while (x != ''): QCoreApplication.processEvents() result = result + x x = await proc.stdout.receive_some() x = x.decode() return result async def KEAlive(self, finishedState): while finishedState.is_set(): QCoreApplication.processEvents() trio.sleep(0.1) return return def appendToOutput(self, add): currentText = self.outputBox.toPlainText() self.outputBox.setText(currentText + 'Running updates\n' + add + '\n') print(add) return def pkgUpdates(self): self.centStack.setCurrentIndex(0) self.refreshIcon.start() QCoreApplication.processEvents() password = self.inputBox.text() if (password == ''): self.passError('The password field cannot be empty') return self.inputBox.clear() self.inputBox.setDisabled(True) self.updateButton.setDisabled(True) trio.run(self.asetup, password) self.centStack.setCurrentIndex(1) self.refreshIcon.stop() self.updateButton.setDisabled(False) self.inputBox.setDisabled(False) return def passError(self, s): passError = QDialog(self) msg = QLabel(s) layout = QVBoxLayout() layout.addWidget(msg) passError.setLayout(layout) okBtn = QPushButton('OK') okBtn.clicked.connect(passError.reject) layout.addWidget(okBtn) passError.exec_() return def cancelUpdates(self): #Needs way of closing subprocess during async run self.reject() return
class UIStackedWidget(object): ''' By default: QWidget or QDockWidget +----------------------------------------------------------+ | QVBoxLayout | | +---------------------------------------------------+ | | | | | | | +-------------------------------------------+ | | | | | QList | | | | | | | | | | | | | | | | | +-------------------------------------------+ | | | | +-------------------------------------------+ | | | | | QStackedWidget | | | | | | | | | | | | | | | | | +-------------------------------------------+ | | | | | | | +---------------------------------------------------+ | | | +----------------------------------------------------------+ or if layout = horizontal: QWidget or QDockWidget +----------------------------------------------------------+ | QHBoxLayout | | +---------------------------------------------------+ | | | | | | | +--------------+ +------------------------+ | | | | | QList | | QStackedWidget | | | | | | | | | | | | | | | | | | | | | +--------------+ +------------------------+ | | | | | | | +---------------------------------------------------+ | | | +----------------------------------------------------------+ ''' def createStack(self, layout='vertical'): self.stack_list = QListWidget() self.list_margin_size = 11 self.Stack = QStackedWidget(self) self.layout_type = layout if self.layout_type == 'horizontal': box = QHBoxLayout(self) self.list_margin_size = 18 self.stack_list.setStyleSheet( "margin-top: {size}px".format(size=self.list_margin_size)) else: box = QVBoxLayout(self) self.stack_list.setStyleSheet( "margin-left : {size}px; margin-right : {size2}px".format( size=self.list_margin_size, size2=self.list_margin_size + 1)) box.addWidget(self.stack_list) box.addWidget(self.Stack) box.setAlignment(self.stack_list, Qt.AlignTop) self.setLayout(box) self.stack_list.currentRowChanged.connect(self.display) self.tabs = {} self.num_tabs = 0 self.widgets = {} def display(self, i): self.Stack.setCurrentIndex(i) def currentIndex(self): return self.Stack.currentIndex() def addTab(self, title, widget='form'): self.stack_list.addItem(title) if widget == 'form': widget = UIFormFactory.getQWidget(self) self.Stack.addWidget(widget) self.tabs[title] = widget self.num_tabs += 1 height_multiplier = self.num_tabs + 1 if self.layout_type != 'vertical': height_multiplier += 1 self.stack_list.setMaximumWidth( self.stack_list.sizeHintForColumn(0) * 1.2 + self.list_margin_size * 2) self.stack_list.setMaximumHeight( self.stack_list.sizeHintForRow(0) * height_multiplier + self.list_margin_size * 2) self.stack_list.setMaximumHeight( self.stack_list.sizeHintForRow(0) * height_multiplier) def addTabs(self, titles): for title in titles: self.addTab(title=title)
class Window(QMainWindow, WindowMenu, Plots): def __init__(self, parent=None): super(Window, self).__init__(parent) self.setWindowTitle("Leak Detection Evaluation Form") self.main_style_qss = "Eclippy.qss" self._main_widget = QWidget(self) self._main_layout = QVBoxLayout(self._main_widget) self._form_widget = QWidget(self) self._form_layout = QVBoxLayout(self._form_widget) self._database_widget = QWidget(self) self._database_layout = QVBoxLayout(self._database_widget) self._database_table_label = QLabel() self._database_table_label.setObjectName("dbtablelabel") self._database_table_label.setStyleSheet( 'QLabel[objectName^="dbtablelabel"] {font-size: 12pt; font-weight: bold;}' ) self._database_table_export_button = QPushButton("Export Table (CSV)") self._database_table_export_button.clicked.connect( self.exportDatabaseTable) database_table_export_row = QWidget() database_table_export_lay = QHBoxLayout(database_table_export_row) database_table_export_lay.setContentsMargins(0, 0, 0, 0) database_table_export_lay.addWidget(HorizontalFiller()) database_table_export_lay.addWidget(self._database_table_export_button) self._database_layout.addWidget(self._database_table_label) self._database_table = DataTable(use_max_height=False) self._database_layout.addWidget(self._database_table) self._database_layout.addWidget(database_table_export_row) self._main_tabs = QTabWidget(self) self._main_layout.addWidget(self._main_tabs) self._main_tabs.addTab(self._form_widget, "Form") self._main_tabs.addTab(self._database_widget, "Database") snwa_logo = QPixmap("snwa_logo.png") self._logo = QLabel(self) self._logo.setPixmap(snwa_logo) search_row = QWidget(self) search_lay = QHBoxLayout(search_row) search_lay.addWidget(self._logo) self._search_toolbar = QToolBar(self) self._new_rec_button = QPushButton("Create New Record") self._new_rec_button.clicked.connect(self.createNewRecord) self._new_rec_button.setIcon(QIcon(QPixmap("plus.png"))) self._search_toolbar.addWidget(self._new_rec_button) # Database DB_LOC = "LeakDetectionDatabase.db" self._database = SQLiteLib(DB_LOC) self.setTabs() Plots.__init__(self) self._data_table_label = QLabel("Client Table") self._data_table = DataTable(1, 1, self) search_bars = self.setSearchBars() search_bars.setContentsMargins(0, 0, 0, 0) search_lay.setContentsMargins(0, 0, 0, 0) search_lay.addWidget(search_bars) search_lay.addWidget(HorizontalFiller(self)) self.readDataFromDatabase() self._ap_phase_widget = Client.AP_PHASE_ID.value self._address_widget = Client.ADDRESS.value self._main_tabs.currentChanged.connect(self.mainTabChange) self._ap_phase_widget.returnPressed.connect( self.checkApPhaseIDsInDatabase) # Layout ----------------------------------- # Docks self._form_menu_dock = QDockWidget("Form Selections", self) self._form_menu_dock.setAllowedAreas(Qt.LeftDockWidgetArea | Qt.RightDockWidgetArea) self._form_menu_dock.setFeatures(self._form_menu_dock.features() & ~QDockWidget.DockWidgetClosable) self._form_menu_dock.setWidget(self._form_menu) self.addDockWidget(Qt.LeftDockWidgetArea, self._form_menu_dock) self._plot_menu_dock = QDockWidget( "Plotted Data (Double Click to Activate Chart)", self) self._plot_menu_dock.setAllowedAreas(Qt.LeftDockWidgetArea | Qt.RightDockWidgetArea) self._plot_menu_dock.setWidget(self._plot_menu) self.addDockWidget(Qt.RightDockWidgetArea, self._plot_menu_dock) WindowMenu.__init__(self) # Tabs form_area = QWidget(self) form_lay = QVBoxLayout(form_area) form_lay.addWidget(search_row) form_lay.addWidget(self._search_toolbar) form_lay.addWidget(BreakLine(self)) form_lay.addWidget(self._tab_label) form_lay.addWidget(self._tabs) form_lay.addWidget(self._data_table_label) form_lay.addWidget(self._data_table) form_lay.setContentsMargins(0, 0, 0, 0) self._form_layout.addWidget(form_area) self.updateDataTable() self.setCentralWidget(self._main_widget) def closeEvent(self, event): for plot_name in self._plots: if self._plots[plot_name].isVisible(): self._plots[plot_name].close() if self._search_window != None and self._search_window.isVisible(): self._search_window.close() def setSearchBars(self): self._database.openDatabase() self._search_apPhase_combo = ExtendedComboBox(self) self._search_address_combo = ExtendedComboBox(self) search_data = getAddresses( self._database.getValuesFrom2Fields(Tables.CLIENT.name, Client.ADDRESS.name, Client.AP_PHASE_ID.name)) self._search_ap_phase_ids = [value[1] for value in search_data] search_addresses = [value[0] for value in search_data] self._search_apPhase_combo.addItems(self._search_ap_phase_ids) self._search_address_combo.addItems(search_addresses) self._search_apPhase_combo.currentIndexChanged.connect( self.apPhaseComboChange) self._search_apPhase_combo.currentTextChanged.connect( self.newSearchApCombo) self._search_address_combo.currentIndexChanged.connect( self.addressComboChange) self._search_address_combo.currentTextChanged.connect( self.newSearchAdrCombo) self._new = False self._ap_phase_change = False self._address_change = False self._database.closeDatabase() bot_menu = QWidget(self) bot_menu_layout = QFormLayout(bot_menu) bot_menu.setStyleSheet("QLabel { font-weight: bold; }") bot_menu_layout.addRow("Search ApPhase ID:", self._search_apPhase_combo) bot_menu_layout.addRow("Search Address:", self._search_address_combo) return bot_menu def setTabs(self): #self._tabs = QTabWidget(self) self._tab_label = QLabel("Client", self) self._tab_label.setObjectName("tablabel") self._tab_label.setStyleSheet( 'QLabel[objectName^="tablabel"] {font-size: 12pt; font-weight: bold;}' ) self._tabs = QStackedWidget(self) table_names = [] for table in Tables: table_name = table.value.getTabName() table_names.append(table_name) table.value.setTab(self, self._database) table.value.setFormParent(self) #self._tabs.addTab(table.value, table_name) self._tabs.addWidget(table.value) self._form_menu = FormMenu(item_names=table_names, parent=self) self._form_menu.currentRowChanged.connect(self.selectMenuItemChange) def selectMenuItemChange(self, index): self._tabs.setCurrentIndex(index) text = self._form_menu.currentItem().text() self._tab_label.setText(text) self._data_table_label.setText(text + " Table") self.updateDataTable() self.updateDatabaseTable() def updateDataTable(self): self._data_table.setRowCount(0) self._data_table.setRowCount(1) data = self._tabs.currentWidget() fields = data.getFields() values = data.getValues() col_count = len(values) self._data_table.setColumnCount(col_count) for col, (field, value) in enumerate(zip(fields, values)): header = QTableWidgetItem(field) item = QTableWidgetItem(str(value)) self._data_table.setHorizontalHeaderItem(col, header) self._data_table.setItem(0, col, item) #self._data_table.resizeColumnsToContents() self._data_table.verticalHeader().setStretchLastSection(True) def readDataFromDatabase(self): for table in Tables: table.value.readDataIntoForm( self._search_apPhase_combo.currentText()) def setApPhaseChangeCheck(self, change): self._ap_phase_change = change for table in Tables: table.value._ap_phase_change = change def setAddressChangeCheck(self, change): self._address_change = change for table in Tables: table.value._address_change = change def setNewForTables(self, new_val): self._new = new_val for table in Tables: table.value._new = new_val def apPhaseComboChange(self): if not self._address_change: self.setApPhaseChangeCheck(True) self.clearForm() self.readDataFromDatabase() self._search_address_combo.setCurrentIndex( self._search_apPhase_combo.currentIndex()) self.updateDataTable() self.setNewForTables(False) self.setApPhaseChangeCheck(False) def addressComboChange(self): if not self._ap_phase_change: self.setAddressChangeCheck(True) self.clearForm() self._search_apPhase_combo.setCurrentIndex( self._search_address_combo.currentIndex()) self.readDataFromDatabase() self.updateDataTable() self.setNewForTables(False) self.setAddressChangeCheck(False) def newSearchApCombo(self): if not self._address_change and self._search_apPhase_combo.currentText( ) == "": self._ap_phase_change = True Tables.CLIENT.value._ap_phase_change = True self._search_address_combo.setCurrentText("") self._ap_phase_widget.setPlaceholderText("Enter new ApPhase ID...") self._address_widget.setPlaceholderText("Enter new Address...") Tables.CLIENT.value.clearValues() self.setNewForTables(True) Tables.CLIENT.value._ap_phase_change = False self._ap_phase_change = False def newSearchAdrCombo(self): if not self._ap_phase_change and self._search_address_combo.currentText( ) == "": self._address_change = True self._search_apPhase_combo.setCurrentText("") self._ap_phase_widget.setPlaceholderText("Enter new ApPhase ID...") self._address_widget.setPlaceholderText("Enter new Address...") self.setNewForTables(True) Tables.CLIENT.value.clearValues() self._address_change = False def updateSearchCombos(self): ap_phase_id = self._ap_phase_widget.text() search_data = getAddresses( self._database.getValuesFrom2Fields(Tables.CLIENT.name, Client.ADDRESS.name, Client.AP_PHASE_ID.name)) search_ap_phase_ids = [value[1] for value in search_data] search_addresses = [value[0] for value in search_data] self._search_apPhase_combo.clear() self._search_address_combo.clear() self._search_apPhase_combo.addItems(search_ap_phase_ids) self._search_address_combo.addItems(search_addresses) index = self._search_apPhase_combo.findText(ap_phase_id) self._search_apPhase_combo.setCurrentIndex(index) def updateDatabaseTable(self): if self._main_tabs.currentIndex() == 1: table_name = self._tabs.currentWidget().getTableName() tab_name = self._tabs.currentWidget().getTabName() self._database_table_label.setText("Table: {tn} ({tbn})".format( tn=table_name, tbn=tab_name)) self._database.openDatabase() data = self._database.readTable(table_name) self._database.closeDatabase() headers = getFieldNames(table_name) data_count = len(data) self._database_table.setRowCount(0) self._database_table.setColumnCount(len(headers)) self._database_table.setRowCount(data_count) for col, header_text in enumerate(headers): header = QTableWidgetItem(header_text) self._database_table.setHorizontalHeaderItem(col, header) for row, data_row in enumerate(data): data_row = list(data_row) data_row.pop(0) for col, data_value in enumerate(data_row): item = QTableWidgetItem(str(data_value)) self._database_table.setItem(row, col, item) def exportDatabaseTable(self): file_name = self.exportCSVDialog() temp_row = [] headers = [] for col in range(self._database_table.columnCount()): headers.append( self._database_table.horizontalHeaderItem(col).text()) if file_name: with open(file_name, 'w', newline='') as n_f: writer = csv.writer(n_f) writer.writerow(headers) for row in range(self._database_table.rowCount()): for col in range(self._database_table.columnCount()): temp_row.append( self._database_table.item(row, col).text()) writer.writerow(temp_row) temp_row.clear() def exportCSVDialog(self): file_dialog = QFileDialog() file_name, ext = file_dialog.getSaveFileName(self, 'Export CSV File', "", "CSV (*.csv)") return file_name def mainTabChange(self, index): if index == 1: self.updateDatabaseTable() def start(self): with open(self.main_style_qss, 'r') as main_qss: main_style = main_qss.read() app.setStyleSheet(main_style) self.showMaximized() sys.exit(app.exec_())
class MainWindow(QMainWindow): # Main window def __init__(self): super().__init__() self.setWindowTitle = 'DD烤肉机' self.resize(1870, 820) self.mainWidget = QWidget() self.mainLayout = QGridLayout() # Grid layout self.mainLayout.setSpacing(10) self.mainWidget.setLayout(self.mainLayout) self.duration = 60000 self.bitrate = 2000 self.fps = 60 self.initProcess = InitProcess() self.previewSubtitle = PreviewSubtitle() self.dnldWindow = YoutubeDnld() self.exportWindow = exportSubtitle() self.videoDecoder = VideoDecoder() self.exportWindow.exportArgs.connect(self.exportSubtitle) self.stack = QStackedWidget() self.stack.setFixedWidth(1300) self.mainLayout.addWidget(self.stack, 0, 0, 10, 8) buttonWidget = QWidget() buttonLayout = QHBoxLayout() buttonWidget.setLayout(buttonLayout) self.playButton = QPushButton('从本地打开') self.playButton.clicked.connect(self.open) self.playButton.setFixedWidth(400) self.playButton.setFixedHeight(75) self.dnldButton = QPushButton('Youtube下载器') self.dnldButton.clicked.connect(self.popDnld) self.dnldButton.setFixedWidth(400) self.dnldButton.setFixedHeight(75) buttonLayout.addWidget(self.playButton) buttonLayout.addWidget(self.dnldButton) self.stack.addWidget(buttonWidget) self.videoPath = '' self.videoWidth = 1920 self.videoHeight = 1080 self.globalInterval = 200 self.setPlayer() self.setSubtitle() self.setToolBar() self.setCentralWidget(self.mainWidget) self.playStatus = False self.volumeStatus = True self.volumeValue = 100 self.subSelectedTxt = '' self.subReplayTime = 1 self.clipBoard = [] self.grabKeyboard() self.show() def setPlayer(self): self.playerWidget = QGraphicsVideoItem() self.scene = QGraphicsScene() self.view = QGraphicsView(self.scene) self.view.resize(1280, 730) self.scene.addItem(self.playerWidget) self.stack.addWidget(self.view) self.player = QMediaPlayer(self, QMediaPlayer.VideoSurface) self.player.setVideoOutput(self.playerWidget) self.view.installEventFilter(self) self.view.show() self.srtTextItemDict = {0: QGraphicsTextItem(), 1: QGraphicsTextItem(), 2: QGraphicsTextItem(), 3: QGraphicsTextItem(), 4: QGraphicsTextItem()} for _, srtTextItem in self.srtTextItemDict.items(): self.scene.addItem(srtTextItem) def setSubtitle(self): self.subtitleDict = {x: {-1: [100, '']} for x in range(5)} self.subTimer = QTimer() self.subTimer.setInterval(100) self.subtitle = QTableWidget() self.subtitle.setAutoScroll(False) self.subtitle.setEditTriggers(QAbstractItemView.NoEditTriggers) self.mainLayout.addWidget(self.subtitle, 0, 8, 10, 12) self.subtitle.setColumnCount(5) self.subtitle.selectRow(0) self.subtitle.setHorizontalHeaderLabels(['%s' % (i + 1) for i in range(5)]) self.subtitle.setVerticalHeaderLabels([cnt2Time2(i, self.globalInterval) for i in range(self.subtitle.rowCount())]) for index in range(5): self.subtitle.setColumnWidth(index, 130) self.subtitle.setHorizontalScrollMode(QAbstractItemView.ScrollPerPixel) self.subtitle.setEditTriggers(QAbstractItemView.DoubleClicked) self.subtitle.horizontalHeader().sectionClicked.connect(self.addSubtitle) self.subtitle.doubleClicked.connect(self.releaseKeyboard) self.subtitle.cellChanged.connect(self.subEdit) self.subtitle.verticalHeader().sectionClicked.connect(self.subHeaderClick) self.subtitle.setContextMenuPolicy(Qt.CustomContextMenu) self.subtitle.customContextMenuRequested.connect(self.popTableMenu) self.initSubtitle() def initSubtitle(self): self.initProcess.show() self.subtitle.cellChanged.disconnect(self.subEdit) for x in range(self.subtitle.columnCount()): for y in range(self.subtitle.rowCount()): self.subtitle.setSpan(y, x, 1, 1) self.subtitle.setRowCount(self.duration // self.globalInterval + 1) for x in range(self.subtitle.columnCount()): for y in range(self.subtitle.rowCount()): self.subtitle.setItem(y, x, QTableWidgetItem('')) self.subtitle.item(y, x).setBackground(QBrush(QColor('#232629'))) self.subtitle.setVerticalHeaderLabels([cnt2Time2(i, self.globalInterval) for i in range(self.subtitle.rowCount())]) self.subtitle.cellChanged.connect(self.subEdit) self.initProcess.hide() def addSubtitle(self, index): subtitlePath = QFileDialog.getOpenFileName(self, "请选择字幕", None, "字幕文件 (*.srt *.vtt *.ass *.ssa)")[0] if subtitlePath: self.initProcess.show() self.subtitle.cellChanged.disconnect(self.subEdit) if subtitlePath.endswith('.ass') or subtitlePath.endswith('.ssa'): p = subprocess.Popen(['utils/ffmpeg.exe', '-y', '-i', subtitlePath, 'temp_sub.srt']) p.wait() subtitlePath = 'temp_sub.srt' subData = {} with open(subtitlePath, 'r', encoding='utf-8') as f: f = f.readlines() subText = '' YoutubeAutoSub = False for l in f: if '<c>' in l: YoutubeAutoSub = True break for cnt, l in enumerate(f): if '<c>' in l: lineData = l.split('c>') if len(lineData) > 3: subText, start, _ = lineData[0].split('<') start = calSubTime(start[:-1]) // self.globalInterval * self.globalInterval if start not in self.subtitleDict[index]: end = calSubTime(lineData[-3][1:-2]) // self.globalInterval * self.globalInterval for i in range(len(lineData) // 2): subText += lineData[i * 2 + 1][:-2] subData[start] = [end - start, subText] else: subText, start, _ = lineData[0].split('<') start = calSubTime(start[:-1]) // self.globalInterval * self.globalInterval if start not in self.subtitleDict[index]: subText += lineData[1][:-2] subData[start] = [self.globalInterval, subText] elif '-->' in l and f[cnt + 2].strip() and '<c>' not in f[cnt + 2]: subText = f[cnt + 2][:-1] start = calSubTime(l[:12]) // self.globalInterval * self.globalInterval if start not in self.subtitleDict[index]: end = calSubTime(l[17:29]) // self.globalInterval * self.globalInterval subData[start] = [end - start, subText] if '-->' in l and f[cnt + 1].strip() and not YoutubeAutoSub: start = calSubTime(l[:12]) // self.globalInterval * self.globalInterval if start not in self.subtitleDict[index]: end = calSubTime(l[17:29]) // self.globalInterval * self.globalInterval delta = end - start if delta > 10: if '<b>' in f[cnt + 1]: subData[start] = [delta, f[cnt + 1].split('<b>')[1].split('<')[0]] else: subData[start] = [delta, f[cnt + 1][:-1]] self.subtitleDict[index].update(subData) maxRow = 0 for _, v in self.subtitleDict.items(): startMax = max(v.keys()) rowCount = (startMax + v[startMax][0]) // self.globalInterval if rowCount > maxRow: maxRow = rowCount if maxRow < self.duration // self.globalInterval + 1: maxRow = self.duration // self.globalInterval else: self.duration = maxRow * self.globalInterval self.subtitle.setRowCount(maxRow) self.subtitle.setVerticalHeaderLabels([cnt2Time2(i, self.globalInterval) for i in range(self.subtitle.rowCount())]) for start, rowData in subData.items(): startRow = start // self.globalInterval endRow = startRow + rowData[0] // self.globalInterval for row in range(startRow, endRow): self.subtitle.setItem(row, index, QTableWidgetItem(rowData[1])) self.subtitle.item(row, index).setBackground(QBrush(QColor('#35545d'))) self.subtitle.setSpan(startRow, index, endRow - startRow, 1) self.refreshComboBox() self.subtitle.cellChanged.connect(self.subEdit) self.initProcess.hide() def subTimeOut(self): fontColor = self.previewSubtitle.fontColor fontSize = (self.previewSubtitle.fontSize + 5) / 2.5 fontBold = self.previewSubtitle.bold fontItalic = self.previewSubtitle.italic fontShadowOffset = self.previewSubtitle.shadowOffset for _, srtTextItem in self.srtTextItemDict.items(): srtTextItem.setDefaultTextColor(fontColor) font = QFont() font.setFamily("微软雅黑") font.setPointSize(fontSize) font.setBold(fontBold) font.setItalic(fontItalic) srtTextItem.setFont(font) srtTextShadow = QGraphicsDropShadowEffect() srtTextShadow.setOffset(fontShadowOffset) srtTextItem.setGraphicsEffect(srtTextShadow) try: selected = self.subtitle.selectionModel().selection().indexes() for x, i in enumerate(selected): if self.subtitle.item(i.row(), x): txt = self.subtitle.item(i.row(), x).text() if txt: self.srtTextItemDict[x].setPlainText('#%s:' % (x + 1) + txt) txtSize = self.srtTextItemDict[x].boundingRect().size() posY = self.playerWidget.size().height() - txtSize.height() * (x + 1) posX = (self.playerWidget.size().width() - txtSize.width()) / 2 self.srtTextItemDict[x].setPos(posX, posY) else: self.srtTextItemDict[x].setPlainText('') else: self.srtTextItemDict[x].setPlainText('') except: pass def subHeaderClick(self, index): if self.player.duration(): position = index * self.globalInterval self.player.setPosition(position) self.videoSlider.setValue(position * 1000 // self.player.duration()) self.setTimeLabel() def subEdit(self, row, index): repeat = self.subtitle.rowSpan(row, index) self.setSubtitleDict(row, index, repeat, self.subtitle.item(row, index).text()) self.subtitle.cellChanged.disconnect(self.subEdit) for cnt in range(repeat): if self.subtitle.item(row + cnt, index).text(): self.subtitle.item(row, index).setBackground(QBrush(QColor('#35545d'))) else: self.subtitle.item(row, index).setBackground(QBrush(QColor('#232629'))) self.subtitle.cellChanged.connect(self.subEdit) def setSubtitleDict(self, row, index, num, text): self.subtitleDict[index][row * self.globalInterval] = [num * self.globalInterval, text] def popTableMenu(self, pos): self.subtitle.cellChanged.disconnect(self.subEdit) pos = QPoint(pos.x() + 55, pos.y() + 30) menu = QMenu() copy = menu.addAction('复制') paste = menu.addAction('粘贴') setSpan = menu.addAction('合并') clrSpan = menu.addAction('拆分') addSub = menu.addAction('导入字幕') cutSub = menu.addAction('裁剪字幕') action = menu.exec_(self.subtitle.mapToGlobal(pos)) selected = self.subtitle.selectionModel().selection().indexes() yList = [selected[0].row(), selected[-1].row()] xSet = set() for i in range(len(selected)): xSet.add(selected[i].column()) if action == copy: for x in xSet: self.clipBoard = [] for y in range(yList[0], yList[1] + 1): if self.subtitle.item(y, x): self.clipBoard.append(self.subtitle.item(y, x).text()) else: self.clipBoard.append('') break elif action == paste: self.subtitle.cellChanged.connect(self.subEdit) for x in xSet: for cnt, text in enumerate(self.clipBoard): self.subtitle.setItem(yList[0] + cnt, x, QTableWidgetItem(text)) self.subtitleDict[x][(yList[0] + cnt) * self.globalInterval] = [self.globalInterval, text] self.subtitle.cellChanged.disconnect(self.subEdit) elif action == setSpan: for x in xSet: if not self.subtitle.item(yList[0], x): firstItem = '' else: firstItem = self.subtitle.item(yList[0], x).text() for y in range(yList[0], yList[1] + 1): self.subtitle.setSpan(y, x, 1, 1) self.subtitle.setItem(y, x, QTableWidgetItem(firstItem)) self.subtitle.item(y, x).setBackground(QBrush(QColor('#35545d'))) if y * self.globalInterval in self.subtitleDict[x]: del self.subtitleDict[x][y * self.globalInterval] for x in xSet: self.subtitle.setSpan(yList[0], x, yList[1] - yList[0] + 1, 1) self.setSubtitleDict(yList[0], x, yList[1] - yList[0] + 1, firstItem) elif action == clrSpan: for x in xSet: if not self.subtitle.item(yList[0], x): firstItem = '' else: firstItem = self.subtitle.item(yList[0], x).text() for cnt, y in enumerate(range(yList[0], yList[1] + 1)): self.subtitle.setSpan(y, x, 1, 1) if not cnt: self.subtitle.setItem(yList[0], x, QTableWidgetItem(firstItem)) if firstItem: self.subtitle.item(y, x).setBackground(QBrush(QColor('#35545d'))) else: self.subtitle.item(y, x).setBackground(QBrush(QColor('#232629'))) else: self.subtitle.setItem(y, x, QTableWidgetItem('')) self.subtitle.item(y, x).setBackground(QBrush(QColor('#232629'))) self.setSubtitleDict(yList[0], x, yList[1] - yList[0] + 1, firstItem) break elif action == addSub: self.subtitle.cellChanged.connect(self.subEdit) for x in xSet: self.addSubtitle(x) self.subtitle.cellChanged.disconnect(self.subEdit) elif action == cutSub: for x in xSet: start = yList[0] * self.globalInterval end = yList[1] * self.globalInterval self.exportSubWindow(start, end, x + 1) self.subtitle.cellChanged.connect(self.subEdit) def setToolBar(self): ''' menu bar, file menu, play menu, tool bar. ''' toolBar = QToolBar() self.setContextMenuPolicy(Qt.NoContextMenu) self.addToolBar(toolBar) fileMenu = self.menuBar().addMenu('&文件') openAction = QAction(QIcon.fromTheme('document-open'), '&打开...', self, shortcut=QKeySequence.Open, triggered=self.open) fileMenu.addAction(openAction) downloadAction = QAction(QIcon.fromTheme('document-open'), '&Youtube下载器', self, triggered=self.popDnld) fileMenu.addAction(downloadAction) exitAction = QAction(QIcon.fromTheme('application-exit'), '&退出', self, shortcut='Ctrl+Q', triggered=self.close) fileMenu.addAction(exitAction) playMenu = self.menuBar().addMenu('&功能') self.playIcon = self.style().standardIcon(QStyle.SP_MediaPlay) self.pauseIcon = self.style().standardIcon(QStyle.SP_MediaPause) self.playAction = toolBar.addAction(self.playIcon, '播放') self.playAction.triggered.connect(self.mediaPlay) self.volumeIcon = self.style().standardIcon(QStyle.SP_MediaVolume) self.volumeMuteIcon = self.style().standardIcon(QStyle.SP_MediaVolumeMuted) self.volumeAction = toolBar.addAction(self.volumeIcon, '静音') self.volumeAction.triggered.connect(self.volumeMute) previewAction = QAction(QIcon.fromTheme('document-open'), '&设置预览字幕', self, triggered=self.popPreview) playMenu.addAction(previewAction) decodeMenu = self.menuBar().addMenu('&输出') decodeAction = QAction(QIcon.fromTheme('document-open'), '&输出字幕及视频', self, triggered=self.decode) decodeMenu.addAction(decodeAction) self.volSlider = Slider() self.volSlider.setOrientation(Qt.Horizontal) self.volSlider.setMinimum(0) self.volSlider.setMaximum(100) self.volSlider.setFixedWidth(120) self.volSlider.setValue(self.player.volume()) self.volSlider.setToolTip(str(self.volSlider.value())) self.volSlider.pointClicked.connect(self.setVolume) toolBar.addWidget(self.volSlider) self.videoPositionEdit = LineEdit('00:00') self.videoPositionEdit.setAlignment(Qt.AlignRight) self.videoPositionEdit.setFixedWidth(75) self.videoPositionEdit.setFont(QFont('Timers', 14)) self.videoPositionEdit.clicked.connect(self.mediaPauseOnly) self.videoPositionEdit.editingFinished.connect(self.mediaPlayOnly) self.videoPositionLabel = QLabel(' / 00:00 ') self.videoPositionLabel.setFont(QFont('Timers', 14)) toolBar.addWidget(QLabel(' ')) toolBar.addWidget(self.videoPositionEdit) toolBar.addWidget(self.videoPositionLabel) self.timer = QTimer() self.timer.setInterval(100) self.videoSlider = Slider() self.videoSlider.setEnabled(False) self.videoSlider.setOrientation(Qt.Horizontal) self.videoSlider.setMinimum(0) self.videoSlider.setMaximum(1000) self.videoSlider.setFixedWidth(1000) self.videoSlider.sliderMoved.connect(self.timeStop) self.videoSlider.sliderReleased.connect(self.timeStart) self.videoSlider.pointClicked.connect(self.videoSliderClick) toolBar.addWidget(self.videoSlider) toolBar.addWidget(QLabel(' ')) self.globalIntervalComBox = QComboBox() self.globalIntervalComBox.addItems(['间隔 100ms', '间隔 200ms', '间隔 500ms', '间隔 1s']) self.globalIntervalComBox.setCurrentIndex(1) self.globalIntervalComBox.currentIndexChanged.connect(self.setGlobalInterval) toolBar.addWidget(self.globalIntervalComBox) toolBar.addWidget(QLabel(' ')) self.subEditComBox = QComboBox() self.refreshComboBox() toolBar.addWidget(self.subEditComBox) toolBar.addWidget(QLabel(' ')) moveForward = QPushButton('- 1') moveForward.setFixedWidth(50) toolBar.addWidget(moveForward) toolBar.addWidget(QLabel(' ')) moveAfterward = QPushButton('+ 1') moveAfterward.setFixedWidth(50) toolBar.addWidget(moveAfterward) toolBar.addWidget(QLabel(' ')) clearSub = QPushButton('清空') clearSub.setFixedWidth(50) toolBar.addWidget(clearSub) toolBar.addWidget(QLabel(' ')) outputSub = QPushButton('裁剪') outputSub.setFixedWidth(50) toolBar.addWidget(outputSub) moveForward.clicked.connect(self.moveForward) moveAfterward.clicked.connect(self.moveAfterward) clearSub.clicked.connect(self.clearSub) outputSub.clicked.connect(self.exportSubWindow) def setGlobalInterval(self, index): if not self.playStatus: self.mediaPlay() self.globalInterval = {0: 100, 1: 200, 2: 500, 3: 1000}[index] self.initSubtitle() self.initProcess.show() self.subtitle.cellChanged.disconnect(self.subEdit) for index, subData in self.subtitleDict.items(): for start, rowData in subData.items(): startRow = start // self.globalInterval deltaRow = rowData[0] // self.globalInterval if deltaRow: endRow = startRow + deltaRow for row in range(startRow, endRow): self.subtitle.setItem(row, index, QTableWidgetItem(rowData[1])) if row >= 0: self.subtitle.item(row, index).setBackground(QBrush(QColor('#35545d'))) self.subtitle.setSpan(startRow, index, endRow - startRow, 1) self.subtitle.cellChanged.connect(self.subEdit) self.initProcess.hide() def moveForward(self): self.initProcess.show() self.subtitle.cellChanged.disconnect(self.subEdit) index = self.subEditComBox.currentIndex() for y in range(self.subtitle.rowCount()): self.subtitle.setSpan(y, index, 1, 1) self.subtitle.setItem(y, index, QTableWidgetItem('')) self.subtitle.item(y, index).setBackground(QBrush(QColor('#232629'))) tmpDict = self.subtitleDict[index] self.subtitleDict[index] = {} for start, rowData in tmpDict.items(): self.subtitleDict[index][start - self.globalInterval] = rowData for start, rowData in self.subtitleDict[index].items(): startRow = start // self.globalInterval endRow = startRow + rowData[0] // self.globalInterval for row in range(startRow, endRow): self.subtitle.setItem(row, index, QTableWidgetItem(rowData[1])) self.subtitle.item(row, index).setBackground(QBrush(QColor('#35545d'))) self.subtitle.setSpan(startRow, index, endRow - startRow, 1) self.subtitle.cellChanged.connect(self.subEdit) self.initProcess.hide() def moveAfterward(self): self.initProcess.show() self.subtitle.cellChanged.disconnect(self.subEdit) index = self.subEditComBox.currentIndex() for y in range(self.subtitle.rowCount()): self.subtitle.setSpan(y, index, 1, 1) self.subtitle.setItem(y, index, QTableWidgetItem('')) self.subtitle.item(y, index).setBackground(QBrush(QColor('#232629'))) tmpDict = self.subtitleDict[index] self.subtitleDict[index] = {} for start, rowData in tmpDict.items(): self.subtitleDict[index][start + self.globalInterval] = rowData for start, rowData in self.subtitleDict[index].items(): startRow = start // self.globalInterval endRow = startRow + rowData[0] // self.globalInterval for row in range(startRow, endRow): self.subtitle.setItem(row, index, QTableWidgetItem(rowData[1])) self.subtitle.item(row, index).setBackground(QBrush(QColor('#35545d'))) self.subtitle.setSpan(startRow, index, endRow - startRow, 1) self.subtitle.cellChanged.connect(self.subEdit) self.initProcess.hide() def clearSub(self): index = self.subEditComBox.currentIndex() reply = QMessageBox.information(self, '清空字幕', '清空第 %s 列字幕条?' % (index + 1), QMessageBox.Yes | QMessageBox.No) if reply == QMessageBox.Yes: self.initProcess.show() self.subtitle.cellChanged.disconnect(self.subEdit) self.subtitleDict[index] = {0: [self.globalInterval, '']} for i in range(self.subtitle.rowCount()): self.subtitle.setSpan(i, index, 1, 1) self.subtitle.setItem(i, index, QTableWidgetItem('')) self.subtitle.item(i, index).setBackground(QBrush(QColor('#232629'))) self.subtitle.setHorizontalHeaderItem(index, QTableWidgetItem('%s' % (index + 1))) self.subtitle.cellChanged.connect(self.subEdit) self.initProcess.hide() def exportSubWindow(self, start=0, end=0, index=None): self.releaseKeyboard() self.exportWindow.hide() self.exportWindow.show() start = '00:00.0' if not start else self.splitTime(start) end = self.splitTime(self.duration) if not end else self.splitTime(end) if not index: index = self.subEditComBox.currentIndex() + 1 self.exportWindow.setDefault(start, end, index) def exportSubtitle(self, exportArgs): start = calSubTime2(exportArgs[0]) end = calSubTime2(exportArgs[1]) subStart = calSubTime2(exportArgs[2]) index = exportArgs[3] - 1 subData = self.subtitleDict[index] rowList = sorted(subData.keys()) exportRange = [] for t in rowList: if t >= start and t <= end: exportRange.append(t) subNumber = 1 with open(exportArgs[-1], 'w', encoding='utf-8') as exportFile: for t in exportRange: text = subData[t][1] if text: start = ms2Time(t + subStart) end = ms2Time(t + subStart + subData[t][0]) exportFile.write('%s\n%s --> %s\n%s\n\n' % (subNumber, start, end, text)) subNumber += 1 QMessageBox.information(self, '导出字幕', '导出完成', QMessageBox.Yes) self.exportWindow.hide() def refreshComboBox(self): self.subEditComBox.clear() for i in range(self.subtitle.columnCount()): self.subEditComBox.addItem('字幕 ' + str(i + 1)) def open(self): self.videoPath = QFileDialog.getOpenFileName(self, "请选择视频文件", None, "MP4格式 (*.mp4);;所有文件(*.*)")[0] if self.videoPath: cmd = ['utils/ffmpeg.exe', '-i', self.videoPath] p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) p.wait() for l in p.stdout.readlines(): l = l.decode('utf8') if 'Duration' in l: self.duration = calSubTime(l.split(' ')[3][:-1]) if 'Stream' in l and 'DAR' in l: self.videoWidth, self.videoHeight = map(int, l.split(' [')[0].split(' ')[-1].split('x')) args = l.split(',') for cnt, arg in enumerate(args): if 'kb' in arg: self.bitrate = int(arg.split('kb')[0]) self.fps = int(args[cnt + 1].split('fps')[0]) break break self.initProcess.show() self.subtitle.cellChanged.disconnect(self.subEdit) self.subtitle.setRowCount(self.duration // self.globalInterval + 1) self.subtitle.setVerticalHeaderLabels([cnt2Time2(i, self.globalInterval) for i in range(self.subtitle.rowCount())]) self.subtitle.cellChanged.connect(self.subEdit) self.initProcess.hide() url = QUrl.fromLocalFile(self.videoPath) self.stack.setCurrentIndex(1) self.playerWidget.setSize(QSizeF(1280, 720)) self.player.setMedia(url) self.playStatus = True self.videoSlider.setEnabled(True) self.mediaPlay() self.timer.start() self.timer.timeout.connect(self.timeOut) self.subTimer.start() self.subTimer.timeout.connect(self.subTimeOut) def popDnld(self): self.releaseKeyboard() self.dnldWindow.hide() self.dnldWindow.show() def popPreview(self): self.releaseKeyboard() self.previewSubtitle.hide() self.previewSubtitle.show() def decode(self): self.releaseKeyboard() self.videoDecoder.setDefault(self.videoPath, self.videoWidth, self.videoHeight, self.duration, self.bitrate, self.fps, self.subtitleDict) self.videoDecoder.hide() self.videoDecoder.show() def mediaPlay(self): if self.playStatus: self.player.play() self.grabKeyboard() self.timeStart() self.playStatus = False self.playAction.setIcon(self.pauseIcon) self.playAction.setText('暂停') else: self.player.pause() self.timeStop() self.playStatus = True self.playAction.setIcon(self.playIcon) self.playAction.setText('播放') def mediaPlayOnly(self): self.grabKeyboard() try: timeText = self.videoPositionEdit.text().split(':') m, s = timeText[:2] if not m: m = '00' if not s: s = '00' if len(m) > 3: m = m[:3] if len(s) > 2: s = s[:2] if m.isdigit(): m = int(m) if s.isdigit(): s = int(s) if s > 60: s = 60 total_m = self.player.duration() // 60000 if m > total_m: m = total_m self.player.setPosition(m * 60000 + s * 1000) self.videoSlider.setValue(self.player.position() * 1000 / self.player.duration()) except: pass self.videoPositionEdit.setReadOnly(True) self.timeStart() def mediaPauseOnly(self): self.releaseKeyboard() self.videoPositionEdit.setReadOnly(False) self.player.pause() self.timeStop() self.playStatus = True self.playAction.setIcon(self.playIcon) self.playAction.setText('播放') def splitTime(self, playTime): playTime = playTime // 1000 m = str(playTime // 60) s = playTime % 60 s = ('0%s' % s)[-2:] if len(m) > 2: t = '%3s:%2s' % (m, s) else: t = '%2s:%2s' % (m, s) return t def timeOut(self): row = self.player.position() // self.globalInterval self.subtitle.selectRow(row) self.subtitle.verticalScrollBar().setValue(row - 10) if self.dnldWindow.isHidden() or self.exportWindow.isHidden() or self.videoDecoder.isHidden(): self.grabKeyboard() try: self.videoSlider.setValue(self.player.position() * 1000 / self.player.duration()) self.setTimeLabel() except: pass def timeStop(self): self.timer.stop() def timeStart(self): self.timer.start() def videoSliderClick(self, p): self.videoSlider.setValue(p.x()) self.player.setPosition(p.x() * self.player.duration() // 1000) self.setTimeLabel() def setVolume(self, p): self.volumeValue = p.x() if self.volumeValue > 100: self.volumeValue = 100 if self.volumeValue < 0: self.volumeValue = 0 self.volSlider.setValue(self.volumeValue) self.player.setVolume(self.volumeValue) self.volSlider.setToolTip(str(self.volSlider.value())) if self.volumeValue: self.volumeStatus = True self.volumeAction.setIcon(self.volumeIcon) else: self.volumeStatus = False self.volumeAction.setIcon(self.volumeMuteIcon) def volumeMute(self): if self.volumeStatus: self.volumeStatus = False self.old_volumeValue = self.player.volume() self.player.setVolume(0) self.volSlider.setValue(0) self.volumeAction.setIcon(self.volumeMuteIcon) else: self.volumeStatus = True self.player.setVolume(self.old_volumeValue) self.volSlider.setValue(self.old_volumeValue) self.volumeAction.setIcon(self.volumeIcon) def setTimeLabel(self): now = self.player.position() total = self.player.duration() now = self.splitTime(now) total = self.splitTime(total) self.videoPositionEdit.setText(now) self.videoPositionLabel.setText(' / %s ' % total) def eventFilter(self, obj, event): if obj == self.view: if event.type() == QEvent.MouseButtonPress: self.mediaPlay() return QMainWindow.eventFilter(self, obj, event) def keyPressEvent(self, QKeyEvent): key = QKeyEvent.key() if key == Qt.Key_Left: if self.videoSlider.isEnabled(): self.player.setPosition(self.player.position() - 5000) self.videoSlider.setValue(self.player.position() * 1000 / self.player.duration()) self.setTimeLabel() elif key == Qt.Key_Right: if self.videoSlider.isEnabled(): self.player.setPosition(self.player.position() + 5000) self.videoSlider.setValue(self.player.position() * 1000 / self.player.duration()) self.setTimeLabel() elif key == Qt.Key_Up: self.volumeValue += 10 if self.volumeValue > 100: self.volumeValue = 100 self.volSlider.setValue(self.volumeValue) self.player.setVolume(self.volumeValue) elif key == Qt.Key_Down: self.volumeValue -= 10 if self.volumeValue < 0: self.volumeValue = 0 self.volSlider.setValue(self.volumeValue) self.player.setVolume(self.volumeValue) elif key == Qt.Key_Space: self.mediaPlay()
# --------------------------- # StackedWidgetに登録されたwidgetをインデックスで指定して表示する # --------------------------- import sys from PySide2.QtWidgets import QApplication, QTextEdit, QStackedWidget app = QApplication(sys.argv) qw_text_edit_1 = QTextEdit() qw_text_edit_1.append('1') qw_text_edit_2 = QTextEdit() qw_text_edit_2.append('2') qw_stack = QStackedWidget() idx_qw_text_edit_1 = qw_stack.addWidget(qw_text_edit_1) idx_qw_text_edit_2 = qw_stack.addWidget(qw_text_edit_2) print(idx_qw_text_edit_1, idx_qw_text_edit_2) # 表示するwidgetをインデックスで指定する qw_stack.setCurrentIndex(idx_qw_text_edit_2) print(qw_stack.currentIndex()) qw_stack.show() sys.exit(app.exec_())
class ChannelArithmeticDialog(ieb.ImarisExtensionBase): """ Channel Arithmetic and Beyond ============================= `View on GitHub <https://github.com/niaid/imaris_extensions>`_ This program enables one to specify arithmetic expressions which are used to create new channels. The basic arithmetic operations are supported: +,-,*,/,**. More advanced operations that run short `SimpleITK <https://simpleitk.org/>`_ code snippets are also supported. Channels are referenced using square brackets and the channel index, starting at **zero**. To apply an expression to all channels, use the channel index 'i'. When creating a single new channel, the arithmetic expression consists of literal channel numbers, one can select a name and color for the new channel. When creating multiple new channels, the arithmetic expression is applied to all channels, the postfix '_modified' is appended to the original channel names and the original color is copied over. Note that for all channels created by the program the channel description will include the arithmetic expression used to create that channel. This transparently supports your efforts to conduct reproducible research. Because an Imaris image has a specific pixel type (8, 16, 32 bit unsigned integer and 32 bit floating point) all computations are performed using a 32 bit floating point representation and then clamped to the range of the image's pixel type. The program allows you to use the same expression on multiple files. In this case literal channel values are limited by the number of shared channels. Thus, if working with two files one with three channels and one with four channels, the valid literal channel values are limited to 0, 1, 2. We cannot use 3 as it does not exist in all files. On the other hand, if our autofluorescence channel is one of these channels, e.g. channel 0, we can subtract it from all channels in both files, `[i]-[0]`. Basic Examples -------------- Multiply channels zero and three: .. code-block:: Python [0]*[3] Multiply channels zero and three and subtract the result from channel four: .. code-block:: Python [4] - ([0]*[3]) Duplicate all channels: .. code-block:: Python [i] Subtract channel zero from all channels: .. code-block:: Python [i]-[0] Advanced Examples ----------------- Threshold channel one using a value of 100, resulting image is binary values in {0,1}: .. code-block:: Python [1]>100 Threshold a specific channel to create a binary result using the Otsu filter: .. code-block:: Python sitk.OtsuThreshold([1], 0, 1) Threshold a specific channel retaining the values above the threshold: .. code-block:: Python sitk.Cast([1]>100, sitk.sitkFloat32)*[1] Threshold a specific channel, get all connected components, then sort the components according to size, discarding those smaller than a minimum size and create a binary mask corresponding to the largest component, which is the first label(second largest component label is 2 etc.) .. code-block:: Python sitk.RelabelComponent(sitk.ConnectedComponent([1]>100), minimumObjectSize = 50)==1 Create a binary mask representing the colocalization of two channels, intensity values below 20 are considred noise: .. code-block:: Python ([1]>20)*([2]>20) Create a binary mask representing the colocalization of two channels. We are interested in all pixels in channel 2 that have a value above 20 and that are less than 1.0um away from pixels in channel 1 that have a value above 100 (**note**: this operation yields different results when run using a slice-by-slice approach vs. a volumetric approach): .. code-block:: Python (sitk.Cast([2]>20, sitk.sitkFloat32) * sitk.Abs(sitk.SignedMaurerDistanceMap([1]>100, insideIsPositive=False, squaredDistance=False, useImageSpacing=True)))<=1.0 Create a binary mask using thresholding and then perform morphological closing (dilation followed by erosion) with distance maps, useful for filling holes: .. code-block:: Python sitk.SignedMaurerDistanceMap(sitk.SignedMaurerDistanceMap([1]>100, insideIsPositive=False, squaredDistance=False, useImageSpacing=True) < 1.0, insideIsPositive=False, squaredDistance=False, useImageSpacing=True)<-1.0 Create a binary mask using thresholding and then perform morphological opening (erosion followed by dilation) with distance maps, useful for removing small islands: .. code-block:: Python sitk.SignedMaurerDistanceMap(sitk.SignedMaurerDistanceMap([1]>100, insideIsPositive=False, squaredDistance=False, useImageSpacing=True) < -0.2, insideIsPositive=False, squaredDistance=False, useImageSpacing=True)<0.2 """ # noqa def __init__(self): super(ChannelArithmeticDialog, self).__init__() # Channel indexes in the arithmetic calculator are denoted using a # regular expression: one or more digits in square brackets (e.g. [1234]). # First digit is zero and nothing afterwards or first digit is in [1-9] and # there are possibly more digits afterwards. # Starting index is zero. self.channel_pattern = re.compile(r"\[(0|[1-9]\d*)\]") # Use QT's global threadpool, documentation says: "This global thread pool # automatically maintains an optimal number of threads based on the # number of cores in the CPU." self.threadpool = QThreadPool.globalInstance() # Configure the help dialog. self.help_dialog = HelpDialog(w=700, h=500) self.help_dialog.setWindowTitle("Channel Arithmetic Help") self.help_dialog.set_rst_text( inspect.getdoc(self), pygments_css_file_name="pygments_dark.css") self.__create_gui() self.setWindowTitle("Channel Arithmetic") self.processing_error = False self.show() def __create_gui(self): menu_bar = self.menuBar() # Force menubar to be displayed in the application on OSX/Linux, otherwise it # is displayed in the system menubar menu_bar.setNativeMenuBar(False) self.help_button = QPushButton("Help") self.help_button.clicked.connect(self.help_dialog.show) menu_bar.setCornerWidget(self.help_button, Qt.TopLeftCorner) central_widget = QWidget(self) gui_layout = QVBoxLayout() central_widget.setLayout(gui_layout) self.setCentralWidget(central_widget) select_files_widget = self.__create_select_files_widget() arithmetic_widget = self.__create_arithmetic_widget() self.stack = QStackedWidget(self) self.stack.addWidget(select_files_widget) self.stack.addWidget(arithmetic_widget) gui_layout.addWidget(self.stack) self.status_bar = self.statusBar() def closeEvent(self, event): """ Override the closeEvent method so that clicking the 'x' button also closes all of the dialogs. """ self.help_dialog.close() event.accept() def __create_arithmetic_widget(self): wid = QWidget(self) arithmetic_layout = QVBoxLayout() wid.setLayout(arithmetic_layout) self.valid_indexes_label = QLabel("") arithmetic_layout.addWidget(self.valid_indexes_label) layout = QHBoxLayout() layout.setAlignment(Qt.AlignLeft) layout.addWidget(QLabel("Enter new channel arithmetic expression:")) arithmetic_layout.addLayout(layout) self.arithmetic_expression_text_edit = QTextEdit() arithmetic_layout.addWidget(self.arithmetic_expression_text_edit) self.slice_by_slice_checkbox = QCheckBox( "Slice by slice (smaller memory footprint).") arithmetic_layout.addWidget(self.slice_by_slice_checkbox) layout = QHBoxLayout() layout.addWidget(QLabel("New channel name:")) self.new_channel_name_line_edit = QLineEdit() layout.addWidget(self.new_channel_name_line_edit) arithmetic_layout.addLayout(layout) layout = QHBoxLayout() layout.addWidget(QLabel("New channel color:")) self.new_channel_color_button = QPushButton() self.new_channel_color_button.clicked.connect( self.__select_color_callback) layout.addWidget(self.new_channel_color_button) arithmetic_layout.addLayout(layout) self.apply_button = QPushButton("Apply") self.apply_button.clicked.connect(self.__channel_arithmetic_wrapper) arithmetic_layout.addWidget(self.apply_button) progress_wid = QWidget() self.progress_grid_layout = QGridLayout() progress_wid.setLayout(self.progress_grid_layout) scroll_area = QScrollArea() scroll_area.setWidget(progress_wid) scroll_area.setWidgetResizable(True) arithmetic_layout.addWidget(scroll_area) layout = QHBoxLayout() layout.setAlignment(Qt.AlignLeft) self.processing_prev_button = QPushButton("Prev") self.processing_prev_button.clicked.connect( lambda: self.stack.setCurrentIndex(0)) layout.addWidget(self.processing_prev_button) arithmetic_layout.addLayout(layout) return wid def __configure_and_show_arithmetic_widget(self): file_names = self.input_files_edit.toPlainText().split("\n") num_channels = [] problematic_images = [] for file_name in file_names: try: meta_data = sio.read_metadata(file_name) num_channels.append(len(meta_data["channels_information"])) except Exception: problematic_images.append(file_name) if problematic_images: self._error_function( "Problem encountered reading the following file(s):\n" + "\n".join(problematic_images)) return self.max_channel_index = min(num_channels) - 1 self.valid_indexes_label.setText( f"Valid channel indexes: 0...{self.max_channel_index}, i") self.arithmetic_expression_text_edit.clear() self.slice_by_slice_checkbox.setChecked(False) self.new_channel_name_line_edit.clear() # Remove all widgets from layout, done in reverse order because # removing from the begining shifts the rest of the items for i in reversed(range(self.progress_grid_layout.count())): self.progress_grid_layout.itemAt(i).widget().setParent(None) for i, file_name in enumerate(file_names): self.progress_grid_layout.addWidget( QLabel(os.path.basename(file_name)), i, 0) progress_bar = QProgressBar() progress_bar.setMaximum(100) self.progress_grid_layout.addWidget(progress_bar, i, 1) self.stack.setCurrentIndex(1) def __create_select_files_widget(self): wid = QWidget() input_layout = QVBoxLayout() wid.setLayout(input_layout) layout = QHBoxLayout() layout.addWidget(QLabel("File names:")) layout.setAlignment(Qt.AlignLeft) button = QPushButton("Browse") button.setToolTip("Select input files for arithmetic operation.") button.clicked.connect(self.__browse_select_input_callback) layout.addWidget(button) input_layout.addLayout(layout) self.input_files_edit = QTextEdit() self.input_files_edit.setReadOnly(True) input_layout.addWidget(self.input_files_edit) layout = QHBoxLayout() layout.setAlignment(Qt.AlignRight) self.input_files_next_button = QPushButton("Next") self.input_files_next_button.setEnabled(False) self.input_files_next_button.clicked.connect( self.__configure_and_show_arithmetic_widget) layout.addWidget(self.input_files_next_button) input_layout.addLayout(layout) return wid def __browse_select_input_callback(self): file_names, _ = QFileDialog.getOpenFileNames( self, "QFileDialog.getOpenFileNames()", "", "Imaris Images (*.ims);;All Files (*)", ) if file_names: self.input_files_edit.setText("\n".join(file_names)) self.input_files_next_button.setEnabled(True) def __select_color_callback(self): color = QColorDialog.getColor() if color.isValid(): self.new_channel_color_button.setStyleSheet( f"background-color :rgb({color.red()},{color.green()},{color.blue()})" ) def __channel_arithmetic_wrapper(self): # Get the arithmetic expression after removing all whitespace arithmetic_expression = "".join( self.arithmetic_expression_text_edit.toPlainText().split()) color = self.new_channel_color_button.palette().button().color() if arithmetic_expression: # Get the explicit channel indexes that appear in the expression and # check that they are in the valid range. channel_indexes = re.findall(self.channel_pattern, arithmetic_expression) invalid_channels = [ ci for ci in channel_indexes if int(ci) not in range(self.max_channel_index + 1) ] if invalid_channels: self._error_function( "The following channels specified in the arithmetic expression" + f" are outside the valid range [0,{self.max_channel_index}]: " + ", ".join(invalid_channels)) return # Disable the UI interaction during computation self.arithmetic_expression_text_edit.setReadOnly(True) self.slice_by_slice_checkbox.setEnabled(False) self.new_channel_name_line_edit.setReadOnly(True) self.new_channel_color_button.setEnabled(False) self.apply_button.setEnabled(False) self.processing_prev_button.setEnabled(False) QApplication.setOverrideCursor(Qt.WaitCursor) file_names = self.input_files_edit.toPlainText().split("\n") self.num_threads_left = len(file_names) for i, input_file_name in enumerate(file_names): # Configure and perform computation in another thread. arithmetic_calculator = ArithmeticCalculator( self.channel_pattern) arithmetic_calculator.signals.finished.connect( self.__arithmetic_finished) arithmetic_calculator.signals.processing_error.connect( self._processing_error_function) arithmetic_calculator.signals.progress_signal.connect( self.progress_grid_layout.itemAtPosition( i, 1).widget().setValue) arithmetic_calculator.signals.update_state_signal.connect( self.status_bar.showMessage) arithmetic_calculator.input_file_name = input_file_name arithmetic_calculator.arithmetic_expression = arithmetic_expression arithmetic_calculator.new_channel_color = [ color.red() / 255.0, color.green() / 255.0, color.blue() / 255.0, ] arithmetic_calculator.new_channel_alpha = color.alpha() / 255.0 arithmetic_calculator.new_channel_name = ( self.new_channel_name_line_edit.text().strip()) arithmetic_calculator.slice_by_slice = ( self.slice_by_slice_checkbox.isChecked()) self.threadpool.start(arithmetic_calculator) else: self._error_function( "No action taken: arithmetic expression not set.") def __arithmetic_finished(self): self.num_threads_left = self.num_threads_left - 1 if self.num_threads_left == 0: QApplication.restoreOverrideCursor() self.status_bar.clearMessage() for i in range(self.progress_grid_layout.rowCount()): self.progress_grid_layout.itemAtPosition( i, 1).widget().setValue(0) # Enable the UI interaction after computation self.arithmetic_expression_text_edit.setReadOnly(False) self.slice_by_slice_checkbox.setEnabled(True) self.new_channel_name_line_edit.setReadOnly(False) self.new_channel_color_button.setEnabled(True) self.apply_button.setEnabled(True) self.processing_prev_button.setEnabled(True) # Inform the user that the calculations completed. If processing errors # occured then the desired operation may not have happened, but the # calculation was completed. QMessageBox().information(self, "Message", "Calculation completed.") self.processing_error = False
class MainWindow(QMainWindow): def __init__(self): super(MainWindow, self).__init__() self.json_path = '' self.currDB = '' self.dc = dataController() self.createWidgetStack() self.createActions() self.createMenus() self.createToolBars() self.createStatusBar() self.resize(1000, 800) self.setWindowTitle("Logger") self.setCentralWidget(self.widget_stack) def createWidgetStack(self): self.widget_stack = QStackedWidget() self.centralWidget = MainWidget(self.dc) self.plotWidget = plotMainWidget(self.dc) self.widget_stack.addWidget(self.centralWidget) self.widget_stack.addWidget(self.plotWidget) self.widget_stack.setCurrentIndex(0) def open(self): fileName = QFileDialog.getOpenFileName(self)[0] if self.json_path == '': QMessageBox.warning(self, "Application", "load json file first\n") return if fileName != '': print(fileName) self.dc.initBinFile(fileName, self.json_path) self.centralWidget.initDomainCombo() self.createNewPlotView() self.widget_stack.setCurrentIndex(0) def openJson(self): fileName = QFileDialog.getOpenFileName(self)[0] if fileName: self.json_path = fileName self.widget_stack.setCurrentIndex(0) def openDB(self): fileName= QFileDialog.getOpenFileName(self)[0] if fileName: self.dc.initDB(fileName) self.centralWidget.initDomainCombo() if fileName != self.currDB: self.createNewPlotView() self.currDB = fileName self.widget_stack.setCurrentIndex(0) def createNewPlotView(self): self.widget_stack.removeWidget(self.plotWidget) self.plotWidget = plotMainWidget(self.dc) self.widget_stack.addWidget(self.plotWidget) def saveToFile(self): path, filtr = QFileDialog.getSaveFileName(self) if path: self.centralWidget.CW_exportToFile(path) def openChart(self): if not self.dc: QMessageBox.warning(self, "Application", "load json file first\n") return self.widget_stack.setCurrentIndex(1) def goBack(self): self.widget_stack.setCurrentIndex(0) def createActions(self): open_bin_file = os.path.join(icon_path,'bin.png') self.openAct = QAction( QIcon(open_bin_file), "&Open...", self, shortcut= QKeySequence.Open, statusTip="Open an existing file", triggered=self.open) open_json_file = os.path.join(icon_path,'json.png') self.openJSONAct = QAction( QIcon(open_json_file), "Open...", self, shortcut= QKeySequence.Open, statusTip="Open JSON file", triggered=self.openJson) open_db= os.path.join(icon_path,'db.png') self.openDBact = QAction( QIcon(open_db), "Open...", self, shortcut= QKeySequence.Open, statusTip="Open DB", triggered=self.openDB) disk_icon_path= os.path.join(icon_path,'save.png') self.saveToFileAct = QAction( QIcon(disk_icon_path), "Save to file...", self, shortcut= QKeySequence.Open, statusTip="save to file", triggered=self.saveToFile) chart_icon_path= os.path.join(icon_path,'chart.png') self.openChartAct = QAction( QIcon(chart_icon_path), "Open chart windows", self, shortcut= QKeySequence.Open, statusTip="open chart windows", triggered=self.openChart) back_icon_path= os.path.join(icon_path,'back.png') self.goBackAct = QAction( QIcon(back_icon_path), "Go Back", self, shortcut= QKeySequence.Open, statusTip="Go back", triggered=self.goBack) def createMenus(self): self.fileMenu = self.menuBar().addMenu("&File") self.fileMenu.addAction(self.openAct) self.fileMenu.addAction(self.openJSONAct) self.fileMenu.addAction(self.openDBact) self.fileMenu.addAction(self.saveToFileAct) self.fileMenu.addSeparator() self.fileMenu.addAction(self.openChartAct) self.fileMenu.addAction(self.goBackAct) def createToolBars(self): self.fileToolBar = self.addToolBar("File") self.fileToolBar.addAction(self.openAct) self.fileToolBar.addAction(self.openJSONAct) self.fileToolBar.addAction(self.openDBact) self.fileToolBar.addAction(self.saveToFileAct) self.fileToolBar.addAction(self.openChartAct) self.fileToolBar.addAction(self.goBackAct) def createStatusBar(self): self.statusBar().showMessage("Ready")
class ControlFrame(QFrame): def __init__(self, dataset, display_panel, threads): super().__init__() if isinstance(display_panel, DisplayFrame): self.display_panel = display_panel else: raise TypeError("'display_panel' must be the instance of " "'DisplayFrame'") self.dataset = dataset self.threads = threads self.__layout = QVBoxLayout() self.setLayout(self.__layout) self.__layout.setContentsMargins(0, 0, 0, 0) self.__set_running_options_ui() self.__set_fuzzy_set_operation_types_ui() self.__set_fuzzy_variables_ui() self.__set_fuzzy_rules_ui() self.__set_console_ui() def __set_running_options_ui(self): group_box = QGroupBox("Running Options") inner_layout = QHBoxLayout() group_box.setLayout(inner_layout) self.data_selector = QComboBox() self.data_selector.addItems(list(self.dataset.keys())) self.data_selector.setStatusTip("Select the road map case.") self.data_selector.currentIndexChanged.connect(self.__change_map) self.fps = QSpinBox() self.fps.setMinimum(1) self.fps.setMaximum(60) self.fps.setValue(20) self.fps.setStatusTip( "The re-drawing rate for car simulator. High fps " "may cause the plot shows discontinuously.") self.start_btn = QPushButton("Run") self.start_btn.setStatusTip("Run the car.") self.start_btn.clicked.connect(self.__run) self.stop_btn = QPushButton("Stop") self.stop_btn.setStatusTip("Force the simulation stop running.") self.stop_btn.setDisabled(True) self.save_btn = QPushButton() self.save_btn.setIcon(QIcon(':/icons/save_icon.png')) self.save_btn.setStatusTip("Save every details for the last time " "running.") self.save_btn.clicked.connect(self.__save_results) self.save_btn.setDisabled(True) self.__change_map() inner_layout.addWidget(self.data_selector, 1) inner_layout.addWidget(QLabel("FPS:")) inner_layout.addWidget(self.fps) inner_layout.addWidget(self.start_btn) inner_layout.addWidget(self.stop_btn) inner_layout.addWidget(self.save_btn) self.__layout.addWidget(group_box) def __set_fuzzy_set_operation_types_ui(self): group_box = QGroupBox("Fuzzy Sets Operation Types") inner_layout = QFormLayout() group_box.setLayout(inner_layout) self.implication_selections = RadioButtonSet([ ("imp_dr", QRadioButton("Dienes-Rescher")), ("imp_l", QRadioButton("Lukasieweicz")), ("imp_z", QRadioButton("Zadel")), ("imp_g", QRadioButton("Godel")), ("imp_m", QRadioButton("Mamdani")), ("imp_p", QRadioButton("Product")) ]) self.combination_vars_selections = RadioButtonSet([ ("tn_min", QRadioButton("Minimum")), ("tn_ap", QRadioButton("Algebraic Product")), ("tn_bp", QRadioButton("Bounded Product")), ("tn_dp", QRadioButton("Drastic Product")) ]) self.combination_rules_selections = RadioButtonSet([ ("tc_max", QRadioButton("Maximum")), ("tc_as", QRadioButton("Algebraic Sum")), ("tc_bs", QRadioButton("Bounded Sum")), ("tc_ds", QRadioButton("Drastic Sum")) ]) self.defuzzifier_selections = RadioButtonSet([ ("gravity_center", QRadioButton("Center of Gravity")), ("maxima_mean", QRadioButton("Mean of Maxima")), ("modified_maxima_mean", QRadioButton("Modified Mean of Maxima")) ]) self.implication_selections.set_selected('imp_m') self.combination_vars_selections.set_selected('tn_min') self.combination_rules_selections.set_selected('tc_max') self.defuzzifier_selections.set_selected('gravity_center') self.implication_selections.setStatusTip("Choose the method for fuzzy " "implication.") self.combination_vars_selections.setStatusTip( "Choose the method of " "combination of multiple " "fuzzy variables.") self.combination_rules_selections.setStatusTip("Choose the method of " "combination of " "multiple fuzzy rules.") self.defuzzifier_selections.setStatusTip("Choose the method for the " "defuzifier.") inner_layout.addRow(QLabel("Implication:"), self.implication_selections) inner_layout.addRow(QLabel("Combination of Variables:"), self.combination_vars_selections) inner_layout.addRow(QLabel("Combination of Rules:"), self.combination_rules_selections) inner_layout.addRow(QLabel("Defuzzifier:"), self.defuzzifier_selections) self.__layout.addWidget(group_box) def __set_fuzzy_variables_ui(self): group_box = QGroupBox("Fuzzy Variables Settings") group_box.setStatusTip("Set the membership functions for each fuzzy " "variable.") inner_layout = QVBoxLayout() self.fuzzyvar_setting_stack = QStackedWidget() self.fuzzyvar_ui_selection = RadioButtonSet([ ("front", QRadioButton("Front Distance Radar")), ("lrdiff", QRadioButton("(Left-Right) Distance Radar")), ("consequence", QRadioButton("Consequence")) ]) self.fuzzyvar_setting_dist_front = FuzzierVarSetting() self.fuzzyvar_setting_dist_front.small.mean.setValue(5) self.fuzzyvar_setting_dist_front.medium.mean.setValue(12) self.fuzzyvar_setting_dist_front.large.mean.setValue(20) self.fuzzyvar_setting_dist_lrdiff = FuzzierVarSetting() self.fuzzyvar_setting_dist_lrdiff.small.mean.setValue(-10) self.fuzzyvar_setting_dist_lrdiff.medium.mean.setValue(0) self.fuzzyvar_setting_dist_lrdiff.large.mean.setValue(10) self.fuzzyvar_setting_consequence = FuzzierVarSetting() self.fuzzyvar_setting_consequence.small.mean.setValue(-12) self.fuzzyvar_setting_consequence.small.sd.setValue(20) self.fuzzyvar_setting_consequence.medium.mean.setValue(0) self.fuzzyvar_setting_consequence.medium.sd.setValue(20) self.fuzzyvar_setting_consequence.large.mean.setValue(12) self.fuzzyvar_setting_consequence.large.sd.setValue(20) inner_layout.addWidget(self.fuzzyvar_ui_selection) inner_layout.addWidget(self.fuzzyvar_setting_stack) group_box.setLayout(inner_layout) self.fuzzyvar_setting_stack.addWidget(self.fuzzyvar_setting_dist_front) self.fuzzyvar_setting_stack.addWidget( self.fuzzyvar_setting_dist_lrdiff) self.fuzzyvar_setting_stack.addWidget( self.fuzzyvar_setting_consequence) self.fuzzyvar_ui_selection.sig_rbtn_changed.connect( self.__change_fuzzyvar_setting_ui_stack) self.__layout.addWidget(group_box) def __set_fuzzy_rules_ui(self): antecedents = ('small', 'medium', 'large') group_box = QGroupBox("Fuzzy Rules Setting") inner_layout = QVBoxLayout() group_box.setStatusTip("Set the rules for the fuzzy system.") self.rules_setting = FuzzyRulesSetting( [p for p in itertools.product(antecedents, repeat=2)]) self.rules_setting.set_consequence_fuzzysets( ('large', 'small', 'small', 'large', 'small', 'small', 'large', 'small', 'small')) inner_layout.addWidget(self.rules_setting) group_box.setLayout(inner_layout) self.__layout.addWidget(group_box) def __set_console_ui(self): self.__console = QTextEdit() self.__console.setReadOnly(True) self.__console.setStatusTip("Show the logs of status changing.") self.__layout.addWidget(self.__console) @Slot(str) def __change_fuzzyvar_setting_ui_stack(self, name): if name == 'front': self.fuzzyvar_setting_stack.setCurrentIndex(0) elif name == 'lrdiff': self.fuzzyvar_setting_stack.setCurrentIndex(1) else: self.fuzzyvar_setting_stack.setCurrentIndex(2) @Slot() def __change_map(self): self.__current_data = self.dataset[self.data_selector.currentText()] self.__car = Car(self.__current_data['start_pos'], self.__current_data['start_angle'], 3, self.__current_data['route_edge']) self.display_panel.change_map(self.__current_data) @Slot(str) def __print_console(self, text): self.__console.append(text) @Slot(list) def __get_results(self, results): """Get the results of last running and draw the path of it.""" self.results = results self.display_panel.show_path([d['x'] for d in results], [d['y'] for d in results]) @Slot() def __save_results(self): save_dir = QFileDialog.getExistingDirectory(self, 'Select Saving Directory') file4d_filepath = os.path.join(save_dir, 'train4D.txt') file6d_filepath = os.path.join(save_dir, 'train6D.txt') with open(file4d_filepath, 'w') as file4d: for result in self.results: file4d.write('{:.7f} {:.7f} {:.7f} {:.7f}\n'.format( result['front_dist'], result['right_dist'], result['left_dist'], result['wheel_angle'])) with open(file6d_filepath, 'w') as file6d: for result in self.results: file6d.write( '{:.7f} {:.7f} {:.7f} {:.7f} {:.7f} {:.7f}\n'.format( result['x'], result['y'], result['front_dist'], result['right_dist'], result['left_dist'], result['wheel_angle'])) self.__print_console('Note: Detailed results have been saved in both' ' "%s" and "%s".' % (file4d_filepath, file6d_filepath)) @Slot() def __init_widgets(self): self.start_btn.setDisabled(True) self.stop_btn.setEnabled(True) self.save_btn.setDisabled(True) self.fps.setDisabled(True) self.data_selector.setDisabled(True) self.implication_selections.setDisabled(True) self.combination_vars_selections.setDisabled(True) self.combination_rules_selections.setDisabled(True) self.defuzzifier_selections.setDisabled(True) self.fuzzyvar_setting_dist_front.setDisabled(True) self.fuzzyvar_setting_dist_lrdiff.setDisabled(True) self.fuzzyvar_setting_consequence.setDisabled(True) self.rules_setting.setDisabled(True) @Slot() def __reset_widgets(self): self.start_btn.setEnabled(True) self.stop_btn.setDisabled(True) self.save_btn.setEnabled(True) self.fps.setEnabled(True) self.data_selector.setEnabled(True) self.implication_selections.setEnabled(True) self.combination_vars_selections.setEnabled(True) self.combination_rules_selections.setEnabled(True) self.defuzzifier_selections.setEnabled(True) self.fuzzyvar_setting_dist_front.setEnabled(True) self.fuzzyvar_setting_dist_lrdiff.setEnabled(True) self.fuzzyvar_setting_consequence.setEnabled(True) self.rules_setting.setEnabled(True) @Slot() def __run(self): # reset the map self.__change_map() # create a QThread self.thread = RunCar(self.__car, self.__create_fuzzy_system(), (self.__current_data['end_area_lt'], self.__current_data['end_area_rb']), self.fps.value()) # Record the new created threads for the closeEvent in gui_base.py # By doing this, user can destroy the QMainWindow elegantly when there # are threads still running. self.threads.append(self.thread) self.stop_btn.clicked.connect(self.thread.stop) self.thread.started.connect(self.__init_widgets) self.thread.finished.connect(self.__reset_widgets) self.thread.sig_console.connect(self.__print_console) self.thread.sig_car.connect(self.display_panel.move_car) self.thread.sig_car_collided.connect( self.display_panel.show_car_collided) self.thread.sig_dists.connect(self.display_panel.show_dists) self.thread.sig_results.connect(self.__get_results) self.thread.start() def __create_fuzzy_system(self): """Create a fuzzy system with the parameter given in control panel.""" dist_front = FuzzyVariable() dist_front.add_membershipf( 'small', get_gaussianf( *self.fuzzyvar_setting_dist_front.small.get_values())) dist_front.add_membershipf( 'medium', get_gaussianf( *self.fuzzyvar_setting_dist_front.medium.get_values())) dist_front.add_membershipf( 'large', get_gaussianf( *self.fuzzyvar_setting_dist_front.large.get_values())) dist_lrdiff = FuzzyVariable() dist_lrdiff.add_membershipf( 'small', get_gaussianf( *self.fuzzyvar_setting_dist_lrdiff.small.get_values())) dist_lrdiff.add_membershipf( 'medium', get_gaussianf( *self.fuzzyvar_setting_dist_lrdiff.medium.get_values())) dist_lrdiff.add_membershipf( 'large', get_gaussianf( *self.fuzzyvar_setting_dist_lrdiff.large.get_values())) consequence = FuzzyVariable() consequence.add_membershipf( 'small', get_gaussianf( *self.fuzzyvar_setting_consequence.small.get_values())) consequence.add_membershipf( 'medium', get_gaussianf( *self.fuzzyvar_setting_consequence.medium.get_values())) consequence.add_membershipf( 'large', get_gaussianf( *self.fuzzyvar_setting_consequence.large.get_values())) fuzzy_system = FuzzySystem(consequence, dist_front, dist_lrdiff) fuzzy_system.set_operation_types( self.implication_selections.get_selected_name(), self.combination_vars_selections.get_selected_name(), self.combination_rules_selections.get_selected_name(), self.defuzzifier_selections.get_selected_name()) for antecendent_names, consequence_name in self.rules_setting.rules.items( ): fuzzy_system.add_rule(consequence_name, antecendent_names) return fuzzy_system
class Ui_MainWindow(object): def setupUi(self, MainWindow): if not MainWindow.objectName(): MainWindow.setObjectName(u"MainWindow") MainWindow.resize(800, 550) MainWindow.setMinimumSize(QSize(800, 550)) self.centralwidget = QWidget(MainWindow) self.centralwidget.setObjectName(u"centralwidget") self.centralwidget.setStyleSheet(u"background:rgb(91,90,90);") self.verticalLayout = QVBoxLayout(self.centralwidget) self.verticalLayout.setSpacing(0) self.verticalLayout.setObjectName(u"verticalLayout") self.verticalLayout.setContentsMargins(0, 0, 0, 0) self.frame_top = QFrame(self.centralwidget) self.frame_top.setObjectName(u"frame_top") self.frame_top.setMaximumSize(QSize(16777215, 55)) self.frame_top.setFrameShape(QFrame.NoFrame) self.frame_top.setFrameShadow(QFrame.Plain) self.horizontalLayout = QHBoxLayout(self.frame_top) self.horizontalLayout.setSpacing(0) self.horizontalLayout.setObjectName(u"horizontalLayout") self.horizontalLayout.setContentsMargins(0, 0, 0, 0) self.frame_toodle = QFrame(self.frame_top) self.frame_toodle.setObjectName(u"frame_toodle") self.frame_toodle.setMinimumSize(QSize(80, 55)) self.frame_toodle.setMaximumSize(QSize(80, 55)) self.frame_toodle.setStyleSheet(u"background:rgb(0,143,150);") self.frame_toodle.setFrameShape(QFrame.NoFrame) self.frame_toodle.setFrameShadow(QFrame.Plain) self.horizontalLayout_3 = QHBoxLayout(self.frame_toodle) self.horizontalLayout_3.setSpacing(0) self.horizontalLayout_3.setObjectName(u"horizontalLayout_3") self.horizontalLayout_3.setContentsMargins(0, 0, 0, 0) self.toodle = QPushButton(self.frame_toodle) self.toodle.setObjectName(u"toodle") self.toodle.setMinimumSize(QSize(80, 55)) self.toodle.setMaximumSize(QSize(80, 55)) self.toodle.setStyleSheet(u"QPushButton {\n" " border: none;\n" " background-color: rgba(0,0,0,0);\n" "}\n" "QPushButton:hover {\n" " background-color: rgb(0,178,178);\n" "}\n" "QPushButton:pressed { \n" " background-color: rgba(0,0,0,0);\n" "}") icon = QIcon() icon.addFile(u"icons/1x/logo.png", QSize(), QIcon.Normal, QIcon.Off) self.toodle.setIcon(icon) self.toodle.setIconSize(QSize(22, 12)) self.toodle.setFlat(True) self.horizontalLayout_3.addWidget(self.toodle) self.horizontalLayout.addWidget(self.frame_toodle) self.frame_top_east = QFrame(self.frame_top) self.frame_top_east.setObjectName(u"frame_top_east") self.frame_top_east.setMaximumSize(QSize(16777215, 55)) self.frame_top_east.setStyleSheet(u"background:rgb(51,51,51);") self.frame_top_east.setFrameShape(QFrame.NoFrame) self.frame_top_east.setFrameShadow(QFrame.Plain) self.horizontalLayout_4 = QHBoxLayout(self.frame_top_east) self.horizontalLayout_4.setSpacing(0) self.horizontalLayout_4.setObjectName(u"horizontalLayout_4") self.horizontalLayout_4.setContentsMargins(0, 0, 0, 0) self.frame_appname = QFrame(self.frame_top_east) self.frame_appname.setObjectName(u"frame_appname") self.frame_appname.setFrameShape(QFrame.NoFrame) self.frame_appname.setFrameShadow(QFrame.Plain) self.horizontalLayout_10 = QHBoxLayout(self.frame_appname) self.horizontalLayout_10.setSpacing(7) self.horizontalLayout_10.setObjectName(u"horizontalLayout_10") self.horizontalLayout_10.setContentsMargins(0, 0, 0, 0) self.lab_appname = QLabel(self.frame_appname) self.lab_appname.setObjectName(u"lab_appname") font = QFont() font.setFamily(u"Segoe UI Light") font.setPointSize(24) self.lab_appname.setFont(font) self.lab_appname.setStyleSheet(u"color:rgb(255,255,255);") self.horizontalLayout_10.addWidget(self.lab_appname) self.horizontalLayout_4.addWidget(self.frame_appname) self.frame_min = QFrame(self.frame_top_east) self.frame_min.setObjectName(u"frame_min") self.frame_min.setMinimumSize(QSize(55, 55)) self.frame_min.setMaximumSize(QSize(55, 55)) self.frame_min.setFrameShape(QFrame.NoFrame) self.frame_min.setFrameShadow(QFrame.Plain) self.horizontalLayout_7 = QHBoxLayout(self.frame_min) self.horizontalLayout_7.setSpacing(0) self.horizontalLayout_7.setObjectName(u"horizontalLayout_7") self.horizontalLayout_7.setContentsMargins(0, 0, 0, 0) self.bn_min = QPushButton(self.frame_min) self.bn_min.setObjectName(u"bn_min") self.bn_min.setMaximumSize(QSize(55, 55)) self.bn_min.setStyleSheet(u"QPushButton {\n" " border: none;\n" " background-color: rgba(0,0,0,0);\n" "}\n" "QPushButton:hover {\n" " background-color: rgb(0,143,150);\n" "}\n" "QPushButton:pressed { \n" " background-color: rgba(0,0,0,0);\n" "}") icon1 = QIcon() icon1.addFile(u"icons/1x/hideAsset 53.png", QSize(), QIcon.Normal, QIcon.Off) self.bn_min.setIcon(icon1) self.bn_min.setIconSize(QSize(22, 22)) self.bn_min.setFlat(True) self.horizontalLayout_7.addWidget(self.bn_min) self.horizontalLayout_4.addWidget(self.frame_min) self.frame_max = QFrame(self.frame_top_east) self.frame_max.setObjectName(u"frame_max") self.frame_max.setMinimumSize(QSize(55, 55)) self.frame_max.setMaximumSize(QSize(55, 55)) self.frame_max.setFrameShape(QFrame.NoFrame) self.frame_max.setFrameShadow(QFrame.Plain) self.horizontalLayout_6 = QHBoxLayout(self.frame_max) self.horizontalLayout_6.setSpacing(0) self.horizontalLayout_6.setObjectName(u"horizontalLayout_6") self.horizontalLayout_6.setContentsMargins(0, 0, 0, 0) self.bn_max = QPushButton(self.frame_max) self.bn_max.setObjectName(u"bn_max") self.bn_max.setMaximumSize(QSize(55, 55)) self.bn_max.setStyleSheet(u"QPushButton {\n" " border: none;\n" " background-color: rgba(0,0,0,0);\n" "}\n" "QPushButton:hover {\n" " background-color: rgb(0,143,150);\n" "}\n" "QPushButton:pressed { \n" " background-color: rgba(0,0,0,0);\n" "}") icon2 = QIcon() icon2.addFile(u"icons/1x/max.png", QSize(), QIcon.Normal, QIcon.Off) self.bn_max.setIcon(icon2) self.bn_max.setIconSize(QSize(22, 22)) self.bn_max.setFlat(True) self.horizontalLayout_6.addWidget(self.bn_max) self.horizontalLayout_4.addWidget(self.frame_max) self.frame_close = QFrame(self.frame_top_east) self.frame_close.setObjectName(u"frame_close") self.frame_close.setMinimumSize(QSize(55, 55)) self.frame_close.setMaximumSize(QSize(55, 55)) self.frame_close.setFrameShape(QFrame.NoFrame) self.frame_close.setFrameShadow(QFrame.Plain) self.horizontalLayout_5 = QHBoxLayout(self.frame_close) self.horizontalLayout_5.setSpacing(0) self.horizontalLayout_5.setObjectName(u"horizontalLayout_5") self.horizontalLayout_5.setContentsMargins(0, 0, 0, 0) self.bn_close = QPushButton(self.frame_close) self.bn_close.setObjectName(u"bn_close") self.bn_close.setMaximumSize(QSize(55, 55)) self.bn_close.setStyleSheet(u"QPushButton {\n" " border: none;\n" " background-color: rgba(0,0,0,0);\n" "}\n" "QPushButton:hover {\n" " background-color: rgb(0,143,150);\n" "}\n" "QPushButton:pressed { \n" " background-color: rgba(0,0,0,0);\n" "}") icon3 = QIcon() icon3.addFile(u"icons/1x/closeAsset 43.png", QSize(), QIcon.Normal, QIcon.Off) self.bn_close.setIcon(icon3) self.bn_close.setIconSize(QSize(22, 22)) self.bn_close.setFlat(True) self.horizontalLayout_5.addWidget(self.bn_close) self.horizontalLayout_4.addWidget(self.frame_close) self.horizontalLayout.addWidget(self.frame_top_east) self.verticalLayout.addWidget(self.frame_top) self.frame_bottom = QFrame(self.centralwidget) self.frame_bottom.setObjectName(u"frame_bottom") self.frame_bottom.setFrameShape(QFrame.NoFrame) self.frame_bottom.setFrameShadow(QFrame.Plain) self.horizontalLayout_2 = QHBoxLayout(self.frame_bottom) self.horizontalLayout_2.setSpacing(0) self.horizontalLayout_2.setObjectName(u"horizontalLayout_2") self.horizontalLayout_2.setContentsMargins(0, 0, 0, 0) self.frame_bottom_west = QFrame(self.frame_bottom) self.frame_bottom_west.setObjectName(u"frame_bottom_west") self.frame_bottom_west.setMinimumSize(QSize(80, 0)) self.frame_bottom_west.setMaximumSize(QSize(80, 16777215)) self.frame_bottom_west.setStyleSheet(u"background:rgb(51,51,51);") self.frame_bottom_west.setFrameShape(QFrame.NoFrame) self.frame_bottom_west.setFrameShadow(QFrame.Plain) self.verticalLayout_3 = QVBoxLayout(self.frame_bottom_west) self.verticalLayout_3.setSpacing(0) self.verticalLayout_3.setObjectName(u"verticalLayout_3") self.verticalLayout_3.setContentsMargins(0, 0, 0, 0) self.frame_home = QFrame(self.frame_bottom_west) self.frame_home.setObjectName(u"frame_home") self.frame_home.setMinimumSize(QSize(80, 55)) self.frame_home.setMaximumSize(QSize(160, 55)) self.frame_home.setFrameShape(QFrame.NoFrame) self.frame_home.setFrameShadow(QFrame.Plain) self.horizontalLayout_15 = QHBoxLayout(self.frame_home) self.horizontalLayout_15.setSpacing(0) self.horizontalLayout_15.setObjectName(u"horizontalLayout_15") self.horizontalLayout_15.setContentsMargins(0, 0, 0, 0) self.bn_home = QPushButton(self.frame_home) self.bn_home.setObjectName(u"bn_home") self.bn_home.setMinimumSize(QSize(80, 55)) self.bn_home.setMaximumSize(QSize(160, 55)) self.bn_home.setStyleSheet(u"QPushButton {\n" " border: none;\n" " background-color: rgba(0,0,0,0);\n" "}\n" "QPushButton:hover {\n" " background-color: rgb(91,90,90);\n" "}\n" "QPushButton:pressed { \n" " background-color: rgba(0,0,0,0);\n" "}") icon4 = QIcon() icon4.addFile(u"icons/1x/homeAsset 46.png", QSize(), QIcon.Normal, QIcon.Off) self.bn_home.setIcon(icon4) self.bn_home.setIconSize(QSize(22, 22)) self.bn_home.setFlat(True) self.horizontalLayout_15.addWidget(self.bn_home) self.verticalLayout_3.addWidget(self.frame_home) self.frame_8 = QFrame(self.frame_bottom_west) self.frame_8.setObjectName(u"frame_8") self.frame_8.setFrameShape(QFrame.NoFrame) self.frame_8.setFrameShadow(QFrame.Plain) self.verticalLayout_4 = QVBoxLayout(self.frame_8) self.verticalLayout_4.setSpacing(0) self.verticalLayout_4.setObjectName(u"verticalLayout_4") self.verticalLayout_4.setContentsMargins(0, 0, 0, 0) self.verticalLayout_3.addWidget(self.frame_8) self.horizontalLayout_2.addWidget(self.frame_bottom_west) self.frame_bottom_east = QFrame(self.frame_bottom) self.frame_bottom_east.setObjectName(u"frame_bottom_east") self.frame_bottom_east.setFrameShape(QFrame.NoFrame) self.frame_bottom_east.setFrameShadow(QFrame.Plain) self.verticalLayout_2 = QVBoxLayout(self.frame_bottom_east) self.verticalLayout_2.setSpacing(0) self.verticalLayout_2.setObjectName(u"verticalLayout_2") self.verticalLayout_2.setContentsMargins(0, 0, 0, 0) self.frame = QFrame(self.frame_bottom_east) self.frame.setObjectName(u"frame") self.frame.setFrameShape(QFrame.NoFrame) self.frame.setFrameShadow(QFrame.Plain) self.horizontalLayout_14 = QHBoxLayout(self.frame) self.horizontalLayout_14.setSpacing(0) self.horizontalLayout_14.setObjectName(u"horizontalLayout_14") self.horizontalLayout_14.setContentsMargins(0, 0, 0, 0) self.stackedWidget = QStackedWidget(self.frame) self.stackedWidget.setObjectName(u"stackedWidget") self.stackedWidget.setMinimumSize(QSize(0, 55)) self.stackedWidget.setStyleSheet(u"") self.page_home = QWidget() self.page_home.setObjectName(u"page_home") self.page_home.setStyleSheet(u"background:rgb(91,90,90);") self.horizontalLayout_19 = QHBoxLayout(self.page_home) self.horizontalLayout_19.setSpacing(0) self.horizontalLayout_19.setObjectName(u"horizontalLayout_19") self.horizontalLayout_19.setContentsMargins(0, 5, 0, 5) self.frame_home_main = QFrame(self.page_home) self.frame_home_main.setObjectName(u"frame_home_main") self.frame_home_main.setFrameShape(QFrame.NoFrame) self.frame_home_main.setFrameShadow(QFrame.Plain) self.verticalLayout_5 = QVBoxLayout(self.frame_home_main) self.verticalLayout_5.setSpacing(5) self.verticalLayout_5.setObjectName(u"verticalLayout_5") self.verticalLayout_5.setContentsMargins(5, 5, 5, 5) font1 = QFont() font1.setFamily(u"Segoe UI Semilight") font1.setPointSize(10) self.lab_home_main_disc = QLabel(self.frame_home_main) self.lab_home_main_disc.setObjectName(u"lab_home_main_disc") font2 = QFont() font2.setFamily(u"Segoe UI") font2.setPointSize(10) self.lab_home_main_disc.setFont(font2) self.lab_home_main_disc.setStyleSheet(u"color:rgb(255,255,255);") self.lab_home_main_disc.setAlignment(Qt.AlignLeading | Qt.AlignLeft | Qt.AlignTop) self.lab_home_main_disc.setWordWrap(True) self.lab_home_main_disc.setMargin(5) self.verticalLayout_5.addWidget(self.lab_home_main_disc) self.horizontalLayout_19.addWidget(self.frame_home_main) self.vert_divide = QFrame(self.page_home) self.vert_divide.setObjectName(u"vert_divide") self.vert_divide.setFrameShape(QFrame.VLine) self.vert_divide.setFrameShadow(QFrame.Sunken) self.horizontalLayout_19.addWidget(self.vert_divide) self.frame_home_stat = QFrame(self.page_home) self.frame_home_stat.setObjectName(u"frame_home_stat") self.frame_home_stat.setMinimumSize(QSize(220, 0)) self.frame_home_stat.setMaximumSize(QSize(220, 16777215)) self.frame_home_stat.setFrameShape(QFrame.NoFrame) self.frame_home_stat.setFrameShadow(QFrame.Plain) self.verticalLayout_6 = QVBoxLayout(self.frame_home_stat) self.verticalLayout_6.setSpacing(5) self.verticalLayout_6.setObjectName(u"verticalLayout_6") self.verticalLayout_6.setContentsMargins(5, 5, 5, 5) self.bn_home_image = QPushButton(self.frame_home_stat) self.bn_home_image.setObjectName(u"bn_home_image") self.bn_home_image.setEnabled(True) self.bn_home_image.setMinimumSize(QSize(205, 25)) self.bn_home_image.setMaximumSize(QSize(205, 25)) self.bn_home_image.setFont(font1) self.bn_home_image.setStyleSheet(u"QPushButton {\n" " border: 2px solid rgb(51,51,51);\n" " border-radius: 5px; \n" " color:rgb(255,255,255);\n" " background-color: rgb(51,51,51);\n" "}\n" "QPushButton:hover {\n" " border: 2px solid rgb(0,143,150);\n" " background-color: rgb(0,143,150);\n" "}\n" "QPushButton:pressed { \n" " border: 2px solid rgb(0,143,150);\n" " background-color: rgb(51,51,51);\n" "}\n" "\n" "QPushButton:disabled { \n" " border-radius: 5px; \n" " border: 2px solid rgb(112,112,112);\n" " background-color: rgb(112,112,112);\n" "}") self.verticalLayout_6.addWidget(self.bn_home_image) self.bn_home_rec = QPushButton(self.frame_home_stat) self.bn_home_rec.setObjectName(u"bn_home_rec") self.bn_home_rec.setEnabled(True) self.bn_home_rec.setMinimumSize(QSize(205, 25)) self.bn_home_rec.setMaximumSize(QSize(205, 25)) self.bn_home_rec.setFont(font1) self.bn_home_rec.setStyleSheet(u"QPushButton {\n" " border: 2px solid rgb(51,51,51);\n" " border-radius: 5px; \n" " color:rgb(255,255,255);\n" " background-color: rgb(51,51,51);\n" "}\n" "QPushButton:hover {\n" " border: 2px solid rgb(0,143,150);\n" " background-color: rgb(0,143,150);\n" "}\n" "QPushButton:pressed { \n" " border: 2px solid rgb(0,143,150);\n" " background-color: rgb(51,51,51);\n" "}\n" "\n" "QPushButton:disabled { \n" " border-radius: 5px; \n" " border: 2px solid rgb(112,112,112);\n" " background-color: rgb(112,112,112);\n" "}") self.verticalLayout_6.addWidget(self.bn_home_rec) self.horizontalLayout_19.addWidget(self.frame_home_stat) self.stackedWidget.addWidget(self.page_home) self.page_about_home = QWidget() self.page_about_home.setObjectName(u"page_about_home") self.page_about_home.setStyleSheet(u"background:rgb(91,90,90);") self.verticalLayout_13 = QVBoxLayout(self.page_about_home) self.verticalLayout_13.setSpacing(5) self.verticalLayout_13.setObjectName(u"verticalLayout_13") self.verticalLayout_13.setContentsMargins(5, 5, 5, 5) self.lab_about_home = QLabel(self.page_about_home) self.lab_about_home.setObjectName(u"lab_about_home") self.lab_about_home.setMinimumSize(QSize(0, 55)) self.lab_about_home.setMaximumSize(QSize(16777215, 55)) font3 = QFont() font3.setFamily(u"Segoe UI") font3.setPointSize(24) self.lab_about_home.setFont(font3) self.lab_about_home.setStyleSheet(u"color:rgb(255,255,255);") self.verticalLayout_13.addWidget(self.lab_about_home) self.frame_about_home = QFrame(self.page_about_home) self.frame_about_home.setObjectName(u"frame_about_home") self.frame_about_home.setFrameShape(QFrame.StyledPanel) self.frame_about_home.setFrameShadow(QFrame.Raised) self.horizontalLayout_28 = QHBoxLayout(self.frame_about_home) self.horizontalLayout_28.setSpacing(0) self.horizontalLayout_28.setObjectName(u"horizontalLayout_28") self.horizontalLayout_28.setContentsMargins(5, 5, 0, 5) self.text_about_home = QTextEdit(self.frame_about_home) self.text_about_home.setObjectName(u"text_about_home") self.text_about_home.setEnabled(True) self.text_about_home.setFont(font2) self.text_about_home.setStyleSheet(u"color:rgb(255,255,255);") self.text_about_home.setFrameShape(QFrame.NoFrame) self.text_about_home.setFrameShadow(QFrame.Plain) self.text_about_home.setReadOnly(True) self.text_about_home.setTextInteractionFlags(Qt.TextBrowserInteraction) self.horizontalLayout_28.addWidget(self.text_about_home) self.vsb_about_home = QScrollBar(self.frame_about_home) self.vsb_about_home.setObjectName(u"vsb_about_home") self.vsb_about_home.setStyleSheet(u"QScrollBar:vertical {\n" " background:rgb(51,51,51);\n" " width:20px;\n" " margin: 0px 0px 0px 0px;\n" "}\n" "QScrollBar::handle:vertical {\n" " background:rgb(0,143,170);\n" "}\n" "QScrollBar::add-page:vertical {\n" " background:rgb(51,51,51);\n" "}\n" "QScrollBar::sub-page:vertical {\n" " background:rgb(51,51,51);\n" "}") self.vsb_about_home.setOrientation(Qt.Vertical) self.horizontalLayout_28.addWidget(self.vsb_about_home) self.verticalLayout_13.addWidget(self.frame_about_home) self.stackedWidget.addWidget(self.page_about_home) self.horizontalLayout_14.addWidget(self.stackedWidget) self.verticalLayout_2.addWidget(self.frame) self.frame_low = QFrame(self.frame_bottom_east) self.frame_low.setObjectName(u"frame_low") self.frame_low.setMinimumSize(QSize(0, 20)) self.frame_low.setMaximumSize(QSize(16777215, 20)) self.frame_low.setStyleSheet(u"") self.frame_low.setFrameShape(QFrame.NoFrame) self.frame_low.setFrameShadow(QFrame.Plain) self.horizontalLayout_11 = QHBoxLayout(self.frame_low) self.horizontalLayout_11.setSpacing(0) self.horizontalLayout_11.setObjectName(u"horizontalLayout_11") self.horizontalLayout_11.setContentsMargins(0, 0, 0, 0) self.frame_tab = QFrame(self.frame_low) self.frame_tab.setObjectName(u"frame_tab") font10 = QFont() font10.setFamily(u"Segoe UI") self.frame_tab.setFont(font10) self.frame_tab.setStyleSheet(u"background:rgb(51,51,51);") self.frame_tab.setFrameShape(QFrame.NoFrame) self.frame_tab.setFrameShadow(QFrame.Plain) self.horizontalLayout_12 = QHBoxLayout(self.frame_tab) self.horizontalLayout_12.setSpacing(0) self.horizontalLayout_12.setObjectName(u"horizontalLayout_12") self.horizontalLayout_12.setContentsMargins(0, 0, 0, 0) self.lab_tab = QLabel(self.frame_tab) self.lab_tab.setObjectName(u"lab_tab") font11 = QFont() font11.setFamily(u"Segoe UI Light") font11.setPointSize(10) self.lab_tab.setFont(font11) self.lab_tab.setStyleSheet(u"color:rgb(255,255,255);") self.horizontalLayout_12.addWidget(self.lab_tab) self.horizontalLayout_11.addWidget(self.frame_tab) self.frame_drag = QFrame(self.frame_low) self.frame_drag.setObjectName(u"frame_drag") self.frame_drag.setMinimumSize(QSize(20, 20)) self.frame_drag.setMaximumSize(QSize(20, 20)) self.frame_drag.setStyleSheet(u"background:rgb(51,51,51);") self.frame_drag.setFrameShape(QFrame.NoFrame) self.frame_drag.setFrameShadow(QFrame.Plain) self.horizontalLayout_13 = QHBoxLayout(self.frame_drag) self.horizontalLayout_13.setSpacing(0) self.horizontalLayout_13.setObjectName(u"horizontalLayout_13") self.horizontalLayout_13.setContentsMargins(0, 0, 0, 0) self.horizontalLayout_11.addWidget(self.frame_drag) self.verticalLayout_2.addWidget(self.frame_low) self.horizontalLayout_2.addWidget(self.frame_bottom_east) self.verticalLayout.addWidget(self.frame_bottom) MainWindow.setCentralWidget(self.centralwidget) self.retranslateUi(MainWindow) self.stackedWidget.setCurrentIndex(7) QMetaObject.connectSlotsByName(MainWindow) # setupUi def retranslateUi(self, MainWindow): MainWindow.setWindowTitle(QCoreApplication.translate("MainWindow", u"MainWindow", None)) self.toodle.setText("") self.lab_appname.setText( QCoreApplication.translate("MainWindow", u"<html><head/><body><p><br/></p></body></html>", None)) self.bn_min.setToolTip(QCoreApplication.translate("MainWindow", u"Minimize", None)) self.bn_min.setText("") self.bn_max.setToolTip(QCoreApplication.translate("MainWindow", u"Maximize", None)) self.bn_max.setText("") self.bn_close.setToolTip(QCoreApplication.translate("MainWindow", u"Close", None)) self.bn_close.setText("") self.bn_home.setToolTip(QCoreApplication.translate("MainWindow", u"Home", None)) self.bn_home.setText("") self.lab_about_home.setText(QCoreApplication.translate("MainWindow", u"How to use", None)) self.bn_home_image.setText(QCoreApplication.translate("MainWindow", u"Upload Image", None)) self.bn_home_rec.setText(QCoreApplication.translate("MainWindow", u"Recognition", None)) self.lab_tab.setText( QCoreApplication.translate("MainWindow", u"<html><head/><body><p><br/></p></body></html>", None)) self.frame_drag.setToolTip(QCoreApplication.translate("MainWindow", u"Drag", None))
def __init__(self, parent=None): super().__init__(parent) self.setMinimumSize(800, 600) self.setWindowTitle(self.tr("Preferences")) self._preferences = Preferences() # # Content self._generalPage = PreferencesGeneralPage() self._generalPage.setZeroMargins() self._generalPage.preferencesChanged.connect( self._onPreferencesChanged) self._documentsPage = PreferencesDocumentsPage() self._documentsPage.setZeroMargins() self._documentsPage.preferencesChanged.connect( self._onPreferencesChanged) self._documentPresetsPage = PreferencesDocumentPresetsPage() self._documentPresetsPage.setZeroMargins() self._documentPresetsPage.preferencesChanged.connect( self._onPreferencesChanged) stackedBox = QStackedWidget() stackedBox.addWidget(self._generalPage) stackedBox.addWidget(self._documentsPage) stackedBox.addWidget(self._documentPresetsPage) stackedBox.setCurrentIndex(0) listBox = QListWidget() listBox.addItem(self._generalPage.title()) listBox.addItem(self._documentsPage.title()) listBox.addItem(self._documentPresetsPage.title()) listBox.setCurrentRow(stackedBox.currentIndex()) listBox.currentRowChanged.connect(stackedBox.setCurrentIndex) preferencesBox = QHBoxLayout() preferencesBox.addWidget(listBox, 1) preferencesBox.addWidget(stackedBox, 3) # Button box buttonBox = QDialogButtonBox(QDialogButtonBox.RestoreDefaults | QDialogButtonBox.Ok | QDialogButtonBox.Apply | QDialogButtonBox.Cancel) self._buttonApply = buttonBox.button(QDialogButtonBox.Apply) buttonBox.button(QDialogButtonBox.RestoreDefaults).clicked.connect( self._onButtonDefaultsClicked) buttonBox.accepted.connect(self._onButtonOkClicked) buttonBox.button(QDialogButtonBox.Apply).clicked.connect( self._onButtonApplyClicked) buttonBox.rejected.connect(self.close) # Main layout layout = QVBoxLayout(self) layout.addLayout(preferencesBox) layout.addWidget(buttonBox) self._updatePreferences() self._buttonApply.setEnabled(False)
class ColorPanel(QWidget): """ 颜色面板柱状条 作者:feiyangqingyun(QQ:517216493) 2017-11-21 译者:sunchuquin(QQ:1715216365) 2021-07-04 """ def __init__(self, parent: QWidget = None): super(ColorPanel, self).__init__(parent) self.resize(650, 450) g_layout = QVBoxLayout() self.stackedWidget = QStackedWidget() self.pageFader = QWidget() layout = QVBoxLayout() self.colorPanelFader = ColorPanelFader() layout.addWidget(self.colorPanelFader) layout.setContentsMargins(0, 0, 0, 0) self.pageFader.setLayout(layout) self.pageHSB = QWidget() layout = QHBoxLayout() self.colorPanelHSB = ColorPanelHSB() self.colorPanelBar = ColorPanelBar() self.colorPanelBar.setMinimumWidth(60) self.colorPanelBar.setMaximumWidth(60) layout.addWidget(self.colorPanelHSB) layout.addWidget(self.colorPanelBar) layout.setContentsMargins(0, 0, 0, 0) self.pageHSB.setLayout(layout) self.pageBtn = QWidget() layout = QVBoxLayout() self.colorPanelBtn = ColorPanelBtn() layout.addWidget(self.colorPanelBtn) layout.setContentsMargins(0, 0, 0, 0) self.pageBtn.setLayout(layout) self.stackedWidget.addWidget(self.pageFader) self.stackedWidget.addWidget(self.pageHSB) self.stackedWidget.addWidget(self.pageBtn) g_layout.addWidget(self.stackedWidget) self.widgetColor = QWidget() self.btnHue = ColorButton() self.btnSat = ColorButton() self.btnBright = ColorButton() self.btnCyan = ColorButton() self.btnMagenta = ColorButton() self.btnYellow = ColorButton() self.btnRed = ColorButton() self.btnGreen = ColorButton() self.btnBlue = ColorButton() self.widgetColor.setMinimumHeight(45) self.widgetColor.setMaximumHeight(45) layout = QHBoxLayout() layout.addWidget(self.btnHue) layout.addWidget(self.btnSat) layout.addWidget(self.btnBright) layout.addWidget(self.btnCyan) layout.addWidget(self.btnMagenta) layout.addWidget(self.btnYellow) layout.addWidget(self.btnRed) layout.addWidget(self.btnGreen) layout.addWidget(self.btnBlue) layout.setContentsMargins(0, 0, 0, 0) layout.setSpacing(1) self.widgetColor.setLayout(layout) g_layout.addWidget(self.widgetColor) self.widgetPanel = QWidget() layout = QHBoxLayout() self.btnPanelFader = ColorButton() self.btnPanelHSB = ColorButton() self.btnPanelBtn = ColorButton() self.labColor = QLabel() self.labColor.setAlignment(Qt.AlignHCenter | Qt.AlignVCenter) layout.addWidget(self.btnPanelFader) layout.addWidget(self.btnPanelHSB) layout.addWidget(self.btnPanelBtn) layout.addWidget(self.labColor) layout.setContentsMargins(0, 0, 0, 0) layout.setSpacing(1) self.widgetPanel.setLayout(layout) g_layout.addWidget(self.widgetPanel) self.setLayout(g_layout) self.initForm() def initForm(self) -> None: self.btnHue.borderColor = QColor(Qt.darkGray) self.btnSat.borderColor = QColor(Qt.darkGray) self.btnBright.borderColor = QColor(Qt.darkGray) self.btnHue.normalColor = QColor(Qt.darkGray).light(20) self.btnSat.normalColor = QColor(Qt.darkGray).light(20) self.btnBright.normalColor = QColor(Qt.darkGray).light(20) self.btnCyan.borderColor = QColor(Qt.cyan) self.btnMagenta.borderColor = QColor(Qt.magenta) self.btnYellow.borderColor = QColor(Qt.yellow) self.btnCyan.normalColor = QColor(Qt.cyan).light(50) self.btnMagenta.normalColor = QColor(Qt.magenta).light(50) self.btnYellow.normalColor = QColor(Qt.yellow).light(50) self.btnRed.borderColor = QColor(Qt.red) self.btnGreen.borderColor = QColor(Qt.green) self.btnBlue.borderColor = QColor(Qt.blue) self.btnRed.normalColor = QColor(Qt.red).light(50) self.btnGreen.normalColor = QColor(Qt.green).light(50) self.btnBlue.normalColor = QColor(Qt.blue).light(50) self.btnPanelFader.text = "颜色滑块面板" self.btnPanelHSB.text = "颜色选取面板" self.btnPanelBtn.text = "颜色按钮面板" self.btnPanelFader.normalColor = QColor("#16A085") self.btnPanelHSB.normalColor = QColor("#C0392B") self.btnPanelBtn.normalColor = QColor("#27AE60") font: QFont = QFont() font.setPixelSize(15) font.setBold(True) self.btnPanelFader.textFont = font self.btnPanelHSB.textFont = font self.btnPanelBtn.textFont = font self.colorPanelFader.colorChanged.connect(self.colorChangedFader) self.colorPanelHSB.colorChanged.connect(self.colorChangedHSB) self.colorPanelBar.colorChanged.connect(self.colorChangedBar) self.colorPanelBtn.colorChanged.connect(self.colorChangedBtn) self.widgetColor.setEnabled(False) self.stackedWidget.setCurrentIndex(0) self.colorPanelBar.staticMode = False self.colorChangedBar(QColor(Qt.red), 0, 100) self.btnPanelFader.clicked.connect(self.buttonClicked) self.btnPanelHSB.clicked.connect(self.buttonClicked) self.btnPanelBtn.clicked.connect(self.buttonClicked) def colorChangedFader(self, color: QColor, hue: float, sat: float, bright: float) -> None: self.btnHue.text = "Hue\n%0.1f" % round(hue, 1) self.btnSat.text = "Sat\n%0.1f" % round(sat, 1) self.btnBright.text = "Bright\n%0.1f" % round(bright, 1) self.setColor(color) def colorChangedHSB(self, color: QColor, hue: float, sat: float) -> None: self.colorPanelBar.topColor = color self.colorPanelBar.borderColor = color self.btnHue.text = "Hue\n%0.1f" % round(hue, 1) self.btnSat.text = "Sat\n%0.1f" % round(sat, 1) self.btnBright.text = "Bright\n%0.1f" % round( self.colorPanelBar.percent, 1) c: QColor = QColor.fromHsvF(hue / 360, sat / 100, self.colorPanelBar.percent / 100) self.setColor(c) def colorChangedBar(self, color: QColor, value: float, percent: float) -> None: if self.colorPanelHSB.isVisible(): self.colorPanelHSB.percent = percent hue: float = color.hue() hue = 360 if hue < 0 else hue sat: float = color.saturationF() * 100 if not self.colorPanelBar.isVisible(): self.btnHue.text = "Hue\n%0.1f" % round(hue, 1) self.btnSat.text = "Sat\n%0.1f" % round(sat, 1) self.btnBright.text = "Bright\n%0.1f" % round(percent, 1) self.setColor(color) def colorChangedBtn(self, color: QColor) -> None: self.colorChangedBar(color, 0, 100) def setColor(self, color: QColor) -> None: # 根据背景色自动计算合适的前景色 gray: float = (0.299 * color.red() + 0.587 * color.green() + 0.114 * color.blue()) / 255 textColor: QColor = QColor(Qt.black) if gray > 0.5 else QColor( Qt.white) self.labColor.setText(color.name().upper()) self.labColor.setStyleSheet( "QLabel{font:25px;color:%s;background:%s;}" % (textColor.name(), color.name())) percentRed: float = color.redF() * 100 percentGreen: float = color.greenF() * 100 percentBlue: float = color.blueF() * 100 self.btnCyan.text = "Cyan\n%0.1f%%" % round(100 - percentRed, 1) self.btnMagenta.text = "Magenta\n%0.1f%%" % round( 100 - percentGreen, 1) self.btnYellow.text = "Yellow\n%0.1f%%" % round(100 - percentBlue, 1) self.btnRed.text = "Red\n%0.1f%%" % round(percentRed, 1) self.btnGreen.text = "Green\n%0.1f%%" % round(percentGreen, 1) self.btnBlue.text = "Blue\n%0.1f%%" % round(percentBlue, 1) def buttonClicked(self) -> None: btn: ColorButton = self.sender() if btn == self.btnPanelFader: self.stackedWidget.setCurrentIndex(0) elif btn == self.btnPanelHSB: self.stackedWidget.setCurrentIndex(1) elif btn == self.btnPanelBtn: self.stackedWidget.setCurrentIndex(2)
class MainWindowUi(QMainWindow): """sets up ui properties of MainWindowUi class""" def __init__(self) -> None: """inits MainWindow class configuring parameters of MainWindow class and inherits from QtWidget.QMainWindow loads .ui file sets up file and directory path vars, inits click events(menuebar, coboboxes, btns) and shows gui the first time Returns: None""" super(MainWindowUi, self).__init__() self.setWindowTitle("It_Hilfe") self.resize(820, 450) self.setWindowIcon(QIcon("./data/favicon2.png")) self.setMinimumSize(700, 250) self.file_path = None self.dir = None self.last_open_file_path = None self.last_open_file_dir = None self.initial_theme = None self.registered_devices = {} self.user_config_file = "./data/user_config.json" # setup stackedwidget self.stacked_widget = QStackedWidget() self.setCentralWidget(self.stacked_widget) self.setup_menubar() self.setup_p_view() self.setup_p_register() self.setup_p_create() self.setup_p_preferences() self.setup_signals() self.font = QFont() self.font.setPointSize(9) self.validate(self.set_user_preferences, file_path=self.user_config_file, schema=validate_json.ItHilfeUserPreferencesSchema, forbidden=[""]) # setup statusbar self.statusbar = self.statusBar() self.stacked_widget.setCurrentWidget(self.p_view) def setup_menubar(self) -> None: """inits menubar Returns: None""" self.menu_Bar = self.menuBar() menu_file = self.menu_Bar.addMenu("file") self.action_open = QAction("open") self.action_save = QAction("save") self.action_new = QAction("new") self.action_print = QAction("print") self.action_preferences = QAction("preferences") self.action_hide_menu_bar = QAction("hide menubar") self.action_print.setShortcut(QKeySequence("Ctrl+p")) self.action_open.setShortcut(QKeySequence("Ctrl+o")) self.action_save.setShortcut(QKeySequence("Ctrl+s")) self.action_hide_menu_bar.setShortcut(QKeySequence("Ctrl+h")) self.action_hide_menu_bar.setIcon(QIcon("./data/show_hide.ico")) self.action_print.setIcon(QIcon("./data/print2.ico")) self.action_open.setIcon(QIcon("./data/open.ico")) self.action_save.setIcon(QIcon("./data/save.ico")) self.action_new.setIcon(QIcon("./data/newfile.ico")) self.action_preferences.setIcon(QIcon("./data/preferences.ico")) menu_file.addAction(self.action_open) menu_file.addAction(self.action_save) menu_file.addAction(self.action_new) menu_file.addAction(self.action_print) menu_file.addAction(self.action_preferences) menu_edit = self.menu_Bar.addMenu("edit") self.action_register = QAction("register") self.action_register.setShortcut(QKeySequence("Ctrl+n")) self.action_register.setIcon(QIcon("./data/register.ico")) menu_edit.addAction(self.action_register) menu_view = self.menu_Bar.addMenu("view") menu_view.addAction(self.action_hide_menu_bar) def setup_p_view(self) -> None: """inits stacked widget page widget Returns: None""" self.p_view = QtWidgets.QWidget() self.stacked_widget.addWidget(self.p_view) self.model = QStandardItemModel(self.p_view) self.model.setHorizontalHeaderLabels(labels) self.filters = [] source_model = self.model for filter_num in range(7): filter = QSortFilterProxyModel() filter.setSourceModel(source_model) filter.setFilterKeyColumn(filter_num) source_model = filter self.filters.append(filter) delegate = ComboDelegate() self.table = QtWidgets.QTableView(self.p_view) self.table.setModel(self.filters[-1]) self.table.setItemDelegateForColumn(2, delegate) self.table.horizontalHeader().setStretchLastSection(True) self.table.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) self.header = FilterHeader(self.table) self.header.set_filter_boxes() self.header.setMaximumHeight(50) self.table.setHorizontalHeader(self.header) self.bt_burger = QPushButton(self.p_view) self.bt_burger.setIcon(QIcon("./data/menu2.svg")) self.bt_burger.setIconSize(QSize(30, 30)) self.bt_burger.setToolTip('slide out description') l_burger = QLabel("menu", self.p_view) self.bt_register_new = QPushButton(self.p_view) self.bt_register_new.setIcon(QIcon("./data/add.ico")) self.bt_register_new.setIconSize(QSize(30, 30)) self.bt_register_new.setToolTip("register new") l_register_new = QLabel("register new", self.p_view) self.bt_delete_column = QPushButton(self.p_view) self.bt_delete_column.setIcon(QIcon("./data/remove.ico")) self.bt_delete_column.setIconSize(QSize(30, 30)) self.bt_delete_column.setToolTip( "delete columns with min 1 cell selected") l_delete = QLabel("delete column", self.p_view) self.bt_hide_show_filter = QPushButton(self.p_view) self.bt_hide_show_filter.setIcon(QIcon("./data/show_hide.ico")) self.bt_hide_show_filter.setIconSize(QSize(30, 30)) self.bt_hide_show_filter.setToolTip("hide/show filter input") l_hide_show = QLabel("hide/show", self.p_view) self.left_btn_frame = QFrame(self.p_view) self.left_btn_frame.setMaximumWidth(40) self.left_btn_frame.setContentsMargins(0, 0, 0, 0) self.left_menu_frame = QFrame(self.p_view) self.left_menu_frame.setMaximumWidth(0) self.left_menu_frame.setContentsMargins(0, 0, 0, 0) p_view_layout2 = QtWidgets.QVBoxLayout(self.left_btn_frame) p_view_layout2.addWidget(self.bt_burger) p_view_layout2.addWidget(self.bt_register_new) p_view_layout2.addWidget(self.bt_delete_column) p_view_layout2.addWidget(self.bt_hide_show_filter) p_view_layout2.setAlignment(Qt.AlignTop) p_view_layout2.setContentsMargins(0, 0, 0, 0) self.p_view_layout3 = QtWidgets.QVBoxLayout(self.left_menu_frame) self.p_view_layout3.addWidget(l_burger) self.p_view_layout3.addWidget(l_register_new) self.p_view_layout3.addWidget(l_delete) self.p_view_layout3.addWidget(l_hide_show) self.p_view_layout3.setAlignment(Qt.AlignTop | Qt.AlignCenter) self.p_view_layout3.setContentsMargins(0, 0, 0, 0) self.p_view_layout3.setSpacing(25) p_view_layout = QHBoxLayout(self.p_view) p_view_layout.setContentsMargins(0, 0, 0, 0) p_view_layout.addWidget(self.left_btn_frame) p_view_layout.addWidget(self.left_menu_frame) p_view_layout.addWidget(self.table) self.p_view.setLayout(p_view_layout) self.p_view.addAction(self.action_open) self.p_view.addAction(self.action_save) self.p_view.addAction(self.action_new) self.p_view.addAction(self.action_print) self.p_view.addAction(self.action_register) self.p_view.addAction(self.action_hide_menu_bar) def setup_p_register(self) -> None: """inits stacked widget page widgets Returns: None""" self.p_register = QtWidgets.QWidget() self.stacked_widget.addWidget(self.p_register) l_user = QtWidgets.QLabel("Username", self.p_register) self.in_username = QtWidgets.QLineEdit(self.p_register) l_devicename = QtWidgets.QLabel("Devicename", self.p_register) self.in_devicename = QtWidgets.QLineEdit(self.p_register) l_devicetype = QtWidgets.QLabel("DeviceType", self.p_register) self.in_combobox_devicetype = QtWidgets.QComboBox(self.p_register) l_os = QtWidgets.QLabel("OS", self.p_register) self.in_combobox_os = QtWidgets.QComboBox(self.p_register) l_comment = QtWidgets.QLabel("Comment", self.p_register) self.in_comment = QtWidgets.QTextEdit(self.p_register) self.bt_enter_register = QPushButton("register", self.p_register) self.bt_cancel_register = QPushButton("cancel", self.p_register) p_register_layout = QtWidgets.QVBoxLayout(self.p_register) p_register_layout.addWidget(l_user) p_register_layout.addWidget(self.in_username) p_register_layout.addWidget(l_devicename) p_register_layout.addWidget(self.in_devicename) p_register_layout.addWidget(l_devicetype) p_register_layout.addWidget(self.in_combobox_devicetype) p_register_layout.addWidget(l_os) p_register_layout.addWidget(self.in_combobox_os) p_register_layout.addWidget(l_comment) p_register_layout.addWidget(self.in_comment) p_register_layout.addWidget(self.bt_enter_register) p_register_layout.addWidget(self.bt_cancel_register) def setup_p_create(self) -> None: """inits stacked widget page widget Returns: None""" self.p_create = QtWidgets.QWidget() self.stacked_widget.addWidget(self.p_create) l_new_filepath = QtWidgets.QLabel("new filepath", self.p_create) self.bt_mod_new_path = QPushButton("mod filepath", self.p_create) self.in_new_filepath = QtWidgets.QLineEdit(self.p_create) l_new_filename = QtWidgets.QLabel("new filename", self.p_create) self.in_new_filename = QtWidgets.QLineEdit(self.p_create) self.bt_create = QPushButton("create", self.p_create) self.bt_cancel_create = QPushButton("cancel", self.p_create) p_create_layout = QtWidgets.QVBoxLayout(self.p_create) p_create_layout.addWidget(l_new_filepath) p_create_layout.addWidget(self.in_new_filepath) p_create_layout.addWidget(l_new_filename) p_create_layout.addWidget(self.in_new_filename) p_create_layout.addStretch(100) p_create_layout.addWidget(self.bt_mod_new_path) p_create_layout.addWidget(self.bt_create) p_create_layout.addWidget(self.bt_cancel_create) def setup_p_preferences(self) -> None: """inits setup_p_preferences stacked widget page widget Returns: None""" self.p_preferences = QWidget() self.p_preferences.resize(500, 250) self.p_preferences.setWindowTitle("preferences") self.list_Widget = QListWidget(self.p_preferences) self.list_Widget.addItems(["appearance", "about"]) self.list_Widget.setMaximumWidth(100) self.stacked_widget_preferences = QStackedWidget(self.p_preferences) # setup appearance self.apperence_widget = QWidget() self.stacked_widget_preferences.addWidget(self.apperence_widget) self.in_combo_themes = QComboBox(self.apperence_widget) self.in_combo_themes.addItems(["dark_theme", "light_theme"]) self.in_combo_theme_initial = QComboBox(self.apperence_widget) self.in_combo_theme_initial.addItems(["dark_theme", "light_theme"]) self.text_size_slider = QSlider(QtCore.Qt.Orientation.Horizontal, self.apperence_widget) self.text_size_slider.setTickPosition(QSlider.TickPosition.TicksAbove) self.text_size_slider.setMaximum(15) self.text_size_slider.setMinimum(8) stacked_widget_preferences_layout = QGridLayout(self.apperence_widget) stacked_widget_preferences_layout.setAlignment(QtCore.Qt.AlignTop) stacked_widget_preferences_layout.addWidget(QLabel("theme"), 0, 0) stacked_widget_preferences_layout.addWidget(self.in_combo_themes, 0, 1) stacked_widget_preferences_layout.addWidget(QLabel("initial theme"), 1, 0) stacked_widget_preferences_layout.addWidget( self.in_combo_theme_initial, 1, 1) stacked_widget_preferences_layout.addWidget(QLabel("Fontsize"), 2, 0) stacked_widget_preferences_layout.addWidget(self.text_size_slider, 2, 1) self.about_widget = QWidget() self.stacked_widget_preferences.addWidget(self.about_widget) about_text_edit = QTextEdit(self.about_widget) about_text_edit.setText( "developed by Maurice Jarck\nwith kind support from Shuai Lou\n07.2020-04.2021" ) about_text_edit.setEnabled(False) stacked_widget_about_layout = QGridLayout(self.about_widget) stacked_widget_about_layout.addWidget(about_text_edit) p_apperance_layout = QHBoxLayout(self.p_preferences) p_apperance_layout.addWidget(self.list_Widget) p_apperance_layout.addWidget(self.stacked_widget_preferences) def setup_signals(self) -> None: """connects signals Returns: None""" # header for filter, editor in zip(self.filters, self.header.editors): editor.textChanged.connect(filter.setFilterRegExp) # line edit self.in_new_filename.returnPressed.connect(lambda: self.validate( self.new, line_edit_list=[self.in_new_filepath, self.in_new_filename], data=False)) # comboboxes self.in_combobox_devicetype.addItems( ["choose here"] + [x.__name__ for x in valid_devices]) self.in_combobox_devicetype.currentIndexChanged.connect( lambda: self.update_combobox( self.in_combobox_os, valid_devices[ self.in_combobox_devicetype.currentIndex() - 1].expected_OS )) self.in_combo_themes.currentIndexChanged.connect( lambda: self.change_theme(self.in_combo_themes.currentText())) self.in_combo_theme_initial.currentTextChanged.connect(lambda: setattr( self, "initial_theme", self.in_combo_theme_initial.currentText())) # btns self.bt_delete_column.clicked.connect(self.delete) # self.bt_hide_show_filter.clicked.connect(lambda: self.toggle_hide_show_ani(37, 47, "height", self.header, b"maximumHeight")) self.bt_hide_show_filter.clicked.connect(self.header.hide_show) # self.bt_hide_show_filter.clicked.connect(lambda: self.toggle_hide_show_ani(30, 44, "height", self.header, b"maximumHeight")) self.bt_register_new.clicked.connect( lambda: self.stacked_widget.setCurrentWidget(self.p_register)) self.bt_enter_register.clicked.connect(lambda: self.validate( self.register, line_edit_list=[self.in_username, self.in_devicename], combo_box_list=[self.in_combobox_devicetype, self.in_combobox_os], forbidden=list(self.registered_devices.keys()), checkfname=True)) self.bt_create.clicked.connect(lambda: self.validate( self.new, line_edit_list=[self.in_new_filepath, self.in_new_filename], data=False)) self.bt_mod_new_path.clicked.connect(lambda: self.new(True)) self.bt_burger.clicked.connect(lambda: self.toggle_hide_show_ani( 0, 66, "width", self.left_menu_frame, b"maximumWidth", )) # menu bar self.action_register.triggered.connect( lambda: self.stacked_widget.setCurrentWidget(self.p_register)) self.action_open.triggered.connect(self.get_open_file_path) self.action_save.triggered.connect(self.save) self.action_new.triggered.connect(lambda: self.new(True)) self.action_print.triggered.connect( lambda: self.validate(self.print, data=False, checkfname=True)) self.action_hide_menu_bar.triggered.connect( lambda: self.toggle_hide_show(self.menu_Bar)) self.action_preferences.triggered.connect(self.p_preferences.show) # cancel self.bt_cancel_register.clicked.connect(lambda: self.cancel([ self.in_username, self.in_devicename, self.in_combobox_os, self. in_comment ])) # list widget self.list_Widget.currentRowChanged.connect( lambda: self.stacked_widget_preferences.setCurrentIndex( self.list_Widget.currentIndex().row())) # slider self.text_size_slider.sliderMoved.connect( lambda: self.change_font_size(self.text_size_slider.value())) # self.text_size_slider.sliderMoved.connect(lambda: print(self.text_size_slider.value())) def change_theme(self, theme) -> None: """changes theme according to combobox selection Returns: None""" with open(f"./data/{theme}.css", "r") as file: stylesheed = " ".join(file.readlines()) self.setStyleSheet(stylesheed) self.p_preferences.setStyleSheet(stylesheed) if self.in_combo_themes.currentText() == "dark_theme": self.left_btn_frame.setStyleSheet( u"background: #455364; border: 0px solid;") self.p_view_layout3.setSpacing(30) else: self.left_btn_frame.setStyleSheet( u"background: #ADADAD; border: 0px solid;") self.p_view_layout3.setSpacing(25) return self.in_combo_themes.currentText() def toggle_hide_show_ani(self, collapsed_val: int, expanded_val: int, actual: str, to_animate, property: bytes): """interpolates over a defined range of vales and sets it to a given property of a given widget""" if getattr(to_animate, actual)() == expanded_val: destination = collapsed_val else: destination = expanded_val print(getattr(to_animate, actual)(), destination) self.ani = QPropertyAnimation(to_animate, property) self.ani.setDuration(300) self.ani.setStartValue(getattr(to_animate, actual)()) self.ani.setEndValue(destination) self.ani.setEasingCurve(QEasingCurve.Linear) self.ani.start() def toggle_hide_show(self, widget: QWidget) -> None: """toggles visibiliy of a given widget Arg: widget: widget which is aimed to be hidden or shown Returs: None""" if widget.isVisible(): widget.hide() else: widget.show() def reopen_last_file(self) -> None: """asks for reopening of the last opened file""" if self.last_open_file_path != "" or self.last_open_file_path is not None: reopen_dialog = QMessageBox.question( self.p_view, "reopen last file?", "Do you want to reopen the last edited file?", QMessageBox.Yes | QMessageBox.No) if reopen_dialog == QMessageBox.Yes: self.file_path = self.last_open_file_path self.load() def change_font_size(self, size: int) -> None: """changes all font sizes""" self.font.setPointSize(size) self.menu_Bar.setFont(self.font) self.header.setFont(self.font) self.table.setFont(self.font) self.p_preferences.setFont(self.font) def set_user_preferences(self) -> None: """Reads user_config file and sets its propertys""" with open(self.user_config_file, "r") as config_file: data = dict(json.load(config_file)) self.last_open_file_path = data["last_open_file_path"] self.initial_theme = data['initial_theme'] self.change_font_size(data['font_size']) self.text_size_slider.setValue(data['font_size']) self.in_combo_theme_initial.setCurrentText(self.initial_theme) self.in_combo_themes.setCurrentText(self.initial_theme) with open(f"./data/{self.initial_theme}.css") as file: style_sheed = " ".join(file.readlines()) self.setStyleSheet(style_sheed) self.p_preferences.setStyleSheet(style_sheed) self.bt_burger.setStyleSheet( "border: 0px solid; background: transparent;") self.bt_register_new.setStyleSheet( "border: 0px solid; background: transparent;") self.bt_delete_column.setStyleSheet( "border: 0px solid; background: transparent;") self.bt_hide_show_filter.setStyleSheet( "border: 0px solid; background: transparent;") self.left_menu_frame.setStyleSheet(u" border: 0px solid;") if self.initial_theme == "dark_theme": self.left_btn_frame.setStyleSheet( u"background: #455364; border: 0px solid;") else: self.left_btn_frame.setStyleSheet( u"background: #ADADAD; border: 0px solid;") def cancel(self, widgets: list) -> None: """click event for all cancel buttons shows fist page in stacked widget and clears all widgets in widgets Args: widgets: defines list containing widgets to clear, only widgets with method .clear() are possible Returns: None""" for widget in widgets: widget.clear() self.stacked_widget.setCurrentWidget(self.p_view) def update_combobox(self, box, data: list) -> None: """ clears combo box updates combobox so that old content not needed any more isnt displayed and adds 'choose here' dummy to ensure an index change will be made (updating next box depends on index change) Args: box: instance of pyqt5.QtWidgets.qComboBox data: data supposed to be inserted into combobox Returns: None""" box.clear() box.addItems(["choose here"] + data) def validate(self, command, file_path: str = None, schema=None, line_edit_list: list = None, combo_box_list: list = None, data=None, forbidden: list = None, checkfname: bool = None) -> None: """validates user input Args: command: function to be called after vailidation process if finished line_edit_list: contents pyqt5.QtWidgets.QlineEdit instances to be checked if empty or current text in forbidden or not in allowed combo_box_list: contents pyqt5.QtWidgets.qComboBox instances to be checked if nothing selected data: data to be passed into command function if needed forbidden: houses keys which are not allowed to be entered checkfname: check weather an file path exists or not Returns: None""" fails = 0 if line_edit_list is not None: for x in line_edit_list: if x.text() == "": x.setText("fill all fields") fails += 1 if forbidden is not None and x.text() in forbidden: x.setText("in forbidden!!") fails += 1 if combo_box_list is not None: for combobox in combo_box_list: if combobox.currentText() == "": self.statusbar.showMessage("all comboboxes must be filled") fails += 1 if checkfname is True and self.file_path is None: self.statusbar.showMessage( "no file path specified, visit Ctrl+o or menuebar/edit/open to fix" ) fails += 1 if file_path is not None: if forbidden is not None and file_path in forbidden: fails += 1 self.statusbar.showMessage("select a file to continue") else: try: validate_json.validate(file_path, schema) except ValidationError as e: self.msg_box = QtWidgets.QMessageBox.critical( self, "validation failed", f"Invalid Json file, problem in: {e.messages}") fails += 1 if fails == 0: if data is None: command() else: command(data) else: message = f"problem\ncommand: {command.__name__}\nfails: {fails}" print(message) return message def register(self) -> None: """registers a new device and saves Returns: None""" logic.register(devname=self.in_devicename.text(), devtype=[ device for device in valid_devices if device.__name__ == self.in_combobox_devicetype.currentText() ].pop(), username=self.in_username.text(), os=self.in_combobox_os.currentText(), comment=self.in_comment.toPlainText(), datetime=str(datetime.datetime.now()), registered_devices=self.registered_devices) new_values = [ self.in_devicename.text(), self.in_username.text(), self.in_combobox_os.currentText(), [ device.__name__ for device in valid_devices if device.__name__ == self.in_combobox_devicetype.currentText() ].pop(), self.in_comment.toPlainText(), str(datetime.datetime.now()) ] row = [QStandardItem(str(item)) for item in new_values] self.model.appendRow(row) self.stacked_widget.setCurrentWidget(self.p_view) self.in_devicename.clear() self.in_username.clear() self.in_combobox_os.clear() self.in_comment.clear() self.save() def delete(self) -> None: """deletes all rows associated with min 1 slected cell Returns: None""" rows = sorted(set(index.row() for index in self.table.selectedIndexes()), reverse=True) qb = QMessageBox() answ = qb.question(self, 'delete rows', f"Are you sure to delete {rows} rows?", qb.Yes | qb.No) if answ == qb.Yes: for row in rows: self.registered_devices.pop( str(self.model.index(row, 0).data())) self.model.removeRow(row) qb.information(self, 'notification', f"deleted {rows} row") else: qb.information(self, 'notification', "Nothing Changed") self.save() def get_open_file_path(self) -> None: """gets file-path and set it to self.file_path, extra step for json validation Returns: None""" self.file_path = \ QFileDialog.getOpenFileName(self, "open file", f"{self.last_open_file_dir or 'c://'}", "json files (*json)")[0] self.validate(command=self.load, file_path=self.file_path, schema=validate_json.ItHilfeDataSchema, forbidden=[""]) def load(self) -> None: """opens json file and loads its content into registered devices Returns: None""" self.model.clear() self.registered_devices.clear() with open(self.file_path, "r") as file: data = dict(json.load(file)) devices = data["devices"].values() self.last_open_file_dir = data["last_open_file_dir"] for value in devices: row = [] for i, item in enumerate(value): cell = QStandardItem(str(item)) row.append(cell) if i == 0 or i == 3 or i == 5: cell.setEditable(False) self.model.appendRow(row) new = [x for x in valid_devices if x.__name__ == value[3]].pop(0)(value[0], value[1], value[4], value[5]) new.OS = value[2] self.registered_devices[value[0]] = new self.model.setHorizontalHeaderLabels(labels) self.statusbar.showMessage("") # auto complete for a in range(len(self.header.editors)): completer = QCompleter([ self.model.data(self.model.index(x, a)) for x in range(self.model.rowCount()) ]) completer.setCompletionMode(QCompleter.InlineCompletion) self.header.editors[a].setCompleter(completer) def save(self) -> None: """saves content fo self.registered_devices into specified json file Returns: None""" if not self.file_path: self.statusbar.showMessage( "no file path set all changes get lost if closed") else: with open( self.file_path, 'w', ) as file: devices = { k: [ v.name, v.user, v.OS, v.__class__.__name__, v.comment, v.datetime ] for (k, v) in enumerate(self.registered_devices.values()) } last_open_file_dir = "/".join(self.file_path.split("/")[:-1]) resulting_dict = { "devices": devices, "last_open_file_dir": last_open_file_dir } json.dump(resulting_dict, file) self.statusbar.showMessage("saved file") with open(self.user_config_file, "w") as user_preferences_file: json.dump( { "last_open_file_path": self.last_open_file_path, "initial_theme": self.initial_theme, "font_size": self.text_size_slider.value() }, user_preferences_file) def new(self, stage: bool, test: bool = False) -> None: """creates new csv file to save into stage is True: set filepath stage is False: set new name, save Args: stage: determines a which stage to execute this function Returns: None""" if stage is True: if not test: self.dir = QFileDialog.getExistingDirectory( self, "select a folder", "c://") self.stacked_widget.setCurrentWidget(self.p_create) self.in_new_filepath.setText(self.dir) self.registered_devices.clear() else: self.file_path = self.dir + f"/{self.in_new_filename.text()}.json" self.save() self.stacked_widget.setCurrentWidget(self.p_view) def print(self, test: bool) -> None: """setup and preview pViewTable for paper printing Returns: None""" with open(self.file_path) as f: self.data = json.dumps(dict(json.load(f)), sort_keys=True, indent=6, separators=(".", "=")) self.document = QtWidgets.QTextEdit() self.document.setText(self.data) if not test: printer = QPrinter() previewDialog = QPrintPreviewDialog(printer, self) previewDialog.paintRequested.connect( lambda: self.document.print_(printer)) previewDialog.exec_()
class Task_ledger(QWidget): def __init__(self, system, parent=None): super(Task_ledger, self).__init__(parent) self.stackedWidgetPage1 = QWidget() self.stackedWidgetPage2 = QWidget() self.stackedWidgetPage3 = QWidget() self.stackedWidgetPage4 = QWidget() self.dialog = dialog_logout.Logout_Dialog() self.system = system def setupUi(self, Form): Form.setObjectName("Form") Form.setFixedSize(1000, 600) Form.setWindowTitle("Task Ledger") self.stackedWidget = QStackedWidget(Form) self.stackedWidget.setGeometry(QRect(0, 0, 1000, 600)) self.stackedWidget.setObjectName("stackedWidget") # Landing Page self.stackedWidgetPage1.setObjectName("stackedWidgetPage1") self.landing = landing.LandingPageUI(self.stackedWidgetPage1) self.landing.setGeometry(0, 0, 1000, 60) self.landing.setupUi(self.stackedWidgetPage1) self.stackedWidget.addWidget(self.stackedWidgetPage1) self.landing.pushButton.clicked.connect(self.goto_login) # Login Page self.stackedWidgetPage2.setObjectName("stackedWidgetPage2") self.login = login.LoginUI(self.stackedWidgetPage2) self.login.setupUi(self.stackedWidgetPage2) self.login.setGeometry(0, 0, 1000, 60) self.login.reg_label.clicked.connect(self.goto_reg) self.login.back.clicked.connect(self.goto_landing) self.stackedWidget.addWidget(self.stackedWidgetPage2) # register Page self.stackedWidgetPage3.setObjectName("stackedWidgetPage3") self.reg = reg.RegisterUI(self.stackedWidgetPage3) self.reg.setupUi(self.stackedWidgetPage3) self.reg.setGeometry(0, 0, 1000, 600) self.reg.back.clicked.connect(self.goto_login) self.stackedWidget.addWidget(self.stackedWidgetPage3) # main self.stackedWidgetPage4.setObjectName("stackedWidgetPage4") self.main = main.MainUI(self.stackedWidgetPage4) # Pass system object to the child Widget self.main.bind_system(self.system) self.main.setupUi(self.stackedWidgetPage4) self.main.setGeometry(0, 0, 1000, 600) self.main.navbar.log_out.clicked.connect(self.logout) self.stackedWidget.addWidget(self.stackedWidgetPage4) self.stackedWidget.setCurrentIndex(0) def logout(self): self.dialog.setupUi(self.dialog) self.dialog.okay.clicked.connect(self.goto_landing) self.dialog.okay.clicked.connect(self.dialog.close) self.dialog.no.clicked.connect(self.dialog.close) self.dialog.show() def goto_landing(self): self.stackedWidget.setCurrentIndex(0) def goto_login(self): self.stackedWidget.setCurrentIndex(1) def goto_reg(self): self.stackedWidget.setCurrentIndex(2) def goto_main(self): self.stackedWidget.setCurrentIndex(3)
class TTVSetsImporterWidget(QWidget): ttv_updated = Signal(TTVSets) def __init__( self, format_to_widget, parent: "QWidget" = None, ): super().__init__(parent) # Components self._ttv = None # Maps a with its respective Widget self._format_to_widget = format_to_widget self._stacked_widgets = QStackedWidget() self._name_textbox = QLineEdit("Unnamed") self._formatter_selector = QComboBox() for (dataset_io_name, widget_factory) in self._format_to_widget.items(): self._formatter_selector.addItem( dataset_io_name, getattr(DatasetIORegistry, dataset_io_name)) self._stacked_widgets.addWidget( self._format_to_widget[dataset_io_name]()) def horizontal_line(): line = QFrame() line.setFrameShape(QFrame.HLine) line.setFrameShadow(QFrame.Sunken) line.setFixedHeight(2) line.setContentsMargins(0, 30, 0, 0) return line self._x_datatype_selector = DatatypeSelectorFactory( title="X (Input) Datatype") self._y_datatype_selector = DatatypeSelectorFactory( title="Y (Output) Datatype") datatypes_layout = QHBoxLayout() datatypes_layout.addWidget(self._x_datatype_selector) datatypes_layout.addWidget(self._y_datatype_selector) self._info_layout = QFormLayout() self._info_layout.addRow("Name", self._name_textbox) self._info_layout.addRow("Format", self._formatter_selector) self._main_layout = QVBoxLayout() self._main_layout.addLayout(self._info_layout) self._main_layout.addWidget(horizontal_line()) self._main_layout.addWidget(self._stacked_widgets) self._main_layout.addWidget(horizontal_line()) self._main_layout.addLayout(datatypes_layout) self._load_ttv_from_file_button = QPushButton("Load from file...") self._load_ttv_from_file_button.clicked.connect( self._load_ttv_from_file) self._update_ttv_button = QPushButton("Load TTV") self._update_ttv_button.clicked.connect(self._update_ttv) self._button_box = QDialogButtonBox() self._button_box.addButton(self._load_ttv_from_file_button, QDialogButtonBox.ResetRole) self._button_box.addButton(self._update_ttv_button, QDialogButtonBox.ApplyRole) self._main_layout.addWidget(self._button_box) self.setLayout(self._main_layout) self._formatter_selector.currentIndexChanged[int].connect( lambda i: self._stacked_widgets.setCurrentIndex(i)) def get_ttv(self) -> "TTVSets": return self._ttv def _update_ttv(self): self._ttv = self._stacked_widgets.currentWidget().load_ttv( self._name_textbox.text(), self._x_datatype_selector.selected_datatype, self._y_datatype_selector.selected_datatype, ) self.ttv_updated.emit(self._ttv) def _load_ttv_from_file(self): print("Loading from file...") description_file_path = QFileDialog.getOpenFileName( self, "Open TTV .json file")[0] if description_file_path: LOGGER.debug("Loading %s...", description_file_path) ttv_dir = os.path.dirname(description_file_path) ttv_description = TTVSetsIO.load_ttv_description( description_file_path) # Update name self._name_textbox.setText(ttv_description["name"]) # Pick the new formatter formatter_idx = self._formatter_selector.findText( ttv_description["format"]) if formatter_idx != -1: self._formatter_selector.setCurrentIndex(formatter_idx) self._selected_index_changed(formatter_idx) # TODO: Cover case when "train" is null self._x_datatype_selector.change_current_datatype( ttv_description["train"]["x_type"]) self._y_datatype_selector.change_current_datatype( ttv_description["train"]["y_type"]) self._stacked_widgets.currentWidget().update_widgets( ttv_dir, ttv_description) self._update_ttv() else: LOGGER.debug("Loading cancelled") def _selected_index_changed(self, index: int): self._stacked_widgets.setCurrentIndex(index) def sizeHint(self) -> "QSize": """Optimal size of the widget.""" return QSize(450, 150) def __reduce__(self): return ( TTVSetsImporterWidget, (self._ttv_sets_dialog, None), )
class ExifViewer(QMainWindow): WINDOW_SIZE = {'width': 900, 'height': 600} def __init__(self, parent=None): super().__init__(parent) self.qw_tree = None self.qw_stack = None self.tree_type_to_stack_idx = None self.exif_reader = None self.setWindowTitle("exif viewer") self.resize(self.WINDOW_SIZE['width'], self.WINDOW_SIZE['height']) def make_viewer(self, exif_reader): self.exif_reader = exif_reader ifds = self.exif_reader.get_exif() self.qw_tree = QTreeWidget(self) qt_util_init_tree(self.qw_tree, header_list=['ifd_name']) self.qw_stack = QStackedWidget(self) self.tree_type_to_stack_idx = {} for tree_item_type, (ifd_name, ifd) in enumerate(ifds.items()): # ----------------------- # treeにifd nameを書き込んでroot_treeに登録する. # ----------------------- qw_tree_item = QTreeWidgetItem(['{} ifd'.format(ifd_name), ''], type=tree_item_type) self.qw_tree.addTopLevelItem(qw_tree_item) # ----------------------- # text_editにexif情報を書き込んでstacked_widgetに登録する. # ----------------------- qw_text_edit = QTextEdit(self) qt_util_init_text_edit(qw_text_edit) qt_util_append_text_edit(qw_text_edit, None, head=True) for _, tag in ifd.items(): qt_util_append_text_edit(qw_text_edit, tag, head=False, display_max_len=50) qt_util_reset_text_edit_cursor(qw_text_edit) idx = self.qw_stack.addWidget(qw_text_edit) self.tree_type_to_stack_idx[tree_item_type] = idx else: self.qw_tree.itemClicked.connect(self._tree_item_clicked) # ----------------------- # widgetをlayoutに登録する. # ----------------------- widgets = [self.qw_tree, self.qw_stack] self._make_layout(widgets) self._make_toolbar() self.show() # widgetを表示する. def _make_toolbar(self): # action_to_text = QAction(QIcon(os.path.join('icon', 'text.png')), 'save', self) save_icon = QApplication.style().standardIcon( QStyle.SP_DialogSaveButton) save_action = QAction(icon=save_icon, text='save', parent=self) save_action.triggered.connect(self._save_text) exit_icon = QApplication.style().standardIcon( QStyle.SP_DialogCloseButton) exit_action = QAction(icon=exit_icon, text='exit', parent=self) exit_action.triggered.connect(self._quit) # ツールバー作成 self.toolbar = self.addToolBar('tool bar') self.toolbar.setIconSize(QSize(35, 35)) self.toolbar.setFixedHeight(35) self.toolbar.addAction(save_action) self.toolbar.addAction(exit_action) def _make_layout(self, widgets): qw_splitter = QSplitter(self) for widget in widgets: qw_splitter.addWidget(widget) qw_splitter.setSizes( [self.WINDOW_SIZE['width'] * 0.1, self.WINDOW_SIZE['width'] * 0.9]) # layout = QHBoxLayout() # layout.addWidget(qw_splitter) # qw = QWidget() # qw.setLayout(layout) # self.setCentralWidget(qw) # self.setWindowFlags(Qt.WindowStaysOnTopHint) # 常に手前に表示する. # MainWindowのCentral領域にWidgetを設定 self.setCentralWidget(qw_splitter) @Slot() def _tree_item_clicked(self, item, column): self.qw_stack.setCurrentIndex(self.tree_type_to_stack_idx[item.type()]) current_text_edit = self.qw_stack.currentWidget() qt_util_reset_text_edit_cursor(current_text_edit) @Slot() def _save_text(self): filename = QFileDialog.getSaveFileName(self, '名前を付けて保存') out_path = filename[0] if out_path: self.exif_reader.save_log(out_path) @Slot() def _quit(self): qApp.quit()
class Application(QMainWindow): # Booleans indicating which window is opened __isTesting = False __isTraining = False __isCreation = False __isPreProcessingText = False __isPreProcessingImage = False __isChoice = False @Slot() def goToTraining(self): if self.__isCreation: self.__isCreation = False self.__isTraining = True self.__trainingWindow = TrainingWindow( self.__creationWindow.dataset, self.__choiceWindow.classes, (self.__creationWindow.modelList, self.__creationWindow.networkName.text()), True) self.__trainingWindow.goBackButton.clicked.connect( self.backtoCreation) self.__windows.addWidget(self.__trainingWindow) self.__windows.setCurrentIndex(3) elif self.__isChoice: if not self.__choiceWindow.datasetLoaded: ret = QMessageBox.warning(self, "Dataset", "Veuillez charger un dataset", QMessageBox.Ok) return if not self.__choiceWindow.networkLoaded: ret = QMessageBox.warning( self, "Reseau de neurones", "Veuillez charger un reseau de neurones ou en creer un nouveau", QMessageBox.Ok) return self.__isTraining = True self.__isChoice = False self.__trainingWindow = TrainingWindow( self.__choiceWindow.dataset, self.__choiceWindow.classes, (self.__choiceWindow.network, self.__choiceWindow.networkName), False) self.__trainingWindow.goBackButton.clicked.connect( self.backToChoice) self.__windows.addWidget(self.__trainingWindow) self.__windows.setCurrentIndex(2) @Slot() def goToTesting(self): self.__isTesting = True self.__testingWindow = TestingWindow() self.__windows.addWidget(self.__testingWindow) self.__windows.setCurrentIndex(1) self.__testingWindow.buttons[3].clicked.connect(self.backToMenu) @Slot() def goToCreation(self): if not self.__choiceWindow.datasetLoaded: ret = QMessageBox.warning(self, "Dataset", "Veuillez d'abord charger un dataset", QMessageBox.Ok) return self.__isChoice = False if self.__choiceWindow.networkLoaded: ret = QMessageBox.warning(self, "Reseau de neurones", "Un reseau de neurones est deja charge", QMessageBox.Ok) return self.__isCreation = True self.__creationWindow = CreationWindow(self.__choiceWindow.dataset) self.__windows.addWidget(self.__creationWindow) self.__windows.setCurrentIndex(2) self.__creationWindow.goBackButton.clicked.connect(self.backToChoice) self.__creationWindow.done.connect(self.goToTraining) @Slot() def goToChoice(self): self.__isChoice = True self.__choiceWindow = ChoiceWindow() self.__windows.addWidget(self.__choiceWindow) self.__windows.setCurrentIndex(1) self.__choiceWindow.buttons[2].clicked.connect(self.goToCreation) self.__choiceWindow.buttons[3].clicked.connect(self.goToTraining) self.__choiceWindow.buttons[4].clicked.connect(self.backToMenu) @Slot() def backtoCreation(self): self.__isCreation = True self.__isTraining = False self.__windows.setCurrentIndex(2) self.__windows.removeWidget(self.__trainingWindow) self.__trainingWindow.deleteLater() @Slot() def goToPreprocessingText(self): self.__isPreProcessingText = True self.__preProcessingTextWindow = PreprocessingTextWindow() self.__preProcessingTextWindow.pushButton_return.clicked.connect( self.backToMenu) self.__windows.addWidget(self.__preProcessingTextWindow) self.__windows.setCurrentIndex(1) @Slot() def goToPreprocessingImage(self): self.__isPreProcessingImage = True self.__preProcessingImageWindow = PreprocessingImageWindow() self.__preProcessingImageWindow.pushButton_return.clicked.connect( self.backToMenu) self.__windows.addWidget(self.__preProcessingImageWindow) self.__windows.setCurrentIndex(1) @Slot() def backToMenu(self): self.__windows.setCurrentIndex(0) if self.__isTesting: self.__isTesting = False self.__windows.removeWidget(self.__testingWindow) self.__testingWindow.deleteLater() elif self.__isPreProcessingText: self.__isPreProcessingText = False self.__windows.removeWidget(self.__preProcessingTextWindow) self.__preProcessingTextWindow.deleteLater() elif self.__isPreProcessingImage: self.__isPreProcessingImage = False self.__windows.removeWidget(self.__preProcessingImageWindow) self.__preProcessingImageWindow.deleteLater() elif self.__isChoice: self.__isChoice = False self.__windows.removeWidget(self.__choiceWindow) self.__choiceWindow.deleteLater() self.__choiceWindow = None @Slot() def backToChoice(self): self.__isChoice = True self.__windows.setCurrentIndex(1) if self.__isCreation: self.__isCreation = False self.__windows.removeWidget(self.__creationWindow) self.__creationWindow.deleteLater() elif self.__isTraining: self.__isTraining = False self.__windows.removeWidget(self.__trainingWindow) self.__trainingWindow.deleteLater() def __init__(self, *args, **kwargs): super(Application, self).__init__(*args, **kwargs) # Initializing menu window self.__menuWindow = MenuWindow() self.__windows = QStackedWidget(self) self.__windows.addWidget(self.__menuWindow) self.__windows.setCurrentIndex(0) self.setCentralWidget(self.__windows) # Connecting buttons self.__menuWindow.buttons[0].clicked.connect( self.goToPreprocessingImage) self.__menuWindow.buttons[1].clicked.connect( self.goToPreprocessingText) self.__menuWindow.buttons[2].clicked.connect(self.goToChoice) self.__menuWindow.buttons[3].clicked.connect(self.goToTesting) self.__menuWindow.buttons[4].clicked.connect(QApplication.quit)
class MainWidget(QWidget): def __init__(self, parent: QWidget, model: Model) -> None: super().__init__(parent) logger.add(self.log) self.mainlayout = QVBoxLayout() self.setLayout(self.mainlayout) self.splitter = QSplitter(Qt.Vertical) self.stack = QStackedWidget() self.splitter.addWidget(self.stack) # mod list widget self.modlistwidget = QWidget() self.modlistlayout = QVBoxLayout() self.modlistlayout.setContentsMargins(0, 0, 0, 0) self.modlistwidget.setLayout(self.modlistlayout) self.stack.addWidget(self.modlistwidget) # search bar self.searchbar = QLineEdit() self.searchbar.setPlaceholderText('Search...') self.modlistlayout.addWidget(self.searchbar) # mod list self.modlist = ModList(self, model) self.modlistlayout.addWidget(self.modlist) self.searchbar.textChanged.connect(lambda e: self.modlist.setFilter(e)) # welcome message welcomelayout = QVBoxLayout() welcomelayout.setAlignment(Qt.AlignHCenter | Qt.AlignVCenter) welcomewidget = QWidget() welcomewidget.setLayout(welcomelayout) welcomewidget.dragEnterEvent = self.modlist.dragEnterEvent # type: ignore welcomewidget.dragMoveEvent = self.modlist.dragMoveEvent # type: ignore welcomewidget.dragLeaveEvent = self.modlist.dragLeaveEvent # type: ignore welcomewidget.dropEvent = self.modlist.dropEvent # type: ignore welcomewidget.setAcceptDrops(True) icon = QIcon(str(getRuntimePath('resources/icons/open-folder.ico'))) iconpixmap = icon.pixmap(32, 32) icon = QLabel() icon.setPixmap(iconpixmap) icon.setAlignment(Qt.AlignHCenter | Qt.AlignVCenter) icon.setContentsMargins(4, 4, 4, 4) welcomelayout.addWidget(icon) welcome = QLabel('''<p><font> No mod installed yet. Drag a mod into this area to get started! </font></p>''') welcome.setAttribute(Qt.WA_TransparentForMouseEvents) welcome.setAlignment(Qt.AlignHCenter | Qt.AlignVCenter) welcomelayout.addWidget(welcome) self.stack.addWidget(welcomewidget) # output log self.output = QTextEdit(self) self.output.setTextInteractionFlags(Qt.NoTextInteraction) self.output.setReadOnly(True) self.output.setContextMenuPolicy(Qt.NoContextMenu) self.output.setPlaceholderText('Program output...') self.splitter.addWidget(self.output) # TODO: enhancement: add a launch game icon # TODO: enhancement: show indicator if scripts have to be merged self.splitter.setStretchFactor(0, 1) self.splitter.setStretchFactor(1, 0) self.mainlayout.addWidget(self.splitter) if len(model): self.stack.setCurrentIndex(0) self.splitter.setSizes([self.splitter.size().height(), 50]) else: self.stack.setCurrentIndex(1) self.splitter.setSizes([self.splitter.size().height(), 0]) model.updateCallbacks.append(self.modelUpdateEvent) def keyPressEvent(self, event: QKeyEvent) -> None: if event.key() == Qt.Key_Escape: self.modlist.setFocus() self.searchbar.setText('') elif event.matches(QKeySequence.Find): self.searchbar.setFocus() elif event.matches(QKeySequence.Paste): self.pasteEvent() super().keyPressEvent(event) def pasteEvent(self) -> None: clipboard = QApplication.clipboard().text().splitlines() if len(clipboard) == 1 and isValidNexusModsUrl(clipboard[0]): self.parentWidget().showDownloadModDialog() else: urls = [ url for url in QApplication.clipboard().text().splitlines() if len(str(url.strip())) ] if all( isValidModDownloadUrl(url) or isValidFileUrl(url) for url in urls): asyncio.create_task(self.modlist.checkInstallFromURLs(urls)) def modelUpdateEvent(self, model: Model) -> None: if len(model) > 0: if self.stack.currentIndex() != 0: self.stack.setCurrentIndex(0) self.repaint() else: if self.stack.currentIndex() != 1: self.stack.setCurrentIndex(1) self.repaint() def unhideOutput(self) -> None: if self.splitter.sizes()[1] < 10: self.splitter.setSizes([self.splitter.size().height(), 50]) def unhideModList(self) -> None: if self.splitter.sizes()[0] < 10: self.splitter.setSizes([50, self.splitter.size().height()]) def log(self, message: Any) -> None: # format log messages to user readable output settings = QSettings() record = message.record message = record['message'] extra = record['extra'] level = record['level'].name.lower() name = str(extra['name'] ) if 'name' in extra and extra['name'] is not None else '' path = str(extra['path'] ) if 'path' in extra and extra['path'] is not None else '' dots = bool( extra['dots'] ) if 'dots' in extra and extra['dots'] is not None else False newline = bool( extra['newline'] ) if 'newline' in extra and extra['newline'] is not None else False output = bool( extra['output'] ) if 'output' in extra and extra['output'] is not None else bool( message) modlist = bool( extra['modlist'] ) if 'modlist' in extra and extra['modlist'] is not None else False if level in ['debug' ] and settings.value('debugOutput', 'False') != 'True': if newline: self.output.append(f'') return n = '<br>' if newline else '' d = '...' if dots else '' if len(name) and len(path): path = f' ({path})' if output: message = html.escape(message, quote=True) if level in ['success', 'error', 'warning']: message = f'<strong>{message}</strong>' if level in ['success']: message = f'<font color="#04c45e">{message}</font>' if level in ['error', 'critical']: message = f'<font color="#ee3b3b">{message}</font>' if level in ['warning']: message = f'<font color="#ff6500">{message}</font>' if level in ['debug', 'trace']: message = f'<font color="#aaa">{message}</font>' path = f'<font color="#aaa">{path}</font>' if path else '' d = f'<font color="#aaa">{d}</font>' if d else '' time = record['time'].astimezone( tz=None).strftime('%Y-%m-%d %H:%M:%S') message = f'<font color="#aaa">{time}</font> {message}' self.output.append( f'{n}{message.strip()}{" " if name or path else ""}{name}{path}{d}' ) else: self.output.append(f'') self.output.verticalScrollBar().setValue( self.output.verticalScrollBar().maximum()) self.output.repaint() if modlist: self.unhideModList() if settings.value('unhideOutput', 'True') == 'True' and output: self.unhideOutput()
class ORIRMain(QFramelessWindow): def __init__(self): super(ORIRMain, self).__init__() self._m_navigation_bar = QSlideNavigationBar() self._m_stacked_widget = QStackedWidget() self._m_stacked_widget.setMouseTracking(True) self._layout = QHBoxLayout() self._init_navigation_bar() self._layout.addWidget(self._m_navigation_bar) self._layout.addWidget(self._m_stacked_widget) self._layout.setStretchFactor(self._m_navigation_bar, 1) self._layout.setStretchFactor(self._m_stacked_widget, 40) self.set_layout(None, self._layout) self._debug_page = ORIR_Debug() self._m_stacked_widget.addWidget(self._debug_page) self._loganalysis_page = ORIR_LogAnalysis() self._m_stacked_widget.addWidget(self._loganalysis_page) self._collectiondata_page = ORIR_CollectionData() self._m_stacked_widget.addWidget(self._collectiondata_page) self._m_tool_page = ORIR_Tool() self._m_stacked_widget.addWidget(self._m_tool_page) self._help_page = ORIR_Help() self._m_stacked_widget.addWidget(self._help_page) self._m_navigation_bar.itemClicked.connect(self._on_change_page) # self.set_window_icon('../resources/icons/battery.ico') self.setWindowFlags(Qt.FramelessWindowHint or Qt.WindowSystemMenuHint or Qt.WindowMinMaxButtonsHint) def _on_update_status_bar(self, str): """ 更新状态栏 :param str:要更新的信息 :return: """ self.set_status_text(str) def _on_change_page(self, index: int, item_name: str): """ 切换页面的槽函数 :param index: :param item_name: :return: """ self._m_stacked_widget.setCurrentIndex(index) def _init_navigation_bar(self): """ 初始化导航栏 :return: None """ self._m_navigation_bar.set_orientation(Qt.Vertical) self._m_navigation_bar.set_fixed(True) self._m_navigation_bar.add_item('前期调试') self._m_navigation_bar.add_item('日志分析') self._m_navigation_bar.add_item('采点工具') self._m_navigation_bar.add_item('辅助工具') self._m_navigation_bar.add_item('帮助') self._m_navigation_bar.set_item_line_style( QSlideNavigationBar.ItemLineStyle.ItemLeft) self._m_navigation_bar.set_item_line_color(QColor('red')) self._m_navigation_bar.setMaximumWidth(20) def closeEvent(self, event): # message为窗口标题 # Are you sure to quit?窗口显示内容 # QtGui.QMessageBox.Yes | QtGui.QMessageBox.No窗口按钮部件 # QtGui.QMessageBox.No默认焦点停留在NO上 reply = QMessageBox.question(self, 'Message', "确定退出?", QMessageBox.Yes | QMessageBox.No, QMessageBox.No) # 判断返回结果处理相应事项 if reply == QMessageBox.Yes: self._debug_page.close_all() self._m_tool_page.close_all() event.accept() else: event.ignore()