class dailyTab(QWidget): def __init__(self): super().__init__() # Виджет для дейлков # Содержит содержит главный таб виджет для дейликов self.tab = QTabWidget() self.vbox = QVBoxLayout() self.vbox.addWidget(self.tab) self.setLayout(self.vbox) self.threads = [] # При ините создаём тред для получения дейликов self.loadDataThread = loadDataThread(r'https://api.guildwars2.com/v2/achievements/daily') self.loadDataThread.signal.connect(self.getEvent, Qt.QueuedConnection) self.loadDataThread.start() def getEvent(self, signal): signal = json.loads(signal) for metaEvent in signal.keys(): setattr(self,metaEvent,frame()) self.tab.addTab(getattr(self,metaEvent),metaEvent) for event in signal[metaEvent]: if (event['level']['max'] == 80 and metaEvent == 'pve') or metaEvent != 'pve': getattr(self,metaEvent).getEvent(event) def addTab(self, event, metaEvent): getattr(self,'{}Tab'.format(metaEvent)).addEvent(event) self.resize(self.grid.sizeHint())
def dialogExtract2csv(self): d = QDialog(self) d.setFixedWidth(450) d.setWindowTitle("Extract data to Csv") d.setVisible(True) vbox = QVBoxLayout() tabWidget = QTabWidget() for name, mod in list(self.moduleDict.items()): wid = QWidget() grid = QGridLayout() grid.setSpacing(20) wid.dateStart = QLineEdit('%s00:00' % dt.datetime.now().strftime("%Y-%m-%dT")) wid.dateEnd = QLineEdit("Now") grid.addWidget(QLabel("From"), 0, 0) grid.addWidget(wid.dateStart, 0, 1) grid.addWidget(QLabel("To"), 0, 2) grid.addWidget(wid.dateEnd, 0, 3) for i, device in enumerate(mod.devices): checkbox = QCheckBox(device.deviceLabel) checkbox.stateChanged.connect(partial(self.csvUpdateTab, name, checkbox, device)) checkbox.setCheckState(2) grid.addWidget(checkbox, 1 + i, 0, 1, 3) wid.setLayout(grid) tabWidget.addTab(wid, name) buttonBox = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel) buttonBox.button(QDialogButtonBox.Ok).clicked.connect(partial(self.extract2csv, tabWidget, d)) buttonBox.button(QDialogButtonBox.Cancel).clicked.connect(d.close) vbox.addWidget(tabWidget) vbox.addWidget(buttonBox) d.setLayout(vbox)
class PluginErrorDialog(QDialog): """ Dialog with tabs each tab is a python traceback """ def __init__(self): super(PluginErrorDialog, self).__init__() self.setWindowTitle(translations.TR_PLUGIN_ERROR_REPORT) self.resize(600, 400) vbox = QVBoxLayout(self) label = QLabel(translations.TR_SOME_PLUGINS_REMOVED) vbox.addWidget(label) self._tabs = QTabWidget() vbox.addWidget(self._tabs) hbox = QHBoxLayout() btnAccept = QPushButton(translations.TR_ACCEPT) btnAccept.setMaximumWidth(100) hbox.addWidget(btnAccept) vbox.addLayout(hbox) #signals btnAccept.clicked['bool'].connect(self.close) def add_traceback(self, plugin_name, traceback_msg): """Add a Traceback to the widget on a new tab""" traceback_widget = TracebackWidget(traceback_msg) self._tabs.addTab(traceback_widget, plugin_name)
class RosConfigDialog(QDialog): configChanged = pyqtSignal() # use this for editable filtering of message types http://www.qtcentre.org/threads/23143-Combobox-entries-filter-as-I-type def __init__(self, name): super(QDialog, self).__init__() self.setWindowTitle(name) self.tabWidget = None self.config = None self.packageTab = None self.topicsTab = None self.tabWidget = QTabWidget() mainLayout = QFormLayout() mainLayout.addWidget(self.tabWidget) self.packageTab = PackageTab() self.packageTab.configChanged.connect(self.configChangedHandler) self.tabWidget.addTab(self.packageTab, 'Package') self.topicsTab = TopicsTab() self.topicsTab.configChanged.connect(self.configChangedHandler) self.tabWidget.addTab(self.topicsTab, 'Topics') self.resize(700, 100) self.setLayout(mainLayout) def setConfig(self, config): self.config = config self.packageTab.setConfig(self.config) self.topicsTab.setConfig(self.config) def configChangedHandler(self): self.configChanged.emit()
def __init__(self, parent): QTabWidget.__init__(self, parent) self.addTab(PaveNumerique(parent), ' Pavé numérique ') self.addTab(Options(parent), 'Options') self.addTab(Avance(parent), 'Avancé') self.setTabPosition(QTabWidget.South) self.setStyleSheet(""" QTabBar::tab:selected { background: white; border: 1px solid #C4C4C3; border-top-color: white; /* same as the pane color */ border-bottom-left-radius: 4px; border-bottom-right-radius: 4px; border-top-left-radius: 0px; border-top-right-radius: 0px; min-width: 8ex; padding: 7px; } QStackedWidget {background:white} QTabBar QToolButton { background:white; border: 1px solid #C4C4C3; border-top-color: white; /* same as the pane color */ border-bottom-left-radius: 4px; border-bottom-right-radius: 4px; border-top-left-radius: 0px; border-top-right-radius: 0px; } """)
def __init__(self,index): QTabWidget.__init__(self) self.index=index lines=[] if inp_load_file(lines,"pulse"+str(self.index)+".inp")==True: self.tab_name=inp_search_token_value(lines, "#sim_menu_name") else: self.tab_name="" self.setTabsClosable(True) self.setMovable(True) self.tmesh = tab_time_mesh(self.index) self.addTab(self.tmesh,_("time mesh")) self.circuit=circuit(self.index) self.addTab(self.circuit,_("Circuit")) tab=tab_class() tab.init("pulse"+str(self.index)+".inp","Configure") self.addTab(tab,"Configure")
def __init__ (self): super().__init__() FlGlob.mainwindow = self self.activeChanged.connect(self.loadnode) self.selectedChanged.connect(self.filteractions) self.viewChanged.connect(self.filterglobactions) self.style = FlNodeStyle(QFont()) self.initactions() self.initmenus() self.inittoolbars() tabs = QTabWidget(parent=self) tabs.setTabsClosable(True) tabs.setTabBarAutoHide(True) tabs.tabCloseRequested.connect(self.closetab) tabs.tabBarDoubleClicked.connect(self.nametab) tabs.currentChanged.connect(self.tabswitched) self.tabs = tabs self.setCentralWidget(tabs) self.initdocks() self.filterglobactions() self.filteractions()
class T_window(QWidget): def __init__(self): super().__init__() self.tabs = QTabWidget() self.com = T_communication() self.pbar = QProgressBar() self.pbar.setFormat("Battery : %p%") self.grid = QGridLayout() self.setLayout(self.grid) self.grid.addWidget(self.pbar) self.grid.addWidget(self.tabs) self.dpi = T_dpi() self.com.getDpi(self.dpi) self.dpi.dataHasBeenSent() self.t = [] for i in range(0, 4): self.t.append(T_tab(i, self.dpi, self.com)) self.tabs.addTab(self.t[i], "Mode " + str(i + 1)) self.data = T_data(self.pbar, self.tabs, self.dpi, self.com) for i in range(0, 4): self.t[i].sendButton.clicked.connect(self.data.sendDpi) self.t[i].resetButton.clicked.connect(self.com.resetDpi) self.timer = QBasicTimer() self.timer.start(100, self.data) self.tabs.currentChanged.connect(self.com.sendMode)
def __init__(self, parent): QTabWidget.__init__(self, parent) self.addTab(APropos(parent), 'À propos') self.addTab(Licence(parent), 'Licence') self.addTab(Notes(parent), 'Notes de version') self.addTab(Credits(parent), 'Crédits') self.setTabPosition(QTabWidget.South) self.setStyleSheet(""" QTabBar::tab:selected { background: white; border: 1px solid #C4C4C3; border-top-color: white; /* same as the pane color */ border-bottom-left-radius: 4px; border-bottom-right-radius: 4px; border-top-left-radius: 0px; border-top-right-radius: 0px; min-width: 8ex; padding: 7px; } QStackedWidget {background:white} QTabBar QToolButton { background:white; border: 1px solid #C4C4C3; border-top-color: white; /* same as the pane color */ border-bottom-left-radius: 4px; border-bottom-right-radius: 4px; border-top-left-radius: 0px; border-top-right-radius: 0px; } """)
class TabWidgets(QTabWidget): def __init__(self, parent): super(TabWidgets, self).__init__(parent) self.wdg1 = QWidget(self) self.lay_wdg1 = QVBoxLayout(self.wdg1) self.wdg2 = QWidget(self) self.lay_wdg2 = QVBoxLayout(self.wdg2) #self.wdg1.setSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.MinimumExpanding) #self.wdg2.setSizePolicy(QSizePolicy.Maximum, QSizePolicy.Maximum) self.wdg1.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Preferred) self.wdg2.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Preferred) self._construct_UI() #------------------------------------------------------------------------------ def _construct_UI(self): """ Initialize UI with tabbed subplots """ self.tabWidget = QTabWidget(self) self.tabWidget.setSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.MinimumExpanding) self.tabWidget.addTab(self.wdg1, 'Wdg 1') self.tabWidget.addTab(self.wdg2, 'Wdg 2') layVMain = QVBoxLayout() layVMain.addWidget(self.tabWidget) self.setLayout(layVMain) # When user has switched the tab, call self.current_tab_redraw print('size wdg1: {0}'.format(self.wdg1.size())) print('size wdg2: {0}'.format(self.wdg2.size())) self.tabWidget.currentChanged.connect(self.current_tab_redraw) #------------------------------------------------------------------------------ def current_tab_redraw(self): pass
def initialize(self): tab_widget = QTabWidget() tab1 = QWidget() tab2 = QWidget() juggle_button = QPushButton("Juggle") juggle_button.clicked.connect(self.start_simulation) hbox = QHBoxLayout() hbox.addStretch(1) hbox.addWidget(juggle_button) vbox = QVBoxLayout(tab1) vbox.addStretch(1) vbox.addLayout(hbox) hbox2 = QHBoxLayout(tab2) tab_widget.addTab(tab1, "Main") tab_widget.addTab(tab2, "Other") self.setCentralWidget(tab_widget) menubar = self.menuBar() file_menu = menubar.addMenu('&File') help_menu = menubar.addMenu('&Help') about = help_menu.addMenu('&About') exit_action = QAction(QIcon('exit.png'), '&Exit', self) exit_action.triggered.connect(qApp.quit) file_menu.addAction(exit_action) self.setGeometry(300, 300, 350, 300) self.setWindowTitle('Juggling Simulator') self.show()
def __init__(self, title, text, image, contributors, parent=None): super(AboutDialog, self).__init__(parent) layout = QVBoxLayout() titleLayout = QHBoxLayout() name_versionLabel = QLabel(title) contentsLayout = QHBoxLayout() aboutBrowser = QTextBrowser() aboutBrowser.append(text) aboutBrowser.setOpenExternalLinks(True) creditsBrowser = QTextBrowser() creditsBrowser.append(contributors) creditsBrowser.setOpenExternalLinks(True) TabWidget = QTabWidget() TabWidget.addTab(aboutBrowser, self.tr('About')) TabWidget.addTab(creditsBrowser, self.tr('Contributors')) aboutBrowser.moveCursor(QTextCursor.Start) creditsBrowser.moveCursor(QTextCursor.Start) imageLabel = QLabel() imageLabel.setPixmap(QPixmap(image)) titleLayout.addWidget(imageLabel) titleLayout.addWidget(name_versionLabel) titleLayout.addStretch() contentsLayout.addWidget(TabWidget) buttonLayout = QHBoxLayout() buttonBox = QDialogButtonBox(QDialogButtonBox.Ok) buttonLayout.addWidget(buttonBox) layout.addLayout(titleLayout) layout.addLayout(contentsLayout) layout.addLayout(buttonLayout) self.setLayout(layout) buttonBox.clicked.connect(self.accept) self.setMinimumSize(QSize(380, 400)) self.setWindowTitle(self.tr('About Onkyo QT'))
def __init__(self,index): QTabWidget.__init__(self) lines=[] self.index=index if inp_load_file(lines,"fit"+str(self.index)+".inp")==True: self.tab_name=inp_search_token_value(lines, "#fit_name") else: self.tab_name="" self.setTabsClosable(True) self.setMovable(True) self.tmesh = fit_window_plot(self.index) self.addTab(self.tmesh,_("Fit error")) self.tmesh_real = fit_window_plot_real(self.index) self.addTab(self.tmesh_real,_("Experimental data")) self.fit_patch = fit_patch(self.index) self.addTab(self.fit_patch, _("Fit patch")) config=tab_class() config.init("fit"+str(self.index)+".inp",self.tab_name) self.addTab(config,_("Configure fit"))
class MyTabWidget(QWidget): def __init__(self): super().__init__() # Initialize tab screen self.tabs = QTabWidget() self.tabs.resize(300, 200) tab1 = QWidget() tab2 = QWidget() # Add tabs self.tabs.addTab(tab1, "Tab 1") self.tabs.addTab(tab2, "Tab 2") # Populate the first tab button1 = QPushButton("PyQt5 button") tab1_layout = QVBoxLayout() tab1_layout.addWidget(button1) tab1.setLayout(tab1_layout) # Set the layout layout = QVBoxLayout() layout.addWidget(self.tabs) self.setLayout(layout)
def __init__(self, parent): self.parent = parent self.feuille = parent.feuille QTabWidget.__init__(self, parent) self.description = ProprietesDescription(self) self.addTab(self.description, "Description") self.statistiques = ProprietesStatistiques(self) self.addTab(self.statistiques, "Statistiques")
class class_config_window(QWidget): def init(self): self.setFixedSize(900, 600) self.setWindowIcon(QIcon(os.path.join(get_image_file_path(),"cog.png"))) self.setWindowTitle(_("Configure (www.gpvdm.com)")) self.main_vbox = QVBoxLayout() toolbar=QToolBar() toolbar.setIconSize(QSize(48, 48)) spacer = QWidget() spacer.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) toolbar.addWidget(spacer) self.undo = QAction(QIcon(os.path.join(get_image_file_path(),"help.png")), 'Hide', self) self.undo.setStatusTip(_("Close")) self.undo.triggered.connect(self.callback_help) toolbar.addAction(self.undo) self.main_vbox.addWidget(toolbar) self.notebook = QTabWidget() self.notebook.setMovable(True) self.main_vbox.addWidget(self.notebook) files=["math.inp","dump.inp","thermal.inp","led.inp","config.inp"] description=["Math","Dump","Thermal","LED","GUI config"] for i in range(0,len(files)): tab=tab_class() tab.init(files[i],description[i]) self.notebook.addTab(tab,description[i]) self.setLayout(self.main_vbox) self.win_list=windows() self.win_list.load() self.win_list.set_window(self,"config_window") #self.connect("delete-event", self.callback_close_window) #self.hide() def callback_help(self,widget): webbrowser.open('http://www.gpvdm.com/man/index.html') def closeEvent(self, event): self.win_list.update(self,"config_window")
def __init__(self, mainwindow): QTabWidget.__init__(self) self.mainwindow = mainwindow self.activeTab = False self.tabCloseRequested.connect(self.delTab) self.currentChanged.connect(self.changeTab) # self.setTabsClosable(True) self.createTabBar()
def __init__(self, parent): self.parent = parent self.objets = parent.objets QTabWidget.__init__(self, parent) self.affichage = ProprietesAffichage(self) self.addTab(self.affichage, "Affichage") self.infos = ProprietesInfos(self) self.addTab(self.infos, "Informations") self.avance = ProprietesAvance(self) self.addTab(self.avance, "Avancé")
def __init__(self): super(Window, self).__init__() self.setWindowTitle("Ledger visualizer") # Create a layout Object, attached to the window. layout = QVBoxLayout(self) self.options = Options(self) layout.addWidget(self.options) tabs = QTabWidget() layout.addWidget(tabs) graph = GraphTab(self.options) tabs.addTab(graph, "Time series") graph = AccountTab(self.options) tabs.addTab(graph, "Account breakdown") graph = BarTab(self.options) tabs.addTab(graph, "Timed breakdown") text = PieTab(self.options) tabs.addTab(text, "Pie charts") button = QPushButton("Quit", self) layout.addWidget(button) button.clicked.connect(app.quit)
class AboutDialog(QDialog): def __init__(self, parent=None): super(AboutDialog, self).__init__(parent) self.create() title = QLabel() title.setText('Pulppy Software\nLinear Programming Software') title.setAlignment(Qt.AlignLeft) title.setStyleSheet("font-weight: bold") icon = QLabel() imagen = QPixmap('pulppy.ico') icon.setPixmap(imagen) topLayout = QHBoxLayout() topLayout.addWidget(icon) topLayout.addWidget(title) mainLayout = QVBoxLayout() mainLayout.addLayout(topLayout) mainLayout.addWidget(self.tabWidget) self.setLayout(mainLayout) self.setGeometry(500, 200, 350, 250) self.setWindowTitle('About') self.setWindowIcon(QIcon('pulppy.ico')) def create(self): self.tabWidget = QTabWidget() tab1 = QWidget() labelDescription = QLabel() labelDescription.setText("Linear Programming Software for optimizing\n" "various practical problems of Operations Research.\n" "\n" "Repository:\n" "https://github.com/bruino/pulppy\n") tab1hbox = QHBoxLayout() tab1hbox.addWidget(labelDescription) tab1.setLayout(tab1hbox) tab2 = QWidget() labelAutors = QLabel() labelAutors.setText("+ Danert, Maldonado Jessica\n" "+ Gutierrez, Mariano\n" "+ Moreno, Victor Ricardo\n" "+ Sarverry, Bruno Alejandro\n") tab2hbox = QHBoxLayout() tab2hbox.setContentsMargins(5, 5, 5, 5) tab2hbox.addWidget(labelAutors) tab2.setLayout(tab2hbox) self.tabWidget.addTab(tab1, "&About") self.tabWidget.addTab(tab2, "&Autors")
async def search_and_show_pubkey(cls, parent, app, pubkey): dialog = QDialog(parent) dialog.setWindowTitle("Informations") layout = QVBoxLayout(dialog) tabwidget = QTabWidget(dialog) layout.addWidget(tabwidget) identities = await app.identities_service.lookup(pubkey) for i in identities: user_info = cls.create(parent, app, i) user_info.refresh() tabwidget.addTab(user_info.view, i.uid) return await dialog_async_exec(dialog)
def __init__(self, parent=None): super(FenetreCentrale, self).__init__(parent) tabWidget = QTabWidget() tabWidget.addTab(ModaliteEvaluation(),"Modalite d'evaluation") tabWidget.addTab(GestionEleve(),"Gestion des eleves") #tabWidget.addTab(Essai(),"Lolilol") layout = QVBoxLayout() layout.addWidget(tabWidget) self.setLayout(layout)
def create_tab_widget(self): tab_widget = QTabWidget() tab_widget.setStyleSheet("QTabWidget::pane {border: 0;}") tab_widget.setTabPosition(QTabWidget.East) tab_widget.setMovable(True) tab_widget.setDocumentMode(True) tabBar = tab_widget.tabBar() tabBar.hide() tabBar.setContextMenuPolicy(Qt.CustomContextMenu) self.addWidget(tab_widget) index = self.indexOf(tab_widget) tabBar.customContextMenuRequested['const QPoint&'].connect( lambda point: self.show_tab_context_menu(index, point)) return tab_widget
class mainWindow(QWidget): def __init__(self): super().__init__() # Виджет для главного таб виджета # Содержит вкладки для дейликов и т.д. self.vbox = QVBoxLayout() self.dailyTab = dailyTab() self.tab = QTabWidget() self.tab.addTab(self.dailyTab,'Daily events') self.vbox.addWidget(self.tab) self.setLayout(self.vbox)
def show_about_referentials(self, referentials): dialog = QDialog(self) layout = QVBoxLayout(dialog) tabwidget = QTabWidget(dialog) layout.addWidget(tabwidget) for ref in referentials: widget = QWidget() layout = QVBoxLayout(widget) label = QLabel() label.setText(self.text_referential(ref)) layout.addWidget(label) tabwidget.addTab(widget, ref.translated_name()) dialog.setWindowTitle(self.tr("Referentials")) dialog.exec()
class MyTableWidget(QWidget): def __init__(self, parent): super(QWidget, self).__init__(parent) self.layout = QVBoxLayout(self) # Initialize tab screen self.tabs = QTabWidget() # self.tabs.tabBar().hide() self.tabs.setTabPosition(QTabWidget.West) self.tab1 = QWidget() self.tab2 = QWidget() self.tabs.resize(300,200) # Add tabs self.tabs.addTab(self.tab1, QIcon('./liveSEC/icones/app.jpg'), "Tab 1") self.tabs.addTab(self.tab2,"Tab 2") # Create first tab self.tab1.layout = QVBoxLayout(self) self.pushButton1 = QPushButton("PyQt5 button") self.tab1.layout.addWidget(self.pushButton1) self.tab1.setLayout(self.tab1.layout) # Add tabs to widget self.layout.addWidget(self.tabs) self.setLayout(self.layout) @pyqtSlot() def on_click(self): print("\n") for currentQTableWidgetItem in self.tableWidget.selectedItems(): print(currentQTableWidgetItem.row(), currentQTableWidgetItem.column(), currentQTableWidgetItem.text())
def __init__(self, parent=None): QMainWindow.__init__(self, parent) self.ui = Ui_ScriptEditor() self.ui.setupUi(self) #self.ui.actionExit.triggered.connect(self.exit) self.splitter = QSplitter(Qt.Vertical, self) self.setCentralWidget(self.splitter) self.edit_tab = QTabWidget(self.splitter) self.console_tab = QTabWidget(self.splitter) self.py_console = PythonConsole(self.console_tab) self.console_tab.addTab(self.py_console, "&Python console") self.js_console = QtQmlConsole(self.console_tab) self.console_tab.addTab(self.js_console, "&QtQml console") self.editors = [] self.on_actionNewPython_triggered()
def createBottomLeftTabWidget(self): self.bottomLeftTabWidget = QTabWidget() self.bottomLeftTabWidget.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Ignored) tab1 = QWidget() tableWidget = QTableWidget(10, 10) tab1hbox = QHBoxLayout() tab1hbox.setContentsMargins(5, 5, 5, 5) tab1hbox.addWidget(tableWidget) tab1.setLayout(tab1hbox) tab2 = QWidget() textEdit = QTextEdit() textEdit.setPlainText("Twinkle, twinkle, little star,\n" "How I wonder what you are.\n" "Up above the world so high,\n" "Like a diamond in the sky.\n" "Twinkle, twinkle, little star,\n" "How I wonder what you are!\n") tab2hbox = QHBoxLayout() tab2hbox.setContentsMargins(5, 5, 5, 5) tab2hbox.addWidget(textEdit) tab2.setLayout(tab2hbox) self.bottomLeftTabWidget.addTab(tab1, "&Table") self.bottomLeftTabWidget.addTab(tab2, "Text &Edit")
def _createElectronicsControl(self): """Creates a tabbed widget of voltage clamp and current clamp controls""" self._electronicsTab = QTabWidget(self) self._electronicsTab.addTab(self._getIClampCtrlBox(), 'Current clamp') self._electronicsTab.addTab(self._getVClampCtrlBox(), 'Voltage clamp') self._electronicsDock = QDockWidget(self) self._electronicsDock.setWidget(self._electronicsTab)
def __init__(self, parent, cli_iface, iface_name): super(ControlPanelWindow, self).__init__(parent) self.setWindowTitle('SLCAN Adapter Control Panel') self.setAttribute(Qt.WA_DeleteOnClose) # This is required to stop background timers! self._cli_iface = cli_iface self._iface_name = iface_name self._state_widget = StateWidget(self, self._cli_iface) self._config_widget = ConfigWidget(self, self._cli_iface) self._cli_widget = CLIWidget(self, self._cli_iface) self._tab_widget = QTabWidget(self) self._tab_widget.addTab(self._state_widget, get_icon('dashboard'), 'Adapter State') self._tab_widget.addTab(self._config_widget, get_icon('wrench'), 'Configuration') self._tab_widget.addTab(self._cli_widget, get_icon('terminal'), 'Command Line') self._status_bar = QStatusBar(self) self._status_bar.setSizeGripEnabled(False) iface_name_label = QLabel(iface_name.split('/')[-1], self) iface_name_label.setFont(get_monospace_font()) layout = QVBoxLayout(self) layout.addWidget(iface_name_label) layout.addWidget(self._tab_widget) layout.addWidget(self._status_bar) left, top, right, bottom = layout.getContentsMargins() bottom = 0 layout.setContentsMargins(left, top, right, bottom) self.setLayout(layout) self.resize(400, 400)
def __init__(self, parent): super(QWidget, self).__init__(parent) self.layout = QVBoxLayout(self) #self.setStyleSheet("background-color:grey") self.setAutoFillBackground(True) # Initialize tab screen self.tabs = QTabWidget() self.tab1 = QWidget() self.tab2 = QWidget() self.tabs.resize(300,200) # Add tabs self.tabs.addTab(self.tab1,"Main") self.tabs.addTab(self.tab2,"Config") # Create first tab self.tab1.layout = QGridLayout(self) # Create Load button self.loadButton = QPushButton("Load Gcode") self.loadButton.clicked.connect(self.loadGcode) self.tab1.layout.addWidget(self.loadButton,0,0) # Create Gcode File Dir textbox self.gcodeDirTextBox = QLabel("Load Gcode file") self.gcodeDirTextBox.setAlignment(Qt.Qt.AlignCenter) self.tab1.layout.addWidget(self.gcodeDirTextBox,0,1,1,2) # Create Connect button self.connectButton = QPushButton("Connect") self.tab1.layout.addWidget(self.connectButton,1,0) self.connectButton.clicked.connect(self.connect) # Create Print button self.Print = QPushButton("Print") self.tab1.layout.addWidget(self.Print,1,1) self.Print.clicked.connect(self.machinePrint) #create pause button self.pauseButton = QPushButton("Pause") self.tab1.layout.addWidget(self.pauseButton,1,2) self.pauseButton.clicked.connect(self.pause) #create machine group box hBox = QGridLayout() groupBox = QGroupBox() #create input command for both machine #both input group box generalInputGroupBox = QGroupBox("Send To Both") generalInputGroupBoxLayout = QGridLayout() #command input box self.generalInputCommandBox = QLineEdit("SEND TO BOTH") #Send button generalInputSendButton = QPushButton("SEND") generalInputSendButton.clicked.connect(self.sendToALL) generalInputGroupBoxLayout.addWidget(self.generalInputCommandBox,0,0,1,2) generalInputGroupBoxLayout.addWidget(generalInputSendButton,0,2) generalInputGroupBox.setLayout(generalInputGroupBoxLayout) #create machine one group groupBox1 = QGroupBox("Machine One") hBox1 = QGridLayout() #create machine one port8 self.portOneButton = QLineEdit("COM7") self.portOne = "COM7" self.portOneButton.textChanged.connect(self.updatePortName) #create machine one baudrate baudrateOneButton = QLineEdit("115200") self.baudrateOne = 115200 #create machine one send comman box self.oneInputCommandBox = QLineEdit("SEND TO ONE") oneInputSendButton = QPushButton("SEND") oneInputSendButton.clicked.connect(self.sendToOne) hBox1.addWidget(self.portOneButton,0,0) hBox1.addWidget(baudrateOneButton,0,1) hBox1.addWidget(self.oneInputCommandBox,1,0,1,2) hBox1.addWidget(oneInputSendButton,1,3) groupBox1.setLayout(hBox1) #create machine two group groupBox2 = QGroupBox("Machine Two") hBox2 = QGridLayout() #create machine two port self.portTwoButton = QLineEdit("COM8") self.portTwo = "COM8" self.portTwoButton.textChanged.connect(self.updatePortName) #create machine two baudrate baudrateTwoButton = QLineEdit("115200") self.baudrateTwo = 115200 #create machine one send comman box self.twoInputCommandBox = QLineEdit("SEND TO TWO") twoInputSendButton = QPushButton("SEND") twoInputSendButton.clicked.connect(self.sendToTwo) hBox2.addWidget(self.portTwoButton,0,0) hBox2.addWidget(baudrateTwoButton,0,1) hBox2.addWidget(self.twoInputCommandBox,1,0,1,2) hBox2.addWidget(twoInputSendButton,1,3) groupBox2.setLayout(hBox2) hBox.addWidget(generalInputGroupBox,0,0,1,2) hBox.addWidget(groupBox1,1,0) hBox.addWidget(groupBox2,1,1) groupBox.setLayout(hBox) self.tab1.layout.addWidget(groupBox,2,0,1,3) ################# self.tab1.setLayout(self.tab1.layout) # Add tabs to widget self.layout.addWidget(self.tabs) self.setLayout(self.layout)
def __init__(self): super(QWidget, self).__init__() globalFont = QFont("Times New Roman", 11) globalFont.setWeight(18) QApplication.setFont(globalFont) # Creating tabs and layouts self.layout = QVBoxLayout(self) self.Hlayout = QHBoxLayout(self) self.HDBlayout = QVBoxLayout(self) self.tabs = QTabWidget() self.MainTab = QWidget() self.DBTab = QWidget() self.tabs.addTab(self.MainTab, "Translate") self.tabs.addTab(self.DBTab, "Saved Words") self.MainTab.layout = QVBoxLayout(self) self.MainTab.layout.addLayout(self.Hlayout) self.DBTab.layout = QGridLayout() # Defining QLabels self.insertionBox = QLineEdit() self.romajiBox = QLabel() self.romajiBox.setTextInteractionFlags(Qt.TextSelectableByMouse) self.katakanaBox = QLabel() self.katakanaBox.setTextInteractionFlags(Qt.TextSelectableByMouse) self.hiraganaBox = QLabel() self.hiraganaBox.setTextInteractionFlags(Qt.TextSelectableByMouse) self.furiganaBox = QLabel() self.furiganaBox.setTextInteractionFlags(Qt.TextSelectableByMouse) self.entriesBox = QLabel() self.entriesBox.setTextInteractionFlags(Qt.TextSelectableByMouse) self.entriesBox.setWordWrap(True) self.charsBox = QLabel() self.charsBox.setTextInteractionFlags(Qt.TextSelectableByMouse) self.charsBox.setWordWrap(True) recordFont = QFont("Times New Roman", 15) recordFont.setWeight(18) self.romajiLabel = QLabel("Romaji") self.romajiLabel.setFont(recordFont) self.katakanaLabel = QLabel("Katakana") self.katakanaLabel.setFont(recordFont) self.hiraganaLabel = QLabel("Hiragana") self.hiraganaLabel.setFont(recordFont) self.furiganaLabel = QLabel("Furigana") self.furiganaLabel.setFont(recordFont) self.entriesLabel = QLabel("Entries") self.entriesLabel.setFont(recordFont) self.charsLabel = QLabel("Characters") self.charsLabel.setFont(recordFont) self.romajiLabelDB = QLabel("Romaji") self.romajiLabelDB.setFont(recordFont) self.katakanaLabelDB = QLabel("Katakana") self.katakanaLabelDB.setFont(recordFont) self.hiraganaLabelDB = QLabel("Hiragana") self.hiraganaLabelDB.setFont(recordFont) self.furiganaLabelDB = QLabel("Furigana") self.furiganaLabelDB.setFont(recordFont) self.entriesLabelDB = QLabel("Entries") self.entriesLabelDB.setFont(recordFont) self.charsLabelDB = QLabel("Characters") self.charsLabelDB.setFont(recordFont) self.romajiBoxDB = QLabel() self.romajiBoxDB.setTextInteractionFlags(Qt.TextSelectableByMouse) self.katakanaBoxDB = QLabel() self.katakanaBoxDB.setTextInteractionFlags(Qt.TextSelectableByMouse) self.hiraganaBoxDB = QLabel() self.hiraganaBoxDB.setTextInteractionFlags(Qt.TextSelectableByMouse) self.furiganaBoxDB = QLabel() self.furiganaBoxDB.setTextInteractionFlags(Qt.TextSelectableByMouse) self.entriesBoxDB = QLabel() self.entriesBoxDB.setTextInteractionFlags(Qt.TextSelectableByMouse) self.entriesBoxDB.setWordWrap(True) self.charsBoxDB = QLabel() self.charsBoxDB.setTextInteractionFlags(Qt.TextSelectableByMouse) self.charsBoxDB.setWordWrap(True) # Globally accessible text from insertionBox in MainTab global text text = self.insertionBox.text # For switching tables in DBTab self.tableDropDown = QComboBox() self.tableDropDown.setDuplicatesEnabled(False) for item in getTables(self.con): self.tableDropDown.addItem(repr(item).strip("('',)")) self.DBTab.layout.addWidget(self.tableDropDown) # List of saved words in selected table in DBTab self.listWords = QListWidget() self.listWords.setFixedWidth(145) self.listWords.itemSelectionChanged.connect(self.selectionView) self.DBTab.layout.addWidget(self.listWords) self.listWordsAdd() self.tableDropDown.currentTextChanged.connect( lambda: self.listWordsAdd()) # Adding widgets to tab layouts self.MainTab.layout.addWidget(self.insertionBox) self.MainTab.layout.addWidget(self.romajiLabel) self.MainTab.layout.addWidget(self.romajiBox) self.MainTab.layout.addWidget(self.katakanaLabel) self.MainTab.layout.addWidget(self.katakanaBox) self.MainTab.layout.addWidget(self.hiraganaLabel) self.MainTab.layout.addWidget(self.hiraganaBox) self.MainTab.layout.addWidget(self.furiganaLabel) self.MainTab.layout.addWidget(self.furiganaBox) self.MainTab.layout.addWidget(self.entriesLabel) self.MainTab.layout.addWidget(self.entriesBox) self.MainTab.layout.addWidget(self.charsLabel) self.MainTab.layout.addWidget(self.charsBox) self.DBTab.layout.addWidget(self.entriesLabelDB, 0, 1) self.DBTab.layout.addWidget(self.entriesBoxDB, 1, 1) self.DBTab.layout.addWidget(self.katakanaLabelDB, 2, 1) self.DBTab.layout.addWidget(self.katakanaBoxDB, 3, 1) self.DBTab.layout.addWidget(self.hiraganaLabelDB, 4, 1) self.DBTab.layout.addWidget(self.hiraganaBoxDB, 5, 1) self.DBTab.layout.addWidget(self.furiganaLabelDB, 6, 1) self.DBTab.layout.addWidget(self.furiganaBoxDB, 7, 1) self.DBTab.layout.addWidget(self.romajiLabelDB, 8, 1) self.DBTab.layout.addWidget(self.romajiBoxDB, 9, 1) self.DBTab.layout.addWidget(self.charsLabelDB, 10, 1) self.DBTab.layout.addWidget(self.charsBoxDB, 11, 1) # Creating buttons, connecting them to functions newTableButton = QPushButton("New List", self) newTableButton.setFixedWidth(145) self.DBTab.layout.addWidget(newTableButton) newTableButton.clicked.connect(self.newTableButtonClicked) deleteButton = QPushButton("Delete", self) deleteButton.setFixedWidth(145) self.DBTab.layout.addWidget(deleteButton) deleteButton.clicked.connect(self.deleteItemClicked) deleteTableButton = QPushButton("Delete List", self) deleteTableButton.setFixedWidth(145) self.DBTab.layout.addWidget(deleteTableButton) deleteTableButton.clicked.connect(self.deleteTableClicked) exportButton = QPushButton("Export to CSV", self) exportButton.setFixedWidth(145) self.DBTab.layout.addWidget(exportButton) exportButton.clicked.connect(self.clickExportCSV) translationButton = QPushButton("Translate", self) translationButton.setFixedWidth(145) saveButton = QPushButton("Save Word", self) saveButton.setFixedWidth(145) self.Hlayout.addWidget(translationButton) self.Hlayout.addWidget(saveButton) translationButton.clicked.connect(self.translationButtonClicked) saveButton.clicked.connect(self.saveButtonClicked) # Window setup self.widget = QWidget() self.widget.setWindowTitle("Japanese Lookup Tool") self.MainTab.setLayout(self.MainTab.layout) self.DBTab.setLayout(self.DBTab.layout) self.layout.addWidget(self.tabs) self.widget.setLayout(self.layout) self.widget.setMinimumSize(400, 700) self.widget.show()
class Window(QMainWindow): sys.excepthook = new_excepthook con = connectDB() Rtext, Ktext, Htext, Ftext = [None for _ in range(4)] def __init__(self): super(QWidget, self).__init__() globalFont = QFont("Times New Roman", 11) globalFont.setWeight(18) QApplication.setFont(globalFont) # Creating tabs and layouts self.layout = QVBoxLayout(self) self.Hlayout = QHBoxLayout(self) self.HDBlayout = QVBoxLayout(self) self.tabs = QTabWidget() self.MainTab = QWidget() self.DBTab = QWidget() self.tabs.addTab(self.MainTab, "Translate") self.tabs.addTab(self.DBTab, "Saved Words") self.MainTab.layout = QVBoxLayout(self) self.MainTab.layout.addLayout(self.Hlayout) self.DBTab.layout = QGridLayout() # Defining QLabels self.insertionBox = QLineEdit() self.romajiBox = QLabel() self.romajiBox.setTextInteractionFlags(Qt.TextSelectableByMouse) self.katakanaBox = QLabel() self.katakanaBox.setTextInteractionFlags(Qt.TextSelectableByMouse) self.hiraganaBox = QLabel() self.hiraganaBox.setTextInteractionFlags(Qt.TextSelectableByMouse) self.furiganaBox = QLabel() self.furiganaBox.setTextInteractionFlags(Qt.TextSelectableByMouse) self.entriesBox = QLabel() self.entriesBox.setTextInteractionFlags(Qt.TextSelectableByMouse) self.entriesBox.setWordWrap(True) self.charsBox = QLabel() self.charsBox.setTextInteractionFlags(Qt.TextSelectableByMouse) self.charsBox.setWordWrap(True) recordFont = QFont("Times New Roman", 15) recordFont.setWeight(18) self.romajiLabel = QLabel("Romaji") self.romajiLabel.setFont(recordFont) self.katakanaLabel = QLabel("Katakana") self.katakanaLabel.setFont(recordFont) self.hiraganaLabel = QLabel("Hiragana") self.hiraganaLabel.setFont(recordFont) self.furiganaLabel = QLabel("Furigana") self.furiganaLabel.setFont(recordFont) self.entriesLabel = QLabel("Entries") self.entriesLabel.setFont(recordFont) self.charsLabel = QLabel("Characters") self.charsLabel.setFont(recordFont) self.romajiLabelDB = QLabel("Romaji") self.romajiLabelDB.setFont(recordFont) self.katakanaLabelDB = QLabel("Katakana") self.katakanaLabelDB.setFont(recordFont) self.hiraganaLabelDB = QLabel("Hiragana") self.hiraganaLabelDB.setFont(recordFont) self.furiganaLabelDB = QLabel("Furigana") self.furiganaLabelDB.setFont(recordFont) self.entriesLabelDB = QLabel("Entries") self.entriesLabelDB.setFont(recordFont) self.charsLabelDB = QLabel("Characters") self.charsLabelDB.setFont(recordFont) self.romajiBoxDB = QLabel() self.romajiBoxDB.setTextInteractionFlags(Qt.TextSelectableByMouse) self.katakanaBoxDB = QLabel() self.katakanaBoxDB.setTextInteractionFlags(Qt.TextSelectableByMouse) self.hiraganaBoxDB = QLabel() self.hiraganaBoxDB.setTextInteractionFlags(Qt.TextSelectableByMouse) self.furiganaBoxDB = QLabel() self.furiganaBoxDB.setTextInteractionFlags(Qt.TextSelectableByMouse) self.entriesBoxDB = QLabel() self.entriesBoxDB.setTextInteractionFlags(Qt.TextSelectableByMouse) self.entriesBoxDB.setWordWrap(True) self.charsBoxDB = QLabel() self.charsBoxDB.setTextInteractionFlags(Qt.TextSelectableByMouse) self.charsBoxDB.setWordWrap(True) # Globally accessible text from insertionBox in MainTab global text text = self.insertionBox.text # For switching tables in DBTab self.tableDropDown = QComboBox() self.tableDropDown.setDuplicatesEnabled(False) for item in getTables(self.con): self.tableDropDown.addItem(repr(item).strip("('',)")) self.DBTab.layout.addWidget(self.tableDropDown) # List of saved words in selected table in DBTab self.listWords = QListWidget() self.listWords.setFixedWidth(145) self.listWords.itemSelectionChanged.connect(self.selectionView) self.DBTab.layout.addWidget(self.listWords) self.listWordsAdd() self.tableDropDown.currentTextChanged.connect( lambda: self.listWordsAdd()) # Adding widgets to tab layouts self.MainTab.layout.addWidget(self.insertionBox) self.MainTab.layout.addWidget(self.romajiLabel) self.MainTab.layout.addWidget(self.romajiBox) self.MainTab.layout.addWidget(self.katakanaLabel) self.MainTab.layout.addWidget(self.katakanaBox) self.MainTab.layout.addWidget(self.hiraganaLabel) self.MainTab.layout.addWidget(self.hiraganaBox) self.MainTab.layout.addWidget(self.furiganaLabel) self.MainTab.layout.addWidget(self.furiganaBox) self.MainTab.layout.addWidget(self.entriesLabel) self.MainTab.layout.addWidget(self.entriesBox) self.MainTab.layout.addWidget(self.charsLabel) self.MainTab.layout.addWidget(self.charsBox) self.DBTab.layout.addWidget(self.entriesLabelDB, 0, 1) self.DBTab.layout.addWidget(self.entriesBoxDB, 1, 1) self.DBTab.layout.addWidget(self.katakanaLabelDB, 2, 1) self.DBTab.layout.addWidget(self.katakanaBoxDB, 3, 1) self.DBTab.layout.addWidget(self.hiraganaLabelDB, 4, 1) self.DBTab.layout.addWidget(self.hiraganaBoxDB, 5, 1) self.DBTab.layout.addWidget(self.furiganaLabelDB, 6, 1) self.DBTab.layout.addWidget(self.furiganaBoxDB, 7, 1) self.DBTab.layout.addWidget(self.romajiLabelDB, 8, 1) self.DBTab.layout.addWidget(self.romajiBoxDB, 9, 1) self.DBTab.layout.addWidget(self.charsLabelDB, 10, 1) self.DBTab.layout.addWidget(self.charsBoxDB, 11, 1) # Creating buttons, connecting them to functions newTableButton = QPushButton("New List", self) newTableButton.setFixedWidth(145) self.DBTab.layout.addWidget(newTableButton) newTableButton.clicked.connect(self.newTableButtonClicked) deleteButton = QPushButton("Delete", self) deleteButton.setFixedWidth(145) self.DBTab.layout.addWidget(deleteButton) deleteButton.clicked.connect(self.deleteItemClicked) deleteTableButton = QPushButton("Delete List", self) deleteTableButton.setFixedWidth(145) self.DBTab.layout.addWidget(deleteTableButton) deleteTableButton.clicked.connect(self.deleteTableClicked) exportButton = QPushButton("Export to CSV", self) exportButton.setFixedWidth(145) self.DBTab.layout.addWidget(exportButton) exportButton.clicked.connect(self.clickExportCSV) translationButton = QPushButton("Translate", self) translationButton.setFixedWidth(145) saveButton = QPushButton("Save Word", self) saveButton.setFixedWidth(145) self.Hlayout.addWidget(translationButton) self.Hlayout.addWidget(saveButton) translationButton.clicked.connect(self.translationButtonClicked) saveButton.clicked.connect(self.saveButtonClicked) # Window setup self.widget = QWidget() self.widget.setWindowTitle("Japanese Lookup Tool") self.MainTab.setLayout(self.MainTab.layout) self.DBTab.setLayout(self.DBTab.layout) self.layout.addWidget(self.tabs) self.widget.setLayout(self.layout) self.widget.setMinimumSize(400, 700) self.widget.show() def translationButtonClicked(self, text): if self.insertionBox.text() == "": msg = QMessageBox() msg.setWindowTitle("Error") msg.setText("There is no word to translate!") msg.exec_() else: jmd = Jamdict() text = self.insertionBox.text() Window.Rtext = toRomaji(text) Window.Ktext = toKatakana(text) Window.Htext = toHiragana(text) result = jmd.lookup(text) text = toTokensDictionary(text) separater = "" Window.Ftext = toFurigana(text) Window.Ftext = separater.join(Window.Ftext) if result == None: result = jmd.lookup(Window.Ktext) if result == None: result = jmd.lookup(Window.Htext) Window.Etext = repr(result.entries).strip("[]") Window.Ctext = repr(result.chars).strip("[]") self.romajiBox.setText(Window.Rtext) self.katakanaBox.setText(Window.Ktext) self.hiraganaBox.setText(Window.Htext) self.furiganaBox.setText(Window.Ftext) self.entriesBox.setText(Window.Etext) self.charsBox.setText(Window.Ctext) return Window.Rtext, Window.Ktext, Window.Htext, Window.Ftext def saveButtonClicked(self): if len(getTables(self.con)) == 0: msg = QMessageBox() msg.setText("No lists to save to!") msg.exec_() return if Window.Rtext: tables = getTables(self.con) tables = [t[0] for t in tables] table, ok = QInputDialog.getItem(self, "List option", "Choose list to save to:", tables, 0, False) if ok and table: msg = QMessageBox() insertWord(self.con, table, self.Rtext, self.Ktext, self.Htext, self.Ftext, self.Etext, self.Ctext) msg.setText("Word saved to " + table) msg.exec_() if self.currentTable().strip("''") == table: self.listWords.addItem(repr(self.Rtext).strip("''")) else: msg = QMessageBox() msg.setText("There is no translated word to save!") msg.exec_() def newTableButtonClicked(self): nameNewTable, ok = QInputDialog.getText(self, 'New list', 'Enter new list name:') if self.tableDropDown.findText(nameNewTable) == -1: self.tableDropDown.addItem(nameNewTable) newTable(self.con, nameNewTable) else: msg = QMessageBox() msg.setWindowTitle("Error") msg.setText("Error creating list") msg.exec_() def deleteItemClicked(self): if self.listWords.selectedItems() != 0: for item in self.listWords.selectedItems(): deleteWord(self.con, self.listWords.currentItem().text(), self.currentTable()) self.listWords.takeItem(self.listWords.row(item)) else: msg = QMessageBox() msg.setWindowTitle("Error") msg.setText("There is no word selected to delete!") msg.exec_() def deleteTableClicked(self): table = self.currentTable() confirm = QMessageBox.question( self, "Confirmation", "Are you sure you want to delete " + table + "?", QMessageBox.Yes, QMessageBox.Cancel) if confirm == QMessageBox.Yes: index = self.tableDropDown.findText(table.strip("''")) self.tableDropDown.removeItem(index) dropTable(self.con, table) def updateDBTabList(self, input): if type(input) is list: self.romajiBoxDB.setText(input[0]) self.katakanaBoxDB.setText(input[1]) self.hiraganaBoxDB.setText(input[2]) self.furiganaBoxDB.setText(input[3]) self.entriesBoxDB.setText(input[4]) self.charsBoxDB.setText(input[5]) else: self.romajiBoxDB.setText(input) self.katakanaBoxDB.setText(input) self.hiraganaBoxDB.setText(input) self.furiganaBoxDB.setText(input) self.entriesBoxDB.setText(input) self.charsBoxDB.setText(input) def selectionView(self): if self.listWords.selectedItems() != 0 and self.listWords.currentItem( ) is not None and self.currentTable() is not None: list = selectWord(self.con, self.listWords.currentItem().text(), self.currentTable()) if list: self.updateDBTabList(list) def currentTable(self): if self.tableDropDown.currentText() is not None: return repr(self.tableDropDown.currentText()) else: msg = QMessageBox() msg.setWindowTitle("Error") msg.setText("Create a list to be able to save words") msg.exec_() return None def listWordsAdd(self): self.updateDBTabList("") if len(getTables(self.con)) != 0 and self.currentTable() is not None: wordList = listTable(self.con, self.currentTable()) self.listWords.clear() for word in wordList: self.listWords.addItem(repr(word).strip("'()',")) def clickExportCSV(self): exportCSV(self.con, self.currentTable()) msg = QMessageBox() msg.setWindowTitle("Export successful") msg.setText("Exported as " + self.currentTable().strip("''") + ".csv") msg.exec_()
def __init__(self, statusBar): super().__init__() # store active image ID self.active_image_id = str() # Saves multiple duplicate windows references: self.duplicateRefs = {} # Gets status bar from QMainWindow: self.statusBar = statusBar # Initializes all window elements: self.folderField = QLineEdit() self.folderButton = QPushButton() self.folderTreeCheckbox = QCheckBox("Include sub-folders") self.processButton = QPushButton("Process media files") self.duplicateButton = QPushButton("Find duplicates") self.reindexButton = QPushButton("Reindex database files") self.tableTabs = QTabWidget() # init main images list table self.imageListTable = QTableWidget() # set main images list table fields unchanged self.imageListTable.setEditTriggers(QTableWidget.NoEditTriggers) # init main videos list table self.videoListTable = QTableWidget() # set main videos list table fields unchanged self.videoListTable.setEditTriggers(QTableWidget.NoEditTriggers) # set up image fields and elements self.imageField = QLabel() self.imageNameField = QLabel() self.imageParamsField = QLabel() self.imageCopyButton = QPushButton("Copy image") self.imageCopyButton.setIcon(QIcon("gui/static/icon_copy.png")) self.imageCopyButton.clicked.connect(self.copy_image_path) self.imageViewButton = QPushButton("View image") self.imageViewButton.setIcon(QIcon("gui/static/icon_open_image.svg")) self.imageViewButton.clicked.connect(self.open_image_file) self.imageOpenDirButton = QPushButton("Open dir") self.imageOpenDirButton.setIcon( QIcon("gui/static/icon_open_folder.svg")) self.imageOpenDirButton.clicked.connect(self.open_image_path) self.imageDeleteButton = QPushButton("Delete") self.imageDeleteButton.setIcon( QIcon("gui/static/icon_delete_file.png")) self.imageDeleteButton.clicked.connect(self.delete_image) self.videoField = QVideoWidget() self.videoPlayer = QMediaPlayer() # Adjusts settings for the window elements: self.folderField.setDisabled(True) self.folderButton.setIcon(QIcon("gui/static/icon_process_folder.png")) self.folderButton.clicked.connect(self.set_folder) self.processButton.clicked.connect(self.process_files) self.processButton.setFixedWidth(160) self.processButton.setDisabled(True) self.duplicateButton.clicked.connect(self.find_duplicates) self.duplicateButton.setFixedWidth(160) self.reindexButton.clicked.connect(self.reindex_db_data) self.reindexButton.setFixedWidth(160) # prepare tables for images and videos self.imagesTab = self.tableTabs.insertTab(0, self.imageListTable, "Images") self.videosTab = self.tableTabs.insertTab(1, self.videoListTable, "Videos") # images list table setup self.imageListTable.setColumnCount(len(self.COLUMNS_DICT.keys())) self.imageListTable.setHorizontalHeaderLabels(self.COLUMNS_DICT.keys()) self.imageListTable.verticalHeader().setVisible(False) self.imageListTable.setHorizontalScrollBarPolicy(Qt.ScrollBarAsNeeded) self.imageListTable.setSortingEnabled(True) self.imageListTable.cellClicked.connect(self.show_image) # videos list table setup self.videoListTable.setColumnCount(len(self.COLUMNS_DICT.keys())) self.videoListTable.setHorizontalHeaderLabels(self.COLUMNS_DICT.keys()) self.videoListTable.verticalHeader().setVisible(False) self.videoListTable.setHorizontalScrollBarPolicy(Qt.ScrollBarAsNeeded) self.videoListTable.setSortingEnabled(True) self.videoListTable.cellClicked.connect(self.show_video) # set images and videos duplicates columns width self.set_columns_width() # Places the window elements on the window: # Top-left cell of main grid box: subGridBox = QWidget() subGrid = QGridLayout() subGrid.addWidget(self.folderField, 0, 0) subGrid.addWidget(self.folderButton, 0, 1) subGrid.addWidget(self.folderTreeCheckbox, 1, 0) subGrid.addWidget(self.processButton, 2, 0, 1, 2, Qt.AlignCenter) subGrid.addWidget(self.duplicateButton, 3, 0, 1, 2, Qt.AlignCenter) subGrid.addWidget(self.reindexButton, 4, 0, 1, 2, Qt.AlignCenter) subGridBox.setLayout(subGrid) # image data grid box imageGridBox = QWidget() imageGrid = QGridLayout() imageGrid.addWidget(self.imageField, 0, 0, 1, 4, Qt.AlignCenter) # add image buttons imageGrid.addWidget(self.imageCopyButton, 1, 0, 1, 1) imageGrid.addWidget(self.imageViewButton, 1, 1, 1, 1) imageGrid.addWidget(self.imageOpenDirButton, 1, 2, 1, 1) imageGrid.addWidget(self.imageDeleteButton, 1, 3, 1, 1) imageGrid.addWidget(self.imageNameField, 2, 0, 1, 1) imageGrid.addWidget(self.imageParamsField, 2, 3, 1, 1) imageGridBox.setLayout(imageGrid) # Main grid box: self.mainGrid = QGridLayout() self.mainGrid.addWidget(subGridBox, 0, 0) self.mainGrid.addWidget(self.tableTabs, 1, 0) self.mainGrid.addWidget(imageGridBox, 1, 1) self.mainGrid.addWidget(self.videoField, 0, 1, 2, 1) self.mainGrid.setColumnMinimumWidth(0, 400) self.mainGrid.setRowMinimumHeight(1, 600) self.mainGrid.setColumnStretch(1, 1) self.setLayout(self.mainGrid) # Creates a QThread instance: self.thread = ProcessingThread(self.folderField, self.imageListTable, self.videoListTable, self.folderTreeCheckbox) self.thread.finishedTrigger.connect(self.finish_thread) # filling table while first run self.reindex_db_data() # hide image interface self.hide_active_image()
class App(QMainWindow): def __init__(self): super().__init__() self.player = QMediaPlayer() self.playlist = QMediaPlaylist() self.playlist.setPlaybackMode(QMediaPlaylist.Sequential) self.title = 'JPR Reader GUI version2' self.left = 50 self.top = 100 self.width = 1200 self.height = 800 self.fileReady = False self.tableRow = 5 self.tableCol = 15 self.row = 2 self.audiolist = [] self.configFile = ".//configurationFile.xlsx" self.dict = { 'num': None, 'partner': None, 'parcel': None, 'exception': None, 'box': None } self.initUI() def initUI(self): self.setWindowTitle(self.title) self.setGeometry(self.left, self.top, self.width, self.height) # menu menubar = self.menuBar() filemenu = menubar.addMenu('File') fileAct = QAction('Open File', self) fileAct.setShortcut('Ctrl+O') filemenu.addAction(fileAct) fileAct.triggered.connect(self.openFileNameDialog) # status_bar self.statusbar = self.statusBar() # tabs self.tabs = QTabWidget() self.tab1 = QWidget(self) self.tab2 = QWidget(self) self.tabs.addTab(self.tab1, "조작") self.tabs.addTab(self.tab2, "설정") self.setCentralWidget(self.tabs) # tab1 gui self.ButtonGroupBox = self.createButtonGroupBox() self.createLogTable() self.tab1.layout = QVBoxLayout() self.tab1.layout.addWidget(self.ButtonGroupBox) self.tab1.layout.addWidget(self.logTable) self.tab1.setLayout(self.tab1.layout) # tab2 gui self.explanation = QLabel() self.explanation.setText("""<<< JPR reader 설정 테이블 >>> 이곳에서 JPR reader가 읽어주는 항목과 아이템을 설정할 수 있습니다. 항목과 아이템의 설정 방법은 셀을 더블 클릭한 후 이름을 입력하는 방식으로진행됩니다. 그 후 떠오른 파일 탐색기에서 지정할 mp3파일을 선택해 주세요. 설정의 확인은 셀의 색으로 확인 가능합니다. 지정이 완료된 항목은 노란색, 아이템은 하늘색으로 표시됩니다. 지정이 되지 않은 셀은 빨간색으로 표시됩니다. 행과 열 버튼으로 테이블의 크기를 조절할 수 있으며 초기화시 모든 설정은 사라지며 테이블은 5by5로 초기화 됩니다. <<<주의사항>>> 1. 첫번째 열은 반드시 항목을 입력해 주세요. 2. 입력되는 이름은 엑셀파일에서 표현된 명칭과 완벽히 일치해야 합니다.(엔터나 스페이스, 오타 주의!) 3. 초기화를 누르면 처음부터 모든 항목과 아이템을 지정해야 합니다. 4. 엑셀 파일의 ()나 /을 통해 구분한 아이템은 따로 입력해 주세요. 5. 사용하는 엑셀파일의 구조를 유지해 주세요. 변경시 프로그램의 수정이 필요할 수 있습니다.(예: '박스'항목은 항상 있을 것으로 간주됩니다.) 제작자 정보: 김현우([email protected])""") self.explanation.setAlignment(Qt.AlignCenter) self.createConfigTable() self.createHorizontalButtons2() self.tab2.layout = QVBoxLayout() self.tab2.layout.addWidget(self.explanation) self.tab2.layout.addWidget(self.configTable) self.tab2.layout.addWidget(self.horizontalButtons2) self.tab2.setLayout(self.tab2.layout) # Show widget self.show() def openFileNameDialog(self): fileName, _ = QFileDialog.getOpenFileName(self, "Open file", "", "All Files (*)") if not fileName: self.statusbar.showMessage('Fail to load file...') else: self.wb = load_workbook(fileName.strip()) self.ws = self.wb.active self.fileReady = True self.row = 2 self.statusbar.showMessage('succeed to load file...') self.logTable.clear() # set logTable's horizontal header self.logTable.setHorizontalHeaderItem(0, QTableWidgetItem("포장순번")) self.logTable.setHorizontalHeaderItem(1, QTableWidgetItem("거래처명")) self.logTable.setHorizontalHeaderItem(2, QTableWidgetItem("배송센터")) self.logTable.setHorizontalHeaderItem(3, QTableWidgetItem("특이사항")) self.logTable.setHorizontalHeaderItem(4, QTableWidgetItem("박스")) # initialize dictionary self.dict = { 'num': None, 'partner': None, 'parcel': None, 'exception': None, 'box': None } col = 6 num = 0 header = str(self.ws.cell(row=1, column=col + 1).value).strip() while header != 'None': self.dict[header] = None col = col + 1 num = num + 1 header = str(self.ws.cell(row=1, column=col + 1).value).strip() self.tableCol = 5 + num self.logTable.setColumnCount(self.tableCol) for c in range(5, self.tableCol): self.logTable.setHorizontalHeaderItem( c, QTableWidgetItem(list(self.dict.keys())[c])) def createButtonGroupBox(self): buttonGroupBox = QGroupBox("Controller") vLayout = QVBoxLayout() pre_button = QPushButton('w', self) pre_button.clicked.connect(self.pre_click) pre_button.setIcon(QIcon('.\\img\\up-arrow.png')) pre_button.setIconSize(QSize(600, 100)) pre_button.setShortcut('w') vLayout.addWidget(pre_button) hBottensWidget = self.createHButtons() vLayout.addWidget(hBottensWidget) next_button = QPushButton('x', self) next_button.clicked.connect(self.next_click) next_button.setIcon(QIcon('.\\img\\down-arrow.png')) next_button.setIconSize(QSize(600, 100)) next_button.setShortcut('x') vLayout.addWidget(next_button) buttonGroupBox.setLayout(vLayout) return buttonGroupBox def createHButtons(self): hBottensWidget = QWidget() hLayout = QHBoxLayout() back_button = QPushButton('a', self) back_button.clicked.connect(self.back_click) back_button.setIcon(QIcon('.\\img\\left-arrow.png')) back_button.setIconSize(QSize(200, 150)) back_button.setShortcut('a') hLayout.addWidget(back_button) cur_button = QPushButton('s', self) cur_button.clicked.connect(self.cur_click) cur_button.setIcon(QIcon('.\\img\\reload.png')) cur_button.setIconSize(QSize(200, 150)) cur_button.setShortcut('s') hLayout.addWidget(cur_button) forward_button = QPushButton('d', self) forward_button.clicked.connect(self.forward_click) forward_button.setIcon(QIcon('.\\img\\right-arrow.png')) forward_button.setIconSize(QSize(200, 150)) forward_button.setShortcut('d') hLayout.addWidget(forward_button) hBottensWidget.setLayout(hLayout) return hBottensWidget def createHorizontalButtons2(self): self.horizontalButtons2 = QGroupBox("설정 변경") layout = QHBoxLayout() plusRowButton = QPushButton('행+', self) plusRowButton.clicked.connect(self.plus_row) layout.addWidget(plusRowButton) plusColButton = QPushButton('열+', self) plusColButton.clicked.connect(self.plus_col) layout.addWidget(plusColButton) init_button = QPushButton('초기화', self) init_button.clicked.connect(self.initialize) layout.addWidget(init_button) self.horizontalButtons2.setLayout(layout) def createLogTable(self): # Create table self.logTable = QTableWidget() self.logTable.setRowCount(self.tableRow) self.logTable.setColumnCount(self.tableCol) self.logTable.move(0, 0) def createConfigTable(self): self.configTable = QTableWidget() try: # load configurationFile cwb = load_workbook(self.configFile.strip()) except: # message box string = "설정파일을 불러올 수 없습니다." QMessageBox.question(self, 'Error', string, QMessageBox.Ok, QMessageBox.Ok) else: # Get matadata cws = cwb.active self.crow = cws.cell(row=1, column=1).value self.ccol = cws.cell(row=1, column=2).value # Configure table self.configTable.setRowCount(self.crow) self.configTable.setColumnCount(self.ccol) self.configTable.move(0, 0) # Load data from configFile for i in range(self.crow): for j in range(self.ccol): item = str(cws.cell(row=i + 2, column=j + 1).value).strip() if item == 'None': item = '' self.configTable.setItem(i, j, QTableWidgetItem(item)) # check if files exist arr = listdir('./audio_clips') for row in range(self.crow): for col in range(self.ccol): # if 박스(0,0) if row == 0 and col == 0: continue # reset backgound color self.configTable.item(row, col).setBackground(QColor(255, 0, 0)) # if file exist, change background color fname = str(row) + '_' + str(col) + '.mp3' if fname in arr: if row == 0: self.configTable.item(row, col).setBackground( QColor(255, 255, 0)) else: self.configTable.item(row, col).setBackground( QColor(0, 255, 255)) # 박스(0,0) self.configTable.setItem(0, 0, QTableWidgetItem('박스')) self.configTable.item(0, 0).setBackground(QColor(255, 255, 255)) # link the callback function self.configTable.itemDoubleClicked.connect(self.item_doubleClicked) self.configTable.itemChanged.connect(self.item_changed) def setLogTable(self): # shifting other rows for r in range(1, self.tableRow): for c in range(self.tableCol): try: self.logTable.item(r, c).setBackground(QColor(255, 255, 255)) self.logTable.setItem(r - 1, c, self.logTable.item(r, c).clone()) except: pass # set current row for idx, key in enumerate(list(self.dict.keys())): if type(self.dict[key]) is list: self.logTable.setItem( self.tableRow - 1, idx, QTableWidgetItem(' '.join(self.dict[key]))) self.logTable.item(self.tableRow - 1, idx).setBackground(QColor(255, 255, 0)) else: self.logTable.setItem(self.tableRow - 1, idx, QTableWidgetItem(self.dict[key])) self.logTable.item(self.tableRow - 1, idx).setBackground(QColor(255, 255, 0)) def read(self): # 포장 순번 self.dict['num'] = str(self.ws.cell(row=self.row, column=3).value[2:]).strip() # 거래처명 self.dict['partner'] = str(self.ws.cell(row=self.row, column=5).value).strip() # 배송센터 self.dict['parcel'] = str(self.ws.cell(row=self.row, column=4).value).strip() # 특이사항 self.dict['exception'] = str( self.ws.cell(row=self.row, column=6).value).strip() # 박스 self.dict['box'] = str(self.ws.cell(row=self.row, column=2).value).strip() # left things print(len(self.dict)) for i in range(5, len(self.dict)): header = str(self.ws.cell(row=1, column=i + 2).value).strip() self.dict[header] = str( self.ws.cell(row=self.row, column=i + 2).value).strip() self.parsing(header, self.dict[header]) print(self.dict) self.setLogTable() def parsing(self, key, val): if val == 'None' or val == '': self.dict[key] = None else: if '(' in val or '/' in val: arr = re.split('[(/]', val) for i in range(len(arr)): if ')' in arr[i]: arr[i] = arr[i][:arr[i].index(')')] arr[i] = arr[i].strip() self.dict[key] = arr else: self.dict[key] = val def itemFromKeyVal(self, key, val): items = self.configTable.findItems(val, Qt.MatchExactly) if len(items) <= 0: # Error string = '(' + key + ', ' + val + ') ' + '아이템을 찾을 수 없습니다.' QMessageBox.question(self, 'Error', string, QMessageBox.Ok, QMessageBox.Ok) else: for item in items: if self.configTable.item(0, item.column()).data(0) == key: return item def load_audiolist(self): for key, val in self.dict.items(): # 포장순번 if key == 'num': for i in range(len(self.dict['num'])): self.audiolist.append('_' + val[i]) # beep self.audiolist.append('_beep') # 택배발송 elif key == 'parcel': if val == '택배발송': self.audiolist.append('_택배발송') # beep self.audiolist.append('_beep') # 박스 elif key == 'box': item = self.itemFromKeyVal('박스', val) if item: self.audiolist.append( str(item.row()) + '_' + str(item.column())) # beep self.audiolist.append('_beep') elif key in ['partner', 'exception']: pass # general case else: # The case(val == None) will be ignored if val == None: pass # when val is list elif type(val) == list: for idx, eachVal in enumerate(val): item = self.itemFromKeyVal(key, eachVal) if item: if idx == 0: self.audiolist.append( '0_' + str(item.column())) # key self.audiolist.append( str(item.row()) + '_' + str(item.column())) # val # beep self.audiolist.append('_beep') # when val is not list else: item = self.itemFromKeyVal(key, val) if item: if val == '1' or key == val: self.audiolist.append('0_' + str(item.column())) # key else: self.audiolist.append('0_' + str(item.column())) # key self.audiolist.append( str(item.row()) + '_' + str(item.column())) # val # beep self.audiolist.append('_beep') print(self.audiolist) def speak(self): self.playlist.clear() for clip in self.audiolist: url = QUrl.fromLocalFile('./audio_clips/' + clip + '.mp3') #print(url) self.playlist.addMedia(QMediaContent(url)) self.player.setPlaylist(self.playlist) self.player.play() #----------------------- Control button callback -----------------------# @pyqtSlot() def pre_click(self): if not self.fileReady: self.cur_click() else: if self.row == 2: self.statusbar.showMessage("Can't be previous.") else: self.row -= 1 self.dict = self.dict.fromkeys(self.dict, None) del self.audiolist[:] self.read() self.load_audiolist() self.speak() @pyqtSlot() def cur_click(self): if not self.fileReady: string = '파일이 준비되어 있지 않습니다.' QMessageBox.question(self, '경고', string, QMessageBox.Ok, QMessageBox.Ok) self.openFileNameDialog() else: self.dict = self.dict.fromkeys(self.dict, None) del self.audiolist[:] self.read() self.load_audiolist() self.speak() @pyqtSlot() def next_click(self): if not self.fileReady: self.cur_click() else: if self.row == self.ws.max_row: self.statusbar.showMessage("It's over.") else: self.row += 1 self.dict = self.dict.fromkeys(self.dict, None) del self.audiolist[:] self.read() self.load_audiolist() self.speak() @pyqtSlot() def back_click(self): if self.playlist.mediaCount() == 0: self.cur_click() elif self.playlist.mediaCount() != 0: p = re.compile('.+_beep.+') cnt = 0 for i in range(self.playlist.mediaCount()): # if it's start point, start at here if self.playlist.currentIndex() == 0: break # go backward self.playlist.setCurrentIndex(self.playlist.previousIndex(1)) # start at previous beep point if p.match(str(self.playlist.currentMedia().canonicalUrl())): cnt = cnt + 1 if cnt == 2: print(self.playlist.currentIndex()) if self.player.state() == QMediaPlayer.StoppedState: self.player.play() break @pyqtSlot() def forward_click(self): if self.playlist.mediaCount() == 0: self.cur_click() elif self.playlist.mediaCount() != 0: p = re.compile('.+_beep.+') for i in range(self.playlist.mediaCount()): # don't go further from end point if self.playlist.currentIndex() < 0: break # go forward self.playlist.setCurrentIndex(self.playlist.nextIndex(1)) # start at next beep point if p.match(str(self.playlist.currentMedia().canonicalUrl())): print(self.playlist.currentIndex()) break #----------------------- Configuration button callback -----------------------# @pyqtSlot() def plus_row(self): # change configTable self.crow = self.crow + 1 self.configTable.setRowCount(self.crow) # fill the generated cell self.configTable.itemChanged.disconnect(self.item_changed) for col in range(self.ccol): self.configTable.setItem(self.crow - 1, col, QTableWidgetItem('')) self.configTable.item(self.crow - 1, col).setBackground(QColor(255, 0, 0)) self.configTable.itemChanged.connect(self.item_changed) @pyqtSlot() def plus_col(self): # change configTable self.ccol = self.ccol + 1 self.configTable.setColumnCount(self.ccol) # fill the generated cell self.configTable.itemChanged.disconnect(self.item_changed) for row in range(self.crow): self.configTable.setItem(row, self.ccol - 1, QTableWidgetItem('')) self.configTable.item(row, self.ccol - 1).setBackground( QColor(255, 0, 0)) self.configTable.itemChanged.connect(self.item_changed) @pyqtSlot() def initialize(self): # remove configurable audio files arr = listdir('./audio_clips') p = re.compile('_.+') for file in arr: if p.match(file): continue remove('./audio_clips/' + file) # init configTable self.configTable.itemChanged.disconnect(self.item_changed) # lock self.configTable.clear() self.crow = 5 self.ccol = 5 self.configTable.setRowCount(self.crow) self.configTable.setColumnCount(self.ccol) # reset configTable item for row in range(self.crow): for col in range(self.ccol): self.configTable.setItem(row, col, QTableWidgetItem('')) self.configTable.item(row, col).setBackground(QColor(255, 0, 0)) self.configTable.setItem(0, 0, QTableWidgetItem('박스')) self.configTable.item(0, 0).setBackground(QColor(255, 255, 255)) self.configTable.itemChanged.connect(self.item_changed) # unlock # init configFile self.update_configFile() #---------------------- ConfigTable signal callback ------------------------# def item_doubleClicked(self, item): self.previousItem = item.data(0) print(self.previousItem) def update_configFile(self): cwb_w = Workbook(write_only=True) cws_w = cwb_w.create_sheet() cws_w.append([self.crow, self.ccol]) for row in range(self.crow): itemList = [] for col in range(self.ccol): itemList.append(self.configTable.item(row, col).data(0)) cws_w.append(itemList) try: cwb_w.save(self.configFile) except: string = """설정파일이 열려있을 수 있습니다. 설정파일을 닫은 후에 다시 시도하세요.""" QMessageBox.question(self, 'Error', string, QMessageBox.Ok, QMessageBox.Ok) def item_changed(self, item): self.configTable.itemChanged.disconnect(self.item_changed) # lock if item.row() == 0 and item.column() == 0: string = "이 항목은 바꿀 수 없습니다." QMessageBox.question(self, 'Error', string, QMessageBox.Ok, QMessageBox.Ok) self.configTable.setItem(item.row(), item.column(), QTableWidgetItem(self.previousItem)) else: # get file name fileName, _ = QFileDialog.getOpenFileName(self, "Open file", "", "All Files (*)") # count the number of key that has same name keys = self.configTable.findItems(item.data(0), Qt.MatchExactly) kcnt = 0 for key in keys: if key.row() == 0: kcnt = kcnt + 1 # count the number of atribute that has same name atributes = self.configTable.findItems(item.data(0), Qt.MatchExactly) acnt = 0 for atribute in atributes: if atribute.row() == 0: pass elif atribute.column() == item.column(): acnt = acnt + 1 # change is accepted only in case of uniqueness and existence and it is not 박스(0,0) if kcnt >= 2: string = "항목명이 같을 수 없습니다." QMessageBox.question(self, 'Error', string, QMessageBox.Ok, QMessageBox.Ok) self.configTable.setItem(item.row(), item.column(), QTableWidgetItem(self.previousItem)) elif acnt >= 2: string = "같은 항목에 같은 이름의 아이템을 둘 수 없습니다." QMessageBox.question(self, 'Error', string, QMessageBox.Ok, QMessageBox.Ok) self.configTable.setItem(item.row(), item.column(), QTableWidgetItem(self.previousItem)) elif fileName: # copy file to local dir dst = "./audio_clips./" + str(item.row()) + '_' + str( item.column()) + '.mp3' copyfile(fileName, dst) # change cell color if item.row() == 0: self.configTable.item(item.row(), item.column()).setBackground( QColor(255, 255, 0)) else: self.configTable.item(item.row(), item.column()).setBackground( QColor(0, 255, 255)) # update configFile self.update_configFile() else: string = "선택이 취소됨." QMessageBox.question(self, 'Error', string, QMessageBox.Ok, QMessageBox.Ok) self.configTable.setItem(item.row(), item.column(), QTableWidgetItem(self.previousItem)) self.configTable.itemChanged.connect(self.item_changed) # unlock
def __init__(self, parent: 'ElectrumWindow', config: 'SimpleConfig'): WindowModalDialog.__init__(self, parent, _('Preferences')) self.config = config self.window = parent self.need_restart = False self.fx = self.window.fx self.wallet = self.window.wallet vbox = QVBoxLayout() tabs = QTabWidget() gui_widgets = [] tx_widgets = [] oa_widgets = [] # language lang_help = _( 'Select which language is used in the GUI (after restart).') lang_label = HelpLabel(_('Language') + ':', lang_help) lang_combo = QComboBox() lang_combo.addItems(list(languages.values())) lang_keys = list(languages.keys()) lang_cur_setting = self.config.get("language", '') try: index = lang_keys.index(lang_cur_setting) except ValueError: # not in list index = 0 lang_combo.setCurrentIndex(index) if not self.config.is_modifiable('language'): for w in [lang_combo, lang_label]: w.setEnabled(False) def on_lang(x): lang_request = list(languages.keys())[lang_combo.currentIndex()] if lang_request != self.config.get('language'): self.config.set_key("language", lang_request, True) self.need_restart = True lang_combo.currentIndexChanged.connect(on_lang) gui_widgets.append((lang_label, lang_combo)) nz_help = _( 'Number of zeros displayed after the decimal point. For example, if this is set to 2, "1." will be displayed as "1.00"' ) nz_label = HelpLabel(_('Zeros after decimal point') + ':', nz_help) nz = QSpinBox() nz.setMinimum(0) nz.setMaximum(self.config.decimal_point) nz.setValue(self.config.num_zeros) if not self.config.is_modifiable('num_zeros'): for w in [nz, nz_label]: w.setEnabled(False) def on_nz(): value = nz.value() if self.config.num_zeros != value: self.config.num_zeros = value self.config.set_key('num_zeros', value, True) self.window.history_list.update() self.window.address_list.update() nz.valueChanged.connect(on_nz) gui_widgets.append((nz_label, nz)) use_rbf = bool(self.config.get('use_rbf', True)) use_rbf_cb = QCheckBox(_('Use Replace-By-Fee')) use_rbf_cb.setChecked(use_rbf) use_rbf_cb.setToolTip( _('If you check this box, your transactions will be marked as non-final,') + '\n' + \ _('and you will have the possibility, while they are unconfirmed, to replace them with transactions that pay higher fees.') + '\n' + \ _('Note that some merchants do not accept non-final transactions until they are confirmed.')) def on_use_rbf(x): self.config.set_key('use_rbf', bool(x)) batch_rbf_cb.setEnabled(bool(x)) use_rbf_cb.stateChanged.connect(on_use_rbf) tx_widgets.append((use_rbf_cb, None)) batch_rbf_cb = QCheckBox(_('Batch RBF transactions')) batch_rbf_cb.setChecked(bool(self.config.get('batch_rbf', False))) batch_rbf_cb.setEnabled(use_rbf) batch_rbf_cb.setToolTip( _('If you check this box, your unconfirmed transactions will be consolidated into a single transaction.') + '\n' + \ _('This will save fees.')) def on_batch_rbf(x): self.config.set_key('batch_rbf', bool(x)) batch_rbf_cb.stateChanged.connect(on_batch_rbf) tx_widgets.append((batch_rbf_cb, None)) # lightning lightning_widgets = [] help_gossip = _( """If this option is enabled, Electrum will download the network channels graph and compute payment path locally, instead of using trampoline payments. """ ) gossip_cb = QCheckBox(_("Download network graph")) gossip_cb.setToolTip(help_gossip) gossip_cb.setChecked(bool(self.config.get('use_gossip', False))) def on_gossip_checked(x): use_gossip = bool(x) self.config.set_key('use_gossip', use_gossip) if use_gossip: self.window.network.start_gossip() else: self.window.network.stop_gossip() util.trigger_callback('ln_gossip_sync_progress') # FIXME: update all wallet windows util.trigger_callback('channels_updated', self.wallet) gossip_cb.stateChanged.connect(on_gossip_checked) lightning_widgets.append((gossip_cb, None)) help_local_wt = _("""If this option is checked, Electrum will run a local watchtower and protect your channels even if your wallet is not open. For this to work, your computer needs to be online regularly.""") local_wt_cb = QCheckBox(_("Run a local watchtower")) local_wt_cb.setToolTip(help_local_wt) local_wt_cb.setChecked( bool(self.config.get('run_local_watchtower', False))) def on_local_wt_checked(x): self.config.set_key('run_local_watchtower', bool(x)) local_wt_cb.stateChanged.connect(on_local_wt_checked) lightning_widgets.append((local_wt_cb, None)) help_persist = _( """If this option is checked, Electrum will persist after you close all your wallet windows, and the Electrum icon will be visible in the taskbar. Use this if you want your local watchtower to keep running after you close your wallet.""" ) persist_cb = QCheckBox(_("Persist after all windows are closed")) persist_cb.setToolTip(help_persist) persist_cb.setChecked(bool(self.config.get('persist_daemon', False))) def on_persist_checked(x): self.config.set_key('persist_daemon', bool(x)) persist_cb.stateChanged.connect(on_persist_checked) lightning_widgets.append((persist_cb, None)) help_remote_wt = _( """To use a remote watchtower, enter the corresponding URL here""") remote_wt_cb = QCheckBox(_("Use a remote watchtower")) remote_wt_cb.setToolTip(help_remote_wt) remote_wt_cb.setChecked(bool(self.config.get('use_watchtower', False))) def on_remote_wt_checked(x): self.config.set_key('use_watchtower', bool(x)) self.watchtower_url_e.setEnabled(bool(x)) remote_wt_cb.stateChanged.connect(on_remote_wt_checked) watchtower_url = self.config.get('watchtower_url') self.watchtower_url_e = QLineEdit(watchtower_url) self.watchtower_url_e.setEnabled( self.config.get('use_watchtower', False)) def on_wt_url(): url = self.watchtower_url_e.text() or None watchtower_url = self.config.set_key('watchtower_url', url) self.watchtower_url_e.editingFinished.connect(on_wt_url) lightning_widgets.append((remote_wt_cb, self.watchtower_url_e)) msg = _('OpenAlias record, used to receive coins and to sign payment requests.') + '\n\n'\ + _('The following alias providers are available:') + '\n'\ + '\n'.join(['https://cryptoname.co/', 'http://xmr.link']) + '\n\n'\ + 'For more information, see https://openalias.org' alias_label = HelpLabel(_('OpenAlias') + ':', msg) alias = self.config.get('alias', '') self.alias_e = QLineEdit(alias) self.set_alias_color() self.alias_e.editingFinished.connect(self.on_alias_edit) oa_widgets.append((alias_label, self.alias_e)) # units units = base_units_list msg = (_( 'Base unit of your wallet.' ) + '\n1 LTC = 1000 mLTC. 1 mLTC = 1000 uLTC. 1 uLTC = 100 sat.\n' + _( 'This setting affects the Send tab, and all balance related fields.' )) unit_label = HelpLabel(_('Base unit') + ':', msg) unit_combo = QComboBox() unit_combo.addItems(units) unit_combo.setCurrentIndex(units.index(self.window.base_unit())) def on_unit(x, nz): unit_result = units[unit_combo.currentIndex()] if self.window.base_unit() == unit_result: return edits = self.window.amount_e, self.window.receive_amount_e amounts = [edit.get_amount() for edit in edits] self.config.set_base_unit(unit_result) nz.setMaximum(self.config.decimal_point) self.window.history_list.update() self.window.request_list.update() self.window.address_list.update() for edit, amount in zip(edits, amounts): edit.setAmount(amount) self.window.update_status() unit_combo.currentIndexChanged.connect(lambda x: on_unit(x, nz)) gui_widgets.append((unit_label, unit_combo)) system_cameras = qrscanner._find_system_cameras() qr_combo = QComboBox() qr_combo.addItem("Default", "default") for camera, device in system_cameras.items(): qr_combo.addItem(camera, device) #combo.addItem("Manually specify a device", config.get("video_device")) index = qr_combo.findData(self.config.get("video_device")) qr_combo.setCurrentIndex(index) msg = _("Install the zbar package to enable this.") qr_label = HelpLabel(_('Video Device') + ':', msg) qr_combo.setEnabled(qrscanner.libzbar is not None) on_video_device = lambda x: self.config.set_key( "video_device", qr_combo.itemData(x), True) qr_combo.currentIndexChanged.connect(on_video_device) gui_widgets.append((qr_label, qr_combo)) colortheme_combo = QComboBox() colortheme_combo.addItem(_('Light'), 'default') colortheme_combo.addItem(_('Dark'), 'dark') index = colortheme_combo.findData( self.config.get('qt_gui_color_theme', 'default')) colortheme_combo.setCurrentIndex(index) colortheme_label = QLabel(_('Color theme') + ':') def on_colortheme(x): self.config.set_key('qt_gui_color_theme', colortheme_combo.itemData(x), True) self.need_restart = True colortheme_combo.currentIndexChanged.connect(on_colortheme) gui_widgets.append((colortheme_label, colortheme_combo)) updatecheck_cb = QCheckBox( _("Automatically check for software updates")) updatecheck_cb.setChecked(bool(self.config.get('check_updates', False))) def on_set_updatecheck(v): self.config.set_key('check_updates', v == Qt.Checked, save=True) updatecheck_cb.stateChanged.connect(on_set_updatecheck) gui_widgets.append((updatecheck_cb, None)) filelogging_cb = QCheckBox(_("Write logs to file")) filelogging_cb.setChecked(bool(self.config.get('log_to_file', False))) def on_set_filelogging(v): self.config.set_key('log_to_file', v == Qt.Checked, save=True) self.need_restart = True filelogging_cb.stateChanged.connect(on_set_filelogging) filelogging_cb.setToolTip( _('Debug logs can be persisted to disk. These are useful for troubleshooting.' )) gui_widgets.append((filelogging_cb, None)) preview_cb = QCheckBox(_('Advanced preview')) preview_cb.setChecked(bool(self.config.get('advanced_preview', False))) preview_cb.setToolTip( _("Open advanced transaction preview dialog when 'Pay' is clicked." )) def on_preview(x): self.config.set_key('advanced_preview', x == Qt.Checked) preview_cb.stateChanged.connect(on_preview) tx_widgets.append((preview_cb, None)) usechange_cb = QCheckBox(_('Use change addresses')) usechange_cb.setChecked(self.window.wallet.use_change) if not self.config.is_modifiable('use_change'): usechange_cb.setEnabled(False) def on_usechange(x): usechange_result = x == Qt.Checked if self.window.wallet.use_change != usechange_result: self.window.wallet.use_change = usechange_result self.window.wallet.db.put('use_change', self.window.wallet.use_change) multiple_cb.setEnabled(self.window.wallet.use_change) usechange_cb.stateChanged.connect(on_usechange) usechange_cb.setToolTip( _('Using change addresses makes it more difficult for other people to track your transactions.' )) tx_widgets.append((usechange_cb, None)) def on_multiple(x): multiple = x == Qt.Checked if self.wallet.multiple_change != multiple: self.wallet.multiple_change = multiple self.wallet.db.put('multiple_change', multiple) multiple_change = self.wallet.multiple_change multiple_cb = QCheckBox(_('Use multiple change addresses')) multiple_cb.setEnabled(self.wallet.use_change) multiple_cb.setToolTip('\n'.join([ _('In some cases, use up to 3 change addresses in order to break ' 'up large coin amounts and obfuscate the recipient address.'), _('This may result in higher transactions fees.') ])) multiple_cb.setChecked(multiple_change) multiple_cb.stateChanged.connect(on_multiple) tx_widgets.append((multiple_cb, None)) def fmt_docs(key, klass): lines = [ln.lstrip(" ") for ln in klass.__doc__.split("\n")] return '\n'.join([key, "", " ".join(lines)]) choosers = sorted(coinchooser.COIN_CHOOSERS.keys()) if len(choosers) > 1: chooser_name = coinchooser.get_name(self.config) msg = _( 'Choose coin (UTXO) selection method. The following are available:\n\n' ) msg += '\n\n'.join( fmt_docs(*item) for item in coinchooser.COIN_CHOOSERS.items()) chooser_label = HelpLabel(_('Coin selection') + ':', msg) chooser_combo = QComboBox() chooser_combo.addItems(choosers) i = choosers.index(chooser_name) if chooser_name in choosers else 0 chooser_combo.setCurrentIndex(i) def on_chooser(x): chooser_name = choosers[chooser_combo.currentIndex()] self.config.set_key('coin_chooser', chooser_name) chooser_combo.currentIndexChanged.connect(on_chooser) tx_widgets.append((chooser_label, chooser_combo)) def on_unconf(x): self.config.set_key('confirmed_only', bool(x)) conf_only = bool(self.config.get('confirmed_only', False)) unconf_cb = QCheckBox(_('Spend only confirmed coins')) unconf_cb.setToolTip(_('Spend only confirmed inputs.')) unconf_cb.setChecked(conf_only) unconf_cb.stateChanged.connect(on_unconf) tx_widgets.append((unconf_cb, None)) def on_outrounding(x): self.config.set_key('coin_chooser_output_rounding', bool(x)) enable_outrounding = bool( self.config.get('coin_chooser_output_rounding', True)) outrounding_cb = QCheckBox(_('Enable output value rounding')) outrounding_cb.setToolTip( _('Set the value of the change output so that it has similar precision to the other outputs.' ) + '\n' + _('This might improve your privacy somewhat.') + '\n' + _('If enabled, at most 100 satoshis might be lost due to this, per transaction.' )) outrounding_cb.setChecked(enable_outrounding) outrounding_cb.stateChanged.connect(on_outrounding) tx_widgets.append((outrounding_cb, None)) block_explorers = sorted(util.block_explorer_info().keys()) BLOCK_EX_CUSTOM_ITEM = _("Custom URL") if BLOCK_EX_CUSTOM_ITEM in block_explorers: # malicious translation? block_explorers.remove(BLOCK_EX_CUSTOM_ITEM) block_explorers.append(BLOCK_EX_CUSTOM_ITEM) msg = _( 'Choose which online block explorer to use for functions that open a web browser' ) block_ex_label = HelpLabel(_('Online Block Explorer') + ':', msg) block_ex_combo = QComboBox() block_ex_custom_e = QLineEdit( self.config.get('block_explorer_custom') or '') block_ex_combo.addItems(block_explorers) block_ex_combo.setCurrentIndex( block_ex_combo.findText( util.block_explorer(self.config) or BLOCK_EX_CUSTOM_ITEM)) def showhide_block_ex_custom_e(): block_ex_custom_e.setVisible( block_ex_combo.currentText() == BLOCK_EX_CUSTOM_ITEM) showhide_block_ex_custom_e() def on_be_combo(x): if block_ex_combo.currentText() == BLOCK_EX_CUSTOM_ITEM: on_be_edit() else: be_result = block_explorers[block_ex_combo.currentIndex()] self.config.set_key('block_explorer_custom', None, False) self.config.set_key('block_explorer', be_result, True) showhide_block_ex_custom_e() block_ex_combo.currentIndexChanged.connect(on_be_combo) def on_be_edit(): val = block_ex_custom_e.text() try: val = ast.literal_eval(val) # to also accept tuples except: pass self.config.set_key('block_explorer_custom', val) block_ex_custom_e.editingFinished.connect(on_be_edit) block_ex_hbox = QHBoxLayout() block_ex_hbox.setContentsMargins(0, 0, 0, 0) block_ex_hbox.setSpacing(0) block_ex_hbox.addWidget(block_ex_combo) block_ex_hbox.addWidget(block_ex_custom_e) block_ex_hbox_w = QWidget() block_ex_hbox_w.setLayout(block_ex_hbox) tx_widgets.append((block_ex_label, block_ex_hbox_w)) # Fiat Currency hist_checkbox = QCheckBox() hist_capgains_checkbox = QCheckBox() fiat_address_checkbox = QCheckBox() ccy_combo = QComboBox() ex_combo = QComboBox() def update_currencies(): if not self.window.fx: return currencies = sorted( self.fx.get_currencies(self.fx.get_history_config())) ccy_combo.clear() ccy_combo.addItems([_('None')] + currencies) if self.fx.is_enabled(): ccy_combo.setCurrentIndex( ccy_combo.findText(self.fx.get_currency())) def update_history_cb(): if not self.fx: return hist_checkbox.setChecked(self.fx.get_history_config()) hist_checkbox.setEnabled(self.fx.is_enabled()) def update_fiat_address_cb(): if not self.fx: return fiat_address_checkbox.setChecked(self.fx.get_fiat_address_config()) def update_history_capgains_cb(): if not self.fx: return hist_capgains_checkbox.setChecked( self.fx.get_history_capital_gains_config()) hist_capgains_checkbox.setEnabled(hist_checkbox.isChecked()) def update_exchanges(): if not self.fx: return b = self.fx.is_enabled() ex_combo.setEnabled(b) if b: h = self.fx.get_history_config() c = self.fx.get_currency() exchanges = self.fx.get_exchanges_by_ccy(c, h) else: exchanges = self.fx.get_exchanges_by_ccy('USD', False) ex_combo.blockSignals(True) ex_combo.clear() ex_combo.addItems(sorted(exchanges)) ex_combo.setCurrentIndex( ex_combo.findText(self.fx.config_exchange())) ex_combo.blockSignals(False) def on_currency(hh): if not self.fx: return b = bool(ccy_combo.currentIndex()) ccy = str(ccy_combo.currentText()) if b else None self.fx.set_enabled(b) if b and ccy != self.fx.ccy: self.fx.set_currency(ccy) update_history_cb() update_exchanges() self.window.update_fiat() def on_exchange(idx): exchange = str(ex_combo.currentText()) if self.fx and self.fx.is_enabled( ) and exchange and exchange != self.fx.exchange.name(): self.fx.set_exchange(exchange) def on_history(checked): if not self.fx: return self.fx.set_history_config(checked) update_exchanges() self.window.history_model.refresh('on_history') if self.fx.is_enabled() and checked: self.fx.trigger_update() update_history_capgains_cb() def on_history_capgains(checked): if not self.fx: return self.fx.set_history_capital_gains_config(checked) self.window.history_model.refresh('on_history_capgains') def on_fiat_address(checked): if not self.fx: return self.fx.set_fiat_address_config(checked) self.window.address_list.refresh_headers() self.window.address_list.update() update_currencies() update_history_cb() update_history_capgains_cb() update_fiat_address_cb() update_exchanges() ccy_combo.currentIndexChanged.connect(on_currency) hist_checkbox.stateChanged.connect(on_history) hist_capgains_checkbox.stateChanged.connect(on_history_capgains) fiat_address_checkbox.stateChanged.connect(on_fiat_address) ex_combo.currentIndexChanged.connect(on_exchange) fiat_widgets = [] fiat_widgets.append((QLabel(_('Fiat currency')), ccy_combo)) fiat_widgets.append((QLabel(_('Source')), ex_combo)) fiat_widgets.append((QLabel(_('Show history rates')), hist_checkbox)) fiat_widgets.append((QLabel(_('Show capital gains in history')), hist_capgains_checkbox)) fiat_widgets.append((QLabel(_('Show Fiat balance for addresses')), fiat_address_checkbox)) tabs_info = [ (gui_widgets, _('General')), (tx_widgets, _('Transactions')), (lightning_widgets, _('Lightning')), (fiat_widgets, _('Fiat')), (oa_widgets, _('OpenAlias')), ] for widgets, name in tabs_info: tab = QWidget() tab_vbox = QVBoxLayout(tab) grid = QGridLayout() for a, b in widgets: i = grid.rowCount() if b: if a: grid.addWidget(a, i, 0) grid.addWidget(b, i, 1) else: grid.addWidget(a, i, 0, 1, 2) tab_vbox.addLayout(grid) tab_vbox.addStretch(1) tabs.addTab(tab, name) vbox.addWidget(tabs) vbox.addStretch(1) vbox.addLayout(Buttons(CloseButton(self))) self.setLayout(vbox)
class MainWindow(QMainWindow): """ MainWindow class. It stores database. """ switch_window = pyqtSignal() show_login_window = pyqtSignal(object) show_data_loading_window = pyqtSignal(str, object) show_data_saving_window = pyqtSignal(str, object) show_measurement_adding_window = pyqtSignal(object) show_conv_rule_adding_window = pyqtSignal(object) def __init__(self, username: str, tab_name: str = "add_data"): """ Using the main window the user can add new data and plot a figure :param username: logged-in user name. :param tab_name: tab that must be opened. """ QMainWindow.__init__(self) self.setWindowTitle(_("QS Dashboard: %s") % username) self.username = username self.db = DataBase(username=username) print(self.db.DB_FOLDER) self.resize(800, 600) self.tabs = QTabWidget() self.tabs.currentChanged.connect(self.tab_click) self.init_add_data_tab() self.figure = CreateFigure(self) self.tabs.addTab(self.figure, _("---> Plots <---")) if tab_name not in TABNAME2IDX: raise ValueError(f"Invalid name of tab: '{tab_name}'") QTabWidget.setCurrentIndex(self.tabs, TABNAME2IDX[tab_name]) self.setCentralWidget(self.tabs) def tab_click(self, i: int): if IDX2TABNAME[i] == "create_figure": self.figure.update_list() # @property # def db(self): # return DataBase(self.username) # # @db.setter # def db(self, database): # """If database is changed you need to update it on disk""" # raise NotImplementedError() def init_add_data_tab(self): self.add_data_tab = QWidget() self.tabs.addTab(self.add_data_tab, _("---> Data <---")) # self.widget = QWidget(self.add_data_tab) self.hbox = QHBoxLayout(self.add_data_tab) lwidget, lvbox = QWidget(), QVBoxLayout() self.list_widget = QListWidget() self.list_widget_conv_rules = QListWidget() self.update_metrics_list() lvbox.addWidget(self.list_widget) lvbox.addWidget(self.list_widget_conv_rules) lwidget.setLayout(lvbox) self.hbox.addWidget(lwidget) rwidget, grid_layout = QWidget(), QGridLayout() button = QPushButton(_("Load Data")) button.setGeometry(QRect(10, 200, 150, 50)) button.clicked.connect(self.handle_data_loading) grid_layout.addWidget(button, 0, 0) button = QPushButton(_("Add Measurement")) button.setGeometry(QRect(10, 200, 150, 50)) button.clicked.connect(self.handle_measurement_adding) grid_layout.addWidget(button, 1, 0) button = QPushButton(_("Add metrics convertation rule")) button.setGeometry(QRect(10, 200, 150, 50)) button.clicked.connect(self.handle_conv_rule_adding) grid_layout.addWidget(button, 2, 0) button = QPushButton(_("Save Data")) button.setGeometry(QRect(10, 200, 150, 50)) button.clicked.connect(self.handle_data_saving) grid_layout.addWidget(button, 3, 0) button = QPushButton(_("Logout")) button.setEnabled(True) button.setGeometry(QRect(10, 200, 150, 50)) button.clicked.connect(self.handle_logout) grid_layout.addWidget(button, 4, 0) rwidget.setLayout(grid_layout) self.hbox.addWidget(rwidget) @property def user_df(self): return self.db.to_dataframe() def update_metrics_list(self): self.list_widget.clear() self.list_widget_conv_rules.clear() df = self.user_df if "measurement_name" in df.columns: metrics = ([_("Added measurements:")] + list(df.measurement_name.unique())) for metric in metrics: QListWidgetItem(metric, self.list_widget) converter_rules = self.db.metrics_converter QListWidgetItem(_("Added convertation rules:"), self.list_widget_conv_rules) for (from_metric, to_metric), val in converter_rules.items(): QListWidgetItem(f'{from_metric} / {to_metric} = {round(val, 5)}', self.list_widget_conv_rules) @staticmethod def on_click(self): print('Simple Button was pushed') def handle_logout(self): self.show_login_window.emit(self) def handle_data_loading(self): print('loading data') self.show_data_loading_window.emit(self.username, self) def handle_data_saving(self): options = QFileDialog.Options() options |= QFileDialog.DontUseNativeDialog filename, trash = QFileDialog.getSaveFileName( self, _("Save file"), "", _("All Files (*);;Text Files (*.txt)"), options=options) if filename: print('saving data to', filename) self.db.save_to_dataframe(filename) def handle_measurement_adding(self): print('adding measurement data') self.show_measurement_adding_window.emit(self) def handle_conv_rule_adding(self): self.show_conv_rule_adding_window.emit(self)
def initUI(self): # 读取配置文件 global config_dic global twolineflag try: with open('config.json', 'r') as load_f: config_dic = json.load(load_f) except: config_dic = config_dic QMessageBox.information( self, # 使用infomation信息框 "注意", "未找到配置文件 请手动选择") self.read_json() # 初始化连接 self.IPbox = QLineEdit() #self.IPbox.setText("192.168.201.129") self.re_num = 1 self.usernamebox = QLineEdit() self.ethbox = QLineEdit() self.passwordbox = QLineEdit() self.connect_button = QPushButton("测试连接") self.update_button = QPushButton("更新配置") hbox1 = QHBoxLayout() hbox1.addWidget(QLabel("被测试IP:")) hbox1.addWidget(self.IPbox) hbox1.addWidget(self.connect_button) hbox1.addWidget(QLabel(" ")) hbox1.addWidget(QLabel("本机用户名:")) hbox1.addWidget(self.usernamebox) hbox1.addWidget(QLabel("本机密码:")) hbox1.addWidget(self.passwordbox) hbox1.addWidget(QLabel("网口号:")) hbox1.addWidget(self.ethbox) hbox1.addWidget(self.update_button) self.connect_button.clicked.connect(self.connect) self.update_button.clicked.connect(self.read_json) # 中间 self.topFiller = QWidget() self.topFiller.setMinimumSize(2500, 2000) #######设置滚动条的尺寸 self.tab = QTabWidget() for test_type in config_dic.keys(): send_list[test_type] = [] tab_name.append(test_type) self.tab.test_type = Checkboxlist(test_type) l = int(len(test_type) / 2) s = test_type[0:l] + '\n' * twolineflag + test_type[l:] self.tab.addTab(self.tab.test_type, s) # tab.tabBar().setFixedHeight(48) hbox2 = QHBoxLayout(self.topFiller) hbox2.addWidget(self.tab) #hbox2.addWidget(self.scroll) self.scroll = QScrollArea() self.scroll.setWidget(self.topFiller) # 辅助功能 hbox3 = QHBoxLayout() hbox4 = QHBoxLayout() self.re_timebox = QSlider(Qt.Horizontal, self) self.re_timebox.setMinimum(1) self.re_timebox.setMaximum(1000) self.re_timebox.valueChanged[int].connect(self.changeValue) self.num = QLabel("1") self.fullspeed = QCheckBox("全速发送") hbox3.addWidget(self.fullspeed) # -R hbox4.addWidget(QLabel(" 重复次数:")) hbox4.addWidget(self.num) hbox4.addWidget(self.re_timebox) hbox3.addWidget(QLabel(" 最大发包数量:")) self.maxpacknumbox = QLineEdit() # -L hbox3.addWidget(self.maxpacknumbox) hbox3.addWidget(QLabel(" 每秒发送报文数:")) self.packpsbox = QLineEdit() # -p hbox3.addWidget(self.packpsbox) '''hbox3.addWidget(QLabel(" 指定MTU:")) self.MTUbox = QLineEdit() # -t hbox3.addWidget(self.MTUbox)''' hbox3.addWidget(QLabel("发包速度/Mbps:")) self.Mbpsbox = QLineEdit() hbox3.addWidget(self.Mbpsbox) # 开始测试 self.start_button = QPushButton("开始发送数据包") self.start_button.clicked.connect(self.start_test) self.stop_button = QPushButton("停止发送数据包") self.stop_button.clicked.connect(self.stop_test) hbox5 = QHBoxLayout() hbox5.addWidget(self.start_button) hbox5.addWidget(self.stop_button) # hbox5.addWidget(QLabel(" time:")) # self.timebox = QLineEdit() # hbox5.addWidget(self.timebox) # 显示输出结果 self.resultbox = QTextBrowser() vbox = QVBoxLayout() vbox.addLayout(hbox1) vbox.addWidget(QLabel("选择测试模式:")) #vbox.addLayout(hbox2) vbox.addWidget(self.scroll) vbox.addWidget(QLabel("可选项:")) vbox.addLayout(hbox3) vbox.addLayout(hbox4) vbox.addLayout(hbox5) vbox.addWidget(QLabel("状态提示信息:")) vbox.addWidget(self.resultbox) self.setLayout(vbox) # self.setGeometry(300, 300, 290, 150) self.setWindowTitle('tcpreplay_gui') self.show()
def __init__(self, window, plugin, keystore, device_id): title = _("{} Settings").format(plugin.device) super(SettingsDialog, self).__init__(window, title) self.setMaximumWidth(540) devmgr = plugin.device_manager() config = devmgr.config handler = keystore.handler thread = keystore.thread hs_cols, hs_rows = (128, 64) def invoke_client(method, *args, **kw_args): unpair_after = kw_args.pop('unpair_after', False) def task(): client = devmgr.client_by_id(device_id) if not client: raise RuntimeError("Device not connected") if method: getattr(client, method)(*args, **kw_args) if unpair_after: devmgr.unpair_id(device_id) return client.features thread.add(task, on_success=update) def update(features): self.features = features set_label_enabled() if features.bootloader_hash: bl_hash = bh2u(features.bootloader_hash) bl_hash = "\n".join([bl_hash[:32], bl_hash[32:]]) else: bl_hash = "N/A" noyes = [_("No"), _("Yes")] endis = [_("Enable Passphrases"), _("Disable Passphrases")] disen = [_("Disabled"), _("Enabled")] setchange = [_("Set a PIN"), _("Change PIN")] version = "%d.%d.%d" % (features.major_version, features.minor_version, features.patch_version) device_label.setText(features.label) pin_set_label.setText(noyes[features.pin_protection]) passphrases_label.setText(disen[features.passphrase_protection]) bl_hash_label.setText(bl_hash) label_edit.setText(features.label) device_id_label.setText(features.device_id) initialized_label.setText(noyes[features.initialized]) version_label.setText(version) clear_pin_button.setVisible(features.pin_protection) clear_pin_warning.setVisible(features.pin_protection) pin_button.setText(setchange[features.pin_protection]) pin_msg.setVisible(not features.pin_protection) passphrase_button.setText(endis[features.passphrase_protection]) language_label.setText(features.language) def set_label_enabled(): label_apply.setEnabled(label_edit.text() != self.features.label) def rename(): invoke_client('change_label', label_edit.text()) def toggle_passphrase(): title = _("Confirm Toggle Passphrase Protection") currently_enabled = self.features.passphrase_protection if currently_enabled: msg = _("After disabling passphrases, you can only pair this " "Electrum-NMC wallet if it had an empty passphrase. " "If its passphrase was not empty, you will need to " "create a new wallet with the install wizard. You " "can use this wallet again at any time by re-enabling " "passphrases and entering its passphrase.") else: msg = _("Your current Electrum-NMC wallet can only be used with " "an empty passphrase. You must create a separate " "wallet with the install wizard for other passphrases " "as each one generates a new set of addresses.") msg += "\n\n" + _("Are you sure you want to proceed?") if not self.question(msg, title=title): return invoke_client('toggle_passphrase', unpair_after=currently_enabled) def change_homescreen(): dialog = QFileDialog(self, _("Choose Homescreen")) filename, __ = dialog.getOpenFileName() if not filename: return # user cancelled if filename.endswith('.toif'): img = open(filename, 'rb').read() if img[:8] != b'TOIf\x90\x00\x90\x00': handler.show_error('File is not a TOIF file with size of 144x144') return else: from PIL import Image # FIXME im = Image.open(filename) if im.size != (128, 64): handler.show_error('Image must be 128 x 64 pixels') return im = im.convert('1') pix = im.load() img = bytearray(1024) for j in range(64): for i in range(128): if pix[i, j]: o = (i + j * 128) img[o // 8] |= (1 << (7 - o % 8)) img = bytes(img) invoke_client('change_homescreen', img) def clear_homescreen(): invoke_client('change_homescreen', b'\x00') def set_pin(): invoke_client('set_pin', remove=False) def clear_pin(): invoke_client('set_pin', remove=True) def wipe_device(): wallet = window.wallet if wallet and sum(wallet.get_balance()): title = _("Confirm Device Wipe") msg = _("Are you SURE you want to wipe the device?\n" "Your wallet still has namecoins in it!") if not self.question(msg, title=title, icon=QMessageBox.Critical): return invoke_client('wipe_device', unpair_after=True) def slider_moved(): mins = timeout_slider.sliderPosition() timeout_minutes.setText(_("{:2d} minutes").format(mins)) def slider_released(): config.set_session_timeout(timeout_slider.sliderPosition() * 60) # Information tab info_tab = QWidget() info_layout = QVBoxLayout(info_tab) info_glayout = QGridLayout() info_glayout.setColumnStretch(2, 1) device_label = QLabel() pin_set_label = QLabel() passphrases_label = QLabel() version_label = QLabel() device_id_label = QLabel() bl_hash_label = QLabel() bl_hash_label.setWordWrap(True) language_label = QLabel() initialized_label = QLabel() rows = [ (_("Device Label"), device_label), (_("PIN set"), pin_set_label), (_("Passphrases"), passphrases_label), (_("Firmware Version"), version_label), (_("Device ID"), device_id_label), (_("Bootloader Hash"), bl_hash_label), (_("Language"), language_label), (_("Initialized"), initialized_label), ] for row_num, (label, widget) in enumerate(rows): info_glayout.addWidget(QLabel(label), row_num, 0) info_glayout.addWidget(widget, row_num, 1) info_layout.addLayout(info_glayout) # Settings tab settings_tab = QWidget() settings_layout = QVBoxLayout(settings_tab) settings_glayout = QGridLayout() # Settings tab - Label label_msg = QLabel(_("Name this {}. If you have multiple devices " "their labels help distinguish them.") .format(plugin.device)) label_msg.setWordWrap(True) label_label = QLabel(_("Device Label")) label_edit = QLineEdit() label_edit.setMinimumWidth(150) label_edit.setMaxLength(plugin.MAX_LABEL_LEN) label_apply = QPushButton(_("Apply")) label_apply.clicked.connect(rename) label_edit.textChanged.connect(set_label_enabled) settings_glayout.addWidget(label_label, 0, 0) settings_glayout.addWidget(label_edit, 0, 1, 1, 2) settings_glayout.addWidget(label_apply, 0, 3) settings_glayout.addWidget(label_msg, 1, 1, 1, -1) # Settings tab - PIN pin_label = QLabel(_("PIN Protection")) pin_button = QPushButton() pin_button.clicked.connect(set_pin) settings_glayout.addWidget(pin_label, 2, 0) settings_glayout.addWidget(pin_button, 2, 1) pin_msg = QLabel(_("PIN protection is strongly recommended. " "A PIN is your only protection against someone " "stealing your namecoins if they obtain physical " "access to your {}.").format(plugin.device)) pin_msg.setWordWrap(True) pin_msg.setStyleSheet("color: red") settings_glayout.addWidget(pin_msg, 3, 1, 1, -1) # Settings tab - Homescreen homescreen_label = QLabel(_("Homescreen")) homescreen_change_button = QPushButton(_("Change...")) homescreen_clear_button = QPushButton(_("Reset")) homescreen_change_button.clicked.connect(change_homescreen) try: import PIL except ImportError: homescreen_change_button.setDisabled(True) homescreen_change_button.setToolTip( _("Required package 'PIL' is not available - Please install it.") ) homescreen_clear_button.clicked.connect(clear_homescreen) homescreen_msg = QLabel(_("You can set the homescreen on your " "device to personalize it. You must " "choose a {} x {} monochrome black and " "white image.").format(hs_cols, hs_rows)) homescreen_msg.setWordWrap(True) settings_glayout.addWidget(homescreen_label, 4, 0) settings_glayout.addWidget(homescreen_change_button, 4, 1) settings_glayout.addWidget(homescreen_clear_button, 4, 2) settings_glayout.addWidget(homescreen_msg, 5, 1, 1, -1) # Settings tab - Session Timeout timeout_label = QLabel(_("Session Timeout")) timeout_minutes = QLabel() timeout_slider = QSlider(Qt.Horizontal) timeout_slider.setRange(1, 60) timeout_slider.setSingleStep(1) timeout_slider.setTickInterval(5) timeout_slider.setTickPosition(QSlider.TicksBelow) timeout_slider.setTracking(True) timeout_msg = QLabel( _("Clear the session after the specified period " "of inactivity. Once a session has timed out, " "your PIN and passphrase (if enabled) must be " "re-entered to use the device.")) timeout_msg.setWordWrap(True) timeout_slider.setSliderPosition(config.get_session_timeout() // 60) slider_moved() timeout_slider.valueChanged.connect(slider_moved) timeout_slider.sliderReleased.connect(slider_released) settings_glayout.addWidget(timeout_label, 6, 0) settings_glayout.addWidget(timeout_slider, 6, 1, 1, 3) settings_glayout.addWidget(timeout_minutes, 6, 4) settings_glayout.addWidget(timeout_msg, 7, 1, 1, -1) settings_layout.addLayout(settings_glayout) settings_layout.addStretch(1) # Advanced tab advanced_tab = QWidget() advanced_layout = QVBoxLayout(advanced_tab) advanced_glayout = QGridLayout() # Advanced tab - clear PIN clear_pin_button = QPushButton(_("Disable PIN")) clear_pin_button.clicked.connect(clear_pin) clear_pin_warning = QLabel( _("If you disable your PIN, anyone with physical access to your " "{} device can spend your namecoins.").format(plugin.device)) clear_pin_warning.setWordWrap(True) clear_pin_warning.setStyleSheet("color: red") advanced_glayout.addWidget(clear_pin_button, 0, 2) advanced_glayout.addWidget(clear_pin_warning, 1, 0, 1, 5) # Advanced tab - toggle passphrase protection passphrase_button = QPushButton() passphrase_button.clicked.connect(toggle_passphrase) passphrase_msg = WWLabel(PASSPHRASE_HELP) passphrase_warning = WWLabel(PASSPHRASE_NOT_PIN) passphrase_warning.setStyleSheet("color: red") advanced_glayout.addWidget(passphrase_button, 3, 2) advanced_glayout.addWidget(passphrase_msg, 4, 0, 1, 5) advanced_glayout.addWidget(passphrase_warning, 5, 0, 1, 5) # Advanced tab - wipe device wipe_device_button = QPushButton(_("Wipe Device")) wipe_device_button.clicked.connect(wipe_device) wipe_device_msg = QLabel( _("Wipe the device, removing all data from it. The firmware " "is left unchanged.")) wipe_device_msg.setWordWrap(True) wipe_device_warning = QLabel( _("Only wipe a device if you have the recovery seed written down " "and the device wallet(s) are empty, otherwise the namecoins " "will be lost forever.")) wipe_device_warning.setWordWrap(True) wipe_device_warning.setStyleSheet("color: red") advanced_glayout.addWidget(wipe_device_button, 6, 2) advanced_glayout.addWidget(wipe_device_msg, 7, 0, 1, 5) advanced_glayout.addWidget(wipe_device_warning, 8, 0, 1, 5) advanced_layout.addLayout(advanced_glayout) advanced_layout.addStretch(1) tabs = QTabWidget(self) tabs.addTab(info_tab, _("Information")) tabs.addTab(settings_tab, _("Settings")) tabs.addTab(advanced_tab, _("Advanced")) dialog_vbox = QVBoxLayout(self) dialog_vbox.addWidget(tabs) dialog_vbox.addLayout(Buttons(CloseButton(self))) # Update information invoke_client(None)
def __init__(self): QMainWindow.__init__(self) logger.debug("Initialising GUI ...") self.setObjectName("GuiMain") self.mainConf = nw.CONFIG self.threadPool = QThreadPool() # System Info # =========== logger.info("OS: %s" % self.mainConf.osType) logger.info("Kernel: %s" % self.mainConf.kernelVer) logger.info("Host: %s" % self.mainConf.hostName) logger.info("Qt5 Version: %s (%d)" % ( self.mainConf.verQtString, self.mainConf.verQtValue) ) logger.info("PyQt5 Version: %s (%d)" % ( self.mainConf.verPyQtString, self.mainConf.verPyQtValue) ) logger.info("Python Version: %s (0x%x)" % ( self.mainConf.verPyString, self.mainConf.verPyHexVal) ) # Core Classes # ============ # Core Classes and Settings self.theTheme = GuiTheme(self) self.theProject = NWProject(self) self.theIndex = NWIndex(self.theProject, self) self.hasProject = False self.isFocusMode = False # Prepare Main Window self.resize(*self.mainConf.getWinSize()) self._updateWindowTitle() self.setWindowIcon(QIcon(self.mainConf.appIcon)) # Build the GUI # ============= # Main GUI Elements self.statusBar = GuiMainStatus(self) self.treeView = GuiProjectTree(self) self.docEditor = GuiDocEditor(self) self.viewMeta = GuiDocViewDetails(self) self.docViewer = GuiDocViewer(self) self.treeMeta = GuiItemDetails(self) self.projView = GuiOutline(self) self.projMeta = GuiOutlineDetails(self) self.mainMenu = GuiMainMenu(self) # Minor Gui Elements self.statusIcons = [] self.importIcons = [] # Project Tree View self.treePane = QWidget() self.treeBox = QVBoxLayout() self.treeBox.setContentsMargins(0, 0, 0, 0) self.treeBox.addWidget(self.treeView) self.treeBox.addWidget(self.treeMeta) self.treePane.setLayout(self.treeBox) # Splitter : Document Viewer / Document Meta self.splitView = QSplitter(Qt.Vertical) self.splitView.addWidget(self.docViewer) self.splitView.addWidget(self.viewMeta) self.splitView.setSizes(self.mainConf.getViewPanePos()) # Splitter : Document Editor / Document Viewer self.splitDocs = QSplitter(Qt.Horizontal) self.splitDocs.addWidget(self.docEditor) self.splitDocs.addWidget(self.splitView) # Splitter : Project Outlie / Outline Details self.splitOutline = QSplitter(Qt.Vertical) self.splitOutline.addWidget(self.projView) self.splitOutline.addWidget(self.projMeta) self.splitOutline.setSizes(self.mainConf.getOutlinePanePos()) # Main Tabs : Edirot / Outline self.tabWidget = QTabWidget() self.tabWidget.setTabPosition(QTabWidget.East) self.tabWidget.setStyleSheet("QTabWidget::pane {border: 0;}") self.tabWidget.addTab(self.splitDocs, "Editor") self.tabWidget.addTab(self.splitOutline, "Outline") self.tabWidget.currentChanged.connect(self._mainTabChanged) # Splitter : Project Tree / Main Tabs xCM = self.mainConf.pxInt(4) self.splitMain = QSplitter(Qt.Horizontal) self.splitMain.setContentsMargins(xCM, xCM, xCM, xCM) self.splitMain.addWidget(self.treePane) self.splitMain.addWidget(self.tabWidget) self.splitMain.setSizes(self.mainConf.getMainPanePos()) # Indices of All Splitter Widgets self.idxTree = self.splitMain.indexOf(self.treePane) self.idxMain = self.splitMain.indexOf(self.tabWidget) self.idxEditor = self.splitDocs.indexOf(self.docEditor) self.idxViewer = self.splitDocs.indexOf(self.splitView) self.idxViewDoc = self.splitView.indexOf(self.docViewer) self.idxViewMeta = self.splitView.indexOf(self.viewMeta) self.idxTabEdit = self.tabWidget.indexOf(self.splitDocs) self.idxTabProj = self.tabWidget.indexOf(self.splitOutline) # Splitter Behaviour self.splitMain.setCollapsible(self.idxTree, False) self.splitMain.setCollapsible(self.idxMain, False) self.splitDocs.setCollapsible(self.idxEditor, False) self.splitDocs.setCollapsible(self.idxViewer, True) self.splitView.setCollapsible(self.idxViewDoc, False) self.splitView.setCollapsible(self.idxViewMeta, False) # Editor / Viewer Default State self.splitView.setVisible(False) self.docEditor.closeSearch() # Initialise the Project Tree self.treeView.itemSelectionChanged.connect(self._treeSingleClick) self.treeView.itemDoubleClicked.connect(self._treeDoubleClick) self.rebuildTree() # Set Main Window Elements self.setMenuBar(self.mainMenu) self.setCentralWidget(self.splitMain) self.setStatusBar(self.statusBar) # Finalise Initialisation # ======================= # Set Up Auto-Save Project Timer self.asProjTimer = QTimer() self.asProjTimer.timeout.connect(self._autoSaveProject) # Set Up Auto-Save Document Timer self.asDocTimer = QTimer() self.asDocTimer.timeout.connect(self._autoSaveDocument) # Shortcuts and Actions self._connectMenuActions() keyReturn = QShortcut(self.treeView) keyReturn.setKey(QKeySequence(Qt.Key_Return)) keyReturn.activated.connect(self._treeKeyPressReturn) keyEscape = QShortcut(self) keyEscape.setKey(QKeySequence(Qt.Key_Escape)) keyEscape.activated.connect(self._keyPressEscape) # Forward Functions self.setStatus = self.statusBar.setStatus self.setProjectStatus = self.statusBar.setProjectStatus # Force a show of the GUI self.show() # Check that config loaded fine self.reportConfErr() # Initialise Main GUI self.initMain() self.asProjTimer.start() self.asDocTimer.start() self.statusBar.clearStatus() # Handle Windows Mode self.showNormal() if self.mainConf.isFullScreen: self.toggleFullScreenMode() logger.debug("GUI initialisation complete") # Check if a project path was provided at command line, and if # not, open the project manager instead. if self.mainConf.cmdOpen is not None: logger.debug("Opening project from additional command line option") self.openProject(self.mainConf.cmdOpen) else: if self.mainConf.showGUI: self.showProjectLoadDialog() # Show the latest release notes, if they haven't been shown before if hexToInt(self.mainConf.lastNotes) < hexToInt(nw.__hexversion__): if self.mainConf.showGUI: self.showAboutNWDialog(showNotes=True) self.mainConf.lastNotes = nw.__hexversion__ logger.debug("novelWriter is ready ...") self.setStatus("novelWriter is ready ...") return
class GuiMain(QMainWindow): def __init__(self): QMainWindow.__init__(self) logger.debug("Initialising GUI ...") self.setObjectName("GuiMain") self.mainConf = nw.CONFIG self.threadPool = QThreadPool() # System Info # =========== logger.info("OS: %s" % self.mainConf.osType) logger.info("Kernel: %s" % self.mainConf.kernelVer) logger.info("Host: %s" % self.mainConf.hostName) logger.info("Qt5 Version: %s (%d)" % ( self.mainConf.verQtString, self.mainConf.verQtValue) ) logger.info("PyQt5 Version: %s (%d)" % ( self.mainConf.verPyQtString, self.mainConf.verPyQtValue) ) logger.info("Python Version: %s (0x%x)" % ( self.mainConf.verPyString, self.mainConf.verPyHexVal) ) # Core Classes # ============ # Core Classes and Settings self.theTheme = GuiTheme(self) self.theProject = NWProject(self) self.theIndex = NWIndex(self.theProject, self) self.hasProject = False self.isFocusMode = False # Prepare Main Window self.resize(*self.mainConf.getWinSize()) self._updateWindowTitle() self.setWindowIcon(QIcon(self.mainConf.appIcon)) # Build the GUI # ============= # Main GUI Elements self.statusBar = GuiMainStatus(self) self.treeView = GuiProjectTree(self) self.docEditor = GuiDocEditor(self) self.viewMeta = GuiDocViewDetails(self) self.docViewer = GuiDocViewer(self) self.treeMeta = GuiItemDetails(self) self.projView = GuiOutline(self) self.projMeta = GuiOutlineDetails(self) self.mainMenu = GuiMainMenu(self) # Minor Gui Elements self.statusIcons = [] self.importIcons = [] # Project Tree View self.treePane = QWidget() self.treeBox = QVBoxLayout() self.treeBox.setContentsMargins(0, 0, 0, 0) self.treeBox.addWidget(self.treeView) self.treeBox.addWidget(self.treeMeta) self.treePane.setLayout(self.treeBox) # Splitter : Document Viewer / Document Meta self.splitView = QSplitter(Qt.Vertical) self.splitView.addWidget(self.docViewer) self.splitView.addWidget(self.viewMeta) self.splitView.setSizes(self.mainConf.getViewPanePos()) # Splitter : Document Editor / Document Viewer self.splitDocs = QSplitter(Qt.Horizontal) self.splitDocs.addWidget(self.docEditor) self.splitDocs.addWidget(self.splitView) # Splitter : Project Outlie / Outline Details self.splitOutline = QSplitter(Qt.Vertical) self.splitOutline.addWidget(self.projView) self.splitOutline.addWidget(self.projMeta) self.splitOutline.setSizes(self.mainConf.getOutlinePanePos()) # Main Tabs : Edirot / Outline self.tabWidget = QTabWidget() self.tabWidget.setTabPosition(QTabWidget.East) self.tabWidget.setStyleSheet("QTabWidget::pane {border: 0;}") self.tabWidget.addTab(self.splitDocs, "Editor") self.tabWidget.addTab(self.splitOutline, "Outline") self.tabWidget.currentChanged.connect(self._mainTabChanged) # Splitter : Project Tree / Main Tabs xCM = self.mainConf.pxInt(4) self.splitMain = QSplitter(Qt.Horizontal) self.splitMain.setContentsMargins(xCM, xCM, xCM, xCM) self.splitMain.addWidget(self.treePane) self.splitMain.addWidget(self.tabWidget) self.splitMain.setSizes(self.mainConf.getMainPanePos()) # Indices of All Splitter Widgets self.idxTree = self.splitMain.indexOf(self.treePane) self.idxMain = self.splitMain.indexOf(self.tabWidget) self.idxEditor = self.splitDocs.indexOf(self.docEditor) self.idxViewer = self.splitDocs.indexOf(self.splitView) self.idxViewDoc = self.splitView.indexOf(self.docViewer) self.idxViewMeta = self.splitView.indexOf(self.viewMeta) self.idxTabEdit = self.tabWidget.indexOf(self.splitDocs) self.idxTabProj = self.tabWidget.indexOf(self.splitOutline) # Splitter Behaviour self.splitMain.setCollapsible(self.idxTree, False) self.splitMain.setCollapsible(self.idxMain, False) self.splitDocs.setCollapsible(self.idxEditor, False) self.splitDocs.setCollapsible(self.idxViewer, True) self.splitView.setCollapsible(self.idxViewDoc, False) self.splitView.setCollapsible(self.idxViewMeta, False) # Editor / Viewer Default State self.splitView.setVisible(False) self.docEditor.closeSearch() # Initialise the Project Tree self.treeView.itemSelectionChanged.connect(self._treeSingleClick) self.treeView.itemDoubleClicked.connect(self._treeDoubleClick) self.rebuildTree() # Set Main Window Elements self.setMenuBar(self.mainMenu) self.setCentralWidget(self.splitMain) self.setStatusBar(self.statusBar) # Finalise Initialisation # ======================= # Set Up Auto-Save Project Timer self.asProjTimer = QTimer() self.asProjTimer.timeout.connect(self._autoSaveProject) # Set Up Auto-Save Document Timer self.asDocTimer = QTimer() self.asDocTimer.timeout.connect(self._autoSaveDocument) # Shortcuts and Actions self._connectMenuActions() keyReturn = QShortcut(self.treeView) keyReturn.setKey(QKeySequence(Qt.Key_Return)) keyReturn.activated.connect(self._treeKeyPressReturn) keyEscape = QShortcut(self) keyEscape.setKey(QKeySequence(Qt.Key_Escape)) keyEscape.activated.connect(self._keyPressEscape) # Forward Functions self.setStatus = self.statusBar.setStatus self.setProjectStatus = self.statusBar.setProjectStatus # Force a show of the GUI self.show() # Check that config loaded fine self.reportConfErr() # Initialise Main GUI self.initMain() self.asProjTimer.start() self.asDocTimer.start() self.statusBar.clearStatus() # Handle Windows Mode self.showNormal() if self.mainConf.isFullScreen: self.toggleFullScreenMode() logger.debug("GUI initialisation complete") # Check if a project path was provided at command line, and if # not, open the project manager instead. if self.mainConf.cmdOpen is not None: logger.debug("Opening project from additional command line option") self.openProject(self.mainConf.cmdOpen) else: if self.mainConf.showGUI: self.showProjectLoadDialog() # Show the latest release notes, if they haven't been shown before if hexToInt(self.mainConf.lastNotes) < hexToInt(nw.__hexversion__): if self.mainConf.showGUI: self.showAboutNWDialog(showNotes=True) self.mainConf.lastNotes = nw.__hexversion__ logger.debug("novelWriter is ready ...") self.setStatus("novelWriter is ready ...") return def clearGUI(self): """Wrapper function to clear all sub-elements of the main GUI. """ # Project Area self.treeView.clearTree() self.treeMeta.clearDetails() # Work Area self.docEditor.clearEditor() self.docEditor.setDictionaries() self.closeDocViewer() self.projMeta.clearDetails() # General self.statusBar.clearStatus() self._updateWindowTitle() return True def initMain(self): """Initialise elements that depend on user settings. """ self.asProjTimer.setInterval(int(self.mainConf.autoSaveProj*1000)) self.asDocTimer.setInterval(int(self.mainConf.autoSaveDoc*1000)) return True ## # Project Actions ## def newProject(self, projData=None): """Create new project via the new project wizard. """ if self.hasProject: if not self.closeProject(): self.makeAlert( "Cannot create new project when another project is open.", nwAlert.ERROR ) return False if projData is None: projData = self.showNewProjectDialog() if projData is None: return False projPath = projData.get("projPath", None) if projPath is None or projData is None: logger.error("No projData or projPath set") return False if os.path.isfile(os.path.join(projPath, self.theProject.projFile)): self.makeAlert( "A project already exists in that location. Please choose another folder.", nwAlert.ERROR ) return False logger.info("Creating new project") if self.theProject.newProject(projData): self.rebuildTree() self.saveProject() self.hasProject = True self.docEditor.setDictionaries() self.rebuildIndex(beQuiet=True) self.statusBar.setRefTime(self.theProject.projOpened) self.statusBar.setProjectStatus(True) self.statusBar.setDocumentStatus(None) self.statusBar.setStatus("New project created ...") self._updateWindowTitle(self.theProject.projName) else: self.theProject.clearProject() return False return True def closeProject(self, isYes=False): """Closes the project if one is open. isYes is passed on from the close application event so the user doesn't get prompted twice to confirm. """ if not self.hasProject: # There is no project loaded, everything OK return True if not isYes: msgYes = self.askQuestion( "Close Project", "Close the current project?<br>Changes are saved automatically." ) if not msgYes: return False if self.docEditor.docChanged: self.saveDocument() if self.theProject.projAltered: saveOK = self.saveProject() doBackup = False if self.theProject.doBackup and self.mainConf.backupOnClose: doBackup = True if self.mainConf.askBeforeBackup: msgYes = self.askQuestion( "Backup Project", "Backup the current project?" ) if not msgYes: doBackup = False if doBackup: self.theProject.zipIt(False) else: saveOK = True if saveOK: self.closeDocument() self.docViewer.clearNavHistory() self.projView.closeOutline() self.theProject.closeProject() self.theIndex.clearIndex() self.clearGUI() self.hasProject = False self.tabWidget.setCurrentWidget(self.splitDocs) return saveOK def openProject(self, projFile): """Open a project from a projFile path. """ if projFile is None: return False # Make sure any open project is cleared out first before we load # another one if not self.closeProject(): return False # Switch main tab to editor view self.tabWidget.setCurrentWidget(self.splitDocs) # Try to open the project if not self.theProject.openProject(projFile): # The project open failed. if self.theProject.lockedBy is None: # The project is not locked, so failed for some other # reason handled by the project class. return False try: lockDetails = ( "<br><br>The project was locked by the computer " "'%s' (%s %s), last active on %s" ) % ( self.theProject.lockedBy[0], self.theProject.lockedBy[1], self.theProject.lockedBy[2], datetime.fromtimestamp( int(self.theProject.lockedBy[3]) ).strftime("%x %X") ) except Exception: lockDetails = "" msgBox = QMessageBox() msgRes = msgBox.warning( self, "Project Locked", ( "The project is already open by another instance of novelWriter, and " "is therefore locked. Override lock and continue anyway?<br><br>" "Note: If the program or the computer previously crashed, the lock " "can safely be overridden. If, however, another instance of " "novelWriter has the project open, overriding the lock may corrupt " "the project, and is not recommended.%s" ) % lockDetails, QMessageBox.Yes | QMessageBox.No, QMessageBox.No ) if msgRes == QMessageBox.Yes: if not self.theProject.openProject(projFile, overrideLock=True): return False else: return False # Project is loaded self.hasProject = True # Load the tag index self.theIndex.loadIndex() # Update GUI self._updateWindowTitle(self.theProject.projName) self.rebuildTree() self.docEditor.setDictionaries() self.docEditor.setSpellCheck(self.theProject.spellCheck) self.mainMenu.setAutoOutline(self.theProject.autoOutline) self.statusBar.setRefTime(self.theProject.projOpened) self.statusBar.setStats(self.theProject.currWCount, 0) # Restore previously open documents, if any if self.theProject.lastEdited is not None: self.openDocument(self.theProject.lastEdited, doScroll=True) if self.theProject.lastViewed is not None: self.viewDocument(self.theProject.lastViewed) # Check if we need to rebuild the index if self.theIndex.indexBroken: self.rebuildIndex() # Make sure the changed status is set to false on all that was # just opened qApp.processEvents() self.docEditor.setDocumentChanged(False) self.theProject.setProjectChanged(False) logger.debug("Project load complete") return True def saveProject(self, autoSave=False): """Save the current project. """ if not self.hasProject: logger.error("No project open") return False # If the project is new, it may not have a path, so we need one if self.theProject.projPath is None: projPath = self.selectProjectPath() self.theProject.setProjectPath(projPath) if self.theProject.projPath is None: return False self.treeView.saveTreeOrder() self.theProject.saveProject(autoSave=autoSave) self.theIndex.saveIndex() return True ## # Document Actions ## def closeDocument(self): """Close the document and clear the editor and title field. """ if not self.hasProject: logger.error("No project open") return False self.docEditor.saveCursorPosition() if self.docEditor.docChanged: self.saveDocument() self.docEditor.clearEditor() return True def openDocument(self, tHandle, tLine=None, changeFocus=True, doScroll=False): """Open a specific document, optionally at a given line. """ if not self.hasProject: logger.error("No project open") return False self.closeDocument() self.tabWidget.setCurrentWidget(self.splitDocs) if self.docEditor.loadText(tHandle, tLine): if changeFocus: self.docEditor.setFocus() self.theProject.setLastEdited(tHandle) self.treeView.setSelectedHandle(tHandle, doScroll=doScroll) else: return False return True def openNextDocument(self, tHandle, wrapAround=False): """Opens the next document in the project tree, following the document with the given handle. Stops when reaching the end. """ if not self.hasProject: logger.error("No project open") return False self.treeView.flushTreeOrder() nHandle = None # The next handle after tHandle fHandle = None # The first file handle we encounter foundIt = False # We've found tHandle, pick the next we see for tItem in self.theProject.projTree: if tItem is None: continue if tItem.itemType != nwItemType.FILE: continue if fHandle is None: fHandle = tItem.itemHandle if tItem.itemHandle == tHandle: foundIt = True elif foundIt: nHandle = tItem.itemHandle break if nHandle is not None: self.openDocument(nHandle, tLine=0, doScroll=True) return True elif wrapAround: self.openDocument(fHandle, tLine=0, doScroll=True) return False return False def saveDocument(self): """Save the current documents. """ if not self.hasProject: logger.error("No project open") return False self.docEditor.saveText() return True def viewDocument(self, tHandle=None, tAnchor=None): """Load a document for viewing in the view panel. """ if not self.hasProject: logger.error("No project open") return False if tHandle is None: logger.debug("Viewing document, but no handle provided") if self.docEditor.hasFocus(): logger.verbose("Trying editor document") tHandle = self.docEditor.theHandle if tHandle is not None: self.saveDocument() else: logger.verbose("Trying selected document") tHandle = self.treeView.getSelectedHandle() if tHandle is None: logger.verbose("Trying last viewed document") tHandle = self.theProject.lastViewed if tHandle is None: logger.verbose("No document to view, giving up") return False # Make sure main tab is in Editor view self.tabWidget.setCurrentWidget(self.splitDocs) logger.debug("Viewing document with handle %s" % tHandle) if self.docViewer.loadText(tHandle): if not self.splitView.isVisible(): bPos = self.splitMain.sizes() self.splitView.setVisible(True) vPos = [0, 0] vPos[0] = int(bPos[1]/2) vPos[1] = bPos[1] - vPos[0] self.splitDocs.setSizes(vPos) self.viewMeta.setVisible(self.mainConf.showRefPanel) self.docViewer.navigateTo(tAnchor) return True def importDocument(self): """Import the text contained in an out-of-project text file, and insert the text into the currently open document. """ if not self.hasProject: logger.error("No project open") return False lastPath = self.mainConf.lastPath extFilter = [ "Text files (*.txt)", "Markdown files (*.md)", "novelWriter files (*.nwd)", "All files (*.*)", ] dlgOpt = QFileDialog.Options() dlgOpt |= QFileDialog.DontUseNativeDialog loadFile, _ = QFileDialog.getOpenFileName( self, "Import File", lastPath, options=dlgOpt, filter=";;".join(extFilter) ) if not loadFile: return False if loadFile.strip() == "": return False theText = None try: with open(loadFile, mode="rt", encoding="utf8") as inFile: theText = inFile.read() self.mainConf.setLastPath(loadFile) except Exception as e: self.makeAlert( ["Could not read file. The file must be an existing text file.", str(e)], nwAlert.ERROR ) return False if self.docEditor.theHandle is None: self.makeAlert( "Please open a document to import the text file into.", nwAlert.ERROR ) return False if not self.docEditor.isEmpty(): msgYes = self.askQuestion("Import Document", ( "Importing the file will overwrite the current content of the document. " "Do you want to proceed?" )) if not msgYes: return False self.docEditor.replaceText(theText) return True def mergeDocuments(self): """Merge multiple documents to one single new document. """ if not self.hasProject: logger.error("No project open") return False dlgMerge = GuiDocMerge(self, self.theProject) dlgMerge.exec_() return True def splitDocument(self): """Split a single document into multiple documents. """ if not self.hasProject: logger.error("No project open") return False dlgSplit = GuiDocSplit(self, self.theProject) dlgSplit.exec_() return True def passDocumentAction(self, theAction): """Pass on document action theAction to the document viewer if it has focus, otherwise pass it to the document editor. """ if self.docViewer.hasFocus(): self.docViewer.docAction(theAction) else: self.docEditor.docAction(theAction) return True ## # Tree Item Actions ## def openSelectedItem(self): """Open the selected documents. """ if not self.hasProject: logger.error("No project open") return False tHandle = self.treeView.getSelectedHandle() if tHandle is None: logger.warning("No item selected") return False logger.verbose("Opening item %s" % tHandle) nwItem = self.theProject.projTree[tHandle] if nwItem.itemType == nwItemType.FILE: logger.verbose("Requested item %s is a file" % tHandle) self.openDocument(tHandle, doScroll=False) else: logger.verbose("Requested item %s is not a file" % tHandle) return True def editItem(self, tHandle=None): """Open the edit item dialog. """ if not self.hasProject: logger.error("No project open") return False if tHandle is None: tHandle = self.treeView.getSelectedHandle() if tHandle is None: logger.warning("No item selected") return tItem = self.theProject.projTree[tHandle] if tItem is None: return if tItem.itemType not in nwLists.REG_TYPES: return logger.verbose("Requesting change to item %s" % tHandle) dlgProj = GuiItemEditor(self, self.theProject, tHandle) dlgProj.exec_() if dlgProj.result() == QDialog.Accepted: self.treeView.setTreeItemValues(tHandle) self.treeMeta.updateViewBox(tHandle) self.docEditor.updateDocInfo(tHandle) self.docViewer.updateDocInfo(tHandle) return def rebuildTree(self): """Rebuild the project tree. """ self._makeStatusIcons() self._makeImportIcons() self.treeView.clearTree() self.treeView.buildTree() return def rebuildIndex(self, beQuiet=False): """Rebuild the entire index. """ if not self.hasProject: logger.error("No project open") return False logger.debug("Rebuilding index ...") qApp.setOverrideCursor(QCursor(Qt.WaitCursor)) tStart = time() self.treeView.saveTreeOrder() self.theIndex.clearIndex() theDoc = NWDoc(self.theProject, self) for nDone, tItem in enumerate(self.theProject.projTree): if tItem is not None: self.setStatus("Indexing: '%s'" % tItem.itemName) else: self.setStatus("Indexing: Unknown item") if tItem is not None and tItem.itemType == nwItemType.FILE: logger.verbose("Scanning: %s" % tItem.itemName) theText = theDoc.openDocument(tItem.itemHandle, showStatus=False) # Build tag index self.theIndex.scanText(tItem.itemHandle, theText) # Get Word Counts cC, wC, pC = self.theIndex.getCounts(tItem.itemHandle) tItem.setCharCount(cC) tItem.setWordCount(wC) tItem.setParaCount(pC) self.treeView.propagateCount(tItem.itemHandle, wC) self.treeView.projectWordCount() tEnd = time() self.setStatus("Indexing completed in %.1f ms" % ((tEnd - tStart)*1000.0)) self.docEditor.updateTagHighLighting() qApp.restoreOverrideCursor() if not beQuiet: self.makeAlert("The project index has been successfully rebuilt.", nwAlert.INFO) return True def rebuildOutline(self): """Force a rebuild of the Outline view. """ if not self.hasProject: logger.error("No project open") return False logger.verbose("Forcing a rebuild of the Project Outline") self.tabWidget.setCurrentWidget(self.splitOutline) self.projView.refreshTree(overRide=True) return True ## # Main Dialogs ## def selectProjectPath(self): """Select where to save project. """ dlgOpt = QFileDialog.Options() dlgOpt |= QFileDialog.ShowDirsOnly dlgOpt |= QFileDialog.DontUseNativeDialog projPath = QFileDialog.getExistingDirectory( self, "Save novelWriter Project", "", options=dlgOpt ) if projPath: return projPath return None def showProjectLoadDialog(self): """Opens the projects dialog for selecting either existing projects from a cache of recently opened projects, or provide a browse button for projects not yet cached. Selecting to create a new project is forwarded to the new project wizard. """ dlgProj = GuiProjectLoad(self) dlgProj.exec_() if dlgProj.result() == QDialog.Accepted: if dlgProj.openState == GuiProjectLoad.OPEN_STATE: self.openProject(dlgProj.openPath) elif dlgProj.openState == GuiProjectLoad.NEW_STATE: self.newProject() return True def showNewProjectDialog(self): """Open the wizard and assemble a project options dict. """ newProj = GuiProjectWizard(self) newProj.exec_() if newProj.result() == QDialog.Accepted: return self._assembleProjectWizardData(newProj) return None def showPreferencesDialog(self): """Open the preferences dialog. """ dlgConf = GuiPreferences(self, self.theProject) dlgConf.exec_() if dlgConf.result() == QDialog.Accepted: logger.debug("Applying new preferences") self.initMain() self.theTheme.updateTheme() self.saveDocument() self.docEditor.initEditor() self.docViewer.initViewer() self.treeView.initTree() self.projView.initOutline() self.projMeta.initDetails() return def showProjectSettingsDialog(self): """Open the project settings dialog. """ if not self.hasProject: logger.error("No project open") return dlgProj = GuiProjectSettings(self, self.theProject) dlgProj.exec_() if dlgProj.result() == QDialog.Accepted: logger.debug("Applying new project settings") self.docEditor.setDictionaries() self._updateWindowTitle(self.theProject.projName) return def showBuildProjectDialog(self): """Open the build project dialog. """ if not self.hasProject: logger.error("No project open") return dlgBuild = getGuiItem("GuiBuildNovel") if dlgBuild is None: dlgBuild = GuiBuildNovel(self, self.theProject) dlgBuild.setModal(False) dlgBuild.show() qApp.processEvents() dlgBuild.viewCachedDoc() return def showWritingStatsDialog(self): """Open the session log dialog. """ if not self.hasProject: logger.error("No project open") return dlgStats = getGuiItem("GuiWritingStats") if dlgStats is None: dlgStats = GuiWritingStats(self, self.theProject) dlgStats.setModal(False) dlgStats.show() qApp.processEvents() dlgStats.populateGUI() return def showAboutNWDialog(self, showNotes=False): """Show the about dialog for novelWriter. """ dlgAbout = GuiAbout(self) dlgAbout.setModal(True) dlgAbout.show() qApp.processEvents() dlgAbout.populateGUI() if showNotes: dlgAbout.showReleaseNotes() return def showAboutQtDialog(self): """Show the about dialog for Qt. """ msgBox = QMessageBox() msgBox.aboutQt(self, "About Qt") return def makeAlert(self, theMessage, theLevel=nwAlert.INFO): """Alert both the user and the logger at the same time. Message can be either a string or an array of strings. """ if isinstance(theMessage, list): popMsg = "<br>".join(theMessage) logMsg = theMessage else: popMsg = theMessage logMsg = [theMessage] # Write to Log if theLevel == nwAlert.INFO: for msgLine in logMsg: logger.info(msgLine) elif theLevel == nwAlert.WARN: for msgLine in logMsg: logger.warning(msgLine) elif theLevel == nwAlert.ERROR: for msgLine in logMsg: logger.error(msgLine) elif theLevel == nwAlert.BUG: for msgLine in logMsg: logger.error(msgLine) # Popup msgBox = QMessageBox() if theLevel == nwAlert.INFO: msgBox.information(self, "Information", popMsg) elif theLevel == nwAlert.WARN: msgBox.warning(self, "Warning", popMsg) elif theLevel == nwAlert.ERROR: msgBox.critical(self, "Error", popMsg) elif theLevel == nwAlert.BUG: popMsg += "<br>This is a bug!" msgBox.critical(self, "Internal Error", popMsg) return def askQuestion(self, theTitle, theQuestion): """Ask the user a Yes/No question. """ msgBox = QMessageBox() msgRes = msgBox.question(self, theTitle, theQuestion) return msgRes == QMessageBox.Yes def reportConfErr(self): """Checks if the Config module has any errors to report, and let the user know if this is the case. The Config module caches errors since it is initialised before the GUI itself. """ if self.mainConf.hasError: self.makeAlert(self.mainConf.getErrData(), nwAlert.ERROR) return True return False ## # Main Window Actions ## def closeMain(self): """Save everything, and close novelWriter. """ if self.hasProject: msgYes = self.askQuestion( "Exit", "Do you want to exit novelWriter?<br>Changes are saved automatically." ) if not msgYes: return False logger.info("Exiting novelWriter") if not self.isFocusMode: self.mainConf.setMainPanePos(self.splitMain.sizes()) self.mainConf.setDocPanePos(self.splitDocs.sizes()) self.mainConf.setOutlinePanePos(self.splitOutline.sizes()) if self.viewMeta.isVisible(): self.mainConf.setViewPanePos(self.splitView.sizes()) self.mainConf.setShowRefPanel(self.viewMeta.isVisible()) self.mainConf.setTreeColWidths(self.treeView.getColumnSizes()) if not self.mainConf.isFullScreen: self.mainConf.setWinSize(self.width(), self.height()) if self.hasProject: self.closeProject(True) self.mainConf.saveConfig() self.reportConfErr() self.mainMenu.closeHelp() qApp.quit() return True def setFocus(self, paneNo): """Switch focus to one of the three main GUI panes. """ if paneNo == 1: self.treeView.setFocus() elif paneNo == 2: self.docEditor.setFocus() elif paneNo == 3: self.docViewer.setFocus() return def closeDocEditor(self): """Close the document edit panel. This does not hide the editor. """ self.closeDocument() self.theProject.setLastEdited(None) return def closeDocViewer(self): """Close the document view panel. """ self.docViewer.clearViewer() self.theProject.setLastViewed(None) bPos = self.splitMain.sizes() self.splitView.setVisible(False) vPos = [bPos[1], 0] self.splitDocs.setSizes(vPos) return not self.splitView.isVisible() def toggleFocusMode(self): """Main GUI Focus Mode hides tree, view pane and optionally also statusbar and menu. """ if self.docEditor.theHandle is None: logger.error("No document open, so not activating Focus Mode") self.mainMenu.aFocusMode.setChecked(self.isFocusMode) return False self.isFocusMode = not self.isFocusMode self.mainMenu.aFocusMode.setChecked(self.isFocusMode) if self.isFocusMode: logger.debug("Activating Focus Mode") self.tabWidget.setCurrentWidget(self.splitDocs) else: logger.debug("Deactivating Focus Mode") isVisible = not self.isFocusMode self.treePane.setVisible(isVisible) self.statusBar.setVisible(isVisible) self.mainMenu.setVisible(isVisible) self.tabWidget.tabBar().setVisible(isVisible) hideDocFooter = self.isFocusMode and self.mainConf.hideFocusFooter self.docEditor.docFooter.setVisible(not hideDocFooter) if self.splitView.isVisible(): self.splitView.setVisible(False) elif self.docViewer.theHandle is not None: self.splitView.setVisible(True) return True def toggleFullScreenMode(self): """Main GUI full screen mode. The mode is tracked by the flag in config. This only tracks whether the window has been maximised using the internal commands, and may not be correct if the user uses the system window manager. Currently, Qt doesn't have access to the exact state of the window. """ self.setWindowState(self.windowState() ^ Qt.WindowFullScreen) winState = self.windowState() & Qt.WindowFullScreen == Qt.WindowFullScreen if winState: logger.debug("Activated full screen mode") else: logger.debug("Deactivated full screen mode") self.mainConf.isFullScreen = winState return ## # Internal Functions ## def _connectMenuActions(self): """Connect to the main window all menu actions that need to be available also when the main menu is hidden. """ # Project self.addAction(self.mainMenu.aSaveProject) self.addAction(self.mainMenu.aExitNW) # Document self.addAction(self.mainMenu.aSaveDoc) self.addAction(self.mainMenu.aFileDetails) # Edit self.addAction(self.mainMenu.aEditUndo) self.addAction(self.mainMenu.aEditRedo) self.addAction(self.mainMenu.aEditCut) self.addAction(self.mainMenu.aEditCopy) self.addAction(self.mainMenu.aEditPaste) self.addAction(self.mainMenu.aSelectAll) self.addAction(self.mainMenu.aSelectPar) # View self.addAction(self.mainMenu.aFocusMode) self.addAction(self.mainMenu.aFullScreen) # Insert self.addAction(self.mainMenu.aInsENDash) self.addAction(self.mainMenu.aInsEMDash) self.addAction(self.mainMenu.aInsEllipsis) self.addAction(self.mainMenu.aInsQuoteLS) self.addAction(self.mainMenu.aInsQuoteRS) self.addAction(self.mainMenu.aInsQuoteLD) self.addAction(self.mainMenu.aInsQuoteRD) self.addAction(self.mainMenu.aInsMSApos) self.addAction(self.mainMenu.aInsHardBreak) self.addAction(self.mainMenu.aInsNBSpace) self.addAction(self.mainMenu.aInsThinSpace) self.addAction(self.mainMenu.aInsThinNBSpace) for mAction, _ in self.mainMenu.mInsKWItems.values(): self.addAction(mAction) # Format self.addAction(self.mainMenu.aFmtEmph) self.addAction(self.mainMenu.aFmtStrong) self.addAction(self.mainMenu.aFmtStrike) self.addAction(self.mainMenu.aFmtDQuote) self.addAction(self.mainMenu.aFmtSQuote) self.addAction(self.mainMenu.aFmtHead1) self.addAction(self.mainMenu.aFmtHead2) self.addAction(self.mainMenu.aFmtHead3) self.addAction(self.mainMenu.aFmtHead4) self.addAction(self.mainMenu.aFmtComment) self.addAction(self.mainMenu.aFmtNoFormat) # Tools self.addAction(self.mainMenu.aSpellCheck) self.addAction(self.mainMenu.aReRunSpell) self.addAction(self.mainMenu.aPreferences) # Help if self.mainConf.hasHelp and self.mainConf.hasAssistant: self.addAction(self.mainMenu.aHelpLoc) self.addAction(self.mainMenu.aHelpWeb) return True def _updateWindowTitle(self, projName=None): """Set the window title and add the project's working title. """ winTitle = self.mainConf.appName if projName is not None: winTitle += " - %s" % projName self.setWindowTitle(winTitle) return True def _autoSaveProject(self): """Triggered by the auto-save project timer to save the project. """ doSave = self.hasProject doSave &= self.theProject.projChanged doSave &= self.theProject.projPath is not None if doSave: logger.debug("Autosaving project") self.saveProject(autoSave=True) return def _autoSaveDocument(self): """Triggered by the auto-save document timer to save the document. """ if self.hasProject and self.docEditor.docChanged: logger.debug("Autosaving document") self.saveDocument() return def _makeStatusIcons(self): """Generate all the item status icons based on project settings. """ self.statusIcons = {} iPx = self.mainConf.pxInt(32) for sLabel, sCol, _ in self.theProject.statusItems: theIcon = QPixmap(iPx, iPx) theIcon.fill(QColor(*sCol)) self.statusIcons[sLabel] = QIcon(theIcon) return def _makeImportIcons(self): """Generate all the item importance icons based on project settings. """ self.importIcons = {} iPx = self.mainConf.pxInt(32) for sLabel, sCol, _ in self.theProject.importItems: theIcon = QPixmap(iPx, iPx) theIcon.fill(QColor(*sCol)) self.importIcons[sLabel] = QIcon(theIcon) return def _assembleProjectWizardData(self, newProj): """Extract the user choices from the New Project Wizard and store them in a dictionary. """ projData = { "projName": newProj.field("projName"), "projTitle": newProj.field("projTitle"), "projAuthors": newProj.field("projAuthors"), "projPath": newProj.field("projPath"), "popSample": newProj.field("popSample"), "popMinimal": newProj.field("popMinimal"), "popCustom": newProj.field("popCustom"), "addRoots": [], "numChapters": 0, "numScenes": 0, "chFolders": False, } if newProj.field("popCustom"): addRoots = [] if newProj.field("addPlot"): addRoots.append(nwItemClass.PLOT) if newProj.field("addChar"): addRoots.append(nwItemClass.CHARACTER) if newProj.field("addWorld"): addRoots.append(nwItemClass.WORLD) if newProj.field("addTime"): addRoots.append(nwItemClass.TIMELINE) if newProj.field("addObject"): addRoots.append(nwItemClass.OBJECT) if newProj.field("addEntity"): addRoots.append(nwItemClass.ENTITY) projData["addRoots"] = addRoots projData["numChapters"] = newProj.field("numChapters") projData["numScenes"] = newProj.field("numScenes") projData["chFolders"] = newProj.field("chFolders") return projData ## # Events ## def closeEvent(self, theEvent): """Capture the closing event of the GUI and call the close function to handle all the close process steps. """ if self.closeMain(): theEvent.accept() else: theEvent.ignore() return ## # Signal Handlers ## def _treeSingleClick(self): """Single click on a project tree item just updates the details panel below the tree. """ sHandle = self.treeView.getSelectedHandle() if sHandle is not None: self.treeMeta.updateViewBox(sHandle) return def _treeDoubleClick(self, tItem, colNo): """The user double-clicked an item in the tree. If it is a file, we open it. Otherwise, we do nothing. """ tHandle = tItem.data(self.treeView.C_NAME, Qt.UserRole) logger.verbose("User double clicked tree item with handle %s" % tHandle) nwItem = self.theProject.projTree[tHandle] if nwItem is not None: if nwItem.itemType == nwItemType.FILE: logger.verbose("Requested item %s is a file" % tHandle) self.openDocument(tHandle, changeFocus=False, doScroll=False) else: logger.verbose("Requested item %s is a folder" % tHandle) return def _treeKeyPressReturn(self): """The user pressed return on an item in the tree. If it is a file, we open it. Otherwise, we do nothing. Pressing return does not change focus to the editor as double click does. """ tHandle = self.treeView.getSelectedHandle() logger.verbose("User pressed return on tree item with handle %s" % tHandle) nwItem = self.theProject.projTree[tHandle] if nwItem is not None: if nwItem.itemType == nwItemType.FILE: logger.verbose("Requested item %s is a file" % tHandle) self.openDocument(tHandle, changeFocus=False, doScroll=False) else: logger.verbose("Requested item %s is a folder" % tHandle) return def _keyPressEscape(self): """When the escape key is pressed somewhere in the main window, do the following, in order: """ if self.docEditor.docSearch.isVisible(): self.docEditor.closeSearch() elif self.isFocusMode: self.toggleFocusMode() return def _mainTabChanged(self, tabIndex): """Activated when the main window tab is changed. """ if tabIndex == self.idxTabEdit: logger.verbose("Editor tab activated") elif tabIndex == self.idxTabProj: logger.verbose("Project outline tab activated") if self.hasProject: self.projView.refreshTree() return
def resizeEvent(self, ev): size = self.tabBar().size() QTabWidget.resizeEvent(self, ev) self.tabBar().resize(size)
def __init__(self, parent=None): super(LicenseDialog, self).__init__(parent) self.resize(420, 320) self.setWindowTitle(tr("Credits & Licensing")) self.setWindowFlags(Qt.Dialog) self.readme = "" self.license = "" self.thanks = "" self.authors = "" self.tabWidget = QTabWidget(self) self.setCentralWidget(self.tabWidget) for folder in (app_folder, os.path.dirname(app_folder)): for fname in os.listdir(folder): if fname.startswith("LICENSE"): try: f = open(os.path.join(folder, fname), "r") except: pass else: self.license = f.read() f.close() elif fname.startswith("THANKS"): try: f = open(os.path.join(folder, fname), "r") except: pass else: self.thanks = f.read() f.close() elif fname.startswith("AUTHORS"): try: f = open(os.path.join(folder, fname), "r") except: pass else: self.authors = f.read() f.close() elif fname.startswith("README"): try: f = open(os.path.join(folder, fname), "r") except: pass else: self.readme = f.read() f.close() self.readmeView = ReadOnlyTextEdit(self) self.readmeView.setText(self.readme) self.tabWidget.addTab(self.readmeView, tr("&README")) self.authorsView = ReadOnlyTextEdit(self) self.authorsView.setText(self.authors) self.tabWidget.addTab(self.authorsView, tr("&Authors")) self.thanksView = ReadOnlyTextEdit(self) self.thanksView.setText(self.thanks) self.tabWidget.addTab(self.thanksView, tr("&Thanks")) self.licenseView = ReadOnlyTextEdit(self) self.licenseView.setText(self.license) self.tabWidget.addTab(self.licenseView, tr("&License")) closeAction = QAction(self) closeAction.setShortcuts(["Esc", "Ctrl+W"]) closeAction.triggered.connect(self.hide) self.addAction(closeAction) self.toolBar = QToolBar(self) self.toolBar.setStyleSheet(blank_toolbar) self.toolBar.setMovable(False) self.toolBar.setContextMenuPolicy(Qt.CustomContextMenu) self.addToolBar(Qt.BottomToolBarArea, self.toolBar) self.toolBar.addWidget(HorizontalExpander(self)) self.closeButton = QPushButton(tr("&OK"), self) self.closeButton.clicked.connect(self.close) self.toolBar.addWidget(self.closeButton) self.closeButton.setFocus()
class AppWindow(QMainWindow): onRestart = pyqtSignal(name='onRestart') def __init__(self, dwarf_args, flags=None): super(AppWindow, self).__init__(flags) self.dwarf_args = dwarf_args self.session_manager = SessionManager(self) self.session_manager.sessionCreated.connect(self.session_created) self.session_manager.sessionStopped.connect(self.session_stopped) self.session_manager.sessionClosed.connect(self.session_closed) self._tab_order = [ 'memory', 'modules', 'ranges', 'jvm-inspector', 'jvm-debugger' ] self.menu = self.menuBar() self._is_newer_dwarf = False self.view_menu = None #dockwidgets self.watchers_dwidget = None self.hooks_dwiget = None self.bookmarks_dwiget = None self.registers_dock = None self.console_dock = None self.backtrace_dock = None self.threads_dock = None #panels self.asm_panel = None self.console_panel = None self.context_panel = None self.backtrace_panel = None self.contexts_list_panel = None self.data_panel = None self.emulator_panel = None self.ftrace_panel = None self.hooks_panel = None self.bookmarks_panel = None self.smali_panel = None self.java_inspector_panel = None self.java_explorer_panel = None self.java_trace_panel = None self.memory_panel = None self.modules_panel = None self.ranges_panel = None self.search_panel = None self.watchers_panel = None self.welcome_window = None self._ui_elems = [] self.setWindowTitle( 'Dwarf - A debugger for reverse engineers, crackers and security analyst' ) # load external assets _app = QApplication.instance() self.remove_tmp_dir() # themes self.prefs = Prefs() self.set_theme(self.prefs.get('dwarf_ui_theme', 'black')) # load font if os.path.exists(utils.resource_path('assets/Anton.ttf')): QFontDatabase.addApplicationFont( utils.resource_path('assets/Anton.ttf')) if os.path.exists(utils.resource_path('assets/OpenSans-Regular.ttf')): QFontDatabase.addApplicationFont( utils.resource_path('assets/OpenSans-Regular.ttf')) font = QFont("OpenSans", 9, QFont.Normal) # TODO: add settingsdlg font_size = self.prefs.get('dwarf_ui_font_size', 12) font.setPixelSize(font_size) _app.setFont(font) if os.path.exists(utils.resource_path('assets/OpenSans-Bold.ttf')): QFontDatabase.addApplicationFont( utils.resource_path('assets/OpenSans-Bold.ttf')) # mainwindow statusbar self.progressbar = QProgressBar() self.progressbar.setRange(0, 0) self.progressbar.setVisible(False) self.progressbar.setFixedHeight(15) self.progressbar.setFixedWidth(100) self.progressbar.setTextVisible(False) self.progressbar.setValue(30) self.statusbar = QStatusBar(self) self.statusbar.setAutoFillBackground(False) self.statusbar.addPermanentWidget(self.progressbar) self.statusbar.setObjectName("statusbar") self.setStatusBar(self.statusbar) self.main_tabs = QTabWidget(self) self.main_tabs.setMovable(False) self.main_tabs.setTabsClosable(True) self.main_tabs.setAutoFillBackground(True) self.main_tabs.tabCloseRequested.connect(self._on_close_tab) self.setCentralWidget(self.main_tabs) if self.dwarf_args.package is None: self.welcome_window = WelcomeDialog(self) self.welcome_window.setModal(True) self.welcome_window.onIsNewerVersion.connect( self._enable_update_menu) self.welcome_window.onUpdateComplete.connect( self._on_dwarf_updated) self.welcome_window.setWindowTitle( 'Welcome to Dwarf - A debugger for reverse engineers, crackers and security analyst' ) self.welcome_window.onSessionSelected.connect(self._start_session) # wait for welcome screen self.hide() self.welcome_window.show() else: if dwarf_args.package is not None: if dwarf_args.type is None: # no device given check if package is local path if os.path.exists(dwarf_args.package): print('* Starting new LocalSession') self._start_session('local') else: print('use -t to set sessiontype') exit(0) else: print('* Starting new Session') self._start_session(dwarf_args.type) def _setup_main_menu(self): self.menu = self.menuBar() dwarf_menu = QMenu('Dwarf', self) theme = QMenu('Theme', dwarf_menu) theme.addAction('Black') theme.addAction('Dark') theme.addAction('Light') theme.triggered.connect(self._set_theme) dwarf_menu.addMenu(theme) dwarf_menu.addSeparator() if self._is_newer_dwarf: dwarf_menu.addAction('Update', self._update_dwarf) dwarf_menu.addAction('Close', self.session_manager.session.stop) self.menu.addMenu(dwarf_menu) session = self.session_manager.session if session is not None: session_menu = session.main_menu if isinstance(session_menu, list): for menu in session_menu: self.menu.addMenu(menu) else: self.menu.addMenu(session_menu) self.view_menu = QMenu('View', self) subview_menu = QMenu('Subview', self.view_menu) subview_menu.addAction('Search', lambda: self.show_main_tab('search'), shortcut=QKeySequence(Qt.CTRL + Qt.Key_F3)) subview_menu.addAction('Emulator', lambda: self.show_main_tab('emulator'), shortcut=QKeySequence(Qt.CTRL + Qt.Key_F2)) subview_menu.addAction('Disassembly', lambda: self.show_main_tab('disassembly'), shortcut=QKeySequence(Qt.CTRL + Qt.Key_F5)) self.view_menu.addMenu(subview_menu) self.view_menu.addSeparator() self.menu.addMenu(self.view_menu) if self.dwarf_args.debug_script: debug_menu = QMenu('Debug', self) debug_menu.addAction('Reload core', self._menu_reload_core) debug_menu.addAction('Debug dwarf js core', self._menu_debug_dwarf_js) self.menu.addMenu(debug_menu) about_menu = QMenu('About', self) about_menu.addAction('Dwarf on GitHub', self._menu_github) about_menu.addAction('Documention', self._menu_documentation) about_menu.addAction('Api', self._menu_api) about_menu.addAction('Slack', self._menu_slack) about_menu.addSeparator() about_menu.addAction('Info', self._show_about_dlg) self.menu.addMenu(about_menu) def _enable_update_menu(self): self._is_newer_dwarf = True def _update_dwarf(self): if self.welcome_window: self.welcome_window._update_dwarf() def _on_close_tab(self, index): tab_text = self.main_tabs.tabText(index) if tab_text: if tab_text.lower() in self.session_manager.session.non_closable: return try: self._ui_elems.remove(tab_text.lower()) except ValueError: # recheck ValueError: list.remove(x): x not in list pass self.main_tabs.removeTab(index) def _handle_tab_change(self): for index in range(self.main_tabs.count()): tab_name = self.main_tabs.tabText(index).lower().replace(' ', '-') if tab_name in self.session_manager.session.non_closable: self.main_tabs.tabBar().setTabButton(index, QTabBar.RightSide, None) if tab_name in self._tab_order: should_index = self._tab_order.index(tab_name) if index != should_index: self.main_tabs.tabBar().moveTab(index, should_index) def _on_dwarf_updated(self): self.onRestart.emit() def remove_tmp_dir(self): if os.path.exists('.tmp'): shutil.rmtree('.tmp', ignore_errors=True) def _set_theme(self, qaction): if qaction: self.set_theme(qaction.text()) def _menu_reload_core(self): self.dwarf.load_script() def _menu_debug_dwarf_js(self): you_know_what_to_do = json.loads( self.dwarf._script.exports.debugdwarfjs()) return you_know_what_to_do def show_main_tab(self, name): # elem doesnt exists? create it if name not in self._ui_elems: self._create_ui_elem(name) index = 0 name = name.join(name.split()).lower() if name == 'memory': index = self.main_tabs.indexOf(self.memory_panel) elif name == 'ranges': index = self.main_tabs.indexOf(self.ranges_panel) elif name == 'search': index = self.main_tabs.indexOf(self.search_panel) elif name == 'modules': index = self.main_tabs.indexOf(self.modules_panel) elif name == 'disassembly': index = self.main_tabs.indexOf(self.asm_panel) elif name == 'data': index = self.main_tabs.indexOf(self.data_panel) elif name == 'emulator': index = self.main_tabs.indexOf(self.emulator_panel) elif name == 'java-trace': index = self.main_tabs.indexOf(self.java_trace_panel) elif name == 'jvm-inspector': index = self.main_tabs.indexOf(self.java_inspector_panel) elif name == 'jvm-debugger': index = self.main_tabs.indexOf(self.java_explorer_panel) elif name == 'smali': index = self.main_tabs.indexOf(self.smali_panel) self.main_tabs.setCurrentIndex(index) def jump_to_address(self, ptr, show_panel=True): if self.memory_panel is not None: if show_panel: self.show_main_tab('memory') self.memory_panel.read_memory(ptr) @pyqtSlot(name='mainMenuGitHub') def _menu_github(self): QDesktopServices.openUrl(QUrl('https://github.com/iGio90/Dwarf')) @pyqtSlot(name='mainMenuDocumentation') def _menu_api(self): QDesktopServices.openUrl(QUrl('https://igio90.github.io/Dwarf/')) @pyqtSlot(name='mainMenuApi') def _menu_documentation(self): QDesktopServices.openUrl(QUrl('https://igio90.github.io/Dwarf/api')) @pyqtSlot(name='mainMenuSlack') def _menu_slack(self): QDesktopServices.openUrl( QUrl('https://join.slack.com/t/resecret/shared_invite' '/enQtMzc1NTg4MzE3NjA1LTlkNzYxNTIwYTc2ZTYyOWY1MT' 'Q1NzBiN2ZhYjQwYmY0ZmRhODQ0NDE3NmRmZjFiMmE1MDYwN' 'WJlNDVjZDcwNGE')) def _show_about_dlg(self): about_dlg = AboutDialog(self) about_dlg.show() def _create_ui_elem(self, elem): if not isinstance(elem, str): return if elem not in self._ui_elems: self._ui_elems.append(elem) if elem == 'watchers': from ui.panel_watchers import WatchersPanel self.watchers_dwidget = QDockWidget('Watchers', self) self.watchers_panel = WatchersPanel(self) # dont respond to dblclick mem cant be shown # self.watchers_panel.onItemDoubleClicked.connect( # self._on_watcher_clicked) self.watchers_panel.onItemRemoved.connect( self._on_watcher_removeditem) self.watchers_panel.onItemAdded.connect(self._on_watcher_added) self.watchers_dwidget.setWidget(self.watchers_panel) self.watchers_dwidget.setObjectName('WatchersPanel') self.addDockWidget(Qt.LeftDockWidgetArea, self.watchers_dwidget) self.view_menu.addAction(self.watchers_dwidget.toggleViewAction()) elif elem == 'hooks': from ui.panel_hooks import HooksPanel self.hooks_dwiget = QDockWidget('Breakpoints', self) self.hooks_panel = HooksPanel(self) self.hooks_panel.onShowMemoryRequest.connect( self._on_watcher_clicked) self.hooks_panel.onHookRemoved.connect(self._on_hook_removed) self.hooks_dwiget.setWidget(self.hooks_panel) self.hooks_dwiget.setObjectName('HooksPanel') self.addDockWidget(Qt.LeftDockWidgetArea, self.hooks_dwiget) self.view_menu.addAction(self.hooks_dwiget.toggleViewAction()) elif elem == 'bookmarks': from ui.panel_bookmarks import BookmarksPanel self.bookmarks_dwiget = QDockWidget('Boomarks', self) self.bookmarks_panel = BookmarksPanel(self) self.bookmarks_panel.onShowMemoryRequest.connect( self._on_watcher_clicked) self.bookmarks_dwiget.setWidget(self.bookmarks_panel) self.bookmarks_dwiget.setObjectName('BookmarksPanel') self.addDockWidget(Qt.LeftDockWidgetArea, self.bookmarks_dwiget) self.view_menu.addAction(self.bookmarks_dwiget.toggleViewAction()) elif elem == 'registers': from ui.panel_context import ContextPanel self.registers_dock = QDockWidget('Context', self) self.context_panel = ContextPanel(self) self.registers_dock.setWidget(self.context_panel) self.registers_dock.setObjectName('ContextsPanel') self.addDockWidget(Qt.RightDockWidgetArea, self.registers_dock) self.view_menu.addAction(self.registers_dock.toggleViewAction()) elif elem == 'memory': from ui.panel_memory import MemoryPanel self.memory_panel = MemoryPanel(self) self.memory_panel.onShowDisassembly.connect( self._disassemble_range) self.memory_panel.dataChanged.connect(self._on_memory_modified) self.memory_panel.statusChanged.connect(self.set_status_text) self.main_tabs.addTab(self.memory_panel, 'Memory') elif elem == 'jvm-debugger': from ui.panel_java_explorer import JavaExplorerPanel self.java_explorer_panel = JavaExplorerPanel(self) self.main_tabs.addTab(self.java_explorer_panel, 'JVM debugger') self.main_tabs.tabBar().moveTab( self.main_tabs.indexOf(self.java_explorer_panel), 1) elif elem == 'jvm-inspector': from ui.panel_java_inspector import JavaInspector self.java_inspector_panel = JavaInspector(self) self.main_tabs.addTab(self.java_inspector_panel, 'JVM inspector') elif elem == 'console': from ui.panel_console import ConsolePanel self.console_dock = QDockWidget('Console', self) self.console_panel = ConsolePanel(self) if self.dwarf_args.script and len( self.dwarf_args.script) > 0 and os.path.exists( self.dwarf_args.script): with open(self.dwarf_args.script, 'r') as f: self.console_panel.get_js_console( ).function_content = f.read() self.dwarf.onLogToConsole.connect(self._log_js_output) self.dwarf.onLogEvent.connect(self._log_event) self.console_dock.setWidget(self.console_panel) self.console_dock.setObjectName('ConsolePanel') self.addDockWidget(Qt.BottomDockWidgetArea, self.console_dock) self.view_menu.addAction(self.console_dock.toggleViewAction()) elif elem == 'backtrace': from ui.panel_backtrace import BacktracePanel self.backtrace_dock = QDockWidget('Backtrace', self) self.backtrace_panel = BacktracePanel(self) self.backtrace_dock.setWidget(self.backtrace_panel) self.backtrace_dock.setObjectName('BacktracePanel') self.backtrace_panel.onShowMemoryRequest.connect( self._on_watcher_clicked) self.addDockWidget(Qt.RightDockWidgetArea, self.backtrace_dock) self.view_menu.addAction(self.backtrace_dock.toggleViewAction()) elif elem == 'threads': from ui.panel_contexts_list import ContextsListPanel self.threads_dock = QDockWidget('Threads', self) self.contexts_list_panel = ContextsListPanel(self) self.dwarf.onThreadResumed.connect( self.contexts_list_panel.resume_tid) self.contexts_list_panel.onItemDoubleClicked.connect( self._manually_apply_context) self.threads_dock.setWidget(self.contexts_list_panel) self.threads_dock.setObjectName('ThreadPanel') self.addDockWidget(Qt.RightDockWidgetArea, self.threads_dock) self.view_menu.addAction(self.threads_dock.toggleViewAction()) elif elem == 'modules': from ui.panel_modules import ModulesPanel self.modules_panel = ModulesPanel(self) self.modules_panel.onModuleSelected.connect( self._on_module_dblclicked) self.modules_panel.onModuleFuncSelected.connect( self._on_modulefunc_dblclicked) self.modules_panel.onAddHook.connect(self._on_addmodule_hook) self.modules_panel.onDumpBinary.connect(self._on_dumpmodule) self.main_tabs.addTab(self.modules_panel, 'Modules') elif elem == 'ranges': from ui.panel_ranges import RangesPanel self.ranges_panel = RangesPanel(self) self.ranges_panel.onItemDoubleClicked.connect( self._range_dblclicked) self.ranges_panel.onDumpBinary.connect(self._on_dumpmodule) # connect to watcherpanel func self.ranges_panel.onAddWatcher.connect( self.watchers_panel.do_addwatcher_dlg) self.main_tabs.addTab(self.ranges_panel, 'Ranges') elif elem == 'search': from ui.panel_search import SearchPanel self.search_panel = SearchPanel(self) self.search_panel.onShowMemoryRequest.connect( self._on_watcher_clicked) self.main_tabs.addTab(self.search_panel, 'Search') elif elem == 'data': from ui.panel_data import DataPanel self.data_panel = DataPanel(self) self.main_tabs.addTab(self.data_panel, 'Data') elif elem == 'disassembly': from ui.widgets.disasm_view import DisassemblyView self.asm_panel = DisassemblyView(self) self.asm_panel.onShowMemoryRequest.connect(self._on_disasm_showmem) self.main_tabs.addTab(self.asm_panel, 'Disassembly') elif elem == 'emulator': from ui.panel_emulator import EmulatorPanel self.emulator_panel = EmulatorPanel(self) self.main_tabs.addTab(self.emulator_panel, 'Emulator') elif elem == 'java-trace': from ui.panel_java_trace import JavaTracePanel self.java_trace_panel = JavaTracePanel(self) self.main_tabs.addTab(self.java_trace_panel, 'JVM tracer') elif elem == 'smali': from ui.panel_smali import SmaliPanel self.smali_panel = SmaliPanel() self.main_tabs.addTab(self.smali_panel, 'Smali') else: print('no handler for elem: ' + elem) # make tabs unclosable and sort self._handle_tab_change() # TODO: remove add @2x for item in self.findChildren(QDockWidget): if item: if 'darwin' in sys.platform: item.setStyleSheet( 'QDockWidget::title { padding-left:-30px; }') def set_theme(self, theme): if theme: theme = theme.replace(os.pardir, '').replace('.', '') theme = theme.join(theme.split()).lower() theme_style = 'assets/' + theme + '_style.qss' if not os.path.exists(utils.resource_path(theme_style)): return self.prefs.put('dwarf_ui_theme', theme) try: _app = QApplication.instance() with open(theme_style) as stylesheet: _app.setStyleSheet(_app.styleSheet() + '\n' + stylesheet.read()) except Exception as e: pass # err = self.dwarf.spawn(dwarf_args.package, dwarf_args.script) def set_status_text(self, txt): self.statusbar.showMessage(txt) # ************************************************************************ # **************************** Properties ******************************** # ************************************************************************ @property def disassembly(self): return self.asm_panel @property def backtrace(self): return self.backtrace_panel @property def console(self): return self.console_panel @property def context(self): return self.context_panel @property def threads(self): return self.contexts_list_panel @property def emulator(self): return self.emulator_panel @property def ftrace(self): return self.ftrace_panel @property def hooks(self): return self.hooks_panel @property def java_inspector(self): return self.java_inspector_panel @property def java_explorer(self): return self.java_explorer_panel @property def memory(self): return self.memory_panel @property def modules(self): return self.memory_panel @property def ranges(self): return self.ranges_panel @property def watchers(self): return self.watchers_panel @property def dwarf(self): if self.session_manager.session is not None: return self.session_manager.session.dwarf else: return None # ************************************************************************ # **************************** Handlers ********************************** # ************************************************************************ # session handlers def _start_session(self, session_type, session_data=None): if self.welcome_window is not None: self.welcome_window.close() self.session_manager.create_session(session_type, session_data=session_data) def _restore_session(self, session_data): if 'session' in session_data: session_type = session_data['session'] self._start_session(session_type, session_data=session_data) def session_created(self): # session init done create ui for it session = self.session_manager.session self._setup_main_menu() for ui_elem in session.session_ui_sections: ui_elem = ui_elem.join(ui_elem.split()).lower() self._create_ui_elem(ui_elem) self.dwarf.onProcessAttached.connect(self._on_attached) self.dwarf.onProcessDetached.connect(self._on_detached) self.dwarf.onScriptLoaded.connect(self._on_script_loaded) # hookup self.dwarf.onSetRanges.connect(self._on_setranges) self.dwarf.onSetModules.connect(self._on_setmodules) self.dwarf.onAddNativeHook.connect(self._on_add_hook) self.dwarf.onApplyContext.connect(self._apply_context) self.dwarf.onThreadResumed.connect(self.on_tid_resumed) self.dwarf.onSetData.connect(self._on_set_data) self.session_manager.start_session(self.dwarf_args) q_settings = QSettings("dwarf_window_pos.ini", QSettings.IniFormat) ui_state = q_settings.value('dwarf_ui_state') if ui_state: self.restoreGeometry(ui_state) window_state = q_settings.value('dwarf_ui_window', self.saveState()) if window_state: self.restoreState(window_state) self.showMaximized() def session_stopped(self): self.remove_tmp_dir() self.menu.clear() self.main_tabs.clear() # actually we need to kill this. needs a refactor if self.java_trace_panel is not None: self.java_trace_panel = None for elem in self._ui_elems: if elem == 'watchers': self.watchers_panel.clear_list() self.watchers_panel.close() self.watchers_panel = None self.removeDockWidget(self.watchers_dwidget) self.watchers_dwidget = None elif elem == 'hooks': self.hooks_panel.close() self.hooks_panel = None self.removeDockWidget(self.hooks_dwiget) self.hooks_dwiget = None elif elem == 'registers': self.context_panel.close() self.context_panel = None self.removeDockWidget(self.registers_dock) self.registers_dock = None elif elem == 'memory': self.memory_panel.close() self.memory_panel = None self.main_tabs.removeTab(0) # self.main_tabs elif elem == 'jvm-debugger': self.java_explorer_panel.close() self.java_explorer_panel = None self.removeDockWidget(self.watchers_dwidget) elif elem == 'console': self.console_panel.close() self.console_panel = None self.removeDockWidget(self.console_dock) self.console_dock = None elif elem == 'backtrace': self.backtrace_panel.close() self.backtrace_panel = None self.removeDockWidget(self.backtrace_dock) elif elem == 'threads': self.contexts_list_panel.close() self.contexts_list_panel = None self.removeDockWidget(self.threads_dock) self.threads_dock = None elif elem == 'bookmarks': self.bookmarks_panel.close() self.bookmarks_panel = None self.removeDockWidget(self.bookmarks_dwiget) self.bookmarks_dwiget = None def session_closed(self): self._ui_elems = [] self.hide() if self.welcome_window is not None: self.welcome_window.exec() # close if it was a commandline session if self.welcome_window is None: if self.dwarf_args.package: self.close() # ui handler def closeEvent(self, event): """ Window closed save stuff or whatever at exit detaches dwarf """ # save windowstuff q_settings = QSettings("dwarf_window_pos.ini", QSettings.IniFormat) q_settings.setValue('dwarf_ui_state', self.saveGeometry()) q_settings.setValue('dwarf_ui_window', self.saveState()) if self.dwarf: self.dwarf.detach() super().closeEvent(event) def _on_watcher_clicked(self, ptr): """ Address in Watcher/Hookpanel was clicked show Memory """ if '.' in ptr: # java_hook file_path = ptr.replace('.', os.path.sep) if os.path.exists('.tmp/smali/' + file_path + '.smali'): if self.smali_panel is None: self._create_ui_elem('smali') self.smali_panel.set_file('.tmp/smali/' + file_path + '.smali') self.show_main_tab('smali') else: self.memory_panel.read_memory(ptr=ptr) self.show_main_tab('memory') def _on_disasm_showmem(self, ptr, length): """ Address in Disasm was clicked adds temphighlight for bytes from current instruction """ self.memory_panel.read_memory(ptr) self.memory_panel.add_highlight( HighLight('attention', utils.parse_ptr(ptr), length)) self.show_main_tab('memory') def _on_watcher_added(self, ptr): """ Watcher Entry was added """ try: # set highlight self.memory_panel.add_highlight( HighLight('watcher', ptr, self.dwarf.pointer_size)) except HighlightExistsError: pass def _on_watcher_removeditem(self, ptr): """ Watcher Entry was removed remove highlight too """ self.memory_panel.remove_highlight(ptr) def _on_module_dblclicked(self, data): """ Module in ModulePanel was doubleclicked """ addr, size = data addr = utils.parse_ptr(addr) size = int(size, 10) self.memory_panel.read_memory(ptr=addr, length=size) self.show_main_tab('Memory') def _on_modulefunc_dblclicked(self, ptr): """ Function in ModulePanel was doubleclicked """ ptr = utils.parse_ptr(ptr) self.memory_panel.read_memory(ptr=ptr) self.show_main_tab('Memory') def _on_dumpmodule(self, data): """ DumpBinary MenuItem in ModulePanel was selected """ ptr, size = data ptr = utils.parse_ptr(ptr) size = int(size, 10) self.dwarf.dump_memory(ptr=ptr, length=size) def _disassemble_range(self, mem_range): """ Disassemble MenuItem in Hexview was selected """ if mem_range: if self.asm_panel is None: self._create_ui_elem('disassembly') if mem_range: self.asm_panel.disassemble(mem_range) self.show_main_tab('disassembly') def _range_dblclicked(self, ptr): """ Range in RangesPanel was doubleclicked """ ptr = utils.parse_ptr(ptr) self.memory_panel.read_memory(ptr=ptr) self.show_main_tab('Memory') # dwarf handlers def _log_js_output(self, output): if self.console_panel is not None: self.console_panel.get_js_console().log(output) def _log_event(self, output): if self.console_panel is not None: self.console_panel.get_events_console().log(output) def _on_setranges(self, ranges): """ Dwarf wants to set Ranges only hooked up to switch tab or create ui its connected in panel after creation """ if self.ranges_panel is None: self.show_main_tab('ranges') # forward only now to panel it connects after creation self.ranges_panel.set_ranges(ranges) def _on_setmodules(self, modules): """ Dwarf wants to set Modules only hooked up to switch tab or create ui its connected in panel after creation """ if self.modules_panel is None: self._create_ui_elem('modules') self.modules_panel.set_modules(modules) if self.modules_panel is not None: self.show_main_tab('modules') def _manually_apply_context(self, context): """ perform additional operation if the context has been manually applied from the context list """ self._apply_context(context, manual=True) def _apply_context(self, context, manual=False): # update current context tid # this should be on top as any further api from js needs to be executed on that thread is_initial_hook = context['reason'] >= 0 if manual or (self.dwarf.context_tid and not is_initial_hook): self.dwarf.context_tid = context['tid'] if 'context' in context: if not manual: self.threads.add_context(context) is_java = context['is_java'] if is_java: if self.java_explorer_panel is None: self._create_ui_elem('jvm-debugger') self.context_panel.set_context(context['ptr'], 1, context['context']) self.java_explorer_panel._set_handle_arg(-1) self.show_main_tab('jvm-debugger') else: self.context_panel.set_context(context['ptr'], 0, context['context']) if 'pc' in context['context']: if not 'disassembly' in self._ui_elems or manual: from lib.range import Range _range = Range(Range.SOURCE_TARGET, self.dwarf) _range.init_with_address( int(context['context']['pc']['value'], 16)) self._disassemble_range(_range) if 'backtrace' in context: self.backtrace_panel.set_backtrace(context['backtrace']) def _on_add_hook(self, hook): try: # set highlight ptr = hook.get_ptr() ptr = utils.parse_ptr(ptr) self.memory_panel.add_highlight( HighLight('hook', ptr, self.dwarf.pointer_size)) except HighlightExistsError: pass def _on_hook_removed(self, ptr): ptr = utils.parse_ptr(ptr) self.memory_panel.remove_highlight(ptr) def _on_addmodule_hook(self, data): ptr, name = data self.dwarf.hook_native(ptr, own_input=name) def on_tid_resumed(self, tid): if self.dwarf: if self.dwarf.context_tid == tid: # clear backtrace if 'backtrace' in self._ui_elems: if self.backtrace_panel is not None: self.backtrace_panel.clear() # remove thread if 'threads' in self._ui_elems: if self.contexts_list_panel is not None: self.contexts_list_panel.resume_tid(tid) # clear registers if 'registers' in self._ui_elems: if self.context_panel is not None: self.context_panel.clear() # clear jvm explorer if 'jvm-debugger' in self._ui_elems: if self.java_explorer_panel is not None: self.java_explorer_panel.clear_panel() # invalidate dwarf context tid self.dwarf.context_tid = 0 def _on_set_data(self, data): if not isinstance(data, list): return if self.data_panel is None: self._create_ui_elem('data') if self.data_panel is not None: self.show_main_tab('Data') self.data_panel.append_data(data[0], data[1], data[2]) def show_progress(self, text): self.progressbar.setVisible(True) self.set_status_text(text) def hide_progress(self): self.progressbar.setVisible(False) self.set_status_text('') def _on_attached(self, data): self.setWindowTitle('Dwarf - Attached to %s (%s)' % (data[1], data[0])) def _on_detached(self, data): reason = data[1] if reason == 'application-requested': self.session_manager.session.stop() return 0 ret = QDialogDetached.show_dialog(self.dwarf, data[0], data[1], data[2]) if ret == 0: self.dwarf.restart_proc() elif ret == 1: self.session_manager.session.stop() return 0 def _on_script_loaded(self): # restore the loaded session if any self.session_manager.restore_session() def _on_memory_modified(self, pos, length): data_pos = self.memory_panel.base + pos data = self.memory_panel.data[pos:pos + length] data = [data[0]] # todo: strange js part if self.dwarf.dwarf_api('writeBytes', [data_pos, data]): pass else: utils.show_message_box('Failed to write Memory') def on_add_bookmark(self, ptr): """ provide ptr as int """ if self.bookmarks_panel is not None: self.bookmarks_panel._create_bookmark(ptr=hex(ptr))
class MyTableWidget(QWidget): def __init__(self, parent): super(QWidget, self).__init__(parent) self.layout = QVBoxLayout(self) #self.setStyleSheet("background-color:grey") self.setAutoFillBackground(True) # Initialize tab screen self.tabs = QTabWidget() self.tab1 = QWidget() self.tab2 = QWidget() self.tabs.resize(300,200) # Add tabs self.tabs.addTab(self.tab1,"Main") self.tabs.addTab(self.tab2,"Config") # Create first tab self.tab1.layout = QGridLayout(self) # Create Load button self.loadButton = QPushButton("Load Gcode") self.loadButton.clicked.connect(self.loadGcode) self.tab1.layout.addWidget(self.loadButton,0,0) # Create Gcode File Dir textbox self.gcodeDirTextBox = QLabel("Load Gcode file") self.gcodeDirTextBox.setAlignment(Qt.Qt.AlignCenter) self.tab1.layout.addWidget(self.gcodeDirTextBox,0,1,1,2) # Create Connect button self.connectButton = QPushButton("Connect") self.tab1.layout.addWidget(self.connectButton,1,0) self.connectButton.clicked.connect(self.connect) # Create Print button self.Print = QPushButton("Print") self.tab1.layout.addWidget(self.Print,1,1) self.Print.clicked.connect(self.machinePrint) #create pause button self.pauseButton = QPushButton("Pause") self.tab1.layout.addWidget(self.pauseButton,1,2) self.pauseButton.clicked.connect(self.pause) #create machine group box hBox = QGridLayout() groupBox = QGroupBox() #create input command for both machine #both input group box generalInputGroupBox = QGroupBox("Send To Both") generalInputGroupBoxLayout = QGridLayout() #command input box self.generalInputCommandBox = QLineEdit("SEND TO BOTH") #Send button generalInputSendButton = QPushButton("SEND") generalInputSendButton.clicked.connect(self.sendToALL) generalInputGroupBoxLayout.addWidget(self.generalInputCommandBox,0,0,1,2) generalInputGroupBoxLayout.addWidget(generalInputSendButton,0,2) generalInputGroupBox.setLayout(generalInputGroupBoxLayout) #create machine one group groupBox1 = QGroupBox("Machine One") hBox1 = QGridLayout() #create machine one port8 self.portOneButton = QLineEdit("COM7") self.portOne = "COM7" self.portOneButton.textChanged.connect(self.updatePortName) #create machine one baudrate baudrateOneButton = QLineEdit("115200") self.baudrateOne = 115200 #create machine one send comman box self.oneInputCommandBox = QLineEdit("SEND TO ONE") oneInputSendButton = QPushButton("SEND") oneInputSendButton.clicked.connect(self.sendToOne) hBox1.addWidget(self.portOneButton,0,0) hBox1.addWidget(baudrateOneButton,0,1) hBox1.addWidget(self.oneInputCommandBox,1,0,1,2) hBox1.addWidget(oneInputSendButton,1,3) groupBox1.setLayout(hBox1) #create machine two group groupBox2 = QGroupBox("Machine Two") hBox2 = QGridLayout() #create machine two port self.portTwoButton = QLineEdit("COM8") self.portTwo = "COM8" self.portTwoButton.textChanged.connect(self.updatePortName) #create machine two baudrate baudrateTwoButton = QLineEdit("115200") self.baudrateTwo = 115200 #create machine one send comman box self.twoInputCommandBox = QLineEdit("SEND TO TWO") twoInputSendButton = QPushButton("SEND") twoInputSendButton.clicked.connect(self.sendToTwo) hBox2.addWidget(self.portTwoButton,0,0) hBox2.addWidget(baudrateTwoButton,0,1) hBox2.addWidget(self.twoInputCommandBox,1,0,1,2) hBox2.addWidget(twoInputSendButton,1,3) groupBox2.setLayout(hBox2) hBox.addWidget(generalInputGroupBox,0,0,1,2) hBox.addWidget(groupBox1,1,0) hBox.addWidget(groupBox2,1,1) groupBox.setLayout(hBox) self.tab1.layout.addWidget(groupBox,2,0,1,3) ################# self.tab1.setLayout(self.tab1.layout) # Add tabs to widget self.layout.addWidget(self.tabs) self.setLayout(self.layout) ################################## ALL SLOT HERE #################################################### @pyqtSlot() def loadGcode(self): print("load gcode file") options = QFileDialog.Options() options |= QFileDialog.DontUseNativeDialog fileName, _ = QFileDialog.getOpenFileName(self,"QFileDialog.getOpenFileName()", "","All Files (*);;Python Files (*.gcode)", options=options) if fileName: print(fileName) self.gcodeDir = fileName self.gcodeDirTextBox.setText(fileName) self.splitGcode() @pyqtSlot() def doConnect(self): try: print("connect to machine") self.onePrinter = virtualPrinter.typeOnePrinter("nameOne","Gcode/one.gcode",self.portOne,self.baudrateOne) self.twoPrinter = virtualPrinter.typeTwoPrinter("nameTwo","Gcode/two.gcode",self.portTwo,self.baudrateTwo) # get gcode data self.onePrinter.getGcodeData() self.twoPrinter.getGcodeData() # get friend printer self.onePrinter.getFirstFriendPrinter(self.twoPrinter) self.twoPrinter.getFirstFriendPrinter(self.onePrinter) # Connect One Printer self.onePrinter.connectToPrinter() #Connect Two Printer self.twoPrinter.connectToPrinter() print("Connect OK") except: print("can not connect to machine") print(Exception) def connect(self): x = threading.Thread(target = self.doConnect) x.daemon = True x.start() def updatePortName(self): self.portOne = str(self.portOneButton.text()) self.portTwo = str(self.portTwoButton.text()) print(self.portOne,self.portTwo) def doMachineOnePrint(self): try: print("3D printer print") virtualPrinter.runningEvent.set() self.onePrinter.start() self.onePrinter.join() except: print("Connect before print") def doMachineTwoPrint(self): try: print("3D printer print") self.twoPrinter.start() self.twoPrinter.join() except: print("Connect before print") @pyqtSlot() def machinePrint(self): x = threading.Thread(target=self.doMachineOnePrint) y = threading.Thread(target=self.doMachineTwoPrint) x.daemon = True y.daemon = True y.start() x.start() def splitGcode(self): try: gcodesplit.split(self.gcodeDir) print("Split OK") except: print("can not split gcode") @pyqtSlot() def pause(self): virtualPrinter.runningEvent.clear() print("pause") def doSendToAll(self): try: self.twoPrinter.sendGcode(str(self.generalInputCommandBox.text())) self.onePrinter.sendGcode(str(self.generalInputCommandBox.text())) except: print("Can not send to all") def sendToALL(self): x = threading.Thread(target=self.doSendToAll) x.start() def doSendToOne(self): try: self.onePrinter.sendGcode(str(self.oneInputCommandBox.text())) except: print("Can not send to one") def sendToOne(self): x = threading.Thread(target=self.doSendToOne) x.daemon = True x.start() def doSendToTwo(self): try: self.twoPrinter.sendGcode(str(self.twoInputCommandBox.text())) except: print("Can not send to two") def sendToTwo(self): x = threading.Thread(target=self.doSendToTwo) x.daemon = True x.start()
class ModeBResultsWidget(QWidget): def __init__(self, models, phaseNames, phaseValues, results, parent=None): super().__init__() self.models = models self.phaseNames = phaseNames self.phaseValues = phaseValues self.results = results self.tabWidget = QTabWidget() self.tabWidget.addTab(self.modeBResultPlot(), "Mode 2 Results") self.tabWidget.addTab(self.mode3ResultPlot(), "Mode 3 Results") layout = QVBoxLayout(self) layout.addWidget(self.tabWidget) #self.setLayout(self.resultPlot()) self.show() def modeBResultPlot(self): widget = QWidget() fig = plt.figure() canvas = FigureCanvas(fig) toolbar = NavigationToolbar(canvas, self) ax = fig.add_subplot(111) ax.set_xticklabels([""] + self.phaseNames) #Shifting names a = self.models[-1].a_est b = self.models[-1].b_est c = self.models[-1].c_est self.estTotalErrors = self.models[-1].a_est self.selPeakLocation = self.models[-1].get_peak_loc() self.numLatentErrors = self.models[-1].fi_t(a, b, c, len(self.models)) self.eff = (1 - self.numLatentErrors / a) * 100 #ax.bar([i+1-0.1 for i in range(len(self.models))], [m.kVec_cumu_sum[-1] for m in self.models], width=0.2, color='b', label="Actual") #ax.bar([i+1+0.1 for i in range(len(self.models))], [m.MVF(m.tVec[-1], a, b, c) for i, m in enumerate(self.models)], width=0.2, color='r', label="Estimated") ax.bar([i + 1 - 0.1 for i in range(len(self.phaseNames))], np.cumsum(self.phaseValues), width=0.2, color='b', label="Actual") ax.bar([i + 1 + 0.1 for i in range(len(self.phaseNames))], [ self.models[-1].MVF(t + 1, a, b, c) for t in range(len(self.phaseNames)) ], width=0.2, color='r', label="Estimated") ax.set_xlabel("Phases") ax.set_ylabel("Cumulative Defects") ax.set_title("Defect Discovery Data and Fitted Histograms") ax.legend() canvas.draw() plt.grid(True) self.setWindowTitle('SwEET - Mode B Results') layoutfig = QVBoxLayout() layoutfig.addWidget(toolbar) layoutfig.addWidget(canvas, 1) widget.setLayout(layoutfig) plt.tight_layout() return widget def mode3ResultPlot(self): widget = QWidget() fig = plt.figure() #plt.tight_layout() plt.grid(True) canvas = FigureCanvas(fig) toolbar = NavigationToolbar(canvas, self) ax = fig.add_subplot(111) ax.set_xticklabels([""] + self.phaseNames) ax.plot(self.results['lower'], color='g', label='Lower') #ax.bar([i+1-0.2 for i in range(len(self.phaseNames))], self.results['lower'], width=0.2, color='g', label="Lower") ax.plot(self.results['nominal'], color='b', label='Nominal') #ax.bar([i+1 for i in range(len(self.phaseNames))], self.results['nominal'], width=0.2, color='b', label="Nominal") ax.plot(self.results['upper'], color='r', label='Upper') #ax.bar([i+1+0.2 for i in range(len(self.phaseNames))], self.results['upper'], width=0.2, color='r', label="Upper") ax.set_xlabel("Phases") ax.set_ylabel("Defects") ax.set_title("Defect Discovery Data and Fitted Histograms") ax.legend() canvas.draw() layoutfig = QVBoxLayout() layoutfig.addWidget(toolbar) layoutfig.addWidget(canvas, 1) widget.setLayout(layoutfig) plt.tight_layout() return widget
def __init__(self, persepolis_setting): super().__init__() self.persepolis_setting = persepolis_setting icons = ':/' + \ str(self.persepolis_setting.value('settings/icons')) + '/' # add support for other languages locale = str(self.persepolis_setting.value('settings/locale')) QLocale.setDefault(QLocale(locale)) self.translator = QTranslator() if self.translator.load(':/translations/locales/ui_' + locale, 'ts'): QCoreApplication.installTranslator(self.translator) # set ui direction ui_direction = self.persepolis_setting.value('ui_direction') if ui_direction == 'rtl': self.setLayoutDirection(Qt.RightToLeft) elif ui_direction in 'ltr': self.setLayoutDirection(Qt.LeftToRight) self.setWindowIcon( QIcon.fromTheme('persepolis', QIcon(':/persepolis.svg'))) window_verticalLayout = QVBoxLayout() self.setLayout(window_verticalLayout) # queue_tabWidget self.queue_tabWidget = QTabWidget(self) window_verticalLayout.addWidget(self.queue_tabWidget) # links_tab self.links_tab = QWidget() links_tab_verticalLayout = QVBoxLayout(self.links_tab) # link table self.links_table = QTableWidget(self.links_tab) links_tab_verticalLayout.addWidget(self.links_table) self.links_table.setSelectionBehavior(QAbstractItemView.SelectRows) self.links_table.setEditTriggers(QAbstractItemView.NoEditTriggers) self.links_table.verticalHeader().hide() self.links_table.setColumnCount(3) links_table_header_labels = [ 'File Name', 'Download Link', 'dictionary' ] self.links_table.setHorizontalHeaderLabels(links_table_header_labels) self.links_table.setColumnHidden(2, True) self.links_table.horizontalHeader().setSectionResizeMode(0) self.links_table.horizontalHeader().setStretchLastSection(True) # add_queue add_queue_horizontalLayout = QHBoxLayout() self.select_all_pushButton = QPushButton(self.links_tab) add_queue_horizontalLayout.addWidget(self.select_all_pushButton) self.deselect_all_pushButton = QPushButton(self.links_tab) add_queue_horizontalLayout.addWidget(self.deselect_all_pushButton) add_queue_horizontalLayout.addStretch(1) self.add_queue_label = QLabel(self.links_tab) add_queue_horizontalLayout.addWidget(self.add_queue_label) self.add_queue_comboBox = QComboBox(self.links_tab) add_queue_horizontalLayout.addWidget(self.add_queue_comboBox) links_tab_verticalLayout.addLayout(add_queue_horizontalLayout) links_tab_verticalLayout.addStretch(1) self.queue_tabWidget.addTab(self.links_tab, "") # options_tab self.options_tab = QWidget() options_tab_verticalLayout = QVBoxLayout(self.options_tab) # proxy proxy_verticalLayout = QVBoxLayout() self.proxy_checkBox = QCheckBox(self.options_tab) proxy_verticalLayout.addWidget(self.proxy_checkBox) self.proxy_frame = QFrame(self.options_tab) self.proxy_frame.setFrameShape(QFrame.StyledPanel) self.proxy_frame.setFrameShadow(QFrame.Raised) proxy_gridLayout = QGridLayout(self.proxy_frame) self.ip_lineEdit = QLineEdit(self.proxy_frame) self.ip_lineEdit.setInputMethodHints(Qt.ImhNone) proxy_gridLayout.addWidget(self.ip_lineEdit, 0, 1, 1, 1) self.proxy_pass_label = QLabel(self.proxy_frame) proxy_gridLayout.addWidget(self.proxy_pass_label, 2, 2, 1, 1) self.proxy_pass_lineEdit = QLineEdit(self.proxy_frame) self.proxy_pass_lineEdit.setEchoMode(QLineEdit.Password) proxy_gridLayout.addWidget(self.proxy_pass_lineEdit, 2, 3, 1, 1) self.ip_label = QLabel(self.proxy_frame) proxy_gridLayout.addWidget(self.ip_label, 0, 0, 1, 1) self.proxy_user_lineEdit = QLineEdit(self.proxy_frame) proxy_gridLayout.addWidget(self.proxy_user_lineEdit, 0, 3, 1, 1) self.proxy_user_label = QLabel(self.proxy_frame) proxy_gridLayout.addWidget(self.proxy_user_label, 0, 2, 1, 1) self.port_label = QLabel(self.proxy_frame) proxy_gridLayout.addWidget(self.port_label, 2, 0, 1, 1) self.port_spinBox = QSpinBox(self.proxy_frame) self.port_spinBox.setMaximum(9999) self.port_spinBox.setSingleStep(1) proxy_gridLayout.addWidget(self.port_spinBox, 2, 1, 1, 1) proxy_verticalLayout.addWidget(self.proxy_frame) options_tab_verticalLayout.addLayout(proxy_verticalLayout) # download UserName & Password download_horizontalLayout = QHBoxLayout() download_horizontalLayout.setContentsMargins(-1, 10, -1, -1) download_verticalLayout = QVBoxLayout() self.download_checkBox = QCheckBox(self.options_tab) download_verticalLayout.addWidget(self.download_checkBox) self.download_frame = QFrame(self.options_tab) self.download_frame.setFrameShape(QFrame.StyledPanel) self.download_frame.setFrameShadow(QFrame.Raised) download_gridLayout = QGridLayout(self.download_frame) self.download_user_lineEdit = QLineEdit(self.download_frame) download_gridLayout.addWidget(self.download_user_lineEdit, 0, 1, 1, 1) self.download_user_label = QLabel(self.download_frame) download_gridLayout.addWidget(self.download_user_label, 0, 0, 1, 1) self.download_pass_label = QLabel(self.download_frame) download_gridLayout.addWidget(self.download_pass_label, 1, 0, 1, 1) self.download_pass_lineEdit = QLineEdit(self.download_frame) self.download_pass_lineEdit.setEchoMode(QLineEdit.Password) download_gridLayout.addWidget(self.download_pass_lineEdit, 1, 1, 1, 1) download_verticalLayout.addWidget(self.download_frame) download_horizontalLayout.addLayout(download_verticalLayout) # select folder self.folder_frame = QFrame(self.options_tab) self.folder_frame.setFrameShape(QFrame.StyledPanel) self.folder_frame.setFrameShadow(QFrame.Raised) folder_gridLayout = QGridLayout(self.folder_frame) self.download_folder_lineEdit = QLineEdit(self.folder_frame) folder_gridLayout.addWidget(self.download_folder_lineEdit, 2, 0, 1, 1) self.folder_pushButton = QPushButton(self.folder_frame) folder_gridLayout.addWidget(self.folder_pushButton, 3, 0, 1, 1) self.folder_pushButton.setIcon(QIcon(icons + 'folder')) self.folder_label = QLabel(self.folder_frame) self.folder_label.setAlignment(Qt.AlignCenter) folder_gridLayout.addWidget(self.folder_label, 1, 0, 1, 1) download_horizontalLayout.addWidget(self.folder_frame) options_tab_verticalLayout.addLayout(download_horizontalLayout) self.queue_tabWidget.addTab(self.options_tab, '') # limit Speed limit_verticalLayout = QVBoxLayout() self.limit_checkBox = QCheckBox(self.options_tab) limit_verticalLayout.addWidget(self.limit_checkBox) self.limit_frame = QFrame(self.options_tab) self.limit_frame.setFrameShape(QFrame.StyledPanel) self.limit_frame.setFrameShadow(QFrame.Raised) limit_horizontalLayout = QHBoxLayout(self.limit_frame) self.limit_spinBox = QSpinBox(self.limit_frame) self.limit_spinBox.setMinimum(1) self.limit_spinBox.setMaximum(1023) limit_horizontalLayout.addWidget(self.limit_spinBox) self.limit_comboBox = QComboBox(self.limit_frame) self.limit_comboBox.addItem("KiB/S") self.limit_comboBox.addItem("MiB/S") limit_horizontalLayout.addWidget(self.limit_comboBox) limit_verticalLayout.addWidget(self.limit_frame) limit_connections_horizontalLayout = QHBoxLayout() limit_connections_horizontalLayout.addLayout(limit_verticalLayout) # number of connections connections_horizontalLayout = QHBoxLayout() connections_horizontalLayout.setContentsMargins(-1, 10, -1, -1) self.connections_frame = QFrame(self.options_tab) self.connections_frame.setFrameShape(QFrame.StyledPanel) self.connections_frame.setFrameShadow(QFrame.Raised) horizontalLayout_3 = QHBoxLayout(self.connections_frame) self.connections_label = QLabel(self.connections_frame) horizontalLayout_3.addWidget(self.connections_label) self.connections_spinBox = QSpinBox(self.connections_frame) self.connections_spinBox.setMinimum(1) #nomaxx# self.connections_spinBox.setMaximum(16) self.connections_spinBox.setProperty("value", 16) horizontalLayout_3.addWidget(self.connections_spinBox) connections_horizontalLayout.addWidget(self.connections_frame) limit_connections_horizontalLayout.addLayout( connections_horizontalLayout) options_tab_verticalLayout.addLayout( limit_connections_horizontalLayout) options_tab_verticalLayout.addStretch(1) # buttons buttons_horizontalLayout = QHBoxLayout() buttons_horizontalLayout.addStretch(1) # ok_pushButton self.ok_pushButton = QPushButton(self) self.ok_pushButton.setIcon(QIcon(icons + 'ok')) buttons_horizontalLayout.addWidget(self.ok_pushButton) # cancel_pushButton self.cancel_pushButton = QPushButton(self) self.cancel_pushButton.setIcon(QIcon(icons + 'remove')) buttons_horizontalLayout.addWidget(self.cancel_pushButton) window_verticalLayout.addLayout(buttons_horizontalLayout) # labels self.setWindowTitle( QCoreApplication.translate("text_ui_tr", "Persepolis Download Manager")) self.queue_tabWidget.setTabText( self.queue_tabWidget.indexOf(self.links_tab), QCoreApplication.translate("text_ui_tr", 'Links')) self.queue_tabWidget.setTabText( self.queue_tabWidget.indexOf(self.options_tab), QCoreApplication.translate("text_ui_tr", 'Download options')) self.select_all_pushButton.setText( QCoreApplication.translate("text_ui_tr", 'Select All')) self.deselect_all_pushButton.setText( QCoreApplication.translate("text_ui_tr", 'Deselect All')) self.add_queue_label.setText( QCoreApplication.translate("text_ui_tr", 'Add to queue: ')) self.proxy_checkBox.setText( QCoreApplication.translate("text_ui_tr", 'Proxy')) self.proxy_pass_label.setText( QCoreApplication.translate("text_ui_tr", "Proxy PassWord: "******"text_ui_tr", "IP:")) self.proxy_user_label.setText( QCoreApplication.translate("text_ui_tr", "Proxy UserName: "******"text_ui_tr", "Port:")) self.download_checkBox.setText( QCoreApplication.translate("text_ui_tr", "Download UserName and PassWord")) self.download_user_label.setText( QCoreApplication.translate("text_ui_tr", "Download UserName: "******"text_ui_tr", "Download PassWord: "******"text_ui_tr", "Change Download Folder")) self.folder_label.setText( QCoreApplication.translate("text_ui_tr", "Download Folder: ")) self.limit_checkBox.setText( QCoreApplication.translate("text_ui_tr", "Limit Speed")) self.connections_label.setText( QCoreApplication.translate("text_ui_tr", "Number Of Connections:")) self.ok_pushButton.setText( QCoreApplication.translate("text_ui_tr", 'OK')) self.cancel_pushButton.setText( QCoreApplication.translate("text_ui_tr", 'Cancel'))
def __init__(self, dwarf_args, flags=None): super(AppWindow, self).__init__(flags) self.dwarf_args = dwarf_args self.session_manager = SessionManager(self) self.session_manager.sessionCreated.connect(self.session_created) self.session_manager.sessionStopped.connect(self.session_stopped) self.session_manager.sessionClosed.connect(self.session_closed) self._tab_order = [ 'memory', 'modules', 'ranges', 'jvm-inspector', 'jvm-debugger' ] self.menu = self.menuBar() self._is_newer_dwarf = False self.view_menu = None #dockwidgets self.watchers_dwidget = None self.hooks_dwiget = None self.bookmarks_dwiget = None self.registers_dock = None self.console_dock = None self.backtrace_dock = None self.threads_dock = None #panels self.asm_panel = None self.console_panel = None self.context_panel = None self.backtrace_panel = None self.contexts_list_panel = None self.data_panel = None self.emulator_panel = None self.ftrace_panel = None self.hooks_panel = None self.bookmarks_panel = None self.smali_panel = None self.java_inspector_panel = None self.java_explorer_panel = None self.java_trace_panel = None self.memory_panel = None self.modules_panel = None self.ranges_panel = None self.search_panel = None self.watchers_panel = None self.welcome_window = None self._ui_elems = [] self.setWindowTitle( 'Dwarf - A debugger for reverse engineers, crackers and security analyst' ) # load external assets _app = QApplication.instance() self.remove_tmp_dir() # themes self.prefs = Prefs() self.set_theme(self.prefs.get('dwarf_ui_theme', 'black')) # load font if os.path.exists(utils.resource_path('assets/Anton.ttf')): QFontDatabase.addApplicationFont( utils.resource_path('assets/Anton.ttf')) if os.path.exists(utils.resource_path('assets/OpenSans-Regular.ttf')): QFontDatabase.addApplicationFont( utils.resource_path('assets/OpenSans-Regular.ttf')) font = QFont("OpenSans", 9, QFont.Normal) # TODO: add settingsdlg font_size = self.prefs.get('dwarf_ui_font_size', 12) font.setPixelSize(font_size) _app.setFont(font) if os.path.exists(utils.resource_path('assets/OpenSans-Bold.ttf')): QFontDatabase.addApplicationFont( utils.resource_path('assets/OpenSans-Bold.ttf')) # mainwindow statusbar self.progressbar = QProgressBar() self.progressbar.setRange(0, 0) self.progressbar.setVisible(False) self.progressbar.setFixedHeight(15) self.progressbar.setFixedWidth(100) self.progressbar.setTextVisible(False) self.progressbar.setValue(30) self.statusbar = QStatusBar(self) self.statusbar.setAutoFillBackground(False) self.statusbar.addPermanentWidget(self.progressbar) self.statusbar.setObjectName("statusbar") self.setStatusBar(self.statusbar) self.main_tabs = QTabWidget(self) self.main_tabs.setMovable(False) self.main_tabs.setTabsClosable(True) self.main_tabs.setAutoFillBackground(True) self.main_tabs.tabCloseRequested.connect(self._on_close_tab) self.setCentralWidget(self.main_tabs) if self.dwarf_args.package is None: self.welcome_window = WelcomeDialog(self) self.welcome_window.setModal(True) self.welcome_window.onIsNewerVersion.connect( self._enable_update_menu) self.welcome_window.onUpdateComplete.connect( self._on_dwarf_updated) self.welcome_window.setWindowTitle( 'Welcome to Dwarf - A debugger for reverse engineers, crackers and security analyst' ) self.welcome_window.onSessionSelected.connect(self._start_session) # wait for welcome screen self.hide() self.welcome_window.show() else: if dwarf_args.package is not None: if dwarf_args.type is None: # no device given check if package is local path if os.path.exists(dwarf_args.package): print('* Starting new LocalSession') self._start_session('local') else: print('use -t to set sessiontype') exit(0) else: print('* Starting new Session') self._start_session(dwarf_args.type)
class MainWinBase(QMainWindow): def __init__(self): super().__init__() self.setupUi() def setupUi(self): self.resize(1200, 600) self.menubar = self.menuBar() self.statusbar = self.statusBar() self.mainWidget = QTabWidget() self.editor = Editor() self.actions = {} self.menus = {} self.submenus = {} self.contexMenus = {} self.toolbars = {} self.status = {} self.docks = {} self.documents = {} self.createActions() self.themeCombo = QComboBox() self.createMenubar() self.createContexMenus() self.createToolbars() self.createStatusBar() self.colorPanelLayout = QFlowLayout() self.createDocks() self.createMainWidget() def createActions(self): def createAct(text, tip=None, shortcut=None, iconimg=None, checkable=False, slot=None): action = QAction(self.tr(text), self) if iconimg is not None: action.setIcon(QIcon(iconimg)) if shortcut is not None: action.setShortcut(shortcut) if tip is not None: tip = self.tr(tip) action.setToolTip(tip) action.setStatusTip(tip) if checkable: action.setCheckable(True) if slot is not None: action.triggered.connect(slot) return action def keys2str(standardkey): return "".join(("(", QKeySequence(standardkey).toString(), ")")) self.actions["new"] = createAct(self.tr("&New", "&New"), self.tr("new") + keys2str(QKeySequence.New), QKeySequence.New, ':appres.img/NewDocument.png') self.actions["open"] = createAct(self.tr("&Open"), self.tr("Open") + keys2str(QKeySequence.Open), QKeySequence.Open, ':appres.img/openHS.png') self.actions["save"] = createAct(self.tr("&Save"), self.tr("Save") + keys2str(QKeySequence.Save), QKeySequence.Save, ':appres.img/save.png') self.actions["saveas"] = createAct(self.tr("&Save as..."), self.tr("Save as..."), None, ':appres.img/SaveAs.png') self.actions["export"] = createAct(self.tr("&ExportQss"), self.tr("ExportQss"), "Ctrl+Alt+E", ':appres.img/export5.png') self.actions["exit"] = createAct(self.tr("&Exit"), self.tr("Exit"), "Ctrl+Q", ':appres.img/close.png') self.actions["undo"] = createAct(self.tr("&Undo"), self.tr("Undo") + keys2str(QKeySequence.Undo), QKeySequence.Undo, ':appres.img/undo.png') self.actions["redo"] = createAct(self.tr("&Redo"), self.tr("Redo") + keys2str(QKeySequence.Redo), QKeySequence.Redo, ':appres.img/redo.png') self.actions["cut"] = createAct(self.tr("&Cut"), self.tr("Cut") + keys2str(QKeySequence.Cut), QKeySequence.Cut, ':appres.img/cut.png') self.actions["copy"] = createAct(self.tr("&Copy"), self.tr("Copy") + keys2str(QKeySequence.Copy), QKeySequence.Copy, ':appres.img/copy.png') self.actions["paste"] = createAct(self.tr("&Paste"), self.tr("Paste") + keys2str(QKeySequence.Paste), QKeySequence.Paste, ':appres.img/paste.png') self.actions["find"] = createAct(self.tr("&Find"), self.tr("Find") + keys2str(QKeySequence.Find), QKeySequence.Find, ':appres.img/find.png') self.actions["replace"] = createAct(self.tr("&Replace"), self.tr("Replace") + keys2str(QKeySequence.Replace), QKeySequence.Replace, ':appres.img/replace.png') self.actions["fontup"] = createAct(self.tr("&BiggerFont"), self.tr("Bigger Font"), None, ':appres.img/fontup.png') self.actions["fontdown"] = createAct(self.tr("&SmallerFont"), self.tr("Smaller Font"), None, ':appres.img/fontdown.png') self.actions["echospace"] = createAct(self.tr("&Space"), self.tr("Show Spaces"), None, ':appres.img/space.png') self.actions["echoeol"] = createAct(self.tr("&Eol"), self.tr("Show Eol"), None, ':appres.img/eol.png') self.actions["autowrap"] = createAct(self.tr("&AutoWrap"), self.tr("Auto wrap text"), None, ":appres.img/autowrap.png") # self.fontcolorAct=QAction(QIcon(":appres.img/broadcast_send_fontcolor_normal.bmp"),"&FontColor",self) # self.fontcolorAct.setShortcut("Ctr+Shit+C") # self.fontcolorAct.setStatusTip("FontColor") self.actions["DisableQss"] = createAct(self.tr("&DisableQss"), self.tr("DisableQss"), checkable=True) self.actions["DisableQss"].setChecked(False) self.actions["ShowColor"] = createAct(self.tr("&ColorPanel"), self.tr("ShowColorPanel"), None, ":appres.img/color.png", checkable=True) self.actions["ShowColor"].setChecked(True) self.actions["ShowPreview"] = createAct(self.tr("&PreviewPanel"), self.tr("ShowPreviewPanel"), None, ":appres.img/view.png", checkable=True) self.actions["Palette"] = createAct(self.tr("&Palette"), self.tr("ShowPaletteSettingDialog"), None, ":appres.img/texture.png") self.actions["ShowPreview"].setChecked(True) self.actions["config"] = createAct(self.tr("&Config"), self.tr("settings."), None, ":appres.img/config.png") self.actions["checkupdate"] = createAct(self.tr("&Check for Updates..."), self.tr("Check is there new version released for update.")) self.actions["about"] = createAct(self.tr("&About"), self.tr("About")) # self.exitAct.triggered.connect(qApp.exit)#等价于qApp.quit self.actions["exit"].triggered.connect(self.close) def createMenubar(self): self.menus["File"] = QMenu(self.tr("&File")) self.menus["Edit"] = QMenu(self.tr("&Edit")) self.menus["View"] = QMenu(self.tr("&View")) self.menus["Tool"] = QMenu(self.tr("&Tool")) self.menus["Help"] = QMenu(self.tr("&Help")) recentMenu = QMenu(self.tr("Recent"), self.menus["File"]) recentMenu.setIcon(QIcon(":appres.img/none.png")) editMenu = QMenu(self.tr("Text"), self.menus["Edit"]) editMenu.setIcon(QIcon(":appres.img/edit_whitepage.png")) editMenu.addAction(self.actions["undo"]) editMenu.addAction(self.actions["redo"]) editMenu.addSeparator() editMenu.addAction(self.actions["cut"]) editMenu.addAction(self.actions["copy"]) editMenu.addAction(self.actions["paste"]) searchMenu = QMenu(self.tr("Search"), self.menus["Edit"]) searchMenu.setIcon(QIcon(":appres.img/findnext.png")) searchMenu.addAction(self.actions["find"]) searchMenu.addAction(self.actions["replace"]) self.submenus["recent"] = recentMenu self.submenus["text"] = editMenu self.submenus["search"] = searchMenu self.menus["File"].addAction(self.actions["new"]) self.menus["File"].addAction(self.actions["open"]) self.menus["File"].addAction(self.actions["save"]) self.menus["File"].addAction(self.actions["saveas"]) self.menus["File"].addAction(self.actions["export"]) self.menus["File"].addMenu(self.submenus["recent"]) self.menus["File"].addSeparator() self.menus["File"].addAction(self.actions["exit"]) self.menus["Edit"].addMenu(editMenu) self.menus["Edit"].addMenu(searchMenu) self.menus["View"].addAction(self.actions["ShowColor"]) self.menus["View"].addAction(self.actions["ShowPreview"]) self.menus["View"].addAction(self.actions["Palette"]) self.menus["Tool"].addAction(self.actions["config"]) self.menus["Help"].addAction(self.actions["about"]) self.menus["Help"].addAction(self.actions["checkupdate"]) for m in self.menus.values(): self.menubar.addMenu(m) def createContexMenus(self): self.contexMenus["Edit"] = QMenu(self.tr("Edit")) self.contexMenus["Edit"].addAction(self.actions["cut"]) self.contexMenus["Edit"].addAction(self.actions["copy"]) self.contexMenus["Edit"].addAction(self.actions["paste"]) def createToolbars(self): checkbox = QCheckBox(self.tr("DisableQSS")) # self.themeCombo = QComboBox() checkbox.setToolTip(self.tr("Using system style, disable qss.")) self.themeCombo.setToolTip(self.tr("Select system style.")) self.themeCombo.addItems(QStyleFactory.keys()) self.themeCombo.setMinimumWidth(105) theme = QApplication.style().objectName() self.themeCombo.setCurrentIndex(self.themeCombo.findText(theme, Qt.MatchFixedString)) # self.themeCombo.setEnabled(False) # themeCombo.activated[str].connect(qApp.setStyle) # themeCombo.currentTextChanged.connect(qApp.setStyle) # checkbox.stateChanged.connect(self.themeCombo.setEnabled) checkbox.stateChanged.connect(self.actions["DisableQss"].setChecked) # checkbox.stateChanged.connect(lambda x:self.actions["DisableQss"].setChecked(checkbox.isChecked())) self.toolbars["Main"] = QToolBar(self.tr("Main", "toolbar")) self.toolbars["Main"].addWidget(checkbox) self.toolbars["Main"].addWidget(self.themeCombo) self.toolbars["File"] = QToolBar(self.tr("File")) self.toolbars["File"].addAction(self.actions["new"]) self.toolbars["File"].addAction(self.actions["open"]) self.toolbars["File"].addAction(self.actions["save"]) # self.toolbars["File"].addAction(self.actions["saveas"]) self.toolbars["File"].addAction(self.actions["export"]) self.toolbars["Edit"] = QToolBar(self.tr("Edit")) self.toolbars["Edit"].addAction(self.actions["undo"]) self.toolbars["Edit"].addAction(self.actions["redo"]) self.toolbars["Edit"].addSeparator() self.toolbars["Edit"].addAction(self.actions["cut"]) self.toolbars["Edit"].addAction(self.actions["copy"]) self.toolbars["Edit"].addAction(self.actions["paste"]) self.toolbars["Search"] = QToolBar(self.tr("Search")) self.toolbars["Search"].addAction(self.actions["find"]) self.toolbars["Search"].addAction(self.actions["replace"]) self.toolbars["View"] = QToolBar(self.tr("View")) self.toolbars["View"].addAction(self.actions["ShowColor"]) self.toolbars["View"].addAction(self.actions["ShowPreview"]) self.toolbars["View"].addAction(self.actions["Palette"]) self.toolbars["Echo"] = QToolBar(self.tr("Echo")) self.toolbars["Echo"].addAction(self.actions["fontup"]) self.toolbars["Echo"].addAction(self.actions["fontdown"]) self.toolbars["Echo"].addAction(self.actions["echospace"]) self.toolbars["Echo"].addAction(self.actions["echoeol"]) self.toolbars["Echo"].addAction(self.actions["autowrap"]) for t in self.toolbars.values(): self.addToolBar(t) def createStatusBar(self): self.statusbar.showMessage(self.tr("Ready")) # self.statusbar.addWidget(QWidget(),1) # self.status["date"] = QLabel() # self.statusbar.addPermanentWidget(self.status["date"]) # self.status["date"].setText(QDate.currentDate().toString()) # self.status["date"].setVisible(False) self.status["line"] = QLabel(self.tr("line:0 pos:0")) self.status["select"] = QLabel(self.tr("select: none")) self.status["coding"] = QLabel(self.tr("coding")) self.status["lines"] = QLabel(self.tr("lines:0")) self.status["line"].setMinimumWidth(120) self.status["select"].setMinimumWidth(150) self.status["coding"].setMinimumWidth(80) self.status["coding"].setAlignment(Qt.AlignCenter) self.status["lines"].setMinimumWidth(60) self.status["lines"].setAlignment(Qt.AlignRight) self.statusbar.addPermanentWidget(self.status["line"]) self.statusbar.addPermanentWidget(self.status["select"]) self.statusbar.addPermanentWidget(self.status["coding"]) self.statusbar.addPermanentWidget(self.status["lines"]) def createDocks(self): self.docks["color"] = QDockWidget(self.tr("Color Variables")) self.docks["preview"] = QDockWidget(self.tr("Preview")) self.docks["color"].setMinimumSize(QSize(80, 20)) self.docks["color"].setFeatures(QDockWidget.AllDockWidgetFeatures) self.docks["preview"].setMinimumSize(QSize(200, 200)) self.docks["preview"].setFeatures(QDockWidget.DockWidgetMovable | QDockWidget.DockWidgetFloatable) self.addDockWidget(Qt.LeftDockWidgetArea, self.docks["color"]) self.addDockWidget(Qt.RightDockWidgetArea, self.docks["preview"]) class ColorPanelWidget(QWidget): def sizeHint(self): return self.layout().sizeHint() colorPanelWidget = ColorPanelWidget() # self.colorPanelLayout = QFlowLayout() colorPanelWidget.setLayout(self.colorPanelLayout) self.docks["color"].setWidget(colorPanelWidget) self.docks["preview"].setWidget(PreviewWidget()) self.docks["color"].visibilityChanged.connect(self.actions["ShowColor"].setChecked) def createMainWidget(self): self.setCentralWidget(self.mainWidget) self.mainWidget.setTabBarAutoHide(True) self.mainWidget.addTab(self.editor, self.tr("main", "CodeEditor tab in tabwidget of mainwidget"))
class OuterWidget(QWidget): def __init__(self, callerObject): super(QWidget, self).__init__(callerObject) self.caller = callerObject self.layout = QVBoxLayout(self) self.tabs = QTabWidget() self.tab1 = Form(self) self.imageLabel = QLabel(self.tab1) self.imageLabel.setGeometry(80, 50, 510, 310) self.imageLabel.setPixmap( QtGui.QPixmap(os.getcwd() + "/" + 'image.jpg')) self.tabs.setTabsClosable(True) self.tabs.tabCloseRequested.connect(self.tabs.removeTab) self.tabs.addTab(self.tab1, "Facial Authentication") self.layout.addWidget(self.tabs) self.setLayout(self.layout) def recognizeFace(self): global imageName, username while not os.path.exists(os.getcwd() + "/" + 'image.jpg'): a = 5 if os.path.isfile('image.jpg'): copyfile('image.jpg', imageName) username = faceRecognition(self) #This is used to handle the case if the image size too small error is returned if username == 'error': _translate = QtCore.QCoreApplication.translate self.tab1.label.setText( _translate( "Form", "*Picture is not upto the required quality. It could be due to poor ambient light " )) self.tab1.label.setStyleSheet('color: red') return 1 #This is used to handle the case if there is no face in the picture frame elif username == 'No Face': _translate = QtCore.QCoreApplication.translate self.tab1.label.setText( _translate( "Form", "*No face detected. Please position the camera pointing towards your face" )) self.tab1.label.setStyleSheet('color: red') return 1 elif username == 'Fail': _translate = QtCore.QCoreApplication.translate self.tab1.label.setText( _translate( "Form", "*Authentication Failed. You have been denied access")) self.tab1.label.setStyleSheet('color: red') return 1 length = len(username) print('Before Independence') self.tab2 = Dash(self) print('After Independence') if length > 0: _translate = QtCore.QCoreApplication.translate p = 'Hello %s' % (username) print('India') self.tab2.label.setText(_translate("Dash", p)) self.tab2.label_2.setText( _translate( "Dash", "You have been granted access to Book My Show, \n" "a Secure Voice Assisted movie show booking website")) self.tab2.label_3.setText( _translate("Dash", "Do you want to add a new user?")) #This function is used to send a message(websockets) containing the username to the control pi msg_type = 'login' send_message(username, msg_type) print(username) dashboardIndex = self.tabs.addTab(self.tab2, "Dashboard") self.tabs.setCurrentIndex(dashboardIndex) faceIdentifyIndex = self.tabs.indexOf(self.tab1) self.tabs.removeTab(faceIdentifyIndex) def registerUser(self): print('Registering User') self.tab3 = AddUser(self) addUserIndex = self.tabs.addTab(self.tab3, "Add User") self.tabs.setCurrentIndex(addUserIndex) dashboardIndex = self.tabs.indexOf(self.tab2) self.tabs.removeTab(dashboardIndex) def logout(self): msg_type = 'logout' send_message(username, msg_type) faceIdentifyIndex = self.tabs.addTab(self.tab1, "Facial Authentication") self.tabs.setCurrentIndex(faceIdentifyIndex) dashboardIndex = self.tabs.indexOf(self.tab2) self.tabs.removeTab(dashboardIndex) def addUser(self): print('Printing Entered text') global new_user_username, new_user_emailid, new_user_password new_user_username = self.tab3.usernameLineEdit.text() new_user_emailid = self.tab3.emailIdLineEdit.text() new_user_password = self.tab3.passwordLineEdit.text() register_url_base = 'http://ec2-184-72-98-174.compute-1.amazonaws.com/' register = { 'postUser': new_user_username, 'postEmail': new_user_emailid, 'postPass': new_user_password } request_parameters = {'s': '3', 'insert': '1'} session = requests.session() r = session.request('POST', register_url_base + 'index.php', json=None, data=register, headers=None, params=request_parameters) print(new_user_username) print(new_user_emailid) print(new_user_password) self.tab4 = AddPhoto(self) self.imageLabel = QLabel(self.tab4) self.imageLabel.setGeometry(50, 50, 510, 310) self.imageLabel.setPixmap( QtGui.QPixmap(os.getcwd() + "/" + 'image.jpg')) self.tabs.addTab(self.tab4, "Adding Photo to a person") addUserIndex = self.tabs.indexOf(self.tab3) self.tabs.removeTab(addUserIndex) def addPhoto(self): global imageName, new_user_username, username copyfile('image.jpg', imageName) print('Photo has been added') personId = createPerson(new_user_username) if personId: returnString = addPersonFace(personId) _translate = QtCore.QCoreApplication.translate if returnString: if returnString == 'Bad Detect Request': self.tab4.label.setText( _translate( "AddPhoto", "*Picture is not upto the required quality. It could be due to poor ambient light " )) self.tab4.label.setStyleSheet('color: red') elif returnString == 'No Face Detected': self.tab4.label.setText( _translate( "AddPhoto", "*No face detected. Please position the camera pointing towards your face" )) self.tab4.label.setStyleSheet('color: red') else: print('Training') trainSuperProjectGroup() dashboardIndex = self.tabs.addTab(self.tab2, "Dashboard") helloString = 'Hello %s' % (username) self.tab2.label.setText(_translate("Dash", helloString)) self.tab2.label_2.setText( _translate( "Dash", "You have been granted access to Book My Show, \n" "a Secure Voice Assisted movie show booking website")) self.tab2.label_3.setText( _translate("Dash", "Do you want to add a new user?")) stringVar = 'You have successfully added %s as a user' % ( new_user_username) self.tab2.label_4.setText(_translate("Dash", stringVar)) self.tab2.label_4.setStyleSheet('color: blue') self.tabs.setCurrentIndex(dashboardIndex) addPhotoIndex = self.tabs.indexOf(self.tab4) self.tabs.removeTab(addPhotoIndex)
class Example(QWidget): def __init__(self): super().__init__() self.initUI() def connect(self): if self.IPbox.text() == "": text, ok = QInputDialog.getText(self, 'Input Dialog', '输入IP:') if ok: self.IPbox.setText(str(text)) # if self.usernamebox.text() == "": # text, ok = QInputDialog.getText(self, 'Input Dialog', '输入用户名:') # if ok: # self.usernamebox.setText(str(text)) # if self.passwordbox.text() == "": # text, ok = QInputDialog.getText(self, 'Input Dialog', '输入密码:') # if ok: # self.passwordbox.setText(str(text)) # if self.ethbox.text() == "": # text, ok = QInputDialog.getText(self, 'Input Dialog', '输入网口号:(eg:eth1)') # if ok: # self.ethbox.setText(str(text)) self.IP = self.IPbox.text() self.username = self.usernamebox.text() self.password = self.passwordbox.text() self.eth = self.ethbox.text() QMessageBox.information(self, "", "需要一段时间,请等待") #a, b = subprocess.getstatusoutput('ping ' + self.IP) # a是退出状态 b是输出的结果 self.thread_connect = MyThread(re='ping -t 100 -c 2 ' + self.IP) # 创建一个线程 发送cmd self.thread_connect.sec_changed_signal.connect( self.update_state_connect) # cmd线程发过来的信号挂接到槽:update_state #self.thread2.sec_changed_signal.connect(self.update_time) # 计时线程发过来的信号挂接到槽:update_time self.thread_connect.start() def update_state_connect(self, b): self.resultbox.setText(b) if "ms" in b and "100% packet loss" not in b: QMessageBox.information( self, # 使用infomation信息框 "注意", "连接成功") else: QMessageBox.information(self, "注意", "连接失败 请检查IP设置") self.thread_connect.terminate() def update(self): QApplication.processEvents() def read_json(self): global config_dic global res_cmd global send_list global tab_name global pcap_path global twolineflag try: fname = QFileDialog.getOpenFileName( self, "选取文件", "./", # 起始路径 "配置文件 (*.json)") # 设置文件扩展名过滤,用双分号间隔 with open(fname[0], 'r') as load_f: config_dic = json.load(load_f) send_list = {} res_cmd = "" tab_name = [] pcap_path = "" res_cmd = fname[0] self.tab.clear() for test_type in config_dic.keys(): send_list[test_type] = [] tab_name.append(test_type) self.tab.test_type = Checkboxlist(test_type) l = int(len(test_type) / 2) s = test_type[0:l] + '\n' * twolineflag + test_type[l:] self.tab.addTab(self.tab.test_type, s) self.update() except: return 1 def initUI(self): # 读取配置文件 global config_dic global twolineflag try: with open('config.json', 'r') as load_f: config_dic = json.load(load_f) except: config_dic = config_dic QMessageBox.information( self, # 使用infomation信息框 "注意", "未找到配置文件 请手动选择") self.read_json() # 初始化连接 self.IPbox = QLineEdit() #self.IPbox.setText("192.168.201.129") self.re_num = 1 self.usernamebox = QLineEdit() self.ethbox = QLineEdit() self.passwordbox = QLineEdit() self.connect_button = QPushButton("测试连接") self.update_button = QPushButton("更新配置") hbox1 = QHBoxLayout() hbox1.addWidget(QLabel("被测试IP:")) hbox1.addWidget(self.IPbox) hbox1.addWidget(self.connect_button) hbox1.addWidget(QLabel(" ")) hbox1.addWidget(QLabel("本机用户名:")) hbox1.addWidget(self.usernamebox) hbox1.addWidget(QLabel("本机密码:")) hbox1.addWidget(self.passwordbox) hbox1.addWidget(QLabel("网口号:")) hbox1.addWidget(self.ethbox) hbox1.addWidget(self.update_button) self.connect_button.clicked.connect(self.connect) self.update_button.clicked.connect(self.read_json) # 中间 self.topFiller = QWidget() self.topFiller.setMinimumSize(2500, 2000) #######设置滚动条的尺寸 self.tab = QTabWidget() for test_type in config_dic.keys(): send_list[test_type] = [] tab_name.append(test_type) self.tab.test_type = Checkboxlist(test_type) l = int(len(test_type) / 2) s = test_type[0:l] + '\n' * twolineflag + test_type[l:] self.tab.addTab(self.tab.test_type, s) # tab.tabBar().setFixedHeight(48) hbox2 = QHBoxLayout(self.topFiller) hbox2.addWidget(self.tab) #hbox2.addWidget(self.scroll) self.scroll = QScrollArea() self.scroll.setWidget(self.topFiller) # 辅助功能 hbox3 = QHBoxLayout() hbox4 = QHBoxLayout() self.re_timebox = QSlider(Qt.Horizontal, self) self.re_timebox.setMinimum(1) self.re_timebox.setMaximum(1000) self.re_timebox.valueChanged[int].connect(self.changeValue) self.num = QLabel("1") self.fullspeed = QCheckBox("全速发送") hbox3.addWidget(self.fullspeed) # -R hbox4.addWidget(QLabel(" 重复次数:")) hbox4.addWidget(self.num) hbox4.addWidget(self.re_timebox) hbox3.addWidget(QLabel(" 最大发包数量:")) self.maxpacknumbox = QLineEdit() # -L hbox3.addWidget(self.maxpacknumbox) hbox3.addWidget(QLabel(" 每秒发送报文数:")) self.packpsbox = QLineEdit() # -p hbox3.addWidget(self.packpsbox) '''hbox3.addWidget(QLabel(" 指定MTU:")) self.MTUbox = QLineEdit() # -t hbox3.addWidget(self.MTUbox)''' hbox3.addWidget(QLabel("发包速度/Mbps:")) self.Mbpsbox = QLineEdit() hbox3.addWidget(self.Mbpsbox) # 开始测试 self.start_button = QPushButton("开始发送数据包") self.start_button.clicked.connect(self.start_test) self.stop_button = QPushButton("停止发送数据包") self.stop_button.clicked.connect(self.stop_test) hbox5 = QHBoxLayout() hbox5.addWidget(self.start_button) hbox5.addWidget(self.stop_button) # hbox5.addWidget(QLabel(" time:")) # self.timebox = QLineEdit() # hbox5.addWidget(self.timebox) # 显示输出结果 self.resultbox = QTextBrowser() vbox = QVBoxLayout() vbox.addLayout(hbox1) vbox.addWidget(QLabel("选择测试模式:")) #vbox.addLayout(hbox2) vbox.addWidget(self.scroll) vbox.addWidget(QLabel("可选项:")) vbox.addLayout(hbox3) vbox.addLayout(hbox4) vbox.addLayout(hbox5) vbox.addWidget(QLabel("状态提示信息:")) vbox.addWidget(self.resultbox) self.setLayout(vbox) # self.setGeometry(300, 300, 290, 150) self.setWindowTitle('tcpreplay_gui') self.show() def changeValue(self, value): self.num.setText(str(value)) self.re_num = value def stop_test(self): if "数据包发送成功" not in self.resultbox.toPlainText( ) and " 默认发包速度下" in self.resultbox.toPlainText(): try: self.thread.terminate() self.thread2.terminate() self.resultbox.setText("") except: self.resultbox.setText("") else: self.resultbox.setText("") def start_test(self): self.resultbox.setText("") # tcprewrite是否需要 self.resultbox.setText("") # -i 设置eth端口 if self.ethbox.text() == "": text, ok = QInputDialog.getText(self, 'Input Dialog', '输入网口号:(eg:eth1)') if ok: self.ethbox.setText(str(text)) if self.passwordbox.text() == "": text, ok = QInputDialog.getText(self, 'Input Dialog', '输入密码') if ok: self.passwordbox.setText(str(text)) re = "echo " + self.passwordbox.text( ) + "|sudo -S " + "tcpreplay -i " + self.ethbox.text() + " " # 最大速率发送 -t if self.fullspeed.isChecked(): re += " -t " else: re = re # 重复次数 if self.re_num > 1: re = re + "-l " + str(self.re_num) + " " ''''#制定MTU if not self.MTUbox.text()=="": re+=" - "+ self.MTUbox.text()+' ''' '' # 每秒发包数量 if not self.packpsbox.text() == "": re += ' -p ' + self.packpsbox.text() + ' ' # 发送速度MB/s if not self.Mbpsbox.text() == "": re += ' -M ' + self.Mbpsbox.text() + ' ' # 最大发包数量 if not self.maxpacknumbox.text() == "": re += ' -L ' + self.maxpacknumbox.text() + ' ' # 数据包名称 路径应和json文件位置相同 tabindex = self.tab.currentIndex() tn = (tab_name[tabindex]) pcaplist = send_list[tn] if len(pcaplist) == 0: QMessageBox.information( self, # 使用infomation信息框 "注意", "请选择至少一个包") return if len(pcaplist) == 1: re += pcap_path + pcaplist[0] else: temp = re re = "" for i in pcaplist: re += temp + pcap_path + i + " &&" re = re[0:-2] # self.resultbox.setText(self.resultbox.toPlainText() + '\r\n' + re) self.starttime = time.time() self.resultbox.setText(self.resultbox.toPlainText() + '\r\n' + "正在发送数据包 默认发包速度下可能需要较长时间 请耐心等待。。。") self.thread = MyThread(re=re) # 创建一个线程 发送cmd self.thread2 = MyThread2(self.starttime) # 创建一个线程 计时 self.thread.sec_changed_signal.connect( self.update_state) # cmd线程发过来的信号挂接到槽:update_state self.thread2.sec_changed_signal.connect( self.update_time) # 计时线程发过来的信号挂接到槽:update_time self.thread.start() self.thread2.start() def update_state(self, b): if "Actual" in b: self.resultbox.setText("数据包发送成功!" + '\r\n结果统计信息:\r\n' + b[b.index("Actual"):]) else: QMessageBox.information( self, # 使用infomation信息框 "注意", "未能成功发送 请检查网口设置与软件是否正确安装") # self.resultbox.setText(self.resultbox.toPlainText() + '\r\n' + b) self.thread.terminate() self.thread2.terminate() def update_time(self, a): self.resultbox.setText(self.resultbox.toPlainText() + '\r\n' + "已用时间:" + str(round(time.time() - self.starttime)) + "s")
class MainForm(QMainWindow, Ui_MainWindow): def __init__(self): super(MainForm, self).__init__() self.setupUi(self) self.db_name = None self.db_connection = None self.tables_tab_widget = None self.modified_data_in_tables = {} self.connect_toolbar_with_functions() def connect_toolbar_with_functions(self): self.open_db_action.triggered.connect(self.open_db) self.delete_selected_elements_action.triggered.connect(self.delete_elem) self.save_current_table_action.triggered.connect(self.save_current_table_changes) self.save_all_tables_action.triggered.connect(self.save_all_tables_changes) self.save_db_as_action.triggered.connect(self.save_db_as) def open_db(self): try: name = QFileDialog.getOpenFileName(self, "Выберите базу данных sqlite", '', "*.sqlite")[0] if name: self.db_name = name if self.db_connection: self.db_connection.close() self.db_connection = sqlite3.connect(self.db_name) cur = self.db_connection.cursor() cur.execute("SELECT name FROM sqlite_master WHERE type='table';") tables_names = [el[0] for el in cur.fetchall()] if not tables_names: raise Exception("Нет таблиц в базе данных") if self.notLoadDbLabel: self.notLoadDbLabel.deleteLater() self.notLoadDbLabel = None if not self.tables_tab_widget: self.tables_tab_widget = QTabWidget(self) self.gridLayout.addWidget(self.tables_tab_widget) else: self.tables_tab_widget.clear() for name in tables_names: self.modified_data_in_tables[name] = {} table_widget = QTableWidget() self.tables_tab_widget.addTab(table_widget, name) self.init_table_widget_for_db_table(table_widget, name) except sqlite3.Error as e: self.statusBar().showMessage(f'Ошибка с бд: {e.args[0]}') except Exception as e: self.statusBar().showMessage(f'Ошибка: {e.args[0]}') def save_db_as(self): try: db_name = QFileDialog.getSaveFileName(self, "Сохранить базу данных sqlite как", '', "*.sqlite")[0] with open(self.db_name, mode='rb') as db_cur: with open(db_name, mode='wb') as out_file: out_file.write(db_cur.read()) except Exception as e: self.statusBar().showMessage(f'Ошибка: {e.args[0]}') def init_table_widget_for_db_table(self, table_widget, name): try: # Получим результат запроса cursor = self.db_connection.cursor() res = cursor.execute(f"SELECT * FROM {name}").fetchall() # Если запись не нашлась, то не будем ничего делать if not res: self.statusBar().showMessage('Ничего не нашлось') return table_widget.clear() # Заполнили размеры таблицы table_widget.setRowCount(len(res)) table_widget.setColumnCount(len(res[0])) # Устанавливаем заголовки таблицы titles = [description[0] for description in cursor.description] table_widget.setHorizontalHeaderLabels(titles) # Заполнили таблицу полученными элементами for i, elem in enumerate(res): for j, val in enumerate(elem): str_val = str(val) if str_val.startswith("b'") and type(val) is bytes: button = QPushButton('Открыть файл') file_type = magic.from_buffer(val).split()[0].lower() image_file_types = ['jpeg', 'jpg'] if file_type in image_file_types: button.imageBytes = val id_el = self.get_id_of_element_at_row_and_column(i, j) prop = titles[j] button.clicked.connect(self.open_image_edit_form(id_el, prop, button.imageBytes, button)) table_widget.setCellWidget(i, j, button) else: table_widget.setItem(i, j, QTableWidgetItem(str_val)) table_widget.resizeColumnsToContents() table_widget.itemChanged.connect(self.item_changed) except sqlite3.Error as e: self.statusBar().showMessage(f'Ошибка с бд: {e.args[0]}') except Exception as e: self.statusBar().showMessage(f'Ошибка: {e.args[0]}') def open_image_edit_form(self, id_el, prop, image_data, button): def callback(): try: self.imageEditForm = ImageReplaceForm(id_el, prop, image_data) self.imageEditForm.show() self.imageEditForm.replaceImageButton.clicked.connect(lambda: self.save_image(button)) except Exception as e: self.statusBar().showMessage(f'Ошибка: {e.args[0]}') return callback def save_image(self, button): try: if not self.imageEditForm.new_image_name: raise Exception("Не отключена кнопка сохранения картинки") with open(self.imageEditForm.new_image_name, mode='rb') as new_image: blob_data = new_image.read() element_id = self.imageEditForm.element_id element_property = self.imageEditForm.element_property self.imageEditForm.close() current_table_name = self.get_current_table_name() cur = self.db_connection.cursor() cur.execute(f"""UPDATE {current_table_name} SET '{element_property}'= ? WHERE id = {element_id}""", (blob_data,)).fetchone() self.db_connection.commit() button.imageBytes = blob_data button.clicked.connect(self.open_image_edit_form(element_id, element_property, button.imageBytes, button)) except Exception as e: self.statusBar().showMessage(f'Ошибка: {e.args[0]}') def get_current_table_name(self): try: current_table_name = self.tables_tab_widget \ .tabText(self.tables_tab_widget.currentIndex()) # clear table name of modified state if current_table_name[-1] == "*": current_table_name = current_table_name[:-1] return current_table_name except Exception as e: self.statusBar().showMessage(f'Ошибка: {e.args[0]}') def get_id_of_element_at_row_and_column(self, item_row, item_column): try: table_widget = self.tables_tab_widget.currentWidget() column_id = -1 for column_i in range(table_widget.columnCount()): column_text = table_widget.horizontalHeaderItem(column_i).text() if column_text == "id": column_id = column_i break if column_id == -1: raise Exception("Поля id не существует в таблице") return int(table_widget.item(item_row, column_id).text()) except Exception as e: self.statusBar().showMessage(f'Ошибка: {e.args[0]}') def item_changed(self, item): try: current_table_name = self.get_current_table_name() # Если значение в ячейке было изменено, # то в словарь записывается пара: название поля, новое значение table_widget = self.tables_tab_widget.currentWidget() prop = table_widget.horizontalHeaderItem(item.column()).text() id = self.get_id_of_element_at_row_and_column(item.row(), item.column()) modified_data = {"property": prop, "text": item.text()} current_modif_table = self.modified_data_in_tables.get(current_table_name) if not current_modif_table.get(id, False): current_modif_table[id] = [modified_data] else: current_modif_table[id].append(modified_data) self.tables_tab_widget.setTabText( self.tables_tab_widget.currentIndex(), f"{current_table_name}*") except Exception as e: self.statusBar().showMessage(f'Ошибка: {e.args[0]}') def save_all_tables_changes(self): try: if self.modified_data_in_tables and self.tables_tab_widget: indx_of_table = 0 for table_name, value in self.modified_data_in_tables.items(): if value: cur = self.db_connection.cursor() for id, changed_props in self.modified_data_in_tables[table_name].items(): que = f"UPDATE {table_name} SET\n" que += ", ".join([f"{props['property']}='{props['text']}'" for props in changed_props]) que += f" WHERE id = {id}" cur.execute(que) self.db_connection.commit() self.modified_data_in_tables[table_name].clear() self.tables_tab_widget.setTabText( indx_of_table, table_name) indx_of_table += 1 else: raise Exception('Нет таблицы') except Exception as e: self.statusBar().showMessage(f'Ошибка: {e.args[0]}') def save_current_table_changes(self): try: if self.modified_data_in_tables and self.tables_tab_widget: current_table_name = self.get_current_table_name() if current_table_name and self.modified_data_in_tables[current_table_name]: cur = self.db_connection.cursor() for id, changed_props in self.modified_data_in_tables[current_table_name].items(): que = f"UPDATE {current_table_name} SET\n" que += ", ".join([f"'{props['property']}'='{props['text']}'" for props in changed_props]) que += f" WHERE id = '{id}'" cur.execute(que) self.db_connection.commit() self.modified_data_in_tables[current_table_name].clear() self.tables_tab_widget.setTabText( self.tables_tab_widget.currentIndex(), current_table_name) else: raise Exception('Нет таблицы') except Exception as e: self.statusBar().showMessage(f'Ошибка: {e.args[0]}') def delete_elem(self): try: if self.tables_tab_widget: table_widget = self.tables_tab_widget.currentWidget() rows = list(set([i.row() for i in table_widget.selectedItems()])) if not rows: raise Exception('Не выделены никакие строки') ids = [table_widget.item(i, 0).text() for i in rows] valid = QMessageBox.question( self, '', "Действительно удалить элементы с id " + ",".join(ids), QMessageBox.Yes, QMessageBox.No) if valid == QMessageBox.Yes: cur = self.db_connection.cursor() current_table_name = self.get_current_table_name() cur.execute(f"DELETE FROM {current_table_name} WHERE id IN (" + ", ".join( '?' * len(ids)) + ")", ids) self.db_connection.commit() self.init_table_widget_for_db_table(table_widget, current_table_name) self.modified_data_in_tables[current_table_name].clear() self.tables_tab_widget.setTabText( self.tables_tab_widget.currentIndex(), current_table_name) else: raise Exception('Нет таблицы') except sqlite3.Error as e: self.statusBar().showMessage(f'Ошибка с бд: {e.args[0]}') except Exception as e: self.statusBar().showMessage(f'Ошибка: {e.args[0]}') def closeEvent(self, event): reply = QMessageBox.question(self, 'Закрытие окна', 'Вы уверены, что хотите закрыть приложение?', QMessageBox.Yes | QMessageBox.No, QMessageBox.No) if reply == QMessageBox.Yes: if self.db_connection: self.db_connection.close() event.accept() else: event.ignore()
class MainWindow(QMainWindow): """ the main render proccess of the browser including all updating methods and the webview """ def __init__(self, *args, **kwargs): super(MainWindow, self).__init__(*args, **kwargs) self.url = {} self.setWindowTitle("Bird-Browser") self.wordlist = [ "dev://", "bookmark://", "bookmarks://", "search://", "http://", "https://", "pwd://" ] self.tabs = QTabWidget() self.tabcreate = QPushButton("+") self.shortcreatetab = QShortcut(self) self.shortcreatetab.setKey("Ctrl+T") self.pwdman = pwdmanager.PwdManager( config["key"], f"{Path.home()}/.config/bird/database.db") if "style" in config: innerstyle = f""" color: {config['style']['color']}; background-color: {config['style']['background-color']}; """ self.setStyleSheet(f""" QMainWindow{ {innerstyle} } """) self.tabs.setStyleSheet(f""" QTabWidget{ {innerstyle} } """) else: self.setStyleSheet(""" background-color: rgb(50, 50, 50); color: rgb(255, 255, 255); """) self.tabs.setStyleSheet(""" background-color: rgb(50, 50, 50); color: rgb(255, 255, 255); """) self.tabs.clear() self.tabs.setTabsClosable(True) self.createtab() #connections self.shortcreatetab.activated.connect(self.createtab) self.tabcreate.clicked.connect(self.createtab) self.tabs.tabCloseRequested.connect(self.closetab) self.tabs.setCornerWidget(self.tabcreate) #show window self.setCentralWidget(self.tabs) self.show() def updatewin(self, browser: QWebEngineView, boolean=True): """ This Method updates the shown browser window to the current url of the search bar it also contains the search engine-integration, and bookmarking """ url = self.url[str(self.tabs.currentIndex)] if url.startswith("search://"): search = url.split("search://", 1)[1] url = config["search-engine"].format(search=search) elif url.startswith("bookmarks://"): try: name = url.split("bookmarks://", 1)[1] url = bmk.getBookmark(cursor, name)[name] except ValueError: url = "about:blank" elif url.startswith("bookmark://"): if not "|" in url.split("bookmark://", 1)[1]: url = "about:blank" else: parts = url.split("bookmark://", 1)[1].split("|") if parts[1] == "-": parts[1] = browser.url().toString() try: bmk.getBookmark(cursor, parts[0]) bmk.modifyBookmark(cursor, parts[0], parts[0], parts[1]) conn.commit() except: bmk.addBookmark(cursor, parts[0], parts[1]) conn.commit() elif url.startswith("file://"): path = url.split("file://", 1)[1] if path.startswith("~/"): path = f"{Path.home()}/{path.split('~/', 1)[1]}" try: with open(path, "r") as reqfile: reqcontent = reqfile.read() browser.page().setHtml(reqcontent) return except: print(f"Error on requested page: Path '{path}' doesn't exist") browser.page().loadUrl(QUrl("about:blank")) elif url.startswith("dev://"): url = url.split("dev://", 1)[1] if not url.startswith("https://") and not url.startswith( "http://"): url = "http://" + url self.tabs.addTab(devtools.DevToolWidget(url), "dev-tools") elif url.startswith("pwd://"): self.tabs.addTab(pwdmanager.PwdManagerWidget(self.pwdman), "Passwords") return elif "additional-search-engines" in config: for source in config["additional-search-engines"]: if url.startswith(source): search = url.split(source, 1)[1] url = config["additional-search-engines"][source].format( search=search) break else: pass else: if not url.startswith("https://") and not url.startswith( "http://"): url = "http://" + url elif not url.startswith("https://") and not url.startswith("http://"): url = "http://" + url browser.page().load(QUrl(url)) self.wordlist.append(url) def updatetext(self, text: str): """ This Method updates the internal text of the search bar """ self.url[str(self.tabs.currentIndex)] = text def updateurl(self, url, bar): """ This method updates the url bar to the currently displayed url. It gets triggered if the displayed page directs you to a different page """ bar.setText(url.toString()) def updatetab(self, arg_1, index, browser): if len(browser.page().title()) > 20: self.tabs.setTabText(index, browser.page().title()[0:17] + "...") else: self.tabs.setTabText(index, browser.page().title()) self.tabs.setTabIcon(index, browser.page().icon()) def updatetitle(self, title: str): self.tabs.setTabText(self.tabs.currentIndex(), title) def updateicon(self, index, icon): self.tabs.setTabIcon(index, icon) def closetab(self, index): if index == self.tabs.currentIndex(): self.tabs.setCurrentIndex(index - 1 if index != 0 else index + 1) self.tabs.removeTab(index) if self.tabs.count() == 0: sys.exit(0) @pyqtSlot() def createtab(self, url: str = config["startup-url"]): layout = QGridLayout() widget = QWidget() widget.setLayout(layout) bar = QLineEdit() completer = QCompleter(self.wordlist) browser = QWebEngineView() backbtn = QPushButton("←") reloadbtn = QPushButton("reload") gotocurrenturlbutton = QPushButton("go!") reloadshort = QShortcut(self) reloadshort.setKey("Ctrl+R") bar.setCompleter(completer) reloadshort.activated.connect(browser.reload) gotocurrenturlbutton.clicked.connect( lambda clicked, browser=browser: self.updatewin(browser, clicked)) reloadbtn.clicked.connect(browser.reload) bar.returnPressed.connect( lambda browser=browser: self.updatewin(browser, True)) bar.textChanged.connect(self.updatetext) browser.load(QUrl(url)) browser.page().urlChanged.connect( lambda qurl, bar=bar: self.updateurl(qurl, bar)) browser.page().loadFinished.connect( lambda arg__1, index=self.tabs.indexOf(browser), browser=browser: self.updatetab(arg__1, index, browser)) browser.page().iconChanged.connect(lambda qicon, index=self.tabs.count( ): self.updateicon(index, qicon)) browser.page().setUrlRequestInterceptor(NetworkFilter) browser.page().titleChanged.connect( lambda title=browser.page().title(): self.updatetitle(title)) backbtn.clicked.connect(browser.back) layout.addWidget(bar, 1, 3) layout.addWidget(reloadbtn, 1, 2) layout.addWidget(browser, 2, 1, 1, 5) layout.addWidget(backbtn, 1, 1) layout.addWidget(gotocurrenturlbutton, 1, 4) self.tabs.addTab(widget, browser.icon(), browser.title()) self.tabs.setCurrentIndex(self.tabs.count() - 1) def renderDevToolsView(self, webview): return self.tabs.setCurrentWidget( devtools.DevToolWidget(webview.page().url().toString()))
def __init__(self, manager, parent=None): QTabWidget.__init__(self, parent) self.manager = manager self.area = None self.reference = None self.index = 0 self.dragCanStart = False self.tabDragCanStart = False self.areaDragCanStart = False self.setTabBar(QToolWindowTabBar(self)) self.setMovable(True) self.setTabShape(QTabWidget.Rounded) self.setDocumentMode( self.manager.config.setdefault(QTWM_AREA_DOCUMENT_MODE, True)) self.useCustomTabCloseButton = self.manager.config.setdefault( QTWM_AREA_TABS_CLOSABLE, False) self.useTableFrame = self.manager.config.setdefault( QTWM_AREA_USE_TAB_FRAME, False) self.setTabsClosable( self.manager.config.setdefault(QTWM_AREA_TABS_CLOSABLE, False) and not self.useCustomTabCloseButton) self.setTabPosition( self.manager.config.setdefault(QTWM_AREA_TAB_POSITION, QTabWidget.North)) # self.setLayoutDirection ( QtCore.Qt.LeftToRight ) self.tabBar().setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Preferred) self.setFocusPolicy(QtCore.Qt.StrongFocus) areaUseImageHandle = self.manager.config.setdefault( QTWM_AREA_IMAGE_HANDLE, False) if self.useTableFrame: self.tabFrame = QToolWindowSingleTabAreaFrame(manager, self) self.tabFrame.hide() self.tabFrame.installEventFilter(self) self.tabFrame.caption.installEventFilter(self) if self.manager.config.setdefault(QTWM_AREA_SHOW_DRAG_HANDLE, False): corner = QLabel(self) corner.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Expanding) corner.setAttribute(QtCore.Qt.WA_TranslucentBackground) self.setCornerWidget(corner) if areaUseImageHandle: corner_img = QPixmap() corner_img.load( self.manager.config.setdefault( QTWM_DROPTARGET_COMBINE, "./resources/drag_handle.png")) corner.setPixmap(corner_img) else: corner.setFixedHeight(8) corner_img = QPixmap(corner.size()) corner_img.fill(QtCore.Qt.transparent) option = QStyleOptionToolBar() option.initFrom(self.tabBar()) option.state |= Qt.QStyle.State_Horizontal option.lineWidth = self.tabBar().style().pixelMetric( Qt.QStyle.PM_ToolBarFrameWidth, None, self.tabBar()) option.features = QStyleOptionToolBar.Movable option.toolBarArea = QtCore.Qt.NoToolBarArea option.direction = QtCore.Qt.RightToLeft option.rect = corner_img.rect() option.rect.moveTo(0, 0) painter = QPainter(corner_img) self.tabBar().style().drawPrimitive( Qt.QStyle.PE_IndicatorToolBarHandle, option, painter, corner) painter.end() corner.setPixmap(corner_img) corner.setCursor(QtCore.Qt.OpenHandCursor) corner.installEventFilter(self) self.tabBar().installEventFilter(self) self.tabBar().setContextMenuPolicy(QtCore.Qt.CustomContextMenu) self.setContextMenuPolicy(QtCore.Qt.CustomContextMenu) self.customContextMenuRequested.connect(self.showContextMenu) self.tabBar().customContextMenuRequested.connect(self.showContextMenu) self.tabBar().tabCloseRequested.connect(self.closeTab)
def eventFilter(self, o, e): if o == self.tabBar() or o == self.cornerWidget( ) or o == self.tabFrame or o == self.tabFrame.caption: # debug msg # if e.type () == Qt.QEvent.MouseButtonPress or e.type () == Qt.QEvent.MouseMove: # if o == self.tabBar (): # qWarning ( "eventFilter o == self.tabBar ()" ) # elif o == self.cornerWidget (): # qWarning ( "eventFilter o == self.cornerWidget ()" ) # elif o == self.tabFrame: # qWarning ( "eventFilter o == self.tabFrame" ) # elif o == self.tabFrame.caption: # qWarning ( "eventFilter o == self.tabFrame.caption" ) if e.type() == QEvent.MouseButtonPress and e.buttons( ) == QtCore.Qt.LeftButton: self.areaDragCanStart = False self.tabDragCanStart = False # if ((pObject == m_pTabFrame & & m_pTabFrame->m_pCaption->rect().contains ( me->pos())) | | pObject == m_pTabFrame->m_pCaption | | (pObject == tabBar() & & tabBar()->tabAt(static_cast < QMouseEvent * > (pEvent)->pos()) >= 0)) if ( self.useTableFrame and o == self.tabFrame and self.tabFrame.caption.rect ().contains ( e.pos () ) )\ or ( self.useTableFrame and o == self.tabFrame.caption ) \ or ( o == self.tabBar () and self.tabBar ().tabAt ( e.pos () ) >= 0 ): self.tabDragCanStart = True # qWarning ( "[QToolWindowArea] eventFilter: tabDragCanStart = true" ) elif self.manager.config.setdefault( QTWM_AREA_SHOW_DRAG_HANDLE, False) and (o == self.cornerWidget() or (o == self.tabBar() and self.cornerWidget().isVisible() and self.manager.config.setdefault( QTWM_AREA_EMPTY_SPACE_DRAG, False))): self.areaDragCanStart = True # qWarning ( "[QToolWindowArea] eventFilter: areaDragCanStart = true" ) # MyCode else: self.dragCanStart = True # qWarning ( "[QToolWindowArea] eventFilter: dragCanStart = true" ) if self.manager.isMainWrapper(self.parentWidget()): self.areaDragCanStart = False if self.count() == 1: self.tabDragCanStart = False elif e.type() == QEvent.MouseButtonRelease: self.tabDragCanStart = False self.dragCanStart = False self.areaDragCanStart = False self.manager.updateDragPosition() # qWarning ( "[QToolWindowArea] eventFilter: MouseButtonRelease" ) elif e.type() == QEvent.MouseMove: self.manager.updateDragPosition() if self.tabDragCanStart: if self.tabBar ().rect ().contains ( e.pos () ) \ or ( self.manager.config.setdefault ( QTWM_AREA_SHOW_DRAG_HANDLE, False ) and self.cornerWidget ().rect ().contains ( e.pos () ) ): return False if e.buttons() != QtCore.Qt.LeftButton: return False # qWarning ( "[QToolWindowArea] eventFilter: tabDragCanStart." ) toolWindow = self.currentWidget() if self.useTableFrame and cast( toolWindow, QToolWindowSingleTabAreaFrame) == self.tabFrame: toolWindow = self.tabFrame.contents if not (toolWindow and self.manager.ownsToolWindow(toolWindow)): return False self.tabDragCanStart = False # qWarning ( "[QToolWindowArea] eventFilter: tabDragCanStart = false" ) # stop internal tab drag in QTabBar releaseEvent = QMouseEvent(QEvent.MouseButtonRelease, e.pos(), QtCore.Qt.LeftButton, QtCore.Qt.LeftButton, QtCore.Qt.NoModifier) qApp.sendEvent(self.tabBar(), releaseEvent) self.manager.startDrag([toolWindow], self) elif self.areaDragCanStart: if qApp.mouseButtons () == QtCore.Qt.LeftButton \ and not ( self.manager.config.setdefault ( QTWM_AREA_SHOW_DRAG_HANDLE, False ) and self.cornerWidget ().rect ().contains ( self.mapFromGlobal ( QCursor.pos () ) ) ): # qWarning ( "[QToolWindowArea] eventFilter: areaDragCanStart." ) toolWindows = [] for i in range(0, self.count()): toolWindow = self.widget(i) if self.useTableFrame and cast( toolWindow, QToolWindowSingleTabAreaFrame ) == self.tabFrame: toolWindow = self.tabFrame.contents toolWindows.append(toolWindow) self.areaDragCanStart = False # qWarning ( "[QToolWindowArea] eventFilter: areaDragCanStart = false" ) if self.cornerWidget() != None: releaseEvent = QMouseEvent( Qt.QEvent.MouseButtonRelease, e.pos(), QtCore.Qt.LeftButton, QtCore.Qt.LeftButton, QtCore.Qt.NoModifier) qApp.sendEvent(self.cornerWidget(), releaseEvent) self.manager.startDrag(toolWindows, self) self.releaseMouse() elif self.dragCanStart: self.check_mouse_move(e) return QTabWidget.eventFilter(self, o, e)
def setCurrentWidget(self, w): if self.useTableFrame and w == self.tabFrame.contents: QTabWidget.setCurrentWidget(self, self.tabFrame) else: QTabWidget.setCurrentWidget(self, w)
class MainWinBase(QMainWindow): def __init__(self): super().__init__() self.resize(1024, 720) self.menubar = self.menuBar() self.statusbar = self.statusBar() self.mainTabWidget = QTabWidget() self.actions = {} self.menus = {} self.submenus = {} self.contexMenus = {} self.toolbars = {} self.status = {} self.docks = {} self.documents = {} self.createActions() self.themeCombo = QComboBox() self.createMenubar() self.createContexMenus() self.createToolbars() self.createStatusBar() self.createDocks() self.createMainWidget() self.setupUiActions() def createActions(self): def createAct(text, tip=None, shortcut=None, iconimg=None, checkable=False, slot=None): action = QAction(self.tr(text), self) if iconimg is not None: action.setIcon(QIcon(iconimg)) if shortcut is not None: action.setShortcut(shortcut) if tip is not None: tip = self.tr(tip) action.setToolTip(tip) action.setStatusTip(tip) if checkable: action.setCheckable(True) if slot is not None: action.triggered.connect(slot) return action def keys2str(standardkey): return "".join(("(", QKeySequence(standardkey).toString(), ")")) # self.actions["new"] = createAct(self.tr("&New", "&New"), # self.tr("new") + keys2str(QKeySequence.New), QKeySequence.New, # ':appres.img/NewDocument.png') self.actions["import"] = createAct(self.tr("&Import Image"), self.tr("Import Image"), None, ':appres.img/importimage.png') self.actions["replaceimage"] = createAct( self.tr("&Replace Image"), self.tr("Replace Image"), None, ':appres.img/replaceimage.png') self.actions["open"] = createAct( self.tr("&Open Project"), self.tr("Open") + keys2str(QKeySequence.Open), QKeySequence.Open, ':appres.img/open.png') self.actions["save"] = createAct( self.tr("&Save"), self.tr("Save") + keys2str(QKeySequence.Save), QKeySequence.Save, ':appres.img/save.png') self.actions["saveas"] = createAct(self.tr("&Save as..."), self.tr("Save as..."), None, ':appres.img/SaveAs.png') self.actions["export"] = createAct( self.tr("&ExportCurves"), self.tr("Export digitized curves data"), "Ctrl+Alt+E", ':appres.img/export.png') self.actions["close"] = createAct(self.tr("&Close"), self.tr("Close")) self.actions["exit"] = createAct(self.tr("&Exit"), self.tr("Exit"), "Ctrl+Q") self.actions["undo"] = createAct( self.tr("&Undo"), self.tr("Undo") + keys2str(QKeySequence.Undo), QKeySequence.Undo, ':appres.img/undo.png') self.actions["redo"] = createAct( self.tr("&Redo"), self.tr("Redo") + keys2str(QKeySequence.Redo), QKeySequence.Redo, ':appres.img/redo.png') self.actions["cut"] = createAct( self.tr("&Cut"), self.tr("Cut") + keys2str(QKeySequence.Cut), QKeySequence.Cut, ':appres.img/cut.png') self.actions["copy"] = createAct( self.tr("&Copy"), self.tr("Copy") + keys2str(QKeySequence.Copy), QKeySequence.Copy, ':appres.img/copy.png') self.actions["paste"] = createAct( self.tr("&Paste"), self.tr("Paste") + keys2str(QKeySequence.Paste), QKeySequence.Paste, ':appres.img/paste.png') self.actions["zoomin"] = createAct(self.tr("&ZoomIn"), self.tr("Zoom In"), "Ctrl++", ':appres.img/zoomin.png') self.actions["zoomout"] = createAct(self.tr("&ZoomOut"), self.tr("Zoom Out"), "Ctrl+-", ':appres.img/zoomout.png') self.actions["showgrid"] = createAct(self.tr("Show Axes &Grid"), self.tr("Show AxesGrid"), None, ':appres.img/grid.png', checkable=True) self.actions["showgrid"].setChecked(True) self.actions["select"] = createAct(self.tr("Select Mode"), self.tr("Select Mode"), None, ':appres.img/select.png', checkable=True) self.actions["axesx"] = createAct(self.tr("Set x axis postions"), self.tr("Set x axis position"), None, ':appres.img/axesx.png', checkable=True) self.actions["axesy"] = createAct(self.tr("Set y axis postions"), self.tr("Set y axis position"), None, ':appres.img/axesy.png', checkable=True) self.actions["curve"] = createAct(self.tr("&AddCurve"), self.tr("Add Curve"), None, ':appres.img/curve.png', checkable=True) self.actions["del"] = createAct(self.tr("&del point or axis"), self.tr("delete"), QKeySequence.Delete, ':appres.img/delete.png') self.actions["addcurve"] = createAct(self.tr("add a new curve"), self.tr("add a new curve"), None, ':appres.img/new.png') self.actions["renamecurve"] = createAct( self.tr("change curve name"), self.tr("change current curve name"), None, ':appres.img/edit.png') self.actions["delcurve"] = createAct(self.tr("&del curve"), self.tr("delete selected curve"), QKeySequence.Delete, ':appres.img/delete.png') self.actions["scalegraph"] = createAct( self.tr("set graph image scale"), self.tr("scale the graph image(background)"), None, ":appres.img/resizeimage.png") self.actions["gridsetting"] = createAct( self.tr("grid settings"), self.tr("set grid range and step"), None, ":appres.img/gridsetting.png") # self.actions["DisableQss"] = createAct(self.tr("&DisableQss"), self.tr("DisableQss"), checkable=True) # self.actions["DisableQss"].setChecked(False) # self.actions["ShowColor"] = createAct(self.tr("&ColorPanel"), # self.tr("ShowColorPanel"), # None, # ":appres.img/color.png", # checkable=True) # self.actions["ShowColor"].setChecked(True) self.actions["showcurves"] = createAct( self.tr("&CurvePanel"), self.tr("ShowCurvesPanel"), None, ":appres.img/view2col2right.png", checkable=True) self.actions["showfit"] = createAct( self.tr("Show Fit&&Interpolation Panel"), self.tr("ShowCurve Fit&Interpolation Panel"), None, None, checkable=True) self.actions["showcurves"].setChecked(True) self.actions["hidegraph"] = createAct( self.tr("Hide Graph"), self.tr("Hide the graph(background image)"), None, None, checkable=True) self.actions["showoriginalgraph"] = createAct( self.tr("Original Graph"), self.tr("Show the original graph(background image)"), None, None, checkable=True) self.actions["config"] = createAct(self.tr("&Config"), self.tr("settings"), None, ":appres.img/config.png") self.actions["about"] = createAct(self.tr("&About"), self.tr("About")) self.actions["help"] = createAct(self.tr("&Help"), self.tr("Help")) # self.exitAct.triggered.connect(qApp.exit)#等价于qApp.quit self.actions["exit"].triggered.connect(self.close) def createMenubar(self): self.menus["File"] = QMenu(self.tr("&File")) self.menus["Edit"] = QMenu(self.tr("&Edit")) self.menus["Digit"] = QMenu(self.tr("&Digit")) self.menus["View"] = QMenu(self.tr("&View")) self.menus["Config"] = QMenu(self.tr("&Config")) self.menus["Help"] = QMenu(self.tr("&Help")) editMenu = QMenu(self.tr("Text"), self.menus["Edit"]) editMenu.setIcon(QIcon(":appres.img/edit_whitepage.png")) editMenu.addAction(self.actions["undo"]) editMenu.addAction(self.actions["redo"]) # editMenu.addSeparator() # editMenu.addAction(self.actions["cut"]) # editMenu.addAction(self.actions["copy"]) # editMenu.addAction(self.actions["paste"]) self.menus["File"].addAction(self.actions["import"]) self.menus["File"].addAction(self.actions["replaceimage"]) self.menus["File"].addAction(self.actions["open"]) self.menus["File"].addAction(self.actions["save"]) self.menus["File"].addAction(self.actions["saveas"]) self.menus["File"].addAction(self.actions["export"]) self.menus["File"].addAction(self.actions["close"]) self.menus["File"].addSeparator() self.menus["File"].addAction(self.actions["exit"]) self.menus["Edit"].addAction(self.actions["undo"]) self.menus["Edit"].addAction(self.actions["redo"]) self.menus["Digit"].addAction(self.actions["select"]) self.menus["Digit"].addAction(self.actions["axesx"]) self.menus["Digit"].addAction(self.actions["axesy"]) self.menus["Digit"].addAction(self.actions["curve"]) self.menus["Digit"].addSeparator() self.menus["Digit"].addAction(self.actions["addcurve"]) self.menus["Digit"].addAction(self.actions["renamecurve"]) self.menus["Digit"].addAction(self.actions["del"]) self.menus["View"].addAction(self.actions["zoomin"]) self.menus["View"].addAction(self.actions["zoomout"]) self.menus["View"].addSeparator() background = QMenu(self.tr("Graph Background"), self.menus["View"]) background.addAction(self.actions["hidegraph"]) background.addAction(self.actions["showoriginalgraph"]) self.menus["View"].addMenu(background) self.menus["View"].addAction(self.actions["showgrid"]) self.menus["View"].addSeparator() self.menus["View"].addAction(self.actions["showcurves"]) self.menus["View"].addAction(self.actions["showfit"]) self.menus["View"].addSeparator() self.menus["Config"].addAction(self.actions["config"]) self.menus["Help"].addAction(self.actions["help"]) self.menus["Help"].addSeparator() self.menus["Help"].addAction(self.actions["about"]) for m in self.menus.values(): self.menubar.addMenu(m) def createContexMenus(self): self.contexMenus["Edit"] = QMenu(self.tr("Edit")) self.contexMenus["Edit"].addAction(self.actions["cut"]) self.contexMenus["Edit"].addAction(self.actions["copy"]) self.contexMenus["Edit"].addAction(self.actions["paste"]) def createToolbars(self): themeLabel = QLabel(self.tr("Theme ")) # self.themeCombo = QComboBox() themeLabel.setToolTip(self.tr("Using system style.")) self.themeCombo.setToolTip(self.tr("Select system style.")) self.themeCombo.addItems(QStyleFactory.keys()) self.themeCombo.setMinimumWidth(105) theme = QApplication.style().objectName() self.themeCombo.setCurrentIndex( self.themeCombo.findText(theme, Qt.MatchFixedString)) # self.themeCombo.setEnabled(False) # themeCombo.activated[str].connect(qApp.setStyle) # themeCombo.currentTextChanged.connect(qApp.setStyle) # checkbox.stateChanged.connect(self.themeCombo.setEnabled) # checkbox.stateChanged.connect(lambda x:self.actions["DisableQss"].setChecked(checkbox.isChecked())) self.toolbars["Main"] = QToolBar(self.tr("Main", "toolbar")) self.toolbars["Main"].addWidget(themeLabel) self.toolbars["Main"].addWidget(self.themeCombo) self.toolbars["File"] = QToolBar(self.tr("File")) self.toolbars["File"].addAction(self.actions["import"]) self.toolbars["File"].addAction(self.actions["open"]) self.toolbars["File"].addAction(self.actions["save"]) # self.toolbars["File"].addAction(self.actions["saveas"]) self.toolbars["File"].addAction(self.actions["export"]) self.toolbars["Edit"] = QToolBar(self.tr("Edit")) self.toolbars["Edit"].addAction(self.actions["undo"]) self.toolbars["Edit"].addAction(self.actions["redo"]) self.toolbars["Edit"].addAction(self.actions["del"]) self.toolbars["Digitize"] = QToolBar(self.tr("Digitize")) self.toolbars["Digitize"].addAction(self.actions["select"]) self.toolbars["Digitize"].addAction(self.actions["axesx"]) self.toolbars["Digitize"].addAction(self.actions["axesy"]) self.toolbars["Digitize"].addAction(self.actions["curve"]) self.toolbars["Display"] = QToolBar(self.tr("Display")) self.toolbars["Display"].addAction(self.actions["zoomin"]) self.toolbars["Display"].addAction(self.actions["zoomout"]) self.toolbars["Display"].addAction(self.actions["showgrid"]) self.toolbars["Setting"] = QToolBar(self.tr("Setting")) self.toolbars["Setting"].addAction(self.actions["scalegraph"]) self.toolbars["Setting"].addAction(self.actions["gridsetting"]) self.toolbars["Setting"].addAction(self.actions["config"]) self.toolbars["View"] = QToolBar(self.tr("View")) self.toolbars["View"].addAction(self.actions["showcurves"]) for t in self.toolbars.values(): self.addToolBar(t) def createStatusBar(self): self.statusbar.showMessage(self.tr("Ready")) # self.statusbar.addWidget(QWidget(),1) # self.status["date"] = QLabel() # self.statusbar.addPermanentWidget(self.status["date"]) # self.status["date"].setText(QDate.currentDate().toString()) # self.status["date"].setVisible(False) self.status["point"] = QLabel(self.tr("Point Coordinate: 0,0")) self.status["pixel"] = QLabel(self.tr("Pixel Location: 0,0")) self.status["point"].setMinimumWidth(200) self.status["pixel"].setMinimumWidth(200) # self.status["coding"].setAlignment(Qt.AlignCenter) self.statusbar.addPermanentWidget(self.status["point"]) self.statusbar.addPermanentWidget(self.status["pixel"]) def createDocks(self): self.docks["curves"] = QDockWidget(self.tr("Axes and Curves")) self.docks["curves"].setMinimumSize(QSize(200, 200)) self.docks["curves"].setFeatures(QDockWidget.DockWidgetMovable | QDockWidget.DockWidgetFloatable) self.addDockWidget(Qt.LeftDockWidgetArea, self.docks["curves"]) self.docks["fit"] = FitDockWidget(self.tr("Poly Fit")) self.docks["fit"].setMinimumSize(QSize(200, 200)) self.docks["fit"].setFeatures(QDockWidget.DockWidgetMovable | QDockWidget.DockWidgetFloatable) self.addDockWidget(Qt.RightDockWidgetArea, self.docks["fit"]) #self.tabifyDockWidget(self.docks["curves"], self.docks["fit"]) #self.docks["curves"].raise_() self.docks["fit"].setVisible(False) self.docks["curves"].visibilityChanged.connect( self.actions["showcurves"].setChecked) self.docks["fit"].visibilityChanged.connect( self.actions["showfit"].setChecked) self.docktabwidget = QTabWidget(self.docks["curves"]) self.docks["curves"].setWidget(self.docktabwidget) self.docktabwidget.setTabPosition(QTabWidget.South) self.axesTab = QSplitter(Qt.Vertical) # QScrollArea() self.curveTab = QSplitter(Qt.Vertical) self.docktabwidget.addTab(self.axesTab, "axes") self.docktabwidget.addTab(self.curveTab, "curve") self.docktabwidget.setCurrentIndex(1) self.axesxTable = QTableView() self.axesyTable = QTableView() self.axesTab.addWidget(self.axesxTable) self.axesTab.addWidget(self.axesyTable) self.curveTable = QTableView() self.pointsTable = QTableView() # self.axesTab.setWidgetResizable(True) w = QWidget() lay = QVBoxLayout() lay.setContentsMargins(0, 0, 0, 0) self.curvePanelToolbar = QToolBar(self.curveTab) lay.addWidget(self.curvePanelToolbar) lay.addWidget(self.curveTable) w.setLayout(lay) self.curveTab.addWidget(w) self.curveTab.addWidget(self.pointsTable) self.curvePanelToolbar.addAction(self.actions["addcurve"]) self.curvePanelToolbar.addAction(self.actions["renamecurve"]) self.curvePanelToolbar.addAction(self.actions["delcurve"]) def createMainWidget(self): self.setCentralWidget(self.mainTabWidget) self.mainTabWidget.setTabBarAutoHide(True) def setupUiActions(self): self.actions["showcurves"].triggered.connect( self.docks["curves"].setVisible) self.actions["showfit"].triggered.connect(self.docks["fit"].setVisible) self.themeCombo.currentTextChanged.connect(qApp.setStyle) # misc func def updatePixelCoordStatus(self, ptorx, y=None): if isinstance(ptorx, QPoint) or isinstance(ptorx, QPointF): self.status["pixel"].setText("Pixel Coordinate: {},{}".format( ptorx.x(), ptorx.y())) else: self.status["pixel"].setText("Pixel Coordinate: {},{}".format( ptorx, y)) def updatePointCoordStatus(self, ptorx, y=None): if isinstance(ptorx, QPoint) or isinstance(ptorx, QPointF): self.status["point"].setText( "Point Coordinate: {:.0f},{:.0f}".format(ptorx.x(), ptorx.y())) else: self.status["point"].setText( "Point Coordinate: {:.0f},{:.0f}".format(ptorx, y))
def indexOf(self, w): if self.useTableFrame and w == self.tabFrame.contents: w = self.tabFrame return QTabWidget.indexOf(self, w)
def setupUi(self, MainWindow): resolution = QDesktopWidget().screenGeometry(self) # MainWindow.setGeometry(QRect(0,0,0,0)) MainWindow.setFixedSize(resolution.size().width() * 0.99, resolution.size().height() * 0.90) #1200 800 # self.setWindowState(Qt.WindowMaximized) print("resol : ", MainWindow.size()) #메인 화면 색상py self.setStyleSheet("color: black;" "background-color: white;") font = QFont() font.setFamily("NanumGothic") MainWindow.setFont(font) self.centralwidget = QWidget(MainWindow) self.label = QLabel(self.centralwidget) self.label.setGeometry(QRect(20, 20, 130, 35)) font = QFont() font.setFamily("NanumGothic") font.setPointSize(18) font.setBold(True) font.setWeight(75) self.label.setFont(font) self.button_add = QPushButton(self.centralwidget) self.button_add.setGeometry( QRect(MainWindow.size().width() * 0.05, MainWindow.size().height() - 80, 131, 34)) #버튼 스타일 변경 self.button_add.setStyleSheet(staticValues.blueButtonStyleSheet) self.button_add.setFont(staticValues.buttonFont) self.button_modi = QPushButton(self.centralwidget) self.button_modi.setGeometry( QRect(MainWindow.size().width() * 0.20, MainWindow.size().height() - 80, 131, 34)) self.button_modi.setStyleSheet(staticValues.blueButtonStyleSheet) self.button_modi.setFont(staticValues.buttonFont) self.button_del = QPushButton(self.centralwidget) self.button_del.setGeometry( QRect(MainWindow.size().width() * 0.35, MainWindow.size().height() - 80, 131, 34)) self.button_del.setStyleSheet(staticValues.redButtonStyleSheet) self.button_del.setFont(staticValues.buttonFont) self.button_upload = QPushButton(self.centralwidget) self.button_upload.setGeometry( QRect(MainWindow.size().width() * 0.7, MainWindow.size().height() - 80, 131, 34)) self.button_upload.setStyleSheet(staticValues.grayButtonStyleSheet) self.button_upload.setFont(staticValues.buttonFont) self.tabWidget = QTabWidget(self.centralwidget) self.tabWidget.setGeometry( QRect(10, 70, MainWindow.size().width() - 15, MainWindow.size().height() - 180)) self.tab = QWidget() self.tab.layout = QVBoxLayout() self.tab2 = QWidget() self.tab2.layout = QVBoxLayout() self.tabWidget.addTab(self.tab, "회원 목록") self.tableWidget = QTableWidget(self.tab) self.tableWidget.setGeometry( QRect(0, 0, self.tabWidget.size().width() - 5, self.tabWidget.size().height() - 25)) self.tableWidget.setColumnCount(13) self.tableWidget.setRowCount(0) self.tableWidget.setSelectionBehavior(QAbstractItemView.SelectRows) self.tableWidget = QTableWidget(self.tab) self.tableWidget.setGeometry( QRect(0, 0, self.tabWidget.size().width() - 5, self.tabWidget.size().height() - 25)) self.tableWidget.setObjectName("회원 목록") self.tableWidget.setColumnCount(13) self.tableWidget.setRowCount(0) self.tableWidget.setSelectionBehavior(QAbstractItemView.SelectRows) self.tableWidget.setHorizontalHeaderLabels([ "회원 ID", "단톡방", "코인명", "구매", "입금", "판매", "출근", "보유잔량", "총구매금액", "총판매금액", "수익", "평단가", "지갑주소" ]) self.edit_search = QLineEdit(self.centralwidget) self.edit_search.setGeometry( QRect(MainWindow.size().width() - 280, 35, 200, 30)) self.edit_search.setStyleSheet(staticValues.solidStyleSheet) self.button_search = QPushButton(self.centralwidget) self.button_search.setGeometry( QRect(MainWindow.size().width() - 80, 35, 70, 30)) self.button_search.setStyleSheet(staticValues.grayButtonStyleSheet) self.button_search.setFont(staticValues.buttonFont) MainWindow.setCentralWidget(self.centralwidget) self.retranslateUi(MainWindow) self.tabWidget.setCurrentIndex(0) QMetaObject.connectSlotsByName(MainWindow)
class Window(QWidget): COLUMNS_DICT = { # "Column label": {'index': column_ID, 'width': column_width} "ID": { "index": 0, "width": 25 }, "File name": { "index": 1, "width": 165 }, "Format": { "index": 2, "width": 50 }, "Dup": { "index": 3, "width": 5 }, } def __init__(self, statusBar): super().__init__() # store active image ID self.active_image_id = str() # Saves multiple duplicate windows references: self.duplicateRefs = {} # Gets status bar from QMainWindow: self.statusBar = statusBar # Initializes all window elements: self.folderField = QLineEdit() self.folderButton = QPushButton() self.folderTreeCheckbox = QCheckBox("Include sub-folders") self.processButton = QPushButton("Process media files") self.duplicateButton = QPushButton("Find duplicates") self.reindexButton = QPushButton("Reindex database files") self.tableTabs = QTabWidget() # init main images list table self.imageListTable = QTableWidget() # set main images list table fields unchanged self.imageListTable.setEditTriggers(QTableWidget.NoEditTriggers) # init main videos list table self.videoListTable = QTableWidget() # set main videos list table fields unchanged self.videoListTable.setEditTriggers(QTableWidget.NoEditTriggers) # set up image fields and elements self.imageField = QLabel() self.imageNameField = QLabel() self.imageParamsField = QLabel() self.imageCopyButton = QPushButton("Copy image") self.imageCopyButton.setIcon(QIcon("gui/static/icon_copy.png")) self.imageCopyButton.clicked.connect(self.copy_image_path) self.imageViewButton = QPushButton("View image") self.imageViewButton.setIcon(QIcon("gui/static/icon_open_image.svg")) self.imageViewButton.clicked.connect(self.open_image_file) self.imageOpenDirButton = QPushButton("Open dir") self.imageOpenDirButton.setIcon( QIcon("gui/static/icon_open_folder.svg")) self.imageOpenDirButton.clicked.connect(self.open_image_path) self.imageDeleteButton = QPushButton("Delete") self.imageDeleteButton.setIcon( QIcon("gui/static/icon_delete_file.png")) self.imageDeleteButton.clicked.connect(self.delete_image) self.videoField = QVideoWidget() self.videoPlayer = QMediaPlayer() # Adjusts settings for the window elements: self.folderField.setDisabled(True) self.folderButton.setIcon(QIcon("gui/static/icon_process_folder.png")) self.folderButton.clicked.connect(self.set_folder) self.processButton.clicked.connect(self.process_files) self.processButton.setFixedWidth(160) self.processButton.setDisabled(True) self.duplicateButton.clicked.connect(self.find_duplicates) self.duplicateButton.setFixedWidth(160) self.reindexButton.clicked.connect(self.reindex_db_data) self.reindexButton.setFixedWidth(160) # prepare tables for images and videos self.imagesTab = self.tableTabs.insertTab(0, self.imageListTable, "Images") self.videosTab = self.tableTabs.insertTab(1, self.videoListTable, "Videos") # images list table setup self.imageListTable.setColumnCount(len(self.COLUMNS_DICT.keys())) self.imageListTable.setHorizontalHeaderLabels(self.COLUMNS_DICT.keys()) self.imageListTable.verticalHeader().setVisible(False) self.imageListTable.setHorizontalScrollBarPolicy(Qt.ScrollBarAsNeeded) self.imageListTable.setSortingEnabled(True) self.imageListTable.cellClicked.connect(self.show_image) # videos list table setup self.videoListTable.setColumnCount(len(self.COLUMNS_DICT.keys())) self.videoListTable.setHorizontalHeaderLabels(self.COLUMNS_DICT.keys()) self.videoListTable.verticalHeader().setVisible(False) self.videoListTable.setHorizontalScrollBarPolicy(Qt.ScrollBarAsNeeded) self.videoListTable.setSortingEnabled(True) self.videoListTable.cellClicked.connect(self.show_video) # set images and videos duplicates columns width self.set_columns_width() # Places the window elements on the window: # Top-left cell of main grid box: subGridBox = QWidget() subGrid = QGridLayout() subGrid.addWidget(self.folderField, 0, 0) subGrid.addWidget(self.folderButton, 0, 1) subGrid.addWidget(self.folderTreeCheckbox, 1, 0) subGrid.addWidget(self.processButton, 2, 0, 1, 2, Qt.AlignCenter) subGrid.addWidget(self.duplicateButton, 3, 0, 1, 2, Qt.AlignCenter) subGrid.addWidget(self.reindexButton, 4, 0, 1, 2, Qt.AlignCenter) subGridBox.setLayout(subGrid) # image data grid box imageGridBox = QWidget() imageGrid = QGridLayout() imageGrid.addWidget(self.imageField, 0, 0, 1, 4, Qt.AlignCenter) # add image buttons imageGrid.addWidget(self.imageCopyButton, 1, 0, 1, 1) imageGrid.addWidget(self.imageViewButton, 1, 1, 1, 1) imageGrid.addWidget(self.imageOpenDirButton, 1, 2, 1, 1) imageGrid.addWidget(self.imageDeleteButton, 1, 3, 1, 1) imageGrid.addWidget(self.imageNameField, 2, 0, 1, 1) imageGrid.addWidget(self.imageParamsField, 2, 3, 1, 1) imageGridBox.setLayout(imageGrid) # Main grid box: self.mainGrid = QGridLayout() self.mainGrid.addWidget(subGridBox, 0, 0) self.mainGrid.addWidget(self.tableTabs, 1, 0) self.mainGrid.addWidget(imageGridBox, 1, 1) self.mainGrid.addWidget(self.videoField, 0, 1, 2, 1) self.mainGrid.setColumnMinimumWidth(0, 400) self.mainGrid.setRowMinimumHeight(1, 600) self.mainGrid.setColumnStretch(1, 1) self.setLayout(self.mainGrid) # Creates a QThread instance: self.thread = ProcessingThread(self.folderField, self.imageListTable, self.videoListTable, self.folderTreeCheckbox) self.thread.finishedTrigger.connect(self.finish_thread) # filling table while first run self.reindex_db_data() # hide image interface self.hide_active_image() def set_columns_width(self): # looping all columns and set with for value in self.COLUMNS_DICT.values(): self.imageListTable.setColumnWidth(value["index"], value["width"]) self.videoListTable.setColumnWidth(value["index"], value["width"]) def reindex_db_data(self): self.duplicateButton.setDisabled(True) self.processButton.setDisabled(True) self.reindexButton.setDisabled(True) # Reindex already exist folders and files; Image and Video files reindex_image_files() reindex_video_files() # run table filling after reindexing self.table_data_init() self.duplicateButton.setEnabled(True) self.processButton.setEnabled(True) self.reindexButton.setEnabled(True) def center_widget_item(self, text: str) -> QTableWidgetItem: # create the item center_align_item = QTableWidgetItem(text) # change the alignment center_align_item.setTextAlignment(Qt.AlignCenter) return center_align_item def table_data_init(self): # get available images from DB with db_session(): images = Image.all() for idx, image in enumerate(images): numRows = self.imageListTable.rowCount() self.imageListTable.insertRow(numRows) str_image_idx = str(idx) IMAGE_PATH_DICT[str_image_idx] = { "id": image.id, "name": image.image_name, "additional_attrs": { "height": image.image_height, "width": image.image_width }, "folder": image.image_path, "type": (image.image_name.split(".")[-1]).lower(), "full_path": image.full_path(), } self.imageListTable.setItem(idx, self.COLUMNS_DICT["ID"]["index"], self.center_widget_item(str_image_idx)) self.imageListTable.setItem( idx, self.COLUMNS_DICT["File name"]["index"], self.center_widget_item(image.image_name), ) self.imageListTable.setItem( idx, self.COLUMNS_DICT["Format"]["index"], self.center_widget_item( IMAGE_PATH_DICT[str_image_idx]["type"]), ) duplicateIcon = QTableWidgetItem() duplicateIcon.setIcon(QIcon("gui/static/icon_view_duplicates.png")) self.imageListTable.setItem(idx, self.COLUMNS_DICT["Dup"]["index"], duplicateIcon) def hide_active_image(self): self.imageField.hide() self.imageCopyButton.hide() self.imageViewButton.hide() self.imageOpenDirButton.hide() self.imageDeleteButton.hide() self.imageNameField.hide() self.imageParamsField.hide() def show_active_image(self): self.imageField.show() self.imageCopyButton.show() self.imageViewButton.show() self.imageOpenDirButton.show() self.imageDeleteButton.show() self.imageNameField.show() self.imageParamsField.show() def open_image_file(self): open_path(path=IMAGE_PATH_DICT[self.active_image_id]["full_path"]) def open_image_path(self): open_path(path=IMAGE_PATH_DICT[self.active_image_id]["folder"]) def delete_image(self): # count image table position image_table_position = list(IMAGE_PATH_DICT.keys()).index( self.active_image_id) message = QMessageBox().question( self, "Confirm deletion", "Delete duplicate media file?", QMessageBox.Yes | QMessageBox.No, ) if message == QMessageBox.Yes: self.imageListTable.removeRow(image_table_position) image_id = IMAGE_PATH_DICT[self.active_image_id]["id"] # run custom delete with db_session(): Image[image_id].custom_delete() # delete image key from dict del IMAGE_PATH_DICT[self.active_image_id] self.hide_active_image() QMessageBox().information(self, "File deletion", "File success deleted", QMessageBox.Ok, QMessageBox.Ok) elif message == QMessageBox.No: pass def copy_image_path(self): try: result = copy_image( IMAGE_PATH_DICT[self.active_image_id]["full_path"]) if result: QMessageBox.information( self, "Copy path", "Success!\nFile copied to clipboard!", QMessageBox.Ok, QMessageBox.Ok, ) else: QMessageBox.warning( self, "Copy path", "Error!\nSorry, i can`t copy image to clipboard.", QMessageBox.Ok, QMessageBox.Ok, ) except Exception: print(traceback.format_exc()) QMessageBox.warning( self, "Copy path", f"Error!\n{traceback.format_exc()}", QMessageBox.Ok, QMessageBox.Ok, ) # Get a folder full of multimedia files to work with def set_folder(self): self.folderPath = QFileDialog.getExistingDirectory(self) if self.folderPath == "": self.folderPath = self.folderField.text() self.folderField.setText(self.folderPath) self.processButton.setEnabled(True) self.statusBar.clearMessage() # Start the thread and fill the table with those files def process_files(self): # set other buttons disabled self.duplicateButton.setDisabled(True) self.reindexButton.setDisabled(True) # Clears both tables upon restarting function: self.imageListTable.clearContents() self.imageListTable.setRowCount(0) self.videoListTable.clearContents() self.videoListTable.setRowCount(0) self.statusBar.setStyleSheet("color: black") self.statusBar.showMessage("Processing...") if self.folderField.text() == "": self.statusBar.setStyleSheet("color: red") self.statusBar.showMessage("Please choose a directory") self.duplicateButton.setEnabled(True) self.reindexButton.setEnabled(True) return None if not self.thread.isRunning(): self.duplicateRefs.clear() self.thread.start() self.processButton.setText("Stop") elif self.thread.isRunning(): self.thread.terminate() self.processButton.setText("Process media files") self.duplicateButton.setEnabled(True) self.reindexButton.setEnabled(True) # Thread done and ded def finish_thread(self): self.statusBar.setStyleSheet("color: black") self.statusBar.showMessage("Finished!") self.processButton.setText("Process media files") # set all buttons able self.duplicateButton.setEnabled(True) self.reindexButton.setEnabled(True) # Start the second thread and remove all unique files from the table def find_duplicates(self): if IMAGE_PATH_DICT == {}: self.statusBar.setStyleSheet("color: red") self.statusBar.showMessage("Please process your media files first") return None self.duplicateButton.setDisabled(True) self.processButton.setDisabled(True) self.reindexButton.setDisabled(True) self.statusBar.setStyleSheet("color: black") self.statusBar.showMessage("Finding duplicates...") with db_session(): # get all images descriptors image_files_query = Image.get_descriptors() pairs_amount = int( len(image_files_query) * (len(image_files_query) - 1) / 2) QMessageBox.information( self, "Find duplicates", f""" Similar images search start. Please wait!\n You have ~{pairs_amount} images pairs; Work will get ~{round(pairs_amount*0.00006, 2)} sec. """, QMessageBox.Ok, QMessageBox.Ok, ) # run function to find duplicates result = feature_description(images_list=image_files_query) with db_session(): # save duplicates to DB save_images_duplicates(result) QMessageBox.information(self, "Find duplicates", "Success!", QMessageBox.Ok, QMessageBox.Ok) # set all buttons able self.duplicateButton.setEnabled(True) self.reindexButton.setEnabled(True) self.processButton.setEnabled(True) # TODO: new thread removing all unique media. Only duplicates remain # Show an image upon clicking its name in the table def show_image(self, row, column): imageId = self.imageListTable.item(row, 0).text() # set new active image id self.active_image_id = imageId # Removes a video from screen if shown: self.videoPlayer.stop() self.videoField.hide() # show active image self.show_active_image() # show image and additional data self.imageNameField.setText(f"{IMAGE_PATH_DICT[imageId]['name']}") self.imageParamsField.setText( f"HxW: {IMAGE_PATH_DICT[imageId]['additional_attrs']['height']}px" + f" x {IMAGE_PATH_DICT[imageId]['additional_attrs']['width']}px") # Shows animated images if IMAGE_PATH_DICT[imageId]["name"].lower().endswith("gif"): gif = QMovie(IMAGE_PATH_DICT[imageId]["full_path"]) gifSize = QSize(*self.smooth_gif_resize( IMAGE_PATH_DICT[imageId]["full_path"], 600, 600)) gif.setScaledSize(gifSize) self.imageField.setMovie(gif) gif.start() # Shows static images else: self.imageField.setPixmap( QPixmap(IMAGE_PATH_DICT[imageId]["full_path"]).scaled( 600, 600, Qt.KeepAspectRatio, Qt.SmoothTransformation)) if column == self.COLUMNS_DICT["Dup"]["index"]: self.duplicateWindow = DuplicateWindow( image_data=IMAGE_PATH_DICT[imageId], raw_id=imageId) if imageId not in self.duplicateRefs.keys(): self.duplicateRefs[imageId] = self.duplicateWindow self.duplicateWindow.show() self.duplicateWindow.closeTrigger.connect(self.delete_reference) # Show a video upon clicking its name in the table def show_video(self, row, column): videoItem = self.videoListTable.item(row, column) videoId = self.videoListTable.item(row, 0).text() self.mainGrid.setColumnMinimumWidth(1, 800) # Prevents from KeyError when clicking the second column: if videoItem.text() == VIDEO_PATH_DICT[videoId][0]: videoItemPath = VIDEO_PATH_DICT[videoId][2] videoContent = QMediaContent(QUrl.fromLocalFile(videoItemPath)) self.videoPlayer.setMedia(videoContent) self.videoPlayer.setVideoOutput(self.videoField) # Removes an image from screen if shown and starts video: self.imageField.clear() self.imageField.hide() self.videoField.show() self.videoPlayer.play() # Remove a previously added reference from a dict if a DuplicateWindow was closed # so it can be opened again def delete_reference(self, itemId): self.duplicateRefs.pop(itemId) # An amazing workaround for gif resizing procedure # because PyQt's native one doesn't work for some reason: def smooth_gif_resize(self, gif, frameWidth, frameHeight): gif = Pil_Image.open(gif) gifWidth0, gifHeight0 = gif.size widthRatio = frameWidth / gifWidth0 heightRatio = frameHeight / gifHeight0 if widthRatio >= heightRatio: gifWidth1 = gifWidth0 * heightRatio gifHeight1 = frameHeight return gifWidth1, gifHeight1 gifWidth1 = frameWidth gifHeight1 = gifHeight0 * widthRatio return gifWidth1, gifHeight1