def __init__(self): super().__init__() path = QDir.rootPath() self.model = QFileSystemModel() self.model.setRootPath(path) self.tree_view = QTreeView() self.tree_view.setModel(self.model) self.tree_view.setSelectionMode(QTreeView.ExtendedSelection) self.tree_view.selectionModel().selectionChanged.connect( self._on_selection_changed) self.list_files = QListWidget() self.button_add = QPushButton('Добавить!') self.button_add.setEnabled(False) self.button_add.clicked.connect(self._on_add) splitter = QSplitter(Qt.Horizontal) splitter.addWidget(self.tree_view) splitter.addWidget(self.list_files) main_layout = QVBoxLayout(self) main_layout.addWidget(splitter) main_layout.addWidget(self.button_add)
def __init__(self, parent=None): super().__init__(parent) self.setWindowTitle('Direct tree') self.model = QFileSystemModel() self.model.setRootPath(QDir.rootPath()) self.tree = QTreeView() self.tree.setModel(self.model) self.tree.setRootIndex(self.model.index(os.getcwd())) self.tree.doubleClicked.connect(self._on_double_clicked) self.tree.setAnimated(False) self.tree.setIndentation(20) self.tree.setSortingEnabled(True) self.textEdit = QTextEdit() self.textEdit.setReadOnly(True) splitter = QSplitter() splitter.addWidget(self.tree) splitter.addWidget(self.textEdit) splitter.setSizes([50, 200]) self.setCentralWidget(splitter)
def __init__(self, model): super().__init__() self._model = model layout = QGridLayout() splitter = QSplitter() self._bot_table = _BotTable() self._responses_tab = _ResponsesTab() self._execute_tab = _ExecuteTab(self._responses_tab, model) self._tab_widget = QTabWidget() self._tab_widget.addTab(self._execute_tab, "Execute") self._tab_widget.addTab(self._responses_tab, "Responses") splitter.setOrientation(Qt.Vertical) splitter.addWidget(self._bot_table) splitter.addWidget(self._tab_widget) splitter.setSizes([50, 100]) layout.addWidget(splitter) self.setLayout(layout) self._register_listeners()
def init_workspace(self): splitter = QSplitter(self) splitter.setOrientation(Qt.Vertical) splitter.sizePolicy().setVerticalPolicy(QSizePolicy.Maximum) query_edit = self.init_query_text_edit() self.on_disconnected() splitter.addWidget(query_edit) results_widget = self.init_results_widget() splitter.addWidget(results_widget) splitter.setSizes([100, 900]) return splitter
def __init__(self, metadata, parent=None): QDialog.__init__(self, parent=parent) self.setWindowFlags(Qt.Window) buttonBox = QDialogButtonBox(QDialogButtonBox.Close) origbrowser = QTextBrowser() origbrowser.setText('') origbrowser.setReadOnly(True) browser = QTextBrowser() browser.setText('') browser.setReadOnly(True) gpbox2 = QGroupBox('Metadata: Original') lay2 = QHBoxLayout() gpbox2.setLayout(lay2) lay2.addWidget(origbrowser) gpbox4 = QGroupBox('Metadata: After scrambling') lay4 = QHBoxLayout() gpbox4.setLayout(lay4) lay4.addWidget(browser) splitter = QSplitter(Qt.Horizontal) splitter.addWidget(gpbox2) splitter.addWidget(gpbox4) splitter.setMinimumHeight(500) splitter.setMinimumWidth(1000) lay = QVBoxLayout() self.setLayout(lay) lay.addWidget(splitter) lay.addWidget(buttonBox) # create connect signals/slots buttonBox.rejected.connect(self.reject) metaorig = metadata.get('orig', '') metaorig = re.sub(r'\n\s*', r'\n ', metaorig) origbrowser.setText(metaorig) browser.setText(metadata.get('scramb', '')) self.setWindowTitle('%s: Metadata' % CAPTION) if not 'scramb' in metadata: gpbox4.setVisible(False)
def init_workspace(self): # Create a splitter consisting of query edit and table view splitter = QSplitter(self) splitter.setOrientation(Qt.Vertical) splitter.sizePolicy().setVerticalPolicy(QSizePolicy.Maximum) # Initialize query edit file query_edit = self.init_query_text_edit() # Disable query control buttons by default self.on_disconnected() splitter.addWidget(query_edit) # Initialize result desiplaying widgets results_widget = self.init_results_widget() splitter.addWidget(results_widget) splitter.setSizes([100, 900]) return splitter
def __init__(self, controller): super().__init__() self.controller = controller self.setWindowTitle(controller.title()) self.win_editor = EditWindow() splitter = QSplitter() splitter.addWidget(SideWindow()) splitter.addWidget(self.win_editor) self.setGeometry(100, 100, 500, 355) self.setCentralWidget(splitter) self.statusBar().setSizeGripEnabled(True) init_file_toolbar(self) init_edit_toolbar(self) init_menu(self)
def setup_layout(self): # group the editor with the console vertical_splitter = QSplitter(self) vertical_splitter.setOrientation(Qt.Vertical) vertical_splitter.addWidget(self.editor_pane) vertical_splitter.addWidget(self.console) vertical_splitter.setStretchFactor(0, 3) vertical_splitter.setStretchFactor(1, 1) # group horizontal_splitter = QSplitter(self) horizontal_splitter.addWidget(self.explorer_tab) horizontal_splitter.addWidget(vertical_splitter) horizontal_splitter.addWidget(self.info_bar) horizontal_splitter.setStretchFactor(0, 1) horizontal_splitter.setStretchFactor(1, 4) horizontal_splitter.setStretchFactor(2, 1) self.setCentralWidget(horizontal_splitter)
class Snipdom(QMainWindow): def __init__(self): QMainWindow.__init__(self) self.hsplit = QSplitter() self.setCentralWidget(self.hsplit) kernel_manager = QtInProcessKernelManager() kernel_manager.start_kernel() self.kernel = kernel_manager.kernel self.kernel.gui = 'qt' self.control = RichIPythonWidget(gui_completion="droplist") self.kernel.shell.push({'snipdom': self}) kernel_client = kernel_manager.client() kernel_client.start_channels() self.control.kernel_manager = kernel_manager self.control.kernel_client = kernel_client self.vsplit = QSplitter() self.vsplit.setOrientation(Qt.Vertical) self.vsplit.addWidget(self.control) self.hsplit.addWidget(self.vsplit) self.sendButton = QPushButton("send") #self.sendButton.clicked.connect(self.sendcode) self.vsplit.addWidget(self.sendButton) self.bridge = Js2Py() self.bridge.sent.connect(self.codeFromJs) #lab = QtGui.QLabel(kernel.shell.history_manager.get_range(start=-1).next()[2]) def codeFromJs(self, code): self.control.input_buffer = code
class BookInfo(QDialog): closed = pyqtSignal(object) def __init__(self, parent, view, row, link_delegate): QDialog.__init__(self, parent) self.normal_brush = QBrush(Qt.white) self.marked_brush = QBrush(Qt.lightGray) self.marked = None self.gui = parent self.splitter = QSplitter(self) self._l = l = QVBoxLayout(self) self.setLayout(l) l.addWidget(self.splitter) self.cover = CoverView(self) self.cover.resizeEvent = self.cover_view_resized self.cover.cover_changed.connect(self.cover_changed) self.cover_pixmap = None self.cover.sizeHint = self.details_size_hint self.splitter.addWidget(self.cover) self.details = Details(parent.book_details.book_info, self) self.details.page().setLinkDelegationPolicy(self.details.page().DelegateAllLinks) self.details.linkClicked.connect(self.link_clicked) s = self.details.page().settings() s.setAttribute(s.JavascriptEnabled, False) self.css = css() self.link_delegate = link_delegate self.details.setAttribute(Qt.WA_OpaquePaintEvent, False) palette = self.details.palette() self.details.setAcceptDrops(False) palette.setBrush(QPalette.Base, Qt.transparent) self.details.page().setPalette(palette) self.c = QWidget(self) self.c.l = l2 = QGridLayout(self.c) self.c.setLayout(l2) l2.addWidget(self.details, 0, 0, 1, -1) self.splitter.addWidget(self.c) self.fit_cover = QCheckBox(_('Fit &cover within view'), self) self.fit_cover.setChecked(gprefs.get('book_info_dialog_fit_cover', True)) l2.addWidget(self.fit_cover, l2.rowCount(), 0, 1, -1) self.previous_button = QPushButton(QIcon(I('previous.png')), _('&Previous'), self) self.previous_button.clicked.connect(self.previous) l2.addWidget(self.previous_button, l2.rowCount(), 0) self.next_button = QPushButton(QIcon(I('next.png')), _('&Next'), self) self.next_button.clicked.connect(self.next) l2.addWidget(self.next_button, l2.rowCount() - 1, 1) self.view = view self.current_row = None self.refresh(row) self.view.model().new_bookdisplay_data.connect(self.slave) self.fit_cover.stateChanged.connect(self.toggle_cover_fit) self.ns = QShortcut(QKeySequence('Alt+Right'), self) self.ns.activated.connect(self.next) self.ps = QShortcut(QKeySequence('Alt+Left'), self) self.ps.activated.connect(self.previous) self.next_button.setToolTip(_('Next [%s]')% unicode(self.ns.key().toString(QKeySequence.NativeText))) self.previous_button.setToolTip(_('Previous [%s]')% unicode(self.ps.key().toString(QKeySequence.NativeText))) geom = QCoreApplication.instance().desktop().availableGeometry(self) screen_height = geom.height() - 100 screen_width = geom.width() - 100 self.resize(max(int(screen_width/2), 700), screen_height) saved_layout = gprefs.get('book_info_dialog_layout', None) if saved_layout is not None: try: self.restoreGeometry(saved_layout[0]) self.splitter.restoreState(saved_layout[1]) except Exception: pass def link_clicked(self, qurl): link = unicode(qurl.toString(NO_URL_FORMATTING)) self.link_delegate(link) def done(self, r): saved_layout = (bytearray(self.saveGeometry()), bytearray(self.splitter.saveState())) gprefs.set('book_info_dialog_layout', saved_layout) ret = QDialog.done(self, r) self.view.model().new_bookdisplay_data.disconnect(self.slave) self.view = self.link_delegate = self.gui = None self.closed.emit(self) return ret def cover_changed(self, data): if self.current_row is not None: id_ = self.view.model().id(self.current_row) self.view.model().db.set_cover(id_, data) self.gui.refresh_cover_browser() ci = self.view.currentIndex() if ci.isValid(): self.view.model().current_changed(ci, ci) def details_size_hint(self): return QSize(350, 550) def toggle_cover_fit(self, state): gprefs.set('book_info_dialog_fit_cover', self.fit_cover.isChecked()) self.resize_cover() def cover_view_resized(self, event): QTimer.singleShot(1, self.resize_cover) def slave(self, mi): self.refresh(mi.row_number, mi) def move(self, delta=1): idx = self.view.currentIndex() if idx.isValid(): m = self.view.model() ni = m.index(idx.row() + delta, idx.column()) if ni.isValid(): if self.view.isVisible(): self.view.scrollTo(ni) self.view.setCurrentIndex(ni) def next(self): self.move() def previous(self): self.move(-1) def resize_cover(self): if self.cover_pixmap is None: return pixmap = self.cover_pixmap if self.fit_cover.isChecked(): scaled, new_width, new_height = fit_image(pixmap.width(), pixmap.height(), self.cover.size().width()-10, self.cover.size().height()-10) if scaled: try: dpr = self.devicePixelRatioF() except AttributeError: dpr = self.devicePixelRatio() pixmap = pixmap.scaled(int(dpr * new_width), int(dpr * new_height), Qt.KeepAspectRatio, Qt.SmoothTransformation) pixmap.setDevicePixelRatio(dpr) self.cover.set_pixmap(pixmap) self.update_cover_tooltip() def update_cover_tooltip(self): tt = '' if self.marked: tt = _('This book is marked') if self.marked in {True, 'true'} else _( 'This book is marked as: %s') % self.marked tt += '\n\n' if self.cover_pixmap is not None: sz = self.cover_pixmap.size() tt += _('Cover size: %(width)d x %(height)d pixels')%dict(width=sz.width(), height=sz.height()) self.cover.setToolTip(tt) def refresh(self, row, mi=None): if isinstance(row, QModelIndex): row = row.row() if row == self.current_row and mi is None: return mi = self.view.model().get_book_display_info(row) if mi is None else mi if mi is None: # Indicates books was deleted from library, or row numbers have # changed return self.previous_button.setEnabled(False if row == 0 else True) self.next_button.setEnabled(False if row == self.view.model().rowCount(QModelIndex())-1 else True) self.current_row = row self.setWindowTitle(mi.title) self.cover_pixmap = QPixmap.fromImage(mi.cover_data[1]) try: dpr = self.devicePixelRatioF() except AttributeError: dpr = self.devicePixelRatio() self.cover_pixmap.setDevicePixelRatio(dpr) self.resize_cover() html = render_html(mi, self.css, True, self, all_fields=True) self.details.setHtml(html) self.marked = mi.marked self.cover.setBackgroundBrush(self.marked_brush if mi.marked else self.normal_brush) self.update_cover_tooltip()
class ThemeCreateDialog(Dialog): def __init__(self, parent, report): self.report = report Dialog.__init__(self, _('Create an icon theme'), 'create-icon-theme', parent) def setup_ui(self): self.splitter = QSplitter(self) self.l = l = QVBoxLayout(self) l.addWidget(self.splitter) l.addWidget(self.bb) self.w = w = QGroupBox(_('Theme Metadata'), self) self.splitter.addWidget(w) l = w.l = QFormLayout(w) l.setFieldGrowthPolicy(l.ExpandingFieldsGrow) self.missing_icons_group = mg = QGroupBox(self) self.mising_icons = mi = QListWidget(mg) mi.setSelectionMode(mi.NoSelection) mg.l = QVBoxLayout(mg) mg.l.addWidget(mi) self.splitter.addWidget(mg) self.title = QLineEdit(self) l.addRow(_('&Title:'), self.title) self.author = QLineEdit(self) l.addRow(_('&Author:'), self.author) self.version = v = QSpinBox(self) v.setMinimum(1), v.setMaximum(1000000) l.addRow(_('&Version:'), v) self.license = lc = QLineEdit(self) l.addRow(_('&License:'), lc) self.url = QLineEdit(self) l.addRow(_('&URL:'), self.url) lc.setText(_( 'The license for the icons in this theme. Common choices are' ' Creative Commons or Public Domain.')) self.description = QTextEdit(self) l.addRow(self.description) self.refresh_button = rb = self.bb.addButton(_('&Refresh'), self.bb.ActionRole) rb.setIcon(QIcon(I('view-refresh.png'))) rb.clicked.connect(self.refresh) self.apply_report() def sizeHint(self): return QSize(900, 670) @property def metadata(self): self.report.theme.title = self.title.text().strip() # Needed for report.name to work return { 'title': self.title.text().strip(), 'author': self.author.text().strip(), 'version': self.version.value(), 'description': self.description.toPlainText().strip(), 'number': len(self.report.name_map) - len(self.report.extra), 'date': utcnow().date().isoformat(), 'name': self.report.name, 'license': self.license.text().strip() or 'Unknown', 'url': self.url.text().strip() or None, } def save_metadata(self): with open(os.path.join(self.report.path, THEME_METADATA), 'wb') as f: json.dump(self.metadata, f, indent=2) def refresh(self): self.save_metadata() self.report = read_theme_from_folder(self.report.path) self.apply_report() def apply_report(self): theme = self.report.theme self.title.setText((theme.title or '').strip()) self.author.setText((theme.author or '').strip()) self.version.setValue(theme.version or 1) self.description.setText((theme.description or '').strip()) self.license.setText((theme.license or 'Unknown').strip()) self.url.setText((theme.url or '').strip()) if self.report.missing: title = _('%d icons missing in this theme') % len(self.report.missing) else: title = _('No missing icons') self.missing_icons_group.setTitle(title) mi = self.mising_icons mi.clear() for name in sorted(self.report.missing): QListWidgetItem(QIcon(I(name, allow_user_override=False)), name, mi) def accept(self): mi = self.metadata if not mi.get('title'): return error_dialog(self, _('No title specified'), _( 'You must specify a title for this icon theme'), show=True) if not mi.get('author'): return error_dialog(self, _('No author specified'), _( 'You must specify an author for this icon theme'), show=True) return Dialog.accept(self)
class MetadataSingleDialogAlt1(MetadataSingleDialogBase): # {{{ cc_two_column = False one_line_comments_toolbar = True use_toolbutton_for_config_metadata = False on_drag_enter = pyqtSignal() def handle_drag_enter(self): self.central_widget.setCurrentIndex(1) def do_layout(self): self.central_widget.clear() self.tabs = [] self.labels = [] sto = QWidget.setTabOrder self.on_drag_enter.connect(self.handle_drag_enter) self.tabs.append(DragTrackingWidget(self, self.on_drag_enter)) self.central_widget.addTab(self.tabs[0], _("&Metadata")) self.tabs[0].l = QGridLayout() self.tabs[0].setLayout(self.tabs[0].l) self.tabs.append(QWidget(self)) self.central_widget.addTab(self.tabs[1], _("&Cover and formats")) self.tabs[1].l = QGridLayout() self.tabs[1].setLayout(self.tabs[1].l) # accept drop events so we can automatically switch to the second tab to # drop covers and formats self.tabs[0].setAcceptDrops(True) # Tab 0 tab0 = self.tabs[0] tl = QGridLayout() gb = QGroupBox(_('&Basic metadata'), self.tabs[0]) self.tabs[0].l.addWidget(gb, 0, 0, 1, 1) gb.setLayout(tl) self.button_box_layout.insertWidget(1, self.fetch_metadata_button) self.button_box_layout.insertWidget(2, self.config_metadata_button) sto(self.button_box, self.fetch_metadata_button) sto(self.fetch_metadata_button, self.config_metadata_button) sto(self.config_metadata_button, self.title) def create_row(row, widget, tab_to, button=None, icon=None, span=1): ql = BuddyLabel(widget) tl.addWidget(ql, row, 1, 1, 1) tl.addWidget(widget, row, 2, 1, 1) if button is not None: tl.addWidget(button, row, 3, span, 1) if icon is not None: button.setIcon(QIcon(I(icon))) if tab_to is not None: if button is not None: sto(widget, button) sto(button, tab_to) else: sto(widget, tab_to) tl.addWidget(self.swap_title_author_button, 0, 0, 2, 1) tl.addWidget(self.manage_authors_button, 2, 0, 1, 1) tl.addWidget(self.paste_isbn_button, 12, 0, 1, 1) tl.addWidget(self.tags_editor_button, 6, 0, 1, 1) create_row(0, self.title, self.title_sort, button=self.deduce_title_sort_button, span=2, icon='auto_author_sort.png') create_row(1, self.title_sort, self.authors) create_row(2, self.authors, self.author_sort, button=self.deduce_author_sort_button, span=2, icon='auto_author_sort.png') create_row(3, self.author_sort, self.series) create_row(4, self.series, self.series_index, button=self.clear_series_button, icon='trash.png') create_row(5, self.series_index, self.tags) create_row(6, self.tags, self.rating, button=self.clear_tags_button) create_row(7, self.rating, self.pubdate, button=self.clear_ratings_button) create_row(8, self.pubdate, self.publisher, button=self.pubdate.clear_button, icon='trash.png') create_row(9, self.publisher, self.languages) create_row(10, self.languages, self.timestamp) create_row(11, self.timestamp, self.identifiers, button=self.timestamp.clear_button, icon='trash.png') create_row(12, self.identifiers, self.comments, button=self.clear_identifiers_button, icon='trash.png') sto(self.clear_identifiers_button, self.swap_title_author_button) sto(self.swap_title_author_button, self.manage_authors_button) sto(self.manage_authors_button, self.tags_editor_button) sto(self.tags_editor_button, self.paste_isbn_button) tl.addItem(QSpacerItem(1, 1, QSizePolicy.Fixed, QSizePolicy.Expanding), 13, 1, 1, 1) w = getattr(self, 'custom_metadata_widgets_parent', None) if w is not None: gb = QGroupBox(_('C&ustom metadata'), tab0) gbl = QVBoxLayout() gb.setLayout(gbl) sr = QScrollArea(tab0) sr.setWidgetResizable(True) sr.setFrameStyle(QFrame.NoFrame) sr.setWidget(w) gbl.addWidget(sr) self.tabs[0].l.addWidget(gb, 0, 1, 1, 1) sto(self.identifiers, gb) w = QGroupBox(_('&Comments'), tab0) sp = QSizePolicy() sp.setVerticalStretch(10) sp.setHorizontalPolicy(QSizePolicy.Expanding) sp.setVerticalPolicy(QSizePolicy.Expanding) w.setSizePolicy(sp) l = QHBoxLayout() w.setLayout(l) l.addWidget(self.comments) tab0.l.addWidget(w, 1, 0, 1, 2) # Tab 1 tab1 = self.tabs[1] wsp = QWidget(tab1) wgl = QVBoxLayout() wsp.setLayout(wgl) # right-hand side of splitter gb = QGroupBox(_('Change cover'), tab1) l = QGridLayout() gb.setLayout(l) for i, b in enumerate(self.cover.buttons[:3]): l.addWidget(b, 0, i, 1, 1) sto(b, self.cover.buttons[i + 1]) hl = QHBoxLayout() for b in self.cover.buttons[3:]: hl.addWidget(b) sto(self.cover.buttons[-2], self.cover.buttons[-1]) l.addLayout(hl, 1, 0, 1, 3) wgl.addWidget(gb) wgl.addItem( QSpacerItem(10, 10, QSizePolicy.Expanding, QSizePolicy.Expanding)) wgl.addItem( QSpacerItem(10, 10, QSizePolicy.Expanding, QSizePolicy.Expanding)) wgl.addWidget(self.formats_manager) self.splitter = QSplitter(Qt.Horizontal, tab1) tab1.l.addWidget(self.splitter) self.splitter.addWidget(self.cover) self.splitter.addWidget(wsp) self.formats_manager.formats.setMaximumWidth(10000) self.formats_manager.formats.setIconSize(QSize(64, 64))
class MainWindow(QWidget): def __init__(self, parent, masternode_list, imgDir): super(QWidget, self).__init__(parent) self.parent = parent self.imgDir = imgDir self.runInThread = ThreadFuns.runInThread ###-- Masternode list self.masternode_list = masternode_list ###-- Create clients and statuses self.hwdevice = None self.hwStatus = 0 self.hwStatusMess = "Not Connected" self.rpcClient = None self.rpcConnected = False self.rpcStatusMess = "Not Connected" self.isBlockchainSynced = False ###-- Load icons & images self.loadIcons() ###-- Create main layout self.layout = QVBoxLayout() self.header = GuiHeader(self) self.initConsole() self.layout.addWidget(self.header) ###-- Create RPC Whatchdog self.rpc_watchdogThread = QThread() self.myRpcWd = RpcWatchdog(self) self.myRpcWd.moveToThread(self.rpc_watchdogThread) self.rpc_watchdogThread.started.connect(self.myRpcWd.run) self.rpc_watchdogThread.start() ###-- Create Queues and redirect stdout and stderr (eventually) self.queue = Queue() self.queue2 = Queue() sys.stdout = WriteStream(self.queue) sys.stderr = WriteStream(self.queue2) ###-- Init last logs logFile = open(log_File, 'w+') timestamp = strftime('%Y-%m-%d %H:%M:%S', gmtime(now())) log_line = '<b style="color: blue">{}</b><br>'.format('STARTING SPMT at '+ timestamp) logFile.write(log_line) logFile.close() ###-- Create the thread to update console log for stdout self.consoleLogThread = QThread() self.myWSReceiver = WriteStreamReceiver(self.queue) self.myWSReceiver.mysignal.connect(self.append_to_console) self.myWSReceiver.moveToThread(self.consoleLogThread) self.consoleLogThread.started.connect(self.myWSReceiver.run) self.consoleLogThread.start() printDbg("Console Log thread started") ###-- Create the thread to update console log for stderr self.consoleLogThread2 = QThread() self.myWSReceiver2 = WriteStreamReceiver(self.queue2) self.myWSReceiver2.mysignal.connect(self.append_to_console) self.myWSReceiver2.moveToThread(self.consoleLogThread2) self.consoleLogThread2.started.connect(self.myWSReceiver2.run) self.consoleLogThread2.start() printDbg("Console Log thread 2 started") ###-- Initialize tabs self.tabs = QTabWidget() self.t_main = TabMain(self) self.t_mnconf = TabMNConf(self) self.t_rewards = TabRewards(self) ###-- Add tabs self.tabs.addTab(self.tabMain, "Masternode Control") #self.tabs.addTab(self.tabMNConf, "MN Configuration") self.tabs.addTab(self.tabRewards, "Transfer Rewards") ###-- Connect change action self.tabs.currentChanged.connect(lambda: self.onTabChange()) ###-- Draw Tabs self.splitter = QSplitter(Qt.Vertical) ###-- Add tabs and console to Layout self.splitter.addWidget(self.tabs) self.splitter.addWidget(self.console) self.splitter.setStretchFactor(0,0) self.splitter.setStretchFactor(1,1) self.splitter.setSizes(self.parent.cache.get("splitter_sizes")) self.layout.addWidget(self.splitter) ###-- Set Layout self.setLayout(self.layout) ###-- Let's go self.mnode_to_change = None printOK("Hello! Welcome to " + parent.title) ###-- Hide console if it was previously hidden if self.parent.cache.get("console_hidden"): self.onToggleConsole() ##-- Check version self.onCheckVersion() @pyqtSlot(str) def append_to_console(self, text): self.consoleArea.moveCursor(QTextCursor.End) self.consoleArea.insertHtml(text) # update last logs logFile = open(log_File, 'a+') logFile.write(text) logFile.close() def initConsole(self): self.console = QGroupBox() self.console.setTitle("Console Log") layout = QVBoxLayout() self.btn_consoleToggle = QPushButton('Hide') self.btn_consoleToggle.setToolTip('Show/Hide console') self.btn_consoleToggle.clicked.connect(lambda: self.onToggleConsole()) consoleHeader = QHBoxLayout() consoleHeader.addWidget(self.btn_consoleToggle) self.consoleSaveButton = QPushButton('Save') self.consoleSaveButton.clicked.connect(lambda: self.onSaveConsole()) consoleHeader.addWidget(self.consoleSaveButton) self.btn_consoleClean = QPushButton('Clean') self.btn_consoleClean.setToolTip('Clean console log area') self.btn_consoleClean.clicked.connect(lambda: self.onCleanConsole()) consoleHeader.addWidget(self.btn_consoleClean) consoleHeader.addStretch(1) self.versionLabel = QLabel("--") self.versionLabel.setOpenExternalLinks(True) consoleHeader.addWidget(self.versionLabel) self.btn_checkVersion = QPushButton("Check SPMT version") self.btn_checkVersion.setToolTip("Check latest stable release of SPMT") self.btn_checkVersion.clicked.connect(lambda: self.onCheckVersion()) consoleHeader.addWidget(self.btn_checkVersion) layout.addLayout(consoleHeader) self.consoleArea = QTextEdit() almostBlack = QColor(40, 40, 40) palette = QPalette() palette.setColor(QPalette.Base, almostBlack) green = QColor(0, 255, 0) palette.setColor(QPalette.Text, green) self.consoleArea.setPalette(palette) layout.addWidget(self.consoleArea) self.console.setLayout(layout) def loadIcons(self): # Load Icons self.ledPurpleH_icon = QPixmap(os.path.join(self.imgDir, 'icon_purpleLedH.png')).scaledToHeight(17, Qt.SmoothTransformation) self.ledGrayH_icon = QPixmap(os.path.join(self.imgDir, 'icon_grayLedH.png')).scaledToHeight(17, Qt.SmoothTransformation) self.ledHalfPurpleH_icon = QPixmap(os.path.join(self.imgDir, 'icon_halfPurpleLedH.png')).scaledToHeight(17, Qt.SmoothTransformation) self.ledRedV_icon = QPixmap(os.path.join(self.imgDir, 'icon_redLedV.png')).scaledToHeight(17, Qt.SmoothTransformation) self.ledGrayV_icon = QPixmap(os.path.join(self.imgDir, 'icon_grayLedV.png')).scaledToHeight(17, Qt.SmoothTransformation) self.ledGreenV_icon = QPixmap(os.path.join(self.imgDir, 'icon_greenLedV.png')).scaledToHeight(17, Qt.SmoothTransformation) def myPopUp(self, messType, messTitle, messText, defaultButton=QMessageBox.No): mess = QMessageBox(messType, messTitle, messText, defaultButton, parent=self) mess.setStandardButtons(QMessageBox.Yes | QMessageBox.No) mess.setDefaultButton(defaultButton) return mess.exec_() def myPopUp2(self, messType, messTitle, messText, singleButton=QMessageBox.Ok): mess = QMessageBox(messType, messTitle, messText, singleButton, parent=self) mess.setStandardButtons(singleButton | singleButton) return mess.exec_() @pyqtSlot() def onCheckHw(self): printDbg("Checking for HW device...") self.updateHWstatus(None) self.showHWstatus() @pyqtSlot() def onCheckRpc(self): printDbg("Checking RPC server...") self.runInThread(self.updateRPCstatus, (), self.showRPCstatus) @pyqtSlot() def onCheckVersion(self): printDbg("Checking SPMT version...") self.versionLabel.setText("--") self.runInThread(self.checkVersion, (), self.updateVersion) def checkVersion(self, ctrl): local_version = self.parent.version['number'].split('.') remote_version = getRemoteSPMTversion().split('.') if (remote_version[0] > local_version[0]) or \ (remote_version[0] == local_version[0] and remote_version[1] > local_version[1]) or \ (remote_version[0] == local_version[0] and remote_version[1] == local_version[1] and remote_version[2] > local_version[2]): self.versionMess = '<b style="color:red">New Version Available:</b> %s.%s.%s ' % (remote_version[0], remote_version[1], remote_version[2]) self.versionMess += '(<a href="https://github.com/PIVX-Project/PIVX-SPMT/releases/">download</a>)' else: self.versionMess = "You have the latest version of SPMT" def updateVersion(self): if self.versionMess is not None: self.versionLabel.setText(self.versionMess) @pyqtSlot() def onCleanConsole(self): self.consoleArea.clear() @pyqtSlot() def onSaveConsole(self): timestamp = strftime('%Y-%m-%d_%H-%M-%S', gmtime(now())) options = QFileDialog.Options() options |= QFileDialog.DontUseNativeDialog fileName, _ = QFileDialog.getSaveFileName(self,"Save Logs to file","SPMT_Logs_%s.txt" % timestamp,"All Files (*);; Text Files (*.txt)", options=options) try: if fileName: printOK("Saving logs to %s" % fileName) log_file = open(fileName, 'w+') log_text = self.consoleArea.toPlainText() log_file.write(log_text) log_file.close() except Exception as e: err_msg = "error writing Log file" printException(getCallerName(), getFunctionName(), err_msg, e.args) @pyqtSlot() def onTabChange(self): # reload (and re-sort)masternode list in tabs if self.tabs.currentWidget() == self.tabRewards: # reload last used address self.tabRewards.destinationLine.setText(self.parent.cache.get("lastAddress")) # get new order mnOrder = {} mnList = self.tabMain.myList for i in range(mnList.count()): mnName = mnList.itemWidget(mnList.item(i)).alias mnOrder[mnName] = i self.parent.cache['mnList_order'] = mnOrder # Sort masternode list (by alias if no previous order set) if self.parent.cache.get('mnList_order') != {}: self.masternode_list.sort(key=self.parent.extract_order) self.t_rewards.loadMnSelect() self.t_rewards.selectedRewards = None @pyqtSlot() def onToggleConsole(self): if self.btn_consoleToggle.text() == 'Hide': self.btn_consoleToggle.setText('Show') self.consoleArea.hide() self.console.setMinimumHeight(70) self.console.setMaximumHeight(70) else: self.console.setMinimumHeight(70) self.console.setMaximumHeight(starting_height) self.btn_consoleToggle.setText('Hide') self.consoleArea.show() def showHWstatus(self): self.updateHWleds() self.myPopUp2(QMessageBox.Information, 'SPMT - hw check', "%s" % self.hwStatusMess, QMessageBox.Ok) def showRPCstatus(self): self.updateRPCled() self.myPopUp2(QMessageBox.Information, 'SPMT - rpc check', "%s" % self.rpcStatusMess, QMessageBox.Ok) def updateHWleds(self): if self.hwStatus == 1: self.header.hwLed.setPixmap(self.ledHalfPurpleH_icon) elif self.hwStatus == 2: self.header.hwLed.setPixmap(self.ledPurpleH_icon) else: self.header.hwLed.setPixmap(self.ledGrayH_icon) self.header.hwLed.setToolTip(self.hwStatusMess) def updateHWstatus(self, ctrl): if self.hwdevice is None: self.hwdevice = HWdevice() device = self.hwdevice statusCode = device.getStatusCode() statusMess = device.getStatusMess(statusCode) printDbg("code: %s - mess: %s" % (statusCode, statusMess)) if statusCode != 2: try: if getattr(self.hwdevice, 'dongle', None) is not None: self.hwdevice.dongle.close() self.hwdevice.initDevice() device = self.hwdevice statusCode = device.getStatusCode() statusMess = device.getStatusMess(statusCode) except Exception as e: err_msg = "error in checkHw" printException(getCallerName(), getFunctionName(), err_msg, e.args) self.hwStatus = statusCode self.hwStatusMess = statusMess def updateLastBlockLabel(self): text = '--' if self.rpcLastBlock == 1: text = "Loading block index..." elif self.rpcLastBlock > 0 and self.rpcConnected: text = str(self.rpcLastBlock) text += " (" if not self.isBlockchainSynced: text += "Synchronizing" else: text += "Synced" text += ")" self.header.lastBlockLabel.setText(text) def updateRPCled(self): if self.rpcConnected: self.header.rpcLed.setPixmap(self.ledPurpleH_icon) else: if self.rpcLastBlock == 1: self.header.rpcLed.setPixmap(self.ledHalfPurpleH_icon) else: self.header.rpcLed.setPixmap(self.ledGrayH_icon) self.header.rpcLed.setToolTip(self.rpcStatusMess) self.updateLastBlockLabel() def updateRPCstatus(self, ctrl): if self.rpcClient is None: try: self.rpcClient = RpcClient() except Exception as e: print(e) status, lastBlock = self.rpcClient.getStatus() statusMess = self.rpcClient.getStatusMess(status) if not status and lastBlock==0: try: self.rpcClient = RpcClient() status, lastBlock = self.rpcClient.getStatus() statusMess = self.rpcClient.getStatusMess(status) except Exception as e: err_msg = "error in checkRpc" printException(getCallerName(), getFunctionName(), err_msg, e) elif lastBlock == 1: statusMess = "PIVX wallet is connected but still synchronizing / verifying blocks" self.rpcConnected = status self.rpcLastBlock = lastBlock self.rpcStatusMess = statusMess self.isBlockchainSynced = self.rpcClient.isBlockchainSynced()
def __init__(self, ebook, orig, is_scrambled, fmap, parent=None): QDialog.__init__(self, parent=parent) self.setWindowFlags(Qt.Window) self.orig = orig self.ebook = ebook self.revfmap = {v: k for (k, v) in iteritems(fmap)} # create widgets lay = QVBoxLayout() self.setLayout(lay) buttonBox = QDialogButtonBox(QDialogButtonBox.Close) self.webview_orig = Webview() self.webview_scram = Webview() settings = self.webview_orig.settings() if hasattr(settings, 'setUserStyleSheetUrl'): # QWebView from QtWebKit style = 'body {%s}' % CSSBG cssurl = 'data:text/css;charset=utf-8;base64,' cssurl += as_base64_unicode(style) self.webview_orig.settings().setUserStyleSheetUrl(QUrl(cssurl)) self.webview_scram.settings().setUserStyleSheetUrl(QUrl(cssurl)) elif hasattr(self.webview_orig, 'setStyleSheet'): # QWebEngineView from QtWebEngine # setStyleSheet doesn't seem to work at the moment self.webview_orig.setStyleSheet('Webview {%s}' % CSSBG) self.webview_scram.setStyleSheet('Webview {%s}' % CSSBG) dummytext = '<body><p>*** Text content could not be displayed ...</p></body>' self.webview_orig.setHtml(dummytext) self.webview_scram.setHtml(dummytext) self.htmlList_orig = QListWidget() self.htmlList_scram = QListWidget() self.htmlList_orig.setMinimumWidth(300) self.htmlList_scram.setMinimumWidth(300) gpbox1 = QGroupBox() lay1 = QHBoxLayout() gpbox1.setLayout(lay1) lay1.addWidget(self.htmlList_orig) gpbox3 = QGroupBox() lay3 = QHBoxLayout() gpbox3.setLayout(lay3) lay3.addWidget(self.htmlList_scram) gpbox2 = QGroupBox('Original text content:') lay2 = QHBoxLayout() gpbox2.setLayout(lay2) lay2.addWidget(self.webview_orig) gpbox4 = QGroupBox('Original text content:') lay4 = QHBoxLayout() gpbox4.setLayout(lay4) lay4.addWidget(self.webview_scram) splitter = QSplitter(Qt.Horizontal) splitter.addWidget(gpbox1) splitter.addWidget(gpbox2) splitter.addWidget(gpbox3) splitter.addWidget(gpbox4) lay.addWidget(splitter) lay.addWidget(buttonBox) # create connect signals/slots buttonBox.rejected.connect(self.reject) self.htmlList_scram.currentRowChanged.connect( self.htmlList_currentRowChanged) self.htmlList_scram.itemDoubleClicked.connect( self.htmlList_itemDoubleClicked) self.htmlList_orig.setEnabled(False) self.htmlnames_scram = get_textnames(self.ebook) self.htmlnames_orig = tuple( [self.revfmap.get(an, an) for an in self.htmlnames_scram]) gpbox1.setTitle('Original HTML files: %s' % len(self.htmlnames_orig)) gpbox3.setTitle('Original HTML files: %s' % len(self.htmlnames_scram)) self.htmlList_orig.clear() self.htmlList_orig.addItems(self.htmlnames_orig) self.htmlList_scram.clear() self.htmlList_scram.addItems(self.htmlnames_scram) if not self.revfmap: gpbox1.setVisible(False) msg = '%s Preview: Original' % CAPTION if not is_scrambled: self.setWindowTitle(msg) gpbox1.setVisible(False) gpbox2.setVisible(False) else: self.setWindowTitle(msg + ' vs. Scrambled') gpbox3.setTitle('Scrambled HTML files: %s' % len(self.htmlnames_scram)) gpbox4.setTitle('Scrambled text content:') self.htmlList_scram.setCurrentRow(0)
class CodeEdit(QWidget): def __init__(self, parent): QWidget.__init__(self, parent) self._text_edit = CodeTextEdit(self) self._output_pane = QTextBrowser(self) self._output_pane.document().setDefaultFont(QFont('Courier', pointSize=7)) self._output_pane.setWordWrapMode(QTextOption.NoWrap) self._output_pane_container = QWidget(self) self._output_pane_layout = QGridLayout(self._output_pane_container) self._output_pane_layout.setContentsMargins(0, 16, 0, 0) self._output_pane_layout.addWidget(QLabel(self.tr('Script Output')), 0, 0) self._output_pane_layout.addWidget(self._output_pane) self._splitter = QSplitter(Qt.Vertical, self) self._splitter.addWidget(self._text_edit) self._splitter.addWidget(self._output_pane_container) self._splitter.setStretchFactor(0, 3) self._splitter.setStretchFactor(1, 1) self._layout = QGridLayout(self) self._layout.setContentsMargins(0, 0, 0, 0) self._layout.addWidget(self._splitter, 0, 0) self._text_edit.editComplete.connect(self.editComplete) editComplete = pyqtSignal(str) @property def textEdit(self): return self._text_edit @property def outputPane(self): return self._output_pane def setFontSize(self, font_size): self._text_edit.document().setDefaultFont(QFont('Courier', pointSize=font_size)) self._output_pane.document().setDefaultFont(QFont('Courier', pointSize=font_size)) self._text_edit.setTabStopWidth(font_size * 2) self._output_pane.setTabStopWidth(font_size * 2) def setModel(self, model): self._text_edit.setPlainText(model.text) model.textChanged.connect(self._set_text(self._text_edit)) if hasattr(model, 'output'): self._output_pane.setPlainText(model.output) model.outputChanged.connect(self._set_text(self._output_pane, True)) self._output_pane.setVisible(True) else: self._output_pane_container.setVisible(False) self.editComplete.connect(self._handle_text_changed(model)) model.validationMessageChanged.connect(self._handle_validation_message) def _set_text(self, widget, scroll_to_end=False): def _(text): if widget.toPlainText() != text: widget.setPlainText(text) if scroll_to_end: widget.verticalScrollBar().setSliderPosition(widget.verticalScrollBar().maximum()) return _ def _handle_text_changed(self, model): def _setter(value): model.text = value return _setter def _handle_validation_message(self, message): if message: palette = self.palette() invalid_color = QColor(Qt.red) invalid_color.setAlphaF(0.2) palette.setColor(QPalette.Base, invalid_color) else: palette = self.style().standardPalette() self._text_edit.setPalette(palette) self._text_edit.setToolTip(message or None)
class MainWidget(QWidget): """Main window class that provides logic of the program. Allows to search concept and to show information about concepts and relations """ def __init__(self, parent=None, application=None): super().__init__(parent) self.parent = parent self.application = application self.db = self.openDatabase() self.initUI() def initActions(self): """initalization of actions for main windows""" self.help_action = QAction("&Help", self) self.help_action.setShortcut("F1") self.help_action.setStatusTip('Help') self.help_action.triggered.connect(lambda: HelpDialog(self)) self.about_action = QAction("&About", self) self.about_action.setStatusTip('About') self.about_action.triggered.connect(self.showAboutMessage) self.new_db_action = QAction("New DB", self) self.new_db_action.setShortcut("Ctrl+N") self.new_db_action.triggered.connect(self.openNewDB) self.delete_сoncept_action = QAction("&Delete", self) self.delete_сoncept_action.setShortcut("DEL") self.delete_сoncept_action.setStatusTip("Delete") self.delete_сoncept_action.triggered.connect(lambda: self.delete(Concept)) self.delete_subcategory_action = QAction("&Delete subcategory", self) self.delete_subcategory_action.triggered.connect(lambda: self.delete(Subcategory)) self.edit_concept_action = QAction("&Edit", self) self.edit_concept_action.setShortcut("F2") self.edit_concept_action.triggered.connect(self.editConcept) self.edit_relation_action = QAction("&Edit", self) self.edit_relation_action.setShortcut("Ctrl+F2") self.edit_relation_action.triggered.connect(self.editRelation) self.exit_action = QAction("&Exit", self) self.exit_action.setShortcut("ESC") self.exit_action.triggered.connect(self.close) self.select_relation_action = QAction("&Select relation", self) self.select_relation_action.triggered.connect(self.setRelationDescription) self.delete_relation_action = QAction("&Delete", self) self.delete_relation_action.setShortcut("Ctrl+DEL") self.delete_relation_action.triggered.connect(lambda: self.delete(Relation)) def showAboutMessage(self): try: with open("about.txt") as f: text = f.read() QMessageBox.about(self.parent, "About application", text) except: pass def openNewDB(self): """This method deletes setting.py file and closes current application session. This uses for creating a new instance of Main Window. """ if os.path.exists("setting.py"): os.remove("setting.py") self.parent.close() self.application.exit(1) def initUI(self): self.initActions() self.add_data_button = QPushButton("Add data", self) self.export_button = QPushButton("Export references", self) self.search_box = QGroupBox() self.concept_list = QListWidget(self) self.relation_list = QListWidget(self) self.result_table = QTextEdit(self) self.concept_list.selectionModel().selectionChanged.connect(self.searchRelations) self.concept_list.setSelectionMode(QAbstractItemView.ContiguousSelection) self.concept_list.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOn) self.concept_list.setContextMenuPolicy(Qt.CustomContextMenu) self.concept_list.customContextMenuRequested.connect(self.showConceptContextMenu) self.relation_list.setContextMenuPolicy(Qt.CustomContextMenu) self.relation_list.customContextMenuRequested.connect(self.showRelationContextMenu) self.relation_list.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOn) self.relation_list.selectionModel().selectionChanged.connect(self.setRelationDescription) self.initSearchFrame(self.search_box) self.grid = QGridLayout() self.result_table.setReadOnly(True) self.search() self.configWidgets() self.bindWidgets() self.show() def showConceptContextMenu(self, pos): if not len(self.concept_list.selectedItems()) == 1: return global_position = self.concept_list.mapToGlobal(pos) my_menu = QMenu() my_menu.addAction(self.edit_concept_action) my_menu.addAction(self.delete_сoncept_action) my_menu.addAction(self.delete_subcategory_action) my_menu.addSeparator() my_menu.addAction(self.help_action) my_menu.addAction(self.exit_action) my_menu.exec(global_position) def initSearchFrame(self, frame): self.search_line = QLineEdit(frame) checkboxes = QGroupBox() checkboxes.setStyleSheet("border:0;") self.concept_checkbox = QCheckBox("Concept", checkboxes) self.description_checkbox = QCheckBox("Description", checkboxes) self.study_checkbox = QCheckBox("Study", checkboxes) self.reference_checkbox = QCheckBox("Reference", checkboxes) self.in_selected_checkbox = QCheckBox("Search in selected concepts", checkboxes) self.search_button = QPushButton("Search", checkboxes) checkboxes_layout = QGridLayout(checkboxes) checkboxes_layout.addWidget(self.concept_checkbox, 0, 0) checkboxes_layout.addWidget(self.description_checkbox, 0, 1) checkboxes_layout.addWidget(self.study_checkbox, 0, 2) checkboxes_layout.addWidget(self.reference_checkbox, 0, 3) checkboxes_layout.addWidget(self.in_selected_checkbox, 0, 4) checkboxes_layout.setContentsMargins(0, 0, 0, 0) self.concept_checkbox.setChecked(True) self.in_selected_checkbox.setChecked(True) grid = QGridLayout(frame) grid.addWidget(self.search_line, 1, 0) grid.addWidget(self.search_button, 1, 1) grid.addWidget(checkboxes, 2, 0, Qt.AlignLeft) frame.setLayout(grid) def getSearchData(self): """This method reads parameters of search line and checkboxes, and returns "Subcategory" class. None and [] uses for showing checkboxes that have been checked. """ concept = self.search_line.text() if self.concept_checkbox.isChecked() else None if self.reference_checkbox.isChecked(): reference = [ #RelationReference(text=self.search_line.text()), ConceptReference(text=self.search_line.text()) ] else: reference = [] if self.description_checkbox.isChecked(): description = [Description(text=self.search_line.text(), reference=reference)] else: description = [Description(reference=reference)] study = self.search_line.text() if self.study_checkbox.isChecked() else None if self.in_selected_checkbox.isChecked and len(self.concept_list.selectedItems()) > 1: selected_items_raw = self.concept_list.selectedItems() selected_items = [item.data(Qt.UserRole)[1] for item in selected_items_raw] else: selected_items = [] return Subcategory(subcategory=concept, description=description, study=study), selected_items # todo: doesn't work, becouse need html + bib --> rendered bib def searchRelations(self): """This method triggers when the user clicked on the concept. This method selected ID of a subcategory from selected item in conceptions list and then searches and setting all information about this selected concept. """ if not self.concept_list.selectedItems(): self.setConceptDescription() self.setResult([], self.relation_list) return subcategory_id = self.concept_list.selectedItems()[0].data(Qt.UserRole)[1].id self.setConceptDescription() result = self.db.search_relation(subcategory_id) self.setResult(result, self.relation_list) def showRelationContextMenu(self, pos): global_position = self.relation_list.mapToGlobal(pos) my_menu = QMenu() my_menu.addAction(self.edit_relation_action) my_menu.addAction(self.delete_relation_action) my_menu.addSeparator() my_menu.exec(global_position) def editRelation(self): """This method opens dialog for editing Relation""" if hasSlelectedItems(self.relation_list): relation = self.relation_list.selectedItems()[0].data(Qt.UserRole)[2] dlg = EditRelationDialog(self, relation) if dlg.exec_(): relation = dlg.getValue() self.db.update_relation(relation) self.select_relation_action.trigger() def editConcept(self): """This method opens dialog for editing Concept""" if hasSlelectedItems(self.concept_list): concept = self.concept_list.selectedItems()[0].data(Qt.UserRole)[0] subcategory = self.concept_list.selectedItems()[0].data(Qt.UserRole)[1] dlg = EditConceptDialog(self, concept, subcategory) if dlg.exec_(): concept, subcategory = dlg.getValue() self.db.update_concept(concept) self.db.update_subcategory(subcategory) self.search() def delete(self, something): """This universal method for deleting something from the list and database. "something" parameter needs to be any of this types: Concept, Subcategory, Relation """ if something == Concept: number = 0 target_list = self.concept_list elif something == Subcategory: number = 1 target_list = self.concept_list elif something == Relation: number = 2 target_list = self.relation_list if hasSlelectedItems(target_list): something = target_list.selectedItems()[0].data(Qt.UserRole)[number] self.db.delete(something) self.search() # need refactoring def setRelationDescription(self): """This method retrieves information about relation, formats and sets it in description field. """ if hasSlelectedItems(self.relation_list): relation = self.relation_list.selectedItems()[0].data(Qt.UserRole)[2] concept1 = "{}{}".format(relation.node1.concept.name, ", {}".format(relation.node1.subcategory) if relation.node1.subcategory else "") concept2 = "{}{}".format(relation.node2.concept.name, ", {}".format(relation.node2.subcategory) if relation.node2.subcategory else "") description = relation.description study = relation.study references = relation.reference # HTML is used for a better information formating text = "<b>Relation between </b> \"{}\" <b>and</b> \"{}\"<p>".format(concept1, concept2) text += r"<b>Description:</b>" + "<br> {} <p>".format(description) text += r"<b>Study:</b> {} <p>".format(study) text += r"<b>References:</b><ol>" for ref in references: text += " <li> {} </li>".format(getBibRenderFromHTML(ref.text)) text += "</ol><br>" self.result_table.setHtml(text) else: self.result_table.clear() # need refactoring def setConceptDescription(self): """This method retrieves information about concept, formats and sets it in description field. """ if not self.concept_list.selectedItems(): self.result_table.setHtml("") return concept = self.concept_list.selectedItems()[0].data(Qt.UserRole)[0] subcategory = self.concept_list.selectedItems()[0].data(Qt.UserRole)[1] description = subcategory.description # HTML is used for a better information formating text = "<b>Concept:</b> {}<p>".format(concept.name) text += "<b>Subcategory:</b> {}<p>".format(subcategory.subcategory) text += "<b>Synonyms:</b> {}<p>".format(concept.synonyms) text += "<ol>" for des in description: text += "<li> {}".format("<b>Description:</b> <br>") text += " {} <p>".format(des.text) text += "<ol>" text += "<b>References: </b>" for ref in des.reference: text += "<li>" text += "{}".format(getBibRenderFromHTML(ref.text)) text += "</li>" text += "</ol>" text += "</li>" text += "</ol>" self.result_table.setHtml(text) def setResult(self, result, list): """This is a universal method for showing information in a relation field or in a concept field. """ list.clear() for concept, subcategory, *other in result: item = QListWidgetItem() item.setData(Qt.UserRole, (concept, subcategory, *other)) item.setText("{}{} {}".format(concept.name, "" if subcategory.subcategory == "" else ", ", subcategory.subcategory)) list.insertItem(0, item) def search(self): subcategory, selected_items = self.getSearchData() result = self.db.search_nodes(subcategory, selected_items) self.setResult([], self.relation_list) self.setResult(result, self.concept_list) self.setResult([], self.relation_list) self.setRelationDescription() def configWidgets(self): self.grid.setAlignment(Qt.AlignTop) self.grid.setSpacing(5) self.menu_bar = QMenuBar(self) self.database_menu = self.menu_bar.addMenu("Database") self.database_menu.addAction(self.new_db_action) self.database_menu.addSeparator() self.database_menu.addAction(self.exit_action) self.concept_menu = self.menu_bar.addMenu("Concept") self.concept_menu.addAction(self.edit_concept_action) self.concept_menu.addAction(self.delete_сoncept_action) self.concept_menu.addAction(self.delete_subcategory_action) self.relation_menu = self.menu_bar.addMenu("Relation") self.relation_menu.addAction(self.edit_relation_action) self.relation_menu.addAction(self.delete_relation_action) self.help_menu = self.menu_bar.addMenu("Help") self.help_menu.addAction(self.help_action) self.help_menu.addAction(self.about_action) self.grid.setMenuBar(self.menu_bar) self.concept_box = QGroupBox("Concepts") self.concept_grid = QGridLayout() self.concept_grid.setContentsMargins(0, 10, 0, 0) self.concept_grid.addWidget(self.concept_list) self.concept_box.setLayout(self.concept_grid) self.relation_box = QGroupBox("Relations") self.relation_grid = QGridLayout() self.relation_grid.setContentsMargins(0, 10, 0, 0) self.relation_grid.addWidget(self.relation_list) self.relation_box.setLayout(self.relation_grid) self.description_box = QGroupBox("Description") self.description_grid = QGridLayout() self.description_grid.setContentsMargins(0, 10, 0, 0) self.description_grid.addWidget(self.result_table) self.description_box.setLayout(self.description_grid) self.concept_splitter = QSplitter(self) self.concept_splitter.addWidget(self.concept_box) self.concept_splitter.addWidget(self.relation_box) self.description_splitter = QSplitter(self) self.description_splitter.setOrientation(Qt.Vertical) self.description_splitter.addWidget(self.concept_splitter) self.description_splitter.addWidget(self.description_box) self.grid.addWidget(self.add_data_button, 1, 0, Qt.AlignLeft) self.grid.addWidget(self.export_button, 1, 1, Qt.AlignRight) self.grid.addWidget(self.search_box, 2, 0, 1, 2) self.grid.addWidget(self.description_splitter, 5, 0, 1, 2) self.setLayout(self.grid) def bindWidgets(self): self.add_data_button.clicked.connect(self.addData) self.search_button.clicked.connect(self.search) self.export_button.clicked.connect(self.exportReferences) self.search_line.returnPressed.connect(self.search) def exportReferences(self): references = self.db.get_all_references() references = [getPlainText(ref.text) for ref in references] references = list(set(references)) references = [prepareBibStr(ref) for ref in references] final_text = ",\n\n".join(references) dlg = QFileDialog() file_path = dlg.getSaveFileName(self, "Save bibtex file")[0] if not (file_path == ""): # todo: check readBibsFromFile try: with open(file_path, 'w') as f: f.write(final_text) except Exception as error: showError(error) def addData(self): dlg = NewDataDialog(self.db) dlg.exec_() self.search() def getPath(self): dialog = NewDBDialog() if dialog.exec_(): path = dialog.path with open("setting.py", "w") as f: f.write("{{ 'last': r'{0}' }}".format(path)) return path else: self.close() def openDatabase(self): if os.path.exists("setting.py"): with open("setting.py") as f: setting = eval(f.read()) if "last" in setting: if os.path.exists(setting["last"]): path = setting["last"] else: path = self.getPath() else: path = self.getPath() else: path = self.getPath() if path is None: self.close() exit(0) else: return Graph(path) def closeEvent(self, event): event.ignore()
class mainWindow(QMainWindow): disableProjectMenu = pyqtSignal() enableProjectMenu = pyqtSignal() def __init__(self, parent=None): super(mainWindow, self).__init__(parent) self.ui = loadUi('UI/mainFrame.ui', self) self.mainWidget = QWidget(self) self.mainWidgetLayout = QVBoxLayout(self.mainWidget) self.setCentralWidget(self.mainWidget) self.setWindowIcon(QIcon('res/pluto.ico')) ############################### # local variables self.defaultDir = '.' self.curOpenProjectHandle = None self.curProjectTabManager = None self.openedProject = ProjectQueue() self.handleToTabWidget = dict() self.plutoVariables = plutoVariables() # flags ############################### # menu newProjectMenu = self.ui.actionNew_Project newProjectMenu.triggered.connect(self.newProjectDialog) openProjectMenu = self.ui.actionOpen_Project openProjectMenu.triggered.connect(self.openProjectDialog) saveProjectMenu = self.ui.actionSave_Project saveProjectMenu.triggered.connect(self.saveProject) self.newProjectDataSetMenu = self.ui.actionNew_DataSet self.newProjectDataSetMenu.triggered.connect( self.onNewProjectDataSetMenu) self.newProjectModel = self.ui.actionNew_Model self.newProjectModel.triggered.connect(self.onNewProjectModel) self.newProjectScript = self.ui.actionNew_Script self.newProjectScript.triggered.connect(self.onNewProjectScript) self.newProjectDataSetMenu.setEnabled(False) self.newProjectModel.setEnabled(False) self.newProjectScript.setEnabled(False) ############################### # init toolbar status bar self.toolBar = QToolBar(self) self.statusBar = QStatusBar(self) self.openAction = QAction(QIcon('res/Open.ico'), 'Open Project', self) self.homePageAction = QAction(QIcon('res/homepage.png'), 'HOME', self) self.projectAction = QAction(QIcon('res/ProjectManager.ico'), 'Project Manager', self) ############################### # init widgets self.leftStage = QTabWidget(self) self.mainStage = QWidget(self) self.bottomStage = QWidget(self) self.bottomLayout = QHBoxLayout(self.bottomStage) self.bottomTabWidget = CollapsibleTabWidget(0, self) self.mainLayout = QStackedLayout(self.mainStage) ############################### # splitter self.vertical_splitter = QSplitter(Qt.Horizontal) self.horizontal_splitter = QSplitter(Qt.Vertical) self.downSize = 0.22 * self.height() ############################### # left stage self.leftTreeView = QTreeView(self) ############################### # bottom stage self.bottomOutputView = QTextEdit(self) ############################### # main stage self.scrollarea = QScrollArea(self) self.scrollbar = QScrollBar(self) self.projectList = QWidget(self.scrollarea) self.projectListLayout = FlowLayout() self.initUI() self.initToolBar() self.setContextMenuPolicy( Qt.NoContextMenu) # close right click of mainWindow def initUI(self): self.mainStage.setLayout(self.mainLayout) # setup splitter self.vertical_splitter.addWidget(self.leftStage) self.vertical_splitter.addWidget(self.mainStage) self.vertical_splitter.setStretchFactor(1, 1) self.vertical_splitter.setCollapsible(0, False) self.vertical_splitter.setCollapsible(1, False) self.horizontal_splitter.addWidget(self.vertical_splitter) self.horizontal_splitter.addWidget(self.bottomStage) self.horizontal_splitter.setStretchFactor(1, 1) self.horizontal_splitter.setCollapsible(0, False) self.horizontal_splitter.setCollapsible(1, False) self.vertical_splitter.setSizes( [self.width() * 0.22, self.width() * 0.78]) # self.horizontal_splitter.setSizes([self.height() * 0.78, self.height() * 0.22]) self.horizontal_splitter.setSizes( [self.height() - self.downSize, self.downSize]) self.horizontal_splitter.splitterMoved.connect(self.onHSplitterMoved) self.mainWidgetLayout.addWidget(self.horizontal_splitter) self.bottomStage.setLayout(self.bottomLayout) self.bottomTabWidget.setSplitter(self.horizontal_splitter) self.bottomLayout.addWidget(self.bottomTabWidget) # setup left stage self.leftStage.setTabPosition(QTabWidget.West) self.leftStage.addTab(self.leftTreeView, 'File') # setup bottom stage # self.bottomStage.setTabPosition(QTabWidget.South) self.bottomTabWidget.addTab(self.bottomOutputView, 'Output') self.bottomLayout.setContentsMargins(20, 0, 0, 0) # init main stage self.initProjectList() # connect self.disableProjectMenu.connect(self.onDisableProjectMenu) self.enableProjectMenu.connect(self.onEnableProjectMenu) def initToolBar(self): self.statusBar.setContentsMargins(10, 0, 0, 10) self.addToolBar(self.toolBar) self.setStatusBar(self.statusBar) # open self.openAction.setStatusTip('Open Project') self.openAction.triggered.connect(self.openProjectDialog) self.toolBar.addAction(self.openAction) self.homePageAction.setStatusTip('Home Page') self.homePageAction.triggered.connect(self.jumpToHomePage) self.toolBar.addAction(self.homePageAction) self.projectAction.setStatusTip('Project Manager') self.projectAction.triggered.connect(self.showOpenProject) self.toolBar.addAction(self.projectAction) def initProjectList(self): self.scrollarea.setWidgetResizable(True) self.projectList.setLayout(self.projectListLayout) self.scrollarea.setWidget(self.projectList) self.scrollarea.setVerticalScrollBar(self.scrollbar) self.scrollarea.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOn) self.mainLayout.addWidget(self.scrollarea) # init projects from plutoVariables projectFiles projectHandleList = [] # projectItemList = [] for projectPath in self.plutoVariables.projectFiles: projectHandle = ProjectReader(projectPath) projectHandleList.append(projectHandle) # projectName, projectLocation, lastOpenTime, projectFiles projectItem = ProjectWidget(projectHandle.projectName, projectHandle.projectPath, projectHandle.lastAccessTime, projectHandle) projectItem.triggered.connect(self.openProject) self.projectListLayout.addWidget(projectItem) def newProjectDialog(self): dialog = newProjectDialog(self) dialog.projectInited.connect(self.onProjectInited) dialog.show() def openProject(self, handle: ProjectReader): if self.openedProject.isExist(handle): QMessageBox.information( self, 'open project', "project \"" + handle.projectName + "\" has been opened") else: print('open project:', handle.projectName) tabWidget = ColorTabWidget(self) tabManager = self.initTabWidget(tabWidget, handle) self.openedProject.add(handle, tabManager) self.mainLayout.addWidget(tabWidget) self.mainLayout.setCurrentIndex(self.openedProject.currentIndex) self.curOpenProjectHandle = handle # self.openedProject.getHandle(self.openedProject.currentIndex) self.curProjectTabManager = tabManager self.updateProjectLastAccessTime() self.enableProjectMenu.emit() def closeProject(self): if self.curOpenProjectHandle: self.saveProject() self.curOpenProjectHandle = None gc.collect() def saveProject(self): # TODO print('save project') if not self.curOpenProjectHandle: QMessageBox.information(self, 'Save Project', 'No open project', QMessageBox.Ok) else: self.curOpenProjectHandle.saveToYaml() QMessageBox.information( self, 'Save Project', 'Project Saved to ' + self.curOpenProjectHandle.yamlFile, QMessageBox.Ok) def changeProjectName(self, name): self.curOpenProjectHandle.setProjectName(name) self.curOpenProjectHandle.updateToYamlDict() def updateProjectLastAccessTime(self): currentTime = datetime.utcnow() self.curOpenProjectHandle.setLastAccessTime(currentTime) self.curOpenProjectHandle.updateToYamlDict() def addFileToProject(self): # TODO pass def delFileFromProject(self): # TODO pass def openProjectDialog(self): dialog = QFileDialog(self) options = QFileDialog.Options() options |= QFileDialog.DontUseNativeDialog dialog.setDirectory(self.defaultDir) projectFile, _ = dialog.getOpenFileName( self, "Open Project", "", "Project File (*.pluto);;All Files (*.*)", options=options) if projectFile: handle = ProjectReader(projectFile) self.openProject(handle) else: raise Exception('Open project failed') def jumpToHomePage(self): self.mainLayout.setCurrentIndex(0) self.disableProjectMenu.emit() def showOpenProject(self): # display a pop menu of current open project if not len(self.openedProject): print('no opened project') return allOpenedProjectID = self.openedProject.getAllID() openedProjectMenu = QMenu() for index, ID in enumerate(allOpenedProjectID): if not ID: continue handle = self.openedProject.getHandle(index) oneProject = QAction(handle.projectName, self) oneProject.triggered.connect(partial(self.showProjectPage, index)) openedProjectMenu.addAction(oneProject) self.projectAction.setMenu(openedProjectMenu) openedProjectMenu.exec(QCursor.pos()) def showProjectPage(self, index): # show opened project page using index self.mainLayout.setCurrentIndex(index) self.curOpenProjectHandle, self.curProjectTabManager = self.openedProject.getHandle( index) self.enableProjectMenu.emit() def initTabWidget(self, tabWidget: ColorTabWidget, handle: ProjectReader): tabManager = TabManager(tabWidget, handle) # init qml main page list projectMainPage = QQuickWidget(tabWidget) projectMainPage.setResizeMode(QQuickWidget.SizeRootObjectToView) projectMainPage.setSource( QUrl.fromLocalFile('QML/ProjectMainPage.qml')) projectMainPage.setMinimumWidth(550) projectMainPage.setMinimumHeight(250) projectMainPage.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) obj_projectMainPage = projectMainPage.rootObject() # send main page init dict obj_projectMainPage.onInitMainPageItems( self.getProjectDetail(tabManager)) obj_projectMainPage.sendData.connect(self.getData_ProjectMainPage) tabManager.qmlHandle = obj_projectMainPage tabManager.qmlWidget = projectMainPage tabWidget.addTab(projectMainPage, 'MainPage:MainPage') return tabManager @staticmethod def getProjectDetail(tabManager: TabManager): info = dict() handle = tabManager.handle info['ID'] = handle.yamlFile info['projectName'] = handle.projectName info['lastAccessTime'] = handle.lastAccessTime.replace( tzinfo=timezone.utc).astimezone( tz=None).strftime('%y/%m/%d %H:%M:%S') info['model'] = handle.modelSourceHandle.getAllPath() info['data'] = handle.dataSourceHandle.getAllPath() info['script'] = handle.scriptSourceHandle.getAllPath() info['result'] = [] return info def onProjectInited(self, projectFile: str): handle = ProjectReader(projectFile) self.openProject(handle) def getData_ProjectMainPage(self, data): dataType, itemIndex, itemName = data.toVariant() # print(dataType, itemIndex, itemName) if dataType == 'data': handle = dataLoader(itemName) self.openDSEditor(handle) def changeEvent(self, e: QEvent) -> None: if e.type() == QEvent.WindowStateChange and self.windowState( ) == Qt.WindowMaximized: self.horizontal_splitter.setSizes( [self.height() - self.downSize, self.downSize]) elif e.type() == QEvent.WindowStateChange and self.windowState( ) == Qt.WindowNoState: self.horizontal_splitter.setSizes( [self.height() - self.downSize, self.downSize]) def onHSplitterMoved(self, pos: int, index: int): self.downSize = self.height() - pos def onDisableProjectMenu(self): self.newProjectDataSetMenu.setEnabled(False) self.newProjectModel.setEnabled(False) self.newProjectScript.setEnabled(False) def onEnableProjectMenu(self): self.newProjectDataSetMenu.setEnabled(True) self.newProjectModel.setEnabled(True) self.newProjectScript.setEnabled(True) def onNewProjectDataSetMenu(self): dialog = newPlutoDSDialog(self.curOpenProjectHandle, self) r = dialog.exec() if r == QDialog.Accepted: obj_projectMainPage = self.curProjectTabManager.qmlWidget.rootObject( ) obj_projectMainPage.addData(dialog.target) handle = dataLoader(dialog.target) self.openDSEditor(handle) def onNewProjectModel(self): pass def onNewProjectScript(self): pass def openDSEditor(self, DSHandle): tabManager = self.curProjectTabManager tabWidget = tabManager.tabWidget if DSHandle.yamlFile in tabManager.dataTabs: print(tabManager.dataTabs[DSHandle.yamlFile]) tabWidget.setCurrentIndex( tabManager.dataTabs[DSHandle.yamlFile][0]) else: widget, title = self.DSEditor(DSHandle, tabWidget) tabWidget.addTab(widget, title) tabManager.dataTabs[ DSHandle.yamlFile] = tabWidget.count() - 1, widget tabWidget.setCurrentIndex(tabWidget.count() - 1) def DSEditor(self, DSHandle: dataLoader, parent=None): handle = DSHandle widget = QWidget(parent) title = 'Data:' + QDir(handle.yamlFile).dirName() return widget, title
class ThemeCreateDialog(Dialog): def __init__(self, parent, report): self.report = report Dialog.__init__(self, _('Create an icon theme'), 'create-icon-theme', parent) def setup_ui(self): self.splitter = QSplitter(self) self.l = l = QVBoxLayout(self) l.addWidget(self.splitter) l.addWidget(self.bb) self.w = w = QGroupBox(_('Theme Metadata'), self) self.splitter.addWidget(w) l = w.l = QFormLayout(w) self.missing_icons_group = mg = QGroupBox(self) self.mising_icons = mi = QListWidget(mg) mi.setSelectionMode(mi.NoSelection) mg.l = QVBoxLayout(mg) mg.l.addWidget(mi) self.splitter.addWidget(mg) self.title = QLineEdit(self) l.addRow(_('&Title:'), self.title) self.author = QLineEdit(self) l.addRow(_('&Author:'), self.author) self.version = v = QSpinBox(self) v.setMinimum(1), v.setMaximum(1000000) l.addRow(_('&Version:'), v) self.description = QTextEdit(self) l.addRow(self.description) self.refresh_button = rb = self.bb.addButton(_('&Refresh'), self.bb.ActionRole) rb.setIcon(QIcon(I('view-refresh.png'))) rb.clicked.connect(self.refresh) self.apply_report() def sizeHint(self): return QSize(900, 600) @property def metadata(self): return { 'title': self.title.text().strip(), 'author': self.author.text().strip(), 'version': self.version.value(), 'description': self.description.toPlainText().strip(), } def save_metadata(self): with open(os.path.join(self.report.path, THEME_METADATA), 'wb') as f: json.dump(self.metadata, f, indent=2) def refresh(self): self.save_metadata() self.report = read_theme_from_folder(self.report.path) self.apply_report() def apply_report(self): theme = self.report.theme self.title.setText((theme.title or '').strip()) self.author.setText((theme.author or '').strip()) self.version.setValue(theme.version or 1) self.description.setText((theme.description or '').strip()) if self.report.missing: title = _('%d icons missing in this theme') % len(self.report.missing) else: title = _('No missing icons') self.missing_icons_group.setTitle(title) mi = self.mising_icons mi.clear() for name in sorted(self.report.missing): QListWidgetItem(QIcon(I(name, allow_user_override=False)), name, mi) def accept(self): mi = self.metadata if not mi.get('title'): return error_dialog(self, _('No title specified'), _( 'You must specify a title for this icon theme'), show=True) if not mi.get('author'): return error_dialog(self, _('No author specified'), _( 'You must specify an author for this icon theme'), show=True) return Dialog.accept(self)
def __init__(self, path, panel, parent): """ Create widget. Arguments: obj (Command, Parameter): Command or Parameter being edited. parent (Optional[QWidget]): Parent widget. """ super(ParameterTableWindow, self).__init__(path, panel, parent) view = ParameterTableView(panel, item_path=path, parent_item=None, parent=self) self._actions = {} self._setView(view) self._search.setVisible(False) self._plot = None if re.match("^.*[.]VALE$", path.path()) or \ re.match("^.*[.](ABSCISSE|ORDONNEE)$", path.path()): self._plot = PlotWidget(view, self) self._plot.hide() splitter = QSplitter(self) splitter.setOrientation(Qt.Vertical) splitter.addWidget(view) splitter.addWidget(self._plot) self._scroll.setWidget(splitter) else: self._scroll.setWidget(view) view.selectionChanged.connect(self._updateState) tbar = QToolBar(self) tbar.setToolButtonStyle(Qt.ToolButtonIconOnly) tbar.addAction(\ self._createAction(ParameterTableWindow.AppendRow, load_icon("as_pic_add_row.png"), translate("ParameterPanel", "Append row"), translate("ParameterPanel", "Append row to the end of table"), view.appendRow)) tbar.addAction(\ self._createAction(ParameterTableWindow.InsertRow, load_icon("as_pic_insert_row.png"), translate("ParameterPanel", "Insert row"), translate("ParameterPanel", "Insert row at current position"), view.insertRow)) tbar.addAction(\ self._createAction(ParameterTableWindow.RemoveRow, load_icon("as_pic_remove_row.png"), translate("ParameterPanel", "Remove rows"), translate("ParameterPanel", "Remove selected rows"), view.removeRows)) tbar.addSeparator() tbar.addAction(\ self._createAction(ParameterTableWindow.MoveRowUp, load_icon("as_pic_move_up.png"), translate("ParameterPanel", "Move up"), translate("ParameterPanel", "Move selected rows up"), view.moveRowsUp)) tbar.addAction(\ self._createAction(ParameterTableWindow.MoveRowDown, load_icon("as_pic_move_down.png"), translate("ParameterPanel", "Move down"), translate("ParameterPanel", "Move selected rows down"), view.moveRowsDown)) tbar.addSeparator() if self._plot is not None: tbar.addAction(\ self._createAction(ParameterTableWindow.FunctionPlot, load_icon("as_pic_show_plot.png"), translate("ParameterPanel", "Plot function"), translate("ParameterPanel", "Show/hide plot view"), self._plot.setVisible, True)) tbar.addSeparator() tbar.addAction(\ self._createAction(ParameterTableWindow.Import, load_icon("as_pic_import_table.png"), translate("ParameterPanel", "Import table"), translate("ParameterPanel", "Import function data from file"), view.importFile)) self.addControlWidget(tbar) self._updateState()
class MainWindow(QWidget): def __init__(self, parent, imgDir): super(QWidget, self).__init__(parent) self.parent = parent self.imgDir = imgDir self.runInThread = ThreadFuns.runInThread ###-- Create clients and statuses self.hwdevice = None self.hwStatus = 0 self.hwStatusMess = "Not Connected" self.rpcClient = None self.rpcConnected = False self.rpcStatusMess = "Not Connected" ###-- Load icons & images self.loadIcons() ###-- Create main layout self.layout = QVBoxLayout() self.header = GuiHeader(self) self.initConsole() self.layout.addWidget(self.header) ###-- Create RPC Whatchdog self.rpc_watchdogThread = QThread() self.myRpcWd = RpcWatchdog(self) self.myRpcWd.moveToThread(self.rpc_watchdogThread) self.rpc_watchdogThread.started.connect(self.myRpcWd.run) self.rpc_watchdogThread.start() ###-- Create Queues and redirect stdout and stderr (eventually) self.queue = Queue() self.queue2 = Queue() sys.stdout = WriteStream(self.queue) sys.stderr = WriteStream(self.queue2) ###-- Init last logs logFile = open(log_File, 'w+') timestamp = strftime('%Y-%m-%d %H:%M:%S', gmtime(now())) log_line = '<b style="color: blue">{}</b><br>'.format( 'STARTING PET4L at ' + timestamp) logFile.write(log_line) logFile.close() ###-- Create the thread to update console log for stdout self.consoleLogThread = QThread() self.myWSReceiver = WriteStreamReceiver(self.queue) self.myWSReceiver.mysignal.connect(self.append_to_console) self.myWSReceiver.moveToThread(self.consoleLogThread) self.consoleLogThread.started.connect(self.myWSReceiver.run) self.consoleLogThread.start() printDbg("Console Log thread started") ###-- Create the thread to update console log for stderr self.consoleLogThread2 = QThread() self.myWSReceiver2 = WriteStreamReceiver(self.queue2) self.myWSReceiver2.mysignal.connect(self.append_to_console) self.myWSReceiver2.moveToThread(self.consoleLogThread2) self.consoleLogThread2.started.connect(self.myWSReceiver2.run) self.consoleLogThread2.start() printDbg("Console Log thread 2 started") ###-- Initialize tabs self.tabs = QTabWidget() self.t_rewards = TabRewards(self) ###-- Add tabs self.tabs.addTab(self.tabRewards, "Spend from Ledger") ###-- Draw Tabs self.splitter = QSplitter(Qt.Vertical) ###-- Add tabs and console to Layout self.splitter.addWidget(self.tabs) self.splitter.addWidget(self.console) self.splitter.setStretchFactor(0, 0) self.splitter.setStretchFactor(1, 1) self.splitter.setSizes([2, 1]) self.layout.addWidget(self.splitter) ###-- Set Layout self.setLayout(self.layout) ###-- Let's go self.mnode_to_change = None printOK("Hello! Welcome to " + parent.title) @pyqtSlot(str) def append_to_console(self, text): self.consoleArea.moveCursor(QTextCursor.End) self.consoleArea.insertHtml(text) # update last logs logFile = open(log_File, 'a+') logFile.write(text) logFile.close() def initConsole(self): self.console = QGroupBox() self.console.setTitle("Console Log") layout = QVBoxLayout() self.btn_consoleToggle = QPushButton('Hide') self.btn_consoleToggle.setToolTip('Show/Hide console') self.btn_consoleToggle.clicked.connect(lambda: self.onToggleConsole()) consoleHeader = QHBoxLayout() consoleHeader.addWidget(self.btn_consoleToggle) self.consoleSaveButton = QPushButton('Save') self.consoleSaveButton.clicked.connect(lambda: self.onSaveConsole()) consoleHeader.addWidget(self.consoleSaveButton) self.btn_consoleClean = QPushButton('Clean') self.btn_consoleClean.setToolTip('Clean console log area') self.btn_consoleClean.clicked.connect(lambda: self.onCleanConsole()) consoleHeader.addWidget(self.btn_consoleClean) consoleHeader.addStretch(1) layout.addLayout(consoleHeader) self.consoleArea = QTextEdit() almostBlack = QColor(40, 40, 40) palette = QPalette() palette.setColor(QPalette.Base, almostBlack) green = QColor(0, 255, 0) palette.setColor(QPalette.Text, green) self.consoleArea.setPalette(palette) layout.addWidget(self.consoleArea) self.console.setLayout(layout) def loadIcons(self): # Load Icons self.ledPurpleH_icon = QPixmap( os.path.join(self.imgDir, 'icon_purpleLedH.png')).scaledToHeight( 17, Qt.SmoothTransformation) self.ledGrayH_icon = QPixmap( os.path.join(self.imgDir, 'icon_grayLedH.png')).scaledToHeight( 17, Qt.SmoothTransformation) self.ledHalfPurpleH_icon = QPixmap( os.path.join(self.imgDir, 'icon_halfPurpleLedH.png')).scaledToHeight( 17, Qt.SmoothTransformation) self.ledRedV_icon = QPixmap( os.path.join(self.imgDir, 'icon_redLedV.png')).scaledToHeight( 17, Qt.SmoothTransformation) self.ledGrayV_icon = QPixmap( os.path.join(self.imgDir, 'icon_grayLedV.png')).scaledToHeight( 17, Qt.SmoothTransformation) self.ledGreenV_icon = QPixmap( os.path.join(self.imgDir, 'icon_greenLedV.png')).scaledToHeight( 17, Qt.SmoothTransformation) self.ledgerImg = QPixmap(os.path.join(self.imgDir, 'ledger.png')) def myPopUp(self, messType, messTitle, messText, defaultButton=QMessageBox.No): mess = QMessageBox(messType, messTitle, messText, defaultButton, parent=self) mess.setStandardButtons(QMessageBox.Yes | QMessageBox.No) mess.setDefaultButton(defaultButton) return mess.exec_() def myPopUp2(self, messType, messTitle, messText, singleButton=QMessageBox.Ok): mess = QMessageBox(messType, messTitle, messText, singleButton, parent=self) mess.setStandardButtons(singleButton | singleButton) return mess.exec_() @pyqtSlot() def onCheckHw(self): printDbg("Checking for HW device...") self.runInThread(self.updateHWstatus, (), self.showHWstatus) @pyqtSlot() def onCheckRpc(self): printDbg("Checking RPC server...") self.runInThread(self.updateRPCstatus, (), self.showRPCstatus) @pyqtSlot() def onCleanConsole(self): self.consoleArea.clear() @pyqtSlot() def onSaveConsole(self): timestamp = strftime('%Y-%m-%d_%H-%M-%S', gmtime(now())) options = QFileDialog.Options() options |= QFileDialog.DontUseNativeDialog fileName, _ = QFileDialog.getSaveFileName( self, "Save Logs to file", "PET4L_Logs_%s.txt" % timestamp, "All Files (*);; Text Files (*.txt)", options=options) try: if fileName: printOK("Saving logs to %s" % fileName) log_file = open(fileName, 'w+') log_text = self.consoleArea.toPlainText() log_file.write(log_text) log_file.close() except Exception as e: err_msg = "error writing Log file" printException(getCallerName(), getFunctionName(), err_msg, e.args) @pyqtSlot() def onToggleConsole(self): if self.btn_consoleToggle.text() == 'Hide': self.btn_consoleToggle.setText('Show') self.consoleArea.hide() self.previousH = self.splitter.sizes()[1] self.console.setMaximumHeight(70) else: self.console.setMinimumHeight(self.previousH) self.console.setMaximumHeight(starting_height) self.btn_consoleToggle.setText('Hide') self.consoleArea.show() def showHWstatus(self): self.updateHWleds() self.myPopUp2(QMessageBox.Information, 'PET4L - hw check', "STATUS: %s" % self.hwStatusMess, QMessageBox.Ok) def showRPCstatus(self): self.updateRPCled() self.myPopUp2(QMessageBox.Information, 'PET4L - rpc check', "STATUS: %s" % self.rpcStatusMess, QMessageBox.Ok) def updateHWleds(self): if self.hwStatus == 1: self.header.hwLed.setPixmap(self.ledHalfPurpleH_icon) elif self.hwStatus == 2: self.header.hwLed.setPixmap(self.ledPurpleH_icon) else: self.header.hwLed.setPixmap(self.ledGrayH_icon) self.header.hwLed.setToolTip(self.hwStatusMess) def updateHWstatus(self, ctrl): if self.hwdevice is None: self.hwdevice = HWdevice() device = self.hwdevice statusCode = device.getStatusCode() statusMess = device.getStatusMess(statusCode) printDbg("code: %s - mess: %s" % (statusCode, statusMess)) if statusCode != 2: try: if getattr(self.hwdevice, 'dongle', None) is not None: self.hwdevice.dongle.close() self.hwdevice.initDevice() device = self.hwdevice statusCode = device.getStatusCode() statusMess = device.getStatusMess(statusCode) except Exception as e: err_msg = "error in checkHw" printException(getCallerName(), getFunctionName(), err_msg, e.args) self.hwStatus = statusCode self.hwStatusMess = statusMess def updateLastBlockLabel(self): text = '--' if self.rpcLastBlock == 1: text = "Loading block index..." elif self.rpcLastBlock > 0 and self.rpcConnected: text = str(self.rpcLastBlock) self.header.lastBlockLabel.setText(text) def updateRPCled(self): if self.rpcConnected: self.header.rpcLed.setPixmap(self.ledPurpleH_icon) else: if self.rpcLastBlock == 1: self.header.rpcLed.setPixmap(self.ledHalfPurpleH_icon) else: self.header.rpcLed.setPixmap(self.ledGrayH_icon) self.header.rpcLed.setToolTip(self.rpcStatusMess) self.updateLastBlockLabel() def updateRPCstatus(self, ctrl): if self.rpcClient is None: try: self.rpcClient = RpcClient() except Exception as e: print(e) status, lastBlock = self.rpcClient.getStatus() statusMess = self.rpcClient.getStatusMess(status) if not status and lastBlock == 0: try: self.rpcClient = RpcClient() status, lastBlock = self.rpcClient.getStatus() statusMess = self.rpcClient.getStatusMess(status) except Exception as e: err_msg = "error in checkRpc" printException(getCallerName(), getFunctionName(), err_msg, e) elif lastBlock == 1: statusMess = "PIVX wallet is connected but still synchronizing / verifying blocks" self.rpcConnected = status self.rpcLastBlock = lastBlock self.rpcStatusMess = statusMess
class MetadataSingleDialogAlt1(MetadataSingleDialogBase): # {{{ cc_two_column = False one_line_comments_toolbar = True use_toolbutton_for_config_metadata = False on_drag_enter = pyqtSignal() def handle_drag_enter(self): self.central_widget.setCurrentIndex(1) def do_layout(self): self.central_widget.clear() self.tabs = [] self.labels = [] sto = QWidget.setTabOrder self.on_drag_enter.connect(self.handle_drag_enter) self.tabs.append(DragTrackingWidget(self, self.on_drag_enter)) self.central_widget.addTab(self.tabs[0], _("&Metadata")) self.tabs[0].l = QGridLayout() self.tabs[0].setLayout(self.tabs[0].l) self.tabs.append(QWidget(self)) self.central_widget.addTab(self.tabs[1], _("&Cover and formats")) self.tabs[1].l = QGridLayout() self.tabs[1].setLayout(self.tabs[1].l) # accept drop events so we can automatically switch to the second tab to # drop covers and formats self.tabs[0].setAcceptDrops(True) # Tab 0 tab0 = self.tabs[0] tl = QGridLayout() gb = QGroupBox(_('&Basic metadata'), self.tabs[0]) self.tabs[0].l.addWidget(gb, 0, 0, 1, 1) gb.setLayout(tl) self.button_box_layout.insertWidget(1, self.fetch_metadata_button) self.button_box_layout.insertWidget(2, self.config_metadata_button) sto(self.button_box, self.fetch_metadata_button) sto(self.fetch_metadata_button, self.config_metadata_button) sto(self.config_metadata_button, self.title) def create_row(row, widget, tab_to, button=None, icon=None, span=1): ql = BuddyLabel(widget) tl.addWidget(ql, row, 1, 1, 1) tl.addWidget(widget, row, 2, 1, 1) if button is not None: tl.addWidget(button, row, 3, span, 1) if icon is not None: button.setIcon(QIcon(I(icon))) if tab_to is not None: if button is not None: sto(widget, button) sto(button, tab_to) else: sto(widget, tab_to) tl.addWidget(self.swap_title_author_button, 0, 0, 2, 1) tl.addWidget(self.manage_authors_button, 2, 0, 1, 1) tl.addWidget(self.paste_isbn_button, 12, 0, 1, 1) tl.addWidget(self.tags_editor_button, 6, 0, 1, 1) create_row(0, self.title, self.title_sort, button=self.deduce_title_sort_button, span=2, icon='auto_author_sort.png') create_row(1, self.title_sort, self.authors) create_row(2, self.authors, self.author_sort, button=self.deduce_author_sort_button, span=2, icon='auto_author_sort.png') create_row(3, self.author_sort, self.series) create_row(4, self.series, self.series_index, button=self.clear_series_button, icon='trash.png') create_row(5, self.series_index, self.tags) create_row(6, self.tags, self.rating, button=self.clear_tags_button) create_row(7, self.rating, self.pubdate, button=self.clear_ratings_button) create_row(8, self.pubdate, self.publisher, button=self.pubdate.clear_button, icon='trash.png') create_row(9, self.publisher, self.languages, button=self.publisher.clear_button, icon='trash.png') create_row(10, self.languages, self.timestamp) create_row(11, self.timestamp, self.identifiers, button=self.timestamp.clear_button, icon='trash.png') create_row(12, self.identifiers, self.comments, button=self.clear_identifiers_button, icon='trash.png') sto(self.clear_identifiers_button, self.swap_title_author_button) sto(self.swap_title_author_button, self.manage_authors_button) sto(self.manage_authors_button, self.tags_editor_button) sto(self.tags_editor_button, self.paste_isbn_button) tl.addItem(QSpacerItem(1, 1, QSizePolicy.Fixed, QSizePolicy.Expanding), 13, 1, 1 ,1) w = getattr(self, 'custom_metadata_widgets_parent', None) if w is not None: gb = QGroupBox(_('C&ustom metadata'), tab0) gbl = QVBoxLayout() gb.setLayout(gbl) sr = QScrollArea(tab0) sr.setWidgetResizable(True) sr.setFrameStyle(QFrame.NoFrame) sr.setWidget(w) gbl.addWidget(sr) self.tabs[0].l.addWidget(gb, 0, 1, 1, 1) sto(self.identifiers, gb) w = QGroupBox(_('&Comments'), tab0) sp = QSizePolicy() sp.setVerticalStretch(10) sp.setHorizontalPolicy(QSizePolicy.Expanding) sp.setVerticalPolicy(QSizePolicy.Expanding) w.setSizePolicy(sp) l = QHBoxLayout() w.setLayout(l) l.addWidget(self.comments) tab0.l.addWidget(w, 1, 0, 1, 2) # Tab 1 tab1 = self.tabs[1] wsp = QWidget(tab1) wgl = QVBoxLayout() wsp.setLayout(wgl) # right-hand side of splitter gb = QGroupBox(_('Change cover'), tab1) l = QGridLayout() gb.setLayout(l) for i, b in enumerate(self.cover.buttons[:3]): l.addWidget(b, 0, i, 1, 1) sto(b, self.cover.buttons[i+1]) hl = QHBoxLayout() for b in self.cover.buttons[3:]: hl.addWidget(b) sto(self.cover.buttons[-2], self.cover.buttons[-1]) l.addLayout(hl, 1, 0, 1, 3) wgl.addWidget(gb) wgl.addItem(QSpacerItem(10, 10, QSizePolicy.Expanding, QSizePolicy.Expanding)) wgl.addItem(QSpacerItem(10, 10, QSizePolicy.Expanding, QSizePolicy.Expanding)) wgl.addWidget(self.formats_manager) self.splitter = QSplitter(Qt.Horizontal, tab1) tab1.l.addWidget(self.splitter) self.splitter.addWidget(self.cover) self.splitter.addWidget(wsp) self.formats_manager.formats.setMaximumWidth(10000) self.formats_manager.formats.setIconSize(QSize(64, 64))
class MainWindow(QWidget): def __init__(self, parent, masternode_list, imgDir): super(QWidget, self).__init__(parent) self.parent = parent self.imgDir = imgDir self.runInThread = ThreadFuns.runInThread ###-- Masternode list self.masternode_list = masternode_list ###-- Create clients and statuses self.hwdevice = None self.hwStatus = 0 self.hwStatusMess = "Not Connected" self.rpcClient = None self.rpcConnected = False self.rpcStatusMess = "Not Connected" self.isBlockchainSynced = False ###-- Load icons & images self.loadIcons() ###-- Create main layout self.layout = QVBoxLayout() self.header = GuiHeader(self) self.initConsole() self.layout.addWidget(self.header) ###-- Create RPC Whatchdog self.rpc_watchdogThread = QThread() self.myRpcWd = RpcWatchdog(self) self.myRpcWd.moveToThread(self.rpc_watchdogThread) self.rpc_watchdogThread.started.connect(self.myRpcWd.run) ###-- Create Queues and redirect stdout and stderr self.queue = Queue() self.queue2 = Queue() sys.stdout = WriteStream(self.queue) sys.stderr = WriteStream(self.queue2) ###-- Init last logs logFile = open(log_File, 'w+') timestamp = strftime('%Y-%m-%d %H:%M:%S', gmtime(now())) log_line = '<b style="color: blue">{}</b><br>'.format('STARTING QMT at ' + timestamp) logFile.write(log_line) logFile.close() ###-- Create the thread to update console log for stdout self.consoleLogThread = QThread() self.myWSReceiver = WriteStreamReceiver(self.queue) self.myWSReceiver.mysignal.connect(self.append_to_console) self.myWSReceiver.moveToThread(self.consoleLogThread) self.consoleLogThread.started.connect(self.myWSReceiver.run) self.consoleLogThread.start() printDbg("Console Log thread started") ###-- Create the thread to update console log for stderr self.consoleLogThread2 = QThread() self.myWSReceiver2 = WriteStreamReceiver(self.queue2) self.myWSReceiver2.mysignal.connect(self.append_to_console) self.myWSReceiver2.moveToThread(self.consoleLogThread2) self.consoleLogThread2.started.connect(self.myWSReceiver2.run) self.consoleLogThread2.start() printDbg("Console Log thread 2 started") ###-- Initialize tabs self.tabs = QTabWidget() self.t_main = TabMain(self) self.t_mnconf = TabMNConf(self) self.t_rewards = TabRewards(self) self.t_governance = TabGovernance(self) self.t_add_torrent = TabAddTorrent(self) ###-- Add tabs self.tabs.addTab(self.tabGovernance, "Search Torrents") self.tabs.addTab(self.tabAddTorrent, "Add Torrents") # self.tabs.addTab(self.tabMain, "Masternode Control") # self.tabs.addTab(self.tabMNConf, "MN Configuration") # We will put these back later, just with RPC instead of messy key handling which we don't need or want anyway ! # self.tabs.addTab(self.tabRewards, "Transfer Rewards") ###-- Connect change action self.tabs.currentChanged.connect(lambda: self.onTabChange()) ###-- Draw Tabs self.splitter = QSplitter(Qt.Vertical) ###-- Add tabs and console to Layout self.splitter.addWidget(self.tabs) self.splitter.addWidget(self.console) self.splitter.setStretchFactor(0, 0) self.splitter.setStretchFactor(1, 1) self.splitter.setSizes(self.parent.cache.get("splitter_sizes")) self.layout.addWidget(self.splitter) ###-- Set Layout self.setLayout(self.layout) ###-- Let's go self.mnode_to_change = None printOK("Hello! Welcome to " + parent.title) ###-- Hide console if it was previously hidden if self.parent.cache.get("console_hidden"): self.onToggleConsole() ##-- Check version self.onCheckVersion() ##-- init Api Client self.apiClient = ApiClient() @pyqtSlot(str) def append_to_console(self, text): self.consoleArea.moveCursor(QTextCursor.End) self.consoleArea.insertHtml(text) def initConsole(self): self.console = QGroupBox() self.console.setTitle("Console Log") layout = QVBoxLayout() self.btn_consoleToggle = QPushButton('Hide') self.btn_consoleToggle.setToolTip('Show/Hide console') self.btn_consoleToggle.clicked.connect(lambda: self.onToggleConsole()) consoleHeader = QHBoxLayout() consoleHeader.addWidget(self.btn_consoleToggle) self.consoleSaveButton = QPushButton('Save') self.consoleSaveButton.clicked.connect(lambda: self.onSaveConsole()) consoleHeader.addWidget(self.consoleSaveButton) self.btn_consoleClean = QPushButton('Clean') self.btn_consoleClean.setToolTip('Clean console log area') self.btn_consoleClean.clicked.connect(lambda: self.onCleanConsole()) consoleHeader.addWidget(self.btn_consoleClean) consoleHeader.addStretch(1) self.versionLabel = QLabel("--") self.versionLabel.setOpenExternalLinks(True) consoleHeader.addWidget(self.versionLabel) self.btn_checkVersion = QPushButton("Check QMT version") self.btn_checkVersion.setToolTip("Check latest stable release of QMT") self.btn_checkVersion.clicked.connect(lambda: self.onCheckVersion()) consoleHeader.addWidget(self.btn_checkVersion) layout.addLayout(consoleHeader) self.consoleArea = QTextEdit() almostBlack = QColor(40, 40, 40) palette = QPalette() palette.setColor(QPalette.Base, almostBlack) green = QColor(0, 255, 0) palette.setColor(QPalette.Text, green) self.consoleArea.setPalette(palette) layout.addWidget(self.consoleArea) self.console.setLayout(layout) def isMasternodeInList(self, mn_alias): return (mn_alias in [x['name'] for x in self.masternode_list]) def loadIcons(self): # Load Icons self.ledPurpleH_icon = QPixmap(os.path.join(self.imgDir, 'icon_purpleLedH.png')).scaledToHeight(17, Qt.SmoothTransformation) self.ledGrayH_icon = QPixmap(os.path.join(self.imgDir, 'icon_grayLedH.png')).scaledToHeight(17, Qt.SmoothTransformation) self.ledHalfPurpleH_icon = QPixmap(os.path.join(self.imgDir, 'icon_halfPurpleLedH.png')).scaledToHeight(17, Qt.SmoothTransformation) self.ledRedV_icon = QPixmap(os.path.join(self.imgDir, 'icon_redLedV.png')).scaledToHeight(17, Qt.SmoothTransformation) self.ledGrayV_icon = QPixmap(os.path.join(self.imgDir, 'icon_grayLedV.png')).scaledToHeight(17, Qt.SmoothTransformation) self.ledGreenV_icon = QPixmap(os.path.join(self.imgDir, 'icon_greenLedV.png')).scaledToHeight(17, Qt.SmoothTransformation) def loadMNConf(self, fileName): hot_masternodes = loadMNConfFile(fileName) if hot_masternodes == None: messText = "Unable to load data from file '%s'" % fileName self.myPopUp2(QMessageBox.Warning, "QMT - warning", messText) else: # Append new masternodes to list new_masternodes = [] skip_masternodes = [] for x in hot_masternodes: if not self.isMasternodeInList(x['name']): self.masternode_list.append(x) new_masternodes.append(x) else: skip_masternodes.append(x) # Show new list for new_masternode in new_masternodes: name = new_masternode['name'] self.tabMain.insert_mn_list(name, new_masternode['ip'], new_masternode['port'], None, isHardware=False) self.tabMain.btn_remove[name].clicked.connect(lambda: self.t_main.onRemoveMN()) # print number of nodes added new_nodes = len(new_masternodes) final_message = "" if new_nodes == 0: final_message = "No External Masternode " elif new_nodes == 1: final_message = "1 External Masternode " else: final_message = "%d External Masternodes " % new_nodes final_message += "added to the list. " if new_nodes > 0: final_message += str([x['name'] for x in new_masternodes]) + ". " if len(skip_masternodes) > 0: final_message += "Following entries skipped due to duplicate names:" final_message += str([x['name'] for x in skip_masternodes]) + ". " printDbg(final_message) if new_nodes > 0: # update files printDbg("saving MN configuration file") writeToFile(self.masternode_list, masternodes_File) printDbg("saved") # Clear voting masternodes configuration and update cache self.t_governance.clear() def myPopUp(self, messType, messTitle, messText, defaultButton=QMessageBox.No): mess = QMessageBox(messType, messTitle, messText, defaultButton, parent=self) mess.setStandardButtons(QMessageBox.Yes | QMessageBox.No) mess.setDefaultButton(defaultButton) return mess.exec_() def myPopUp2(self, messType, messTitle, messText, singleButton=QMessageBox.Ok): mess = QMessageBox(messType, messTitle, messText, singleButton, parent=self) mess.setStandardButtons(singleButton | singleButton) return mess.exec_() @pyqtSlot() def onCheckHw(self): printDbg("Checking for HW device...") self.updateHWstatus(None) self.showHWstatus() @pyqtSlot() def onCheckRpc(self): printDbg("Checking RPC server...") self.runInThread(self.updateRPCstatus, (), self.showRPCstatus) @pyqtSlot() def onCheckVersion(self): printDbg("Checking QMT version...") self.versionLabel.setText("--") self.runInThread(self.checkVersion, (), self.updateVersion) def checkVersion(self, ctrl): local_version = self.parent.version['number'].split('.') remote_version = getRemoteQMTversion().split('.') if (remote_version[0] > local_version[0]) or \ (remote_version[0] == local_version[0] and remote_version[1] > local_version[1]) or \ (remote_version[0] == local_version[0] and remote_version[1] == local_version[1] and remote_version[2] > local_version[2]): self.versionMess = '<b style="color:red">New Version Available:</b> %s.%s.%s ' % ( remote_version[0], remote_version[1], remote_version[2]) self.versionMess += '(<a href="https://github.com/project-qmc/QMT/releases/">download</a>)' else: self.versionMess = "You have the latest version of QMT" def updateVersion(self): if self.versionMess is not None: self.versionLabel.setText(self.versionMess) @pyqtSlot() def onCleanConsole(self): self.consoleArea.clear() @pyqtSlot() def onSaveConsole(self): timestamp = strftime('%Y-%m-%d_%H-%M-%S', gmtime(now())) options = QFileDialog.Options() options |= QFileDialog.DontUseNativeDialog fileName, _ = QFileDialog.getSaveFileName(self, "Save Logs to file", "QMT_Logs_%s.txt" % timestamp, "All Files (*);; Text Files (*.txt)", options=options) try: if fileName: printOK("Saving logs to %s" % fileName) log_file = open(fileName, 'w+') log_text = self.consoleArea.toPlainText() log_file.write(log_text) log_file.close() except Exception as e: err_msg = "error writing Log file" printException(getCallerName(), getFunctionName(), err_msg, e.args) @pyqtSlot() def onTabChange(self): # reload (and re-sort)masternode list in tabs if self.tabs.currentWidget() == self.tabRewards: # reload last used address self.tabRewards.destinationLine.setText(self.parent.cache.get("lastAddress")) # get new order mnOrder = {} mnList = self.tabMain.myList for i in range(mnList.count()): mnName = mnList.itemWidget(mnList.item(i)).alias mnOrder[mnName] = i self.parent.cache['mnList_order'] = mnOrder # Sort masternode list (by alias if no previous order set) if self.parent.cache.get('mnList_order') != {}: self.masternode_list.sort(key=self.parent.extract_order) self.t_rewards.loadMnSelect() self.t_rewards.selectedRewards = None # reload torrent and voting masternode list if self.tabs.currentWidget() == self.tabGovernance: self.t_governance.onRefreshTorrents() self.t_governance.updateSelectedMNlabel() @pyqtSlot() def onToggleConsole(self): if self.btn_consoleToggle.text() == 'Hide': self.btn_consoleToggle.setText('Show') self.consoleArea.hide() self.console.setMinimumHeight(70) self.console.setMaximumHeight(70) else: self.console.setMinimumHeight(70) self.console.setMaximumHeight(starting_height) self.btn_consoleToggle.setText('Hide') self.consoleArea.show() def showHWstatus(self): self.updateHWleds() self.myPopUp2(QMessageBox.Information, 'QMT - hw check', "%s" % self.hwStatusMess, QMessageBox.Ok) def showRPCstatus(self): self.updateRPCled() self.myPopUp2(QMessageBox.Information, 'QMT - rpc check', "%s" % self.rpcStatusMess, QMessageBox.Ok) def updateHWleds(self): if self.hwStatus == 1: self.header.hwLed.setPixmap(self.ledHalfPurpleH_icon) elif self.hwStatus == 2: self.header.hwLed.setPixmap(self.ledPurpleH_icon) else: self.header.hwLed.setPixmap(self.ledGrayH_icon) self.header.hwLed.setToolTip(self.hwStatusMess) def updateHWstatus(self, ctrl): if self.hwdevice is not None: if hasattr(self.hwdevice, 'dongle'): self.hwdevice.dongle.close() self.hwdevice = HWdevice() statusCode, statusMess = self.hwdevice.getStatus() printDbg("mess: %s" % statusMess) if statusCode != 2: # If is not connected try again try: if hasattr(self.hwdevice, 'dongle'): self.hwdevice.dongle.close() self.hwdevice = HWdevice() self.hwdevice.initDevice() statusCode, statusMess = self.hwdevice.getStatus() except Exception as e: err_msg = "error in checkHw" printException(getCallerName(), getFunctionName(), err_msg, e.args) self.hwStatus = statusCode self.hwStatusMess = statusMess # if all is good connect the signals if statusCode == 2: self.hwdevice.sigTxdone.connect(self.t_rewards.FinishSend) self.hwdevice.sigTxabort.connect(self.t_rewards.onCancel) self.hwdevice.tx_progress.connect(self.t_rewards.updateProgressPercent) def updateLastBlockLabel(self): text = '--' if self.rpcLastBlock == 1: text = "Loading block index..." elif self.rpcLastBlock > 0 and self.rpcConnected: text = str(self.rpcLastBlock) text += " (" if not self.isBlockchainSynced: text += "Synchronizing" else: text += "Synced" text += ")" self.header.lastBlockLabel.setText(text) def updateRPCled(self): if self.rpcConnected: self.header.rpcLed.setPixmap(self.ledPurpleH_icon) else: if self.rpcLastBlock == 1: self.header.rpcLed.setPixmap(self.ledHalfPurpleH_icon) else: self.header.rpcLed.setPixmap(self.ledGrayH_icon) self.header.rpcLed.setToolTip(self.rpcStatusMess) self.updateLastBlockLabel() def updateRPCstatus(self, ctrl): if self.rpcClient is None: self.rpcClient = RpcClient() status, statusMess, lastBlock = self.rpcClient.getStatus() self.rpcConnected = status self.rpcLastBlock = lastBlock self.rpcStatusMess = statusMess self.isBlockchainSynced = self.rpcClient.isBlockchainSynced() # If is not connected try again if not status: self.rpcClient = RpcClient()
def __init__(self): super().__init__() self.setWindowTitle(WINDOW_TITLE) from pathlib import Path file_name = str(Path(__file__).resolve().parent / 'favicon.ico') icon = QIcon(file_name) self.setWindowIcon(icon) self.tray = QSystemTrayIcon(icon) self.tray.setToolTip(self.windowTitle()) self.tray.activated.connect(self._on_tray_activated) self.tray.show() self.logged_dict = dict() self.pb_refresh = QPushButton('REFRESH') self.pb_refresh.clicked.connect(self.refresh) self.cb_show_log = QCheckBox() self.cb_show_log.setChecked(True) self.log = QPlainTextEdit() self.log.setReadOnly(True) self.log.setWordWrapMode(QTextOption.NoWrap) log_font = self.log.font() log_font.setFamily('Courier New') self.log.setFont(log_font) self.cb_show_log.clicked.connect(self.log.setVisible) self.log.setVisible(self.cb_show_log.isChecked()) header_labels = ['DATE', 'TOTAL LOGGED TIME'] self.table_logged = QTableWidget() self.table_logged.setEditTriggers(QTableWidget.NoEditTriggers) self.table_logged.setSelectionBehavior(QTableWidget.SelectRows) self.table_logged.setSelectionMode(QTableWidget.SingleSelection) self.table_logged.setColumnCount(len(header_labels)) self.table_logged.setHorizontalHeaderLabels(header_labels) self.table_logged.horizontalHeader().setStretchLastSection(True) self.table_logged.itemClicked.connect( self._on_table_logged_item_clicked) header_labels = ['TIME', 'LOGGED', 'JIRA'] self.table_logged_info = QTableWidget() self.table_logged_info.setEditTriggers(QTableWidget.NoEditTriggers) self.table_logged_info.setSelectionBehavior(QTableWidget.SelectRows) self.table_logged_info.setSelectionMode(QTableWidget.SingleSelection) self.table_logged_info.setColumnCount(len(header_labels)) self.table_logged_info.setHorizontalHeaderLabels(header_labels) self.table_logged_info.horizontalHeader().setSectionResizeMode( 1, QHeaderView.ResizeToContents) self.table_logged_info.horizontalHeader().setStretchLastSection(True) self.table_logged_info.itemDoubleClicked.connect( self._on_table_logged_info_item_double_clicked) main_layout = QVBoxLayout() central_widget = QWidget() central_widget.setLayout(main_layout) self.setCentralWidget(central_widget) self.pb_refresh.setSizePolicy( QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Preferred)) h_layout = QHBoxLayout() h_layout.addWidget(self.pb_refresh) h_layout.addWidget(self.cb_show_log) layout_table_widget = QVBoxLayout() layout_table_widget.setContentsMargins(0, 0, 0, 0) layout_table_widget.addWidget(self.table_logged) layout_table_widget.addWidget(self.table_logged_info) table_widget = QWidget() table_widget.setLayout(layout_table_widget) splitter = QSplitter(Qt.Horizontal) splitter.addWidget(table_widget) splitter.addWidget(self.log) main_layout.addLayout(h_layout) main_layout.addWidget(splitter)
class CheckLibraryDialog(QDialog): def __init__(self, parent, db): QDialog.__init__(self, parent) self.db = db self.setWindowTitle(_('Check Library -- Problems Found')) self.setWindowIcon(QIcon(I('debug.png'))) self._tl = QHBoxLayout() self.setLayout(self._tl) self.splitter = QSplitter(self) self.left = QWidget(self) self.splitter.addWidget(self.left) self.helpw = QTextEdit(self) self.splitter.addWidget(self.helpw) self._tl.addWidget(self.splitter) self._layout = QVBoxLayout() self.left.setLayout(self._layout) self.helpw.setReadOnly(True) self.helpw.setText(_('''\ <h1>Help</h1> <p>calibre stores the list of your books and their metadata in a database. The actual book files and covers are stored as normal files in the calibre library folder. The database contains a list of the files and covers belonging to each book entry. This tool checks that the actual files in the library folder on your computer match the information in the database.</p> <p>The result of each type of check is shown to the left. The various checks are: </p> <ul> <li><b>Invalid titles</b>: These are files and folders appearing in the library where books titles should, but that do not have the correct form to be a book title.</li> <li><b>Extra titles</b>: These are extra files in your calibre library that appear to be correctly-formed titles, but have no corresponding entries in the database</li> <li><b>Invalid authors</b>: These are files appearing in the library where only author folders should be.</li> <li><b>Extra authors</b>: These are folders in the calibre library that appear to be authors but that do not have entries in the database</li> <li><b>Missing book formats</b>: These are book formats that are in the database but have no corresponding format file in the book's folder. <li><b>Extra book formats</b>: These are book format files found in the book's folder but not in the database. <li><b>Unknown files in books</b>: These are extra files in the folder of each book that do not correspond to a known format or cover file.</li> <li><b>Missing cover files</b>: These represent books that are marked in the database as having covers but the actual cover files are missing.</li> <li><b>Cover files not in database</b>: These are books that have cover files but are marked as not having covers in the database.</li> <li><b>Folder raising exception</b>: These represent folders in the calibre library that could not be processed/understood by this tool.</li> </ul> <p>There are two kinds of automatic fixes possible: <i>Delete marked</i> and <i>Fix marked</i>.</p> <p><i>Delete marked</i> is used to remove extra files/folders/covers that have no entries in the database. Check the box next to the item you want to delete. Use with caution.</p> <p><i>Fix marked</i> is applicable only to covers and missing formats (the three lines marked 'fixable'). In the case of missing cover files, checking the fixable box and pushing this button will tell calibre that there is no cover for all of the books listed. Use this option if you are not going to restore the covers from a backup. In the case of extra cover files, checking the fixable box and pushing this button will tell calibre that the cover files it found are correct for all the books listed. Use this when you are not going to delete the file(s). In the case of missing formats, checking the fixable box and pushing this button will tell calibre that the formats are really gone. Use this if you are not going to restore the formats from a backup.</p> ''')) self.log = QTreeWidget(self) self.log.itemChanged.connect(self.item_changed) self.log.itemExpanded.connect(self.item_expanded_or_collapsed) self.log.itemCollapsed.connect(self.item_expanded_or_collapsed) self._layout.addWidget(self.log) self.check_button = QPushButton(_('&Run the check again')) self.check_button.setDefault(False) self.check_button.clicked.connect(self.run_the_check) self.copy_button = QPushButton(_('Copy &to clipboard')) self.copy_button.setDefault(False) self.copy_button.clicked.connect(self.copy_to_clipboard) self.ok_button = QPushButton(_('&Done')) self.ok_button.setDefault(True) self.ok_button.clicked.connect(self.accept) self.mark_delete_button = QPushButton(_('Mark &all for delete')) self.mark_delete_button.setToolTip(_('Mark all deletable subitems')) self.mark_delete_button.setDefault(False) self.mark_delete_button.clicked.connect(self.mark_for_delete) self.delete_button = QPushButton(_('Delete &marked')) self.delete_button.setToolTip(_('Delete marked files (checked subitems)')) self.delete_button.setDefault(False) self.delete_button.clicked.connect(self.delete_marked) self.mark_fix_button = QPushButton(_('Mar&k all for fix')) self.mark_fix_button.setToolTip(_('Mark all fixable items')) self.mark_fix_button.setDefault(False) self.mark_fix_button.clicked.connect(self.mark_for_fix) self.fix_button = QPushButton(_('&Fix marked')) self.fix_button.setDefault(False) self.fix_button.setEnabled(False) self.fix_button.setToolTip(_('Fix marked sections (checked fixable items)')) self.fix_button.clicked.connect(self.fix_items) self.bbox = QGridLayout() self.bbox.addWidget(self.check_button, 0, 0) self.bbox.addWidget(self.copy_button, 0, 1) self.bbox.addWidget(self.ok_button, 0, 2) self.bbox.addWidget(self.mark_delete_button, 1, 0) self.bbox.addWidget(self.delete_button, 1, 1) self.bbox.addWidget(self.mark_fix_button, 2, 0) self.bbox.addWidget(self.fix_button, 2, 1) h = QHBoxLayout() ln = QLabel(_('Names to ignore:')) h.addWidget(ln) self.name_ignores = QLineEdit() self.name_ignores.setText(db.prefs.get('check_library_ignore_names', '')) self.name_ignores.setToolTip( _('Enter comma-separated standard file name wildcards, such as synctoy*.dat')) ln.setBuddy(self.name_ignores) h.addWidget(self.name_ignores) le = QLabel(_('Extensions to ignore')) h.addWidget(le) self.ext_ignores = QLineEdit() self.ext_ignores.setText(db.prefs.get('check_library_ignore_extensions', '')) self.ext_ignores.setToolTip( _('Enter comma-separated extensions without a leading dot. Used only in book folders')) le.setBuddy(self.ext_ignores) h.addWidget(self.ext_ignores) self._layout.addLayout(h) self._layout.addLayout(self.bbox) self.resize(950, 500) def do_exec(self): self.run_the_check() probs = 0 for c in self.problem_count: probs += self.problem_count[c] if probs == 0: return False self.exec_() return True def accept(self): self.db.new_api.set_pref('check_library_ignore_extensions', unicode(self.ext_ignores.text())) self.db.new_api.set_pref('check_library_ignore_names', unicode(self.name_ignores.text())) QDialog.accept(self) def box_to_list(self, txt): return [f.strip() for f in txt.split(',') if f.strip()] def run_the_check(self): checker = CheckLibrary(self.db.library_path, self.db) checker.scan_library(self.box_to_list(unicode(self.name_ignores.text())), self.box_to_list(unicode(self.ext_ignores.text()))) plaintext = [] def builder(tree, checker, check): attr, h, checkable, fixable = check list = getattr(checker, attr, None) if list is None: self.problem_count[attr] = 0 return else: self.problem_count[attr] = len(list) tl = Item() tl.setText(0, h) if fixable and list: tl.setText(1, _('(fixable)')) tl.setFlags(Qt.ItemIsEnabled | Qt.ItemIsUserCheckable) tl.setCheckState(1, False) else: tl.setFlags(Qt.ItemIsEnabled) self.top_level_items[attr] = tl for problem in list: it = Item() if checkable: it.setFlags(Qt.ItemIsEnabled | Qt.ItemIsUserCheckable) it.setCheckState(1, False) else: it.setFlags(Qt.ItemIsEnabled) it.setText(0, problem[0]) it.setData(0, Qt.UserRole, problem[2]) it.setText(1, problem[1]) tl.addChild(it) self.all_items.append(it) plaintext.append(','.join([h, problem[0], problem[1]])) tree.addTopLevelItem(tl) t = self.log t.clear() t.setColumnCount(2) t.setHeaderLabels([_('Name'), _('Path from library')]) self.all_items = [] self.top_level_items = {} self.problem_count = {} for check in CHECKS: builder(t, checker, check) t.resizeColumnToContents(0) t.resizeColumnToContents(1) self.delete_button.setEnabled(False) self.fix_button.setEnabled(False) self.text_results = '\n'.join(plaintext) def item_expanded_or_collapsed(self, item): self.log.resizeColumnToContents(0) self.log.resizeColumnToContents(1) def item_changed(self, item, column): self.fix_button.setEnabled(False) for it in self.top_level_items.values(): if it.checkState(1): self.fix_button.setEnabled(True) self.delete_button.setEnabled(False) for it in self.all_items: if it.checkState(1): self.delete_button.setEnabled(True) return def mark_for_fix(self): for it in self.top_level_items.values(): if it.flags() & Qt.ItemIsUserCheckable: it.setCheckState(1, Qt.Checked) def mark_for_delete(self): for it in self.all_items: if it.flags() & Qt.ItemIsUserCheckable: it.setCheckState(1, Qt.Checked) def delete_marked(self): if not confirm('<p>'+_('The marked files and folders will be ' '<b>permanently deleted</b>. Are you sure?') +'</p>', 'check_library_editor_delete', self): return # Sort the paths in reverse length order so that we can be sure that # if an item is in another item, the sub-item will be deleted first. items = sorted(self.all_items, key=lambda x: len(x.text(1)), reverse=True) for it in items: if it.checkState(1): try: p = os.path.join(self.db.library_path ,unicode(it.text(1))) if os.path.isdir(p): delete_tree(p) else: delete_file(p) except: prints('failed to delete', os.path.join(self.db.library_path, unicode(it.text(1)))) self.run_the_check() def fix_missing_formats(self): tl = self.top_level_items['missing_formats'] child_count = tl.childCount() for i in range(0, child_count): item = tl.child(i) id = int(item.data(0, Qt.UserRole)) all = self.db.formats(id, index_is_id=True, verify_formats=False) all = set([f.strip() for f in all.split(',')]) if all else set() valid = self.db.formats(id, index_is_id=True, verify_formats=True) valid = set([f.strip() for f in valid.split(',')]) if valid else set() for fmt in all-valid: self.db.remove_format(id, fmt, index_is_id=True, db_only=True) def fix_missing_covers(self): tl = self.top_level_items['missing_covers'] child_count = tl.childCount() for i in range(0, child_count): item = tl.child(i) id = int(item.data(0, Qt.UserRole)) self.db.set_has_cover(id, False) def fix_extra_covers(self): tl = self.top_level_items['extra_covers'] child_count = tl.childCount() for i in range(0, child_count): item = tl.child(i) id = int(item.data(0, Qt.UserRole)) self.db.set_has_cover(id, True) def fix_items(self): for check in CHECKS: attr = check[0] fixable = check[3] tl = self.top_level_items[attr] if fixable and tl.checkState(1): func = getattr(self, 'fix_' + attr, None) if func is not None and callable(func): func() self.run_the_check() def copy_to_clipboard(self): QApplication.clipboard().setText(self.text_results)
class BookInfo(QDialog): closed = pyqtSignal(object) def __init__(self, parent, view, row, link_delegate): QDialog.__init__(self, parent) self.normal_brush = QBrush(Qt.white) self.marked_brush = QBrush(Qt.lightGray) self.marked = None self.gui = parent self.splitter = QSplitter(self) self._l = l = QVBoxLayout(self) self.setLayout(l) l.addWidget(self.splitter) self.cover = CoverView(self) self.cover.resizeEvent = self.cover_view_resized self.cover.cover_changed.connect(self.cover_changed) self.cover_pixmap = None self.cover.sizeHint = self.details_size_hint self.splitter.addWidget(self.cover) self.details = QWebView(self) self.details.sizeHint = self.details_size_hint self.details.page().setLinkDelegationPolicy( self.details.page().DelegateAllLinks) self.details.linkClicked.connect(self.link_clicked) s = self.details.page().settings() s.setAttribute(s.JavascriptEnabled, False) self.css = css() self.link_delegate = link_delegate self.details.setAttribute(Qt.WA_OpaquePaintEvent, False) palette = self.details.palette() self.details.setAcceptDrops(False) palette.setBrush(QPalette.Base, Qt.transparent) self.details.page().setPalette(palette) self.c = QWidget(self) self.c.l = l2 = QGridLayout(self.c) self.c.setLayout(l2) l2.addWidget(self.details, 0, 0, 1, -1) self.splitter.addWidget(self.c) self.fit_cover = QCheckBox(_('Fit &cover within view'), self) self.fit_cover.setChecked( gprefs.get('book_info_dialog_fit_cover', True)) l2.addWidget(self.fit_cover, l2.rowCount(), 0, 1, -1) self.previous_button = QPushButton(QIcon(I('previous.png')), _('&Previous'), self) self.previous_button.clicked.connect(self.previous) l2.addWidget(self.previous_button, l2.rowCount(), 0) self.next_button = QPushButton(QIcon(I('next.png')), _('&Next'), self) self.next_button.clicked.connect(self.next) l2.addWidget(self.next_button, l2.rowCount() - 1, 1) self.view = view self.current_row = None self.refresh(row) self.view.model().new_bookdisplay_data.connect(self.slave) self.fit_cover.stateChanged.connect(self.toggle_cover_fit) self.ns = QShortcut(QKeySequence('Alt+Right'), self) self.ns.activated.connect(self.next) self.ps = QShortcut(QKeySequence('Alt+Left'), self) self.ps.activated.connect(self.previous) self.next_button.setToolTip( _('Next [%s]') % unicode(self.ns.key().toString(QKeySequence.NativeText))) self.previous_button.setToolTip( _('Previous [%s]') % unicode(self.ps.key().toString(QKeySequence.NativeText))) geom = QCoreApplication.instance().desktop().availableGeometry(self) screen_height = geom.height() - 100 screen_width = geom.width() - 100 self.resize(max(int(screen_width / 2), 700), screen_height) saved_layout = gprefs.get('book_info_dialog_layout', None) if saved_layout is not None: try: self.restoreGeometry(saved_layout[0]) self.splitter.restoreState(saved_layout[1]) except Exception: pass def link_clicked(self, qurl): link = unicode(qurl.toString(QUrl.None)) self.link_delegate(link) def done(self, r): saved_layout = (bytearray(self.saveGeometry()), bytearray(self.splitter.saveState())) gprefs.set('book_info_dialog_layout', saved_layout) ret = QDialog.done(self, r) self.view.model().new_bookdisplay_data.disconnect(self.slave) self.view = self.link_delegate = self.gui = None self.closed.emit(self) return ret def cover_changed(self, data): if self.current_row is not None: id_ = self.view.model().id(self.current_row) self.view.model().db.set_cover(id_, data) if self.gui.cover_flow: self.gui.cover_flow.dataChanged() ci = self.view.currentIndex() if ci.isValid(): self.view.model().current_changed(ci, ci) def details_size_hint(self): return QSize(350, 550) def toggle_cover_fit(self, state): gprefs.set('book_info_dialog_fit_cover', self.fit_cover.isChecked()) self.resize_cover() def cover_view_resized(self, event): QTimer.singleShot(1, self.resize_cover) def slave(self, mi): self.refresh(mi.row_number, mi) def move(self, delta=1): idx = self.view.currentIndex() if idx.isValid(): m = self.view.model() ni = m.index(idx.row() + delta, idx.column()) if ni.isValid(): if self.view.isVisible(): self.view.scrollTo(ni) self.view.setCurrentIndex(ni) def next(self): self.move() def previous(self): self.move(-1) def resize_cover(self): if self.cover_pixmap is None: return pixmap = self.cover_pixmap if self.fit_cover.isChecked(): scaled, new_width, new_height = fit_image( pixmap.width(), pixmap.height(), self.cover.size().width() - 10, self.cover.size().height() - 10) if scaled: pixmap = pixmap.scaled(new_width, new_height, Qt.KeepAspectRatio, Qt.SmoothTransformation) self.cover.set_pixmap(pixmap) self.update_cover_tooltip() def update_cover_tooltip(self): tt = '' if self.marked: tt = _('This book is marked') if self.marked in { True, 'true' } else _('This book is marked as: %s') % self.marked tt += '\n\n' if self.cover_pixmap is not None: sz = self.cover_pixmap.size() tt += _('Cover size: %(width)d x %(height)d') % dict( width=sz.width(), height=sz.height()) self.cover.setToolTip(tt) def refresh(self, row, mi=None): if isinstance(row, QModelIndex): row = row.row() if row == self.current_row and mi is None: return mi = self.view.model().get_book_display_info(row) if mi is None else mi if mi is None: # Indicates books was deleted from library, or row numbers have # changed return self.previous_button.setEnabled(False if row == 0 else True) self.next_button.setEnabled(False if row == self.view.model().rowCount(QModelIndex()) - 1 else True) self.current_row = row self.setWindowTitle(mi.title) self.cover_pixmap = QPixmap.fromImage(mi.cover_data[1]) self.resize_cover() html = render_html(mi, self.css, True, self, all_fields=True) self.details.setHtml(html) self.marked = mi.marked self.cover.setBackgroundBrush( self.marked_brush if mi.marked else self.normal_brush) self.update_cover_tooltip()
class BookInfo(QDialog): closed = pyqtSignal(object) def __init__(self, parent, view, row, link_delegate): QDialog.__init__(self, parent) self.normal_brush = QBrush(Qt.white) self.marked_brush = QBrush(Qt.lightGray) self.marked = None self.gui = parent self.splitter = QSplitter(self) self._l = l = QVBoxLayout(self) self.setLayout(l) l.addWidget(self.splitter) self.cover = CoverView(self, show_size=gprefs['bd_overlay_cover_size']) self.cover.resizeEvent = self.cover_view_resized self.cover.cover_changed.connect(self.cover_changed) self.cover_pixmap = None self.cover.sizeHint = self.details_size_hint self.splitter.addWidget(self.cover) self.details = Details(parent.book_details.book_info, self) self.details.anchor_clicked.connect(self.on_link_clicked) self.link_delegate = link_delegate self.details.setAttribute(Qt.WA_OpaquePaintEvent, False) palette = self.details.palette() self.details.setAcceptDrops(False) palette.setBrush(QPalette.Base, Qt.transparent) self.details.setPalette(palette) self.c = QWidget(self) self.c.l = l2 = QGridLayout(self.c) l2.setContentsMargins(0, 0, 0, 0) self.c.setLayout(l2) l2.addWidget(self.details, 0, 0, 1, -1) self.splitter.addWidget(self.c) self.fit_cover = QCheckBox(_('Fit &cover within view'), self) self.fit_cover.setChecked( gprefs.get('book_info_dialog_fit_cover', True)) self.hl = hl = QHBoxLayout() hl.setContentsMargins(0, 0, 0, 0) l2.addLayout(hl, l2.rowCount(), 0, 1, -1) hl.addWidget(self.fit_cover), hl.addStretch() self.clabel = QLabel( '<div style="text-align: right"><a href="calibre:conf" title="{}" style="text-decoration: none">{}</a>' .format(_('Configure this view'), _('Configure'))) self.clabel.linkActivated.connect(self.configure) hl.addWidget(self.clabel) self.previous_button = QPushButton(QIcon(I('previous.png')), _('&Previous'), self) self.previous_button.clicked.connect(self.previous) l2.addWidget(self.previous_button, l2.rowCount(), 0) self.next_button = QPushButton(QIcon(I('next.png')), _('&Next'), self) self.next_button.clicked.connect(self.next) l2.addWidget(self.next_button, l2.rowCount() - 1, 1) self.view = view self.current_row = None self.refresh(row) self.view.model().new_bookdisplay_data.connect(self.slave) self.fit_cover.stateChanged.connect(self.toggle_cover_fit) self.ns = QShortcut(QKeySequence('Alt+Right'), self) self.ns.activated.connect(self.next) self.ps = QShortcut(QKeySequence('Alt+Left'), self) self.ps.activated.connect(self.previous) self.next_button.setToolTip( _('Next [%s]') % unicode_type(self.ns.key().toString(QKeySequence.NativeText))) self.previous_button.setToolTip( _('Previous [%s]') % unicode_type(self.ps.key().toString(QKeySequence.NativeText))) geom = QCoreApplication.instance().desktop().availableGeometry(self) screen_height = geom.height() - 100 screen_width = geom.width() - 100 self.resize(max(int(screen_width / 2), 700), screen_height) saved_layout = gprefs.get('book_info_dialog_layout', None) if saved_layout is not None: try: self.restoreGeometry(saved_layout[0]) self.splitter.restoreState(saved_layout[1]) except Exception: pass def configure(self): d = Configure(get_gui().current_db, self) if d.exec_() == d.Accepted: if self.current_row is not None: mi = self.view.model().get_book_display_info(self.current_row) if mi is not None: self.refresh(self.current_row, mi=mi) def on_link_clicked(self, qurl): link = unicode_type(qurl.toString(NO_URL_FORMATTING)) self.link_delegate(link) def done(self, r): saved_layout = (bytearray(self.saveGeometry()), bytearray(self.splitter.saveState())) gprefs.set('book_info_dialog_layout', saved_layout) ret = QDialog.done(self, r) self.view.model().new_bookdisplay_data.disconnect(self.slave) self.view = self.link_delegate = self.gui = None self.closed.emit(self) return ret def cover_changed(self, data): if self.current_row is not None: id_ = self.view.model().id(self.current_row) self.view.model().db.set_cover(id_, data) self.gui.refresh_cover_browser() ci = self.view.currentIndex() if ci.isValid(): self.view.model().current_changed(ci, ci) def details_size_hint(self): return QSize(350, 550) def toggle_cover_fit(self, state): gprefs.set('book_info_dialog_fit_cover', self.fit_cover.isChecked()) self.resize_cover() def cover_view_resized(self, event): QTimer.singleShot(1, self.resize_cover) def slave(self, mi): self.refresh(mi.row_number, mi) def move(self, delta=1): idx = self.view.currentIndex() if idx.isValid(): m = self.view.model() ni = m.index(idx.row() + delta, idx.column()) if ni.isValid(): if self.view.isVisible(): self.view.scrollTo(ni) self.view.setCurrentIndex(ni) def next(self): self.move() def previous(self): self.move(-1) def resize_cover(self): if self.cover_pixmap is None: return pixmap = self.cover_pixmap if self.fit_cover.isChecked(): scaled, new_width, new_height = fit_image( pixmap.width(), pixmap.height(), self.cover.size().width() - 10, self.cover.size().height() - 10) if scaled: try: dpr = self.devicePixelRatioF() except AttributeError: dpr = self.devicePixelRatio() pixmap = pixmap.scaled(int(dpr * new_width), int(dpr * new_height), Qt.KeepAspectRatio, Qt.SmoothTransformation) pixmap.setDevicePixelRatio(dpr) self.cover.set_pixmap(pixmap) self.update_cover_tooltip() def update_cover_tooltip(self): tt = '' if self.marked: tt = _('This book is marked') if self.marked in { True, 'true' } else _('This book is marked as: %s') % self.marked tt += '\n\n' if self.cover_pixmap is not None: sz = self.cover_pixmap.size() tt += _('Cover size: %(width)d x %(height)d pixels') % dict( width=sz.width(), height=sz.height()) self.cover.setToolTip(tt) self.cover.pixmap_size = sz.width(), sz.height() def refresh(self, row, mi=None): if isinstance(row, QModelIndex): row = row.row() if row == self.current_row and mi is None: return mi = self.view.model().get_book_display_info(row) if mi is None else mi if mi is None: # Indicates books was deleted from library, or row numbers have # changed return self.previous_button.setEnabled(False if row == 0 else True) self.next_button.setEnabled(False if row == self.view.model().rowCount(QModelIndex()) - 1 else True) self.current_row = row self.setWindowTitle(mi.title) self.cover_pixmap = QPixmap.fromImage(mi.cover_data[1]) try: dpr = self.devicePixelRatioF() except AttributeError: dpr = self.devicePixelRatio() self.cover_pixmap.setDevicePixelRatio(dpr) self.resize_cover() html = render_html(mi, True, self, pref_name='popup_book_display_fields') set_html(mi, html, self.details) self.marked = mi.marked self.cover.setBackgroundBrush( self.marked_brush if mi.marked else self.normal_brush) self.update_cover_tooltip()
class MainWindow(QWidget): """Main window class that provides logic of the program. Allows to search concept and to show information about concepts and relations """ def __init__(self, application): super().__init__() self.application = application self.db = self.openDatabase() self._initUI() def initActions(self): """initalization of actions for main windows""" self.help_action = QAction("&Help", self) self.help_action.setShortcut("F1") self.help_action.setStatusTip('Help') self.help_action.triggered.connect(lambda: HelpDialog(self)) self.new_db_action = QAction("New DB", self) self.new_db_action.setShortcut("Ctrl+N") self.new_db_action.triggered.connect(self.openNewDB) self.delete_сoncept_action = QAction("&Delete", self) self.delete_сoncept_action.setShortcut("DEL") self.delete_сoncept_action.setStatusTip("Delete") self.delete_сoncept_action.triggered.connect(lambda: self.delete(Concept)) self.delete_subcategory_action = QAction("&Delete subcategory", self) self.delete_subcategory_action.triggered.connect(lambda: self.delete(Subcategory)) self.edit_concept_action = QAction("&Edit", self) self.edit_concept_action.setShortcut("F2") self.edit_concept_action.triggered.connect(self.editConcept) self.edit_relation_action = QAction("&Edit", self) self.edit_relation_action.setShortcut("Ctrl+F2") self.edit_relation_action.triggered.connect(self.editRelation) self.exit_action = QAction("&Exit", self) self.exit_action.setShortcut("ESC") self.exit_action.triggered.connect(self.close) self.select_relation_action = QAction("&Select relation", self) self.select_relation_action.triggered.connect(self.setRelationDescription) self.delete_relation_action = QAction("&Delete", self) self.delete_relation_action.setShortcut("Ctrl+DEL") self.delete_relation_action.triggered.connect(lambda: self.delete(Relation)) def openNewDB(self): """This method deletes setting.py file and closes current application session. This uses for creating a new instance of Main Window. """ if os.path.exists("setting.py"): os.remove("setting.py") self.application.exit(1) def _initUI(self): self.setWindowIcon(QIcon("logo.png")) self.setWindowTitle('GraphNotes') self.resize(1000, 700) self.initActions() self.add_data_button = QPushButton("Add data", self) self.search_box = QGroupBox() self._initSearchFrame(self.search_box) self.concept_list = QListWidget(self) self.relation_list = QListWidget(self) self.result_table = QTextEdit(self) self.concept_list.itemClicked.connect(self.searchRelations) self.concept_list.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOn) self.concept_list.setContextMenuPolicy(Qt.CustomContextMenu) self.concept_list.customContextMenuRequested.connect(self._showConceptContextMenu) self.relation_list.setContextMenuPolicy(Qt.CustomContextMenu) self.relation_list.customContextMenuRequested.connect(self._showRelationContextMenu) self.relation_list.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOn) self.relation_list.itemClicked.connect(self.setRelationDescription) self.grid = QGridLayout() self.result_table.setReadOnly(True) self.search() self._configWidgets() self._bindWidgets() self.show() def _showConceptContextMenu(self, pos): global_position = self.concept_list.mapToGlobal(pos) my_menu = QMenu() my_menu.addAction(self.edit_concept_action) my_menu.addAction(self.delete_сoncept_action) my_menu.addAction(self.delete_subcategory_action) my_menu.addSeparator() my_menu.addAction(self.help_action) my_menu.addAction(self.exit_action) my_menu.exec(global_position) def _initSearchFrame(self, frame): self.search_line = QLineEdit(frame) checkboxes = QGroupBox() checkboxes.setStyleSheet("border:0;") self.concept_checkbox = QCheckBox("Concept", checkboxes) self.description_checkbox = QCheckBox("Description", checkboxes) self.study_checkbox = QCheckBox("Study", checkboxes) self.search_button = QPushButton("Search", checkboxes) checkboxes_layout = QGridLayout(checkboxes) checkboxes_layout.addWidget(self.concept_checkbox, 0, 0) checkboxes_layout.addWidget(self.description_checkbox, 0, 1) checkboxes_layout.addWidget(self.study_checkbox, 0, 2) checkboxes_layout.setContentsMargins(0, 0, 0, 0) self.concept_checkbox.setChecked(True) grid = QGridLayout(frame) grid.addWidget(self.search_line, 1, 0) grid.addWidget(self.search_button, 1, 1) grid.addWidget(checkboxes, 2, 0, Qt.AlignLeft) frame.setLayout(grid) def getSearchData(self): """This method reads parameters of search line and checkboxes, and returns "Subcategory" class. None and [] uses for showing checkboxes that have been checked. """ concept = self.search_line.text() if self.concept_checkbox.isChecked() else None description = [Description(text=self.search_line.text())] if self.description_checkbox.isChecked() else [] study = self.search_line.text() if self.study_checkbox.isChecked() else None return Subcategory(subcategory=concept, description=description, study=study) def searchRelations(self): """This method triggers when the user clicked on the concept. This method selected ID of a subcategory from selected item in conceptions list and then searches and setting all information about this selected concept. """ subcategory_id = self.concept_list.selectedItems()[0].data(Qt.UserRole)[1].id self.setConceptDescription() result = self.db.search_relation(subcategory_id) self.setResult(result, self.relation_list) def _showRelationContextMenu(self, pos): global_position = self.relation_list.mapToGlobal(pos) my_menu = QMenu() my_menu.addAction(self.edit_relation_action) my_menu.addAction(self.delete_relation_action) my_menu.addSeparator() my_menu.exec(global_position) def editRelation(self): """This method opens dialog for editing Relation""" if self.relation_list.currentIndex().isValid(): relation = self.relation_list.selectedItems()[0].data(Qt.UserRole)[2] dlg = EditRelationDialog(self, relation) if dlg.exec_(): relation = dlg.getValue() self.db.update_relation(relation) self.select_relation_action.trigger() def editConcept(self): """This method opens dialog for editing Concept""" if self.concept_list.currentIndex().isValid(): concept = self.concept_list.selectedItems()[0].data(Qt.UserRole)[0] subcategory = self.concept_list.selectedItems()[0].data(Qt.UserRole)[1] dlg = EditConceptDialog(self, concept, subcategory) if dlg.exec_(): concept, subcategory = dlg.getValue() self.db.update_concept(concept) self.db.update_subcategory(subcategory) self.search() def delete(self, something): """This universal method for deleting something from the list and database. "something" parameter needs to be any of this types: Concept, Subcategory, Relation """ if something == Concept: number = 0 target_list = self.concept_list elif something == Subcategory: number = 1 target_list = self.concept_list elif something == Relation: number = 2 target_list = self.relation_list if target_list.currentIndex().isValid(): something = target_list.selectedItems()[0].data(Qt.UserRole)[number] self.db.delete(something) self.search() def setRelationDescription(self): """This method retrieves information about relation, formats and sets it in description field. """ if self.relation_list.currentIndex().isValid(): relation = self.relation_list.selectedItems()[0].data(Qt.UserRole)[2] concept1 = "{}{}".format(relation.node1.concept.name, ", {}".format(relation.node1.subcategory) if relation.node1.subcategory else "") concept2 = "{}{}".format(relation.node2.concept.name, ", {}".format(relation.node2.subcategory) if relation.node2.subcategory else "") description = relation.description study = relation.study references = relation.reference # HTML is used for a better information formating text = "<b>Relation between </b> \"{}\" <b>and</b> \"{}\"<p>".format(concept1, concept2) text += r"<b>Description:</b>" + "<br> {} <p>".format(description) text += r"<b>Study:</b> {} <p>".format(study) text += r"<b>References:</b><ol>" for ref in references: text += " <li> {} </li>".format(ref.text) text += "</ol><br>" self.result_table.setText(text) else: self.result_table.clear() def setConceptDescription(self): """This method retrieves information about concept, formats and sets it in description field. """ concept = self.concept_list.selectedItems()[0].data(Qt.UserRole)[0] subcategory = self.concept_list.selectedItems()[0].data(Qt.UserRole)[1] description = subcategory.description # HTML is used for a better information formating text = "<b>Concept:</b> {}<p>".format(concept.name) text += "<b>Subcategory:</b> {}<p>".format(subcategory.subcategory) text += "<b>Synonyms:</b> {}<p>".format(concept.synonyms) text += "<ol>" for des in description: text += "<li> {}".format("<b>Description:</b> <br>") text += " {} <p>".format(des.text) text += "<ol>" text += "<b>References: </b>" for ref in des.reference: text += "<li>" text += "{}".format(ref.text) text += "</li>" text += "</ol>" text += "</li>" text += "</ol>" self.result_table.setText(text) def setResult(self, result, list): """This is a universal method for showing information in a relation field or in a concept field. """ list.clear() for concept, subcategory, *other in result: item = QListWidgetItem() item.setData(Qt.UserRole, (concept, subcategory, *other)) item.setText("{}{} {}".format(concept.name, "" if subcategory.subcategory == "" else ", ", subcategory.subcategory)) list.insertItem(0, item) def search(self): parameters = self.getSearchData() result = self.db.search_nodes(parameters) self.setResult([], self.relation_list) self.setResult(result, self.concept_list) self.setResult([], self.relation_list) self.setRelationDescription() def _configWidgets(self): self.grid.setAlignment(Qt.AlignTop) self.grid.setSpacing(5) self.menu_bar = QMenuBar(self) self.database_menu = self.menu_bar.addMenu("Database") self.database_menu.addAction(self.new_db_action) self.database_menu.addSeparator() self.database_menu.addAction(self.exit_action) self.concept_menu = self.menu_bar.addMenu("Concept") self.concept_menu.addAction(self.edit_concept_action) self.concept_menu.addAction(self.delete_сoncept_action) self.concept_menu.addAction(self.delete_subcategory_action) self.relation_menu = self.menu_bar.addMenu("Relation") self.relation_menu.addAction(self.edit_relation_action) self.relation_menu.addAction(self.delete_relation_action) self.help_menu = self.menu_bar.addMenu("Help") self.help_menu.addAction(self.help_action) self.grid.setMenuBar(self.menu_bar) self.concept_box = QGroupBox("Concepts") self.concept_grid = QGridLayout() self.concept_grid.setContentsMargins(0, 10, 0, 0) self.concept_grid.addWidget(self.concept_list) self.concept_box.setLayout(self.concept_grid) self.relation_box = QGroupBox("Relations") self.relation_grid = QGridLayout() self.relation_grid.setContentsMargins(0, 10, 0, 0) self.relation_grid.addWidget(self.relation_list) self.relation_box.setLayout(self.relation_grid) self.description_box = QGroupBox("Description") self.description_grid = QGridLayout() self.description_grid.setContentsMargins(0, 10, 0, 0) self.description_grid.addWidget(self.result_table) self.description_box.setLayout(self.description_grid) self.concept_splitter = QSplitter(self) self.concept_splitter.addWidget(self.concept_box) self.concept_splitter.addWidget(self.relation_box) self.description_splitter = QSplitter(self) self.description_splitter.setOrientation(Qt.Vertical) self.description_splitter.addWidget(self.concept_splitter) self.description_splitter.addWidget(self.description_box) self.grid.addWidget(self.add_data_button, 1, 0, Qt.AlignLeft) self.grid.addWidget(self.search_box, 2, 0, 1, 2) self.grid.addWidget(self.description_splitter, 5, 0, 1, 2) self.setLayout(self.grid) def _bindWidgets(self): self.add_data_button.clicked.connect(self.addData) self.search_button.clicked.connect(self.search) self.search_line.returnPressed.connect(self.search) def addData(self): dlg = NewDataDialog(self.db) dlg.exec_() self.search() def getPath(self): dialog = NewDBDialog() if dialog.exec_(): print("EXEc") path = dialog.path with open("setting.py", "w") as f: f.write("{{ 'last': r'{0}' }}".format(path)) return path else: self.close() def openDatabase(self): if os.path.exists("setting.py"): with open("setting.py") as f: setting = eval(f.read()) if "last" in setting: if os.path.exists(setting["last"]): path = setting["last"] else: path = self.getPath() else: path = self.getPath() else: path = self.getPath() if path is None: self.close() exit(0) else: return Graph(path)
class ThemeCreateDialog(Dialog): def __init__(self, parent, report): self.report = report Dialog.__init__(self, _('Create an icon theme'), 'create-icon-theme', parent) def setup_ui(self): self.splitter = QSplitter(self) self.l = l = QVBoxLayout(self) l.addWidget(self.splitter) l.addWidget(self.bb) self.w = w = QGroupBox(_('Theme Metadata'), self) self.splitter.addWidget(w) l = w.l = QFormLayout(w) l.setFieldGrowthPolicy( QFormLayout.FieldGrowthPolicy.ExpandingFieldsGrow) self.missing_icons_group = mg = QGroupBox(self) self.mising_icons = mi = QListWidget(mg) mi.setSelectionMode(QAbstractItemView.SelectionMode.NoSelection) mg.l = QVBoxLayout(mg) mg.l.addWidget(mi) self.splitter.addWidget(mg) self.title = QLineEdit(self) l.addRow(_('&Title:'), self.title) self.author = QLineEdit(self) l.addRow(_('&Author:'), self.author) self.version = v = QSpinBox(self) v.setMinimum(1), v.setMaximum(1000000) l.addRow(_('&Version:'), v) self.license = lc = QLineEdit(self) l.addRow(_('&License:'), lc) self.url = QLineEdit(self) l.addRow(_('&URL:'), self.url) lc.setText( _('The license for the icons in this theme. Common choices are' ' Creative Commons or Public Domain.')) self.description = QTextEdit(self) l.addRow(self.description) self.refresh_button = rb = self.bb.addButton( _('&Refresh'), QDialogButtonBox.ButtonRole.ActionRole) rb.setIcon(QIcon(I('view-refresh.png'))) rb.clicked.connect(self.refresh) self.apply_report() def sizeHint(self): return QSize(900, 670) @property def metadata(self): self.report.theme.title = self.title.text().strip( ) # Needed for report.name to work return { 'title': self.title.text().strip(), 'author': self.author.text().strip(), 'version': self.version.value(), 'description': self.description.toPlainText().strip(), 'number': len(self.report.name_map) - len(self.report.extra), 'date': utcnow().date().isoformat(), 'name': self.report.name, 'license': self.license.text().strip() or 'Unknown', 'url': self.url.text().strip() or None, } def save_metadata(self): data = json.dumps(self.metadata, indent=2) if not isinstance(data, bytes): data = data.encode('utf-8') with open(os.path.join(self.report.path, THEME_METADATA), 'wb') as f: f.write(data) def refresh(self): self.save_metadata() self.report = read_theme_from_folder(self.report.path) self.apply_report() def apply_report(self): theme = self.report.theme self.title.setText((theme.title or '').strip()) self.author.setText((theme.author or '').strip()) self.version.setValue(theme.version or 1) self.description.setText((theme.description or '').strip()) self.license.setText((theme.license or 'Unknown').strip()) self.url.setText((theme.url or '').strip()) if self.report.missing: title = _('%d icons missing in this theme') % len( self.report.missing) else: title = _('No missing icons') self.missing_icons_group.setTitle(title) mi = self.mising_icons mi.clear() for name in sorted(self.report.missing): QListWidgetItem(QIcon(I(name, allow_user_override=False)), name, mi) def accept(self): mi = self.metadata if not mi.get('title'): return error_dialog( self, _('No title specified'), _('You must specify a title for this icon theme'), show=True) if not mi.get('author'): return error_dialog( self, _('No author specified'), _('You must specify an author for this icon theme'), show=True) return Dialog.accept(self)
class MainWin(QMainWindow): def __init__(self, fileName=None, logName=None, parent=None): super(MainWin, self).__init__(parent) #self.setWindowIcon(QIcon(':/images/logo.png')) self.setToolButtonStyle(Qt.ToolButtonFollowStyle) self.setupFileActions() self.setupEditActions() self.setupTextActions() self.setupRunActions() self.initializeSettings() self.populateRunSettings() # FIXME put in initializeSettings()? settingsMenu = QMenu('Settings', self) self.menuBar().addMenu(settingsMenu) settingsMenu.addAction('Configure...', self.configure) helpMenu = QMenu("Help", self) self.menuBar().addMenu(helpMenu) helpMenu.addAction("About", self.about) helpMenu.addAction("About &Qt", QApplication.instance().aboutQt) self.splitter = QSplitter(self) self.splitter.setOrientation(Qt.Vertical) self.textPane = TextPane() self.logPane = LogPane() self.logBox = QGroupBox() self.logBox.setFlat(True) vbox = QVBoxLayout() vbox.addWidget(self.logPane) self.logBox.setLayout(vbox) self.splitter.addWidget(self.textPane) self.splitter.addWidget(QLabel()) # spacer self.splitter.addWidget(self.logBox) self.setCentralWidget(self.splitter) self.loadSrc(fileName) self.loadLog(logName) #if logName and (-1 == self.comboLogFile.findText(logName)): #self.comboLogFile.addItem(logName) self.logPane.setFocus() self.fontChanged(self.textPane.font()) self.textPane.document().modificationChanged.connect(self.actionSave.setEnabled) self.textPane.document().modificationChanged.connect(self.setWindowModified) self.textPane.document().undoAvailable.connect(self.actionUndo.setEnabled) self.textPane.document().redoAvailable.connect( self.actionRedo.setEnabled) self.setWindowModified(self.textPane.document().isModified()) self.actionSave.setEnabled(self.textPane.document().isModified()) self.actionUndo.setEnabled(self.textPane.document().isUndoAvailable()) self.actionRedo.setEnabled(self.textPane.document().isRedoAvailable()) self.actionUndo.triggered.connect(self.textPane.undo) self.actionRedo.triggered.connect(self.textPane.redo) self.actionCut.setEnabled(False) self.actionCopy.setEnabled(False) self.actionCut.triggered.connect(self.textPane.cut) self.actionCopy.triggered.connect(self.textPane.copy) self.actionPaste.triggered.connect(self.textPane.paste) self.textPane.copyAvailable.connect(self.actionCut.setEnabled) self.textPane.copyAvailable.connect(self.actionCopy.setEnabled) QApplication.clipboard().dataChanged.connect(self.clipboardDataChanged) self.actionRun.triggered.connect(self.scannoCheck) self.logPane.lineMatchChanged.connect(self.logLineMatchChanged) def closeEvent(self, e): if self.maybeSave(): e.accept() else: e.ignore() def setupFileActions(self): tb = QToolBar(self) tb.setWindowTitle("File Actions") self.addToolBar(tb) menu = QMenu("&File", self) self.menuBar().addMenu(menu) self.actionNew = QAction("&New", self, priority=QAction.LowPriority, shortcut=QKeySequence.New, triggered=self.fileNew) tb.addAction(self.actionNew) menu.addAction(self.actionNew) self.actionOpen = QAction("&Open...", self, shortcut=QKeySequence.Open, triggered=self.fileOpen) tb.addAction(self.actionOpen) menu.addAction(self.actionOpen) menu.addSeparator() self.actionSave = QAction("&Save", self, shortcut=QKeySequence.Save, triggered=self.fileSave, enabled=False) tb.addAction(self.actionSave) menu.addAction(self.actionSave) self.actionSaveAs = QAction("Save &As...", self, priority=QAction.LowPriority, shortcut=Qt.CTRL + Qt.SHIFT + Qt.Key_S, triggered=self.fileSaveAs) menu.addAction(self.actionSaveAs) menu.addSeparator() self.actionQuit = QAction("&Quit", self, shortcut=QKeySequence.Quit, triggered=self.close) menu.addAction(self.actionQuit) def setupEditActions(self): tb = QToolBar(self) tb.setWindowTitle("Edit Actions") self.addToolBar(tb) menu = QMenu("&Edit", self) self.menuBar().addMenu(menu) self.actionUndo = QAction("&Undo", self, shortcut=QKeySequence.Undo) tb.addAction(self.actionUndo) menu.addAction(self.actionUndo) self.actionRedo = QAction("&Redo", self, priority=QAction.LowPriority, shortcut=QKeySequence.Redo) tb.addAction(self.actionRedo) menu.addAction(self.actionRedo) menu.addSeparator() self.actionCut = QAction("Cu&t", self, priority=QAction.LowPriority, shortcut=QKeySequence.Cut) tb.addAction(self.actionCut) menu.addAction(self.actionCut) self.actionCopy = QAction("&Copy", self, priority=QAction.LowPriority, shortcut=QKeySequence.Copy) tb.addAction(self.actionCopy) menu.addAction(self.actionCopy) self.actionPaste = QAction("&Paste", self, priority=QAction.LowPriority, shortcut=QKeySequence.Paste, enabled=(len(QApplication.clipboard().text()) != 0)) tb.addAction(self.actionPaste) menu.addAction(self.actionPaste) def setupTextActions(self): tb = QToolBar(self) tb.setWindowTitle("Format Actions") self.addToolBar(tb) tb = QToolBar(self) tb.setAllowedAreas(Qt.TopToolBarArea | Qt.BottomToolBarArea) tb.setWindowTitle("Format Actions") self.addToolBarBreak(Qt.TopToolBarArea) self.addToolBar(tb) self.comboFont = QFontComboBox(tb) tb.addWidget(self.comboFont) self.comboFont.activated[str].connect(self.textFamily) self.comboSize = QComboBox(tb) self.comboSize.setObjectName("comboSize") tb.addWidget(self.comboSize) self.comboSize.setEditable(True) db = QFontDatabase() for size in db.standardSizes(): self.comboSize.addItem('{}'.format(size)) self.comboSize.activated[str].connect(self.textSize) self.comboSize.setCurrentIndex(self.comboSize.findText('{}'.format(QApplication.font().pointSize()))) def setupRunActions(self): tb = QToolBar(self) tb.setWindowTitle("Run Actions") self.addToolBar(tb) menu = QMenu("Run", self) self.menuBar().addMenu(menu) self.actionRun = QAction( "&Run", self, shortcut=Qt.CTRL + Qt.Key_R) tb.addAction(self.actionRun) menu.addAction(self.actionRun) self.comboScannoFile = QComboBox(tb) self.comboScannoFile.setObjectName("comboScannoFile") tb.addWidget(self.comboScannoFile) self.comboScannoFile.setEditable(True) self.comboLogFile= QComboBox(tb) self.comboLogFile.setObjectName("comboLogFile") self.comboLogFile.setEditable(True) tb.addWidget(self.comboLogFile) def populateRunSettings(self): for f in self.scannoFiles(): self.comboScannoFile.addItem(f) if self.defaultScannoFile: idx = self.comboScannoFile.findText(self.defaultScannoFile) self.comboScannoFile.setCurrentIndex(idx) self.comboLogFile.addItem('plog.txt') def loadSrc(self, src): if src: if not self.textPane.load(src): return False self.setCurrentFileName(src) return True def loadLog(self, log): if log: if not self.logPane.load(log): return False self.comboLogFile.clear() self.comboLogFile.addItem(log) else: self.logPane.clear() self.logPane.setEnabled(False) self.logBox.setTitle(self.tr('No log file loaded.')) return True def maybeSave(self): if not self.textPane.document().isModified(): return True ret = QMessageBox.warning(self, 'GuiScannos', 'The document has been modified.\n' 'Do you want to save your changes?', QMessageBox.Save | QMessageBox.Discard | QMessageBox.Cancel) if ret == QMessageBox.Save: return self.fileSave() if ret == QMessageBox.Cancel: return False return True def setCurrentFileName(self, fileName=''): self.fileName = fileName self.textPane.document().setModified(False) if not fileName: shownName = 'untitled.txt' self.actionRun.setEnabled(False) else: shownName = QFileInfo(fileName).fileName() self.actionRun.setEnabled(True) self.setWindowTitle(self.tr('{}[*] - {}'.format(shownName, 'GUI Scannos'))) self.setWindowModified(False) def fileNew(self): if self.maybeSave(): self.textPane.clear() self.loadLog(None) # clears logPane, logBox title, etc self.setCurrentFileName() def fileOpen(self): fn, _ = QFileDialog.getOpenFileName(self, 'Open File...', None, 'Text Files (*.txt);;All Files (*)') if fn: self.loadSrc(fn) self.loadLog(None) # clears logPane, logBox title, etc def fileSave(self): if not self.fileName: return self.fileSaveAs() return self.textpane.save(self.fileName) def fileSaveAs(self): fn, _ = QFileDialog.getSaveFileName(self, "Save as...", None, "text files (*.txt);;All Files (*)") if not fn: return False self.setCurrentFileName(fn) return self.fileSave() def logLineMatchChanged(self): linenum = self.logPane.srcLineNum() col = self.logPane.srcColNum() s = self.logPane.srcScanno() self.textPane.setSelection(linenum, col, len(s)) def textFamily(self, family): """Set font family for text and log panes.""" self.textPane.setFontFamily(family) self.logPane.setFontFamily(family) def textSize(self, pointSize): """Set font size for text and log panes.""" self.textPane.setFontPointSize(pointSize) self.logPane.setFontPointSize(pointSize) def clipboardDataChanged(self): self.actionPaste.setEnabled(len(QApplication.clipboard().text()) != 0) def about(self): QMessageBox.about(self, 'About', 'GUI for ppscannos.') def fontChanged(self, font): self.comboFont.setCurrentIndex(self.comboFont.findText(QFontInfo(font).family())) self.comboSize.setCurrentIndex(self.comboSize.findText('{}'.format(font.pointSize()))) def scannoCheck(self): """Run ppscannos.""" scannodir = os.path.dirname(self.ppscannos) cmd = sys.executable assert(cmd) scannoFile = self.comboScannoFile.currentText() if not scannoFile: scannoFile = self.defaultScannoFile scannoFile = scannodir + '/' + scannoFile src = self.fileName log = self.comboLogFile.currentText() if not log: log = './plog.txt' subprocess.call([cmd, self.ppscannos, '-s' + scannoFile, '-o' + log, '-i' + src]) self.loadLog(log) self.logPane.setEnabled(True) def configure(self): """Configure application settings by way of a dialog.""" dlg = ConfigDialog() if dlg.exec(): self.setPPScannos(dlg.lineEditPPScannos.text()) self.setDefaultScannoFile(dlg.comboScannoFiles.currentText()) settings = QSettings(QApplication.organizationName(), QApplication.applicationName()) settings.setValue('ppscannos', self.ppscannos) settings.setValue('defaultScannoFile', self.defaultScannoFile) def setPPScannos(self, s): self.ppscannos = s self.actionRun.setEnabled(self.ppscannos and os.path.exists(self.ppscannos)) def scannoFiles(self): """Return list of .rc filenames (without path) that are in ppscannos directory.""" if not self.ppscannos: return [] return getRCFilesForDir(os.path.dirname(self.ppscannos)) def setDefaultScannoFile(self, s): self.defaultScannoFile = s valid = False if self.defaultScannoFile and self.ppscannos and os.path.exists(self.ppscannos): if os.path.exists(os.path.dirname(self.ppscannos) + '/' + self.defaultScannoFile): valid = True self.actionRun.setEnabled(valid) def initializeSettings(self): """Load persistent config settings.""" settings = QSettings() s = settings.value('ppscannos', type=str) if not s: # try the default s = os.path.expanduser('~') + '/ppscannos1/ppscannos1.py' #s = os.environ['HOME'] + '/ppscannos1/ppscannos1.py' self.setPPScannos(s) s = settings.value('defaultScannoFile', type=str) if (not s) and self.ppscannos: # try the default lst = getRCFilesForDir(os.path.dirname(self.ppscannos)) if len(lst): # prefer 'regex.rc'; otherwise use the first one s = lst[0] for f in lst: if f == 'regex.rc': s = f break self.setDefaultScannoFile(s)