class MainWindow(QMainWindow): """Main window class.""" def __init__(self, parent=None): """Init class.""" super(MainWindow, self).__init__() QNetworkProxyFactory.setUseSystemConfiguration(True) self.statusBar().showMessage(__doc__ + get_nuitka_version()) self.setWindowTitle(__doc__.strip().capitalize()) self.setMinimumSize(480, 400) self.setMaximumSize(1024, 800) self.resize(self.minimumSize()) self.setWindowIcon(QIcon.fromTheme("python")) self.center() QShortcut("Ctrl+q", self, activated=lambda: self.close()) self.menuBar().addMenu("&File").addAction("Exit", lambda: self.close()) windowMenu = self.menuBar().addMenu("&Window") windowMenu.addAction("Minimize", lambda: self.showMinimized()) windowMenu.addAction("Maximize", lambda: self.showMaximized()) windowMenu.addAction("Restore", lambda: self.showNormal()) windowMenu.addAction("FullScreen", lambda: self.showFullScreen()) windowMenu.addAction("Center", lambda: self.center()) windowMenu.addAction("Top-Left", lambda: self.move(0, 0)) windowMenu.addAction("To Mouse", lambda: self.move_to_mouse_position()) windowMenu.addSeparator() windowMenu.addAction( "Increase size", lambda: self.resize(self.size().width() * 1.4, self.size().height() * 1.4)) windowMenu.addAction("Decrease size", lambda: self.resize( self.size().width() // 1.4, self.size().height() // 1.4)) windowMenu.addAction("Minimum size", lambda: self.resize(self.minimumSize())) windowMenu.addAction("Maximum size", lambda: self.resize(self.maximumSize())) windowMenu.addAction("Horizontal Wide", lambda: self.resize( self.maximumSize().width(), self.minimumSize().height())) windowMenu.addAction("Vertical Tall", lambda: self.resize( self.minimumSize().width(), self.maximumSize().height())) windowMenu.addSeparator() windowMenu.addAction("Disable Resize", lambda: self.setFixedSize(self.size())) windowMenu.addAction("Set Interface Font...", lambda: self.setFont(QFontDialog.getFont()[0])) windowMenu.addAction( "Load .qss Skin", lambda: self.setStyleSheet(self.skin())) helpMenu = self.menuBar().addMenu("&Help") helpMenu.addAction("About Qt 5", lambda: QMessageBox.aboutQt(self)) helpMenu.addAction("About Python 3", lambda: open_new_tab('https://www.python.org')) helpMenu.addAction("About " + __doc__, lambda: QMessageBox.about(self, __doc__, HELP)) helpMenu.addSeparator() helpMenu.addAction( "Keyboard Shortcut", lambda: QMessageBox.information(self, __doc__, "<b>Quit = CTRL+Q")) if sys.platform.startswith('linux'): helpMenu.addAction("View Source Code", lambda: call('xdg-open ' + __file__, shell=True)) helpMenu.addAction("View GitHub Repo", lambda: open_new_tab(__url__)) helpMenu.addAction("Check Updates", lambda: Downloader(self)) # process self.process = QProcess() self.process.readyReadStandardOutput.connect(self._read_output) self.process.readyReadStandardError.connect(self._read_errors) self.process.finished.connect(self._process_finished) self.process.error.connect(self._process_failed) # widgets self.group0, self.group1 = QGroupBox("Options"), QGroupBox("Paths") self.group4, self.group5 = QGroupBox("Details"), QGroupBox("Miscs") g0grid, g1vlay = QGridLayout(self.group0), QGridLayout(self.group1) g5vlay, g4vlay = QVBoxLayout(self.group5), QVBoxLayout(self.group4) # group 0 the options self.module = QCheckBox("Create compiled extension module") self.standalone = QCheckBox("Standalone executable binary output") self.nofreeze = QCheckBox("No freeze all modules of standard library") self.python_debug = QCheckBox("Use Python Debug") self.warning = QCheckBox("Warnings for implicit exceptions at compile") self.recurse_std = QCheckBox("Recursive compile the standard library") self.recurse_not = QCheckBox("Force No recursive compiling") self.execute = QCheckBox("Execute the created binary after compiling") self.pythonpath = QCheckBox("Keep pythonpath when executing") self.enhaced = QCheckBox("Enhaced compile, Not CPython compatible") self.nolineno = QCheckBox("No Statements line numbers on compile") self.rmbuilddir = QCheckBox("Remove build directory after compile.") self.nuitka_debug = QCheckBox("Use Nuitka Debug") self.keep_debug = QCheckBox("Keep debug info on compile for GDB") self.traced = QCheckBox("Traced execution output") self.plusplus = QCheckBox("Compile C++ Only on generated source files") self.experimental = QCheckBox("Experimental features") self.force_clang = QCheckBox("Force use of CLang") self.force_mingw = QCheckBox("Force use of MinGW on MS Windows") self.force_lto = QCheckBox("Use link time optimizations LTO") self.show_scons = QCheckBox("Show Scons executed commands") self.show_progress = QCheckBox("Show progress info and statistics") self.show_summary = QCheckBox("Show final summary of included modules") self.disable_console = QCheckBox("Disable the Console on MS Windows") for i, widget in enumerate(( self.module, self.standalone, self.nofreeze, self.python_debug, self.warning, self.recurse_std, self.recurse_not, self.execute, self.pythonpath, self.enhaced, self.nolineno, self.rmbuilddir, self.nuitka_debug, self.keep_debug, self.traced, self.plusplus, self.experimental, self.force_clang, self.force_mingw, self.force_lto, self.show_scons, self.show_progress, self.show_summary, self.disable_console)): widget.setToolTip(widget.text()) g0grid.addWidget(widget, i if i < i + 1 else i - (i - 1), i % 2) # group 1 paths self.target = QLineEdit() self.outdir = QLineEdit(os.path.expanduser("~")) self.t_icon = QLineEdit() self.target.setToolTip("Python App file you want to Compile to Binary") self.outdir.setToolTip("Folder to write Compiled Output Binary files") self.t_icon.setToolTip("Icon image file to embed for your Python App") self.target.setPlaceholderText("/full/path/to/target/python_app.py") self.outdir.setPlaceholderText("/full/path/to/output/folder/") self.t_icon.setPlaceholderText("/full/path/to/python_app/icon.png") self.completer, self.dirs = QCompleter(self), QDirModel(self) self.completer.setModel(self.dirs) self.completer.setCaseSensitivity(Qt.CaseInsensitive) self.completer.setCompletionMode(QCompleter.PopupCompletion) self.completer.popup().setStyleSheet("border: 1px solid gray") self.completer.popup().setVerticalScrollBarPolicy( Qt.ScrollBarAlwaysOff) self.outdir.setCompleter(self.completer) self.t_icon.setCompleter(self.completer) self.target.setCompleter(self.completer) self.clear_1 = QPushButton(QIcon.fromTheme("edit-clear"), "", self, clicked=lambda: self.target.clear()) self.clear_2 = QPushButton(QIcon.fromTheme("edit-clear"), "", self, clicked=lambda: self.t_icon.clear()) self.clear_3 = QPushButton(QIcon.fromTheme("edit-clear"), "", self, clicked=lambda: self.outdir.clear()) self.open_1 = QPushButton( QIcon.fromTheme("folder-open"), "", self, clicked=lambda: self.target.setText(str(QFileDialog.getOpenFileName( self, __doc__, os.path.expanduser("~"), """Python (*.py);; Python for Windows (*.pyw);;All (*.*)""")[0]))) self.open_2 = QPushButton( QIcon.fromTheme("folder-open"), "", self, clicked=lambda: self.t_icon.setText(str(QFileDialog.getOpenFileName( self, __doc__, os.path.expanduser("~"), "PNG (*.png);;JPG (*.jpg);;ICO (*.ico);;All (*.*)")[0]))) self.open_3 = QPushButton( QIcon.fromTheme("folder-open"), "", self, clicked=lambda: self.outdir.setText(str(QFileDialog.getExistingDirectory( self, __doc__, os.path.expanduser("~"))))) self.l_icon = QLabel("Target Icon") g1vlay.addWidget(QLabel("<b>Target Python"), 0, 0) g1vlay.addWidget(self.target, 0, 1) g1vlay.addWidget(self.clear_1, 0, 2) g1vlay.addWidget(self.open_1, 0, 3) g1vlay.addWidget(self.l_icon, 1, 0) g1vlay.addWidget(self.t_icon, 1, 1) g1vlay.addWidget(self.clear_2, 1, 2) g1vlay.addWidget(self.open_2, 1, 3) g1vlay.addWidget(QLabel("<b>Output Folder"), 2, 0) g1vlay.addWidget(self.outdir, 2, 1) g1vlay.addWidget(self.clear_3, 2, 2) g1vlay.addWidget(self.open_3, 2, 3) # group 4 the dome view mode self.jobs = QSpinBox() self.jobs.setRange(1, cpu_count()) self.jobs.setValue(cpu_count()) self.jobs.setToolTip("Backend Worker Jobs Processes") self.python_version = QComboBox() self.python_version.addItems(["2.7", "3.2", "3.3", "3.4"]) self.python_version.setToolTip("Python version to use with Nuitka") self.display_tree = QPushButton("Display Tree") self.display_tree.clicked.connect( lambda: call(NUITKA + " --display-tree {}".format( self.target.text()), shell=True)) self.dump_tree = QPushButton( "View Docs", clicked=lambda: open_new_tab("http://nuitka.net/doc/user-manual.html")) self.open_log = QPushButton("View Logs") _log = os.path.join(gettempdir(), "nuitka-gui.log") _open = "xdg-open " if sys.platform.startswith("lin") else "open " self.open_log.clicked.connect(lambda: call(_open + _log, shell=True)) self.open_folder = QPushButton("Open Build Folder") self.open_folder.clicked.connect(lambda: call( _open + str(self.outdir.text()).strip(), shell=True)) # self.display_tree.clicked.connect(self._display_tree) g4vlay.addWidget(QLabel("<b>Worker Jobs")) g4vlay.addWidget(self.jobs) g4vlay.addWidget(QLabel("<b>Python Version")) g4vlay.addWidget(self.python_version) g4vlay.addWidget(QLabel("<b>Actions")) g4vlay.addWidget(self.display_tree) g4vlay.addWidget(self.dump_tree) g4vlay.addWidget(self.open_log) g4vlay.addWidget(self.open_folder) # group 5 miscelaneous stuff self.debug, self.scr = QCheckBox("Use Debug"), QCheckBox("Make Script") self.chrt, self.ionice = QCheckBox("Slow CPU"), QCheckBox("Slow HDD") self.minimi = QCheckBox("Auto Minimize") self.chrt.setToolTip("Use Low CPU speed priority (Linux only)") self.ionice.setToolTip("Use Low HDD speed priority (Linux only)") self.scr.setToolTip("Generate a Bash Script to Compile with Nuitka") self.debug.setToolTip("Use Debug Verbose mode") self.minimi.setToolTip("Automatically Minimize when compiling starts") self.scr.setChecked(True) self.chrt.setChecked(True) self.ionice.setChecked(True) self.minimi.setChecked(True) g5vlay.addWidget(self.debug) g5vlay.addWidget(self.scr) g5vlay.addWidget(self.chrt) g5vlay.addWidget(self.ionice) g5vlay.addWidget(self.minimi) # option to show or hide some widgets on the gui self.guimode = QComboBox() self.guimode.addItems(('Full UX / UI', 'Simple UX / UI')) self.guimode.setCurrentIndex(1) self._set_guimode() self.guimode.setStyleSheet("""QComboBox{background:transparent; margin-left:25px;color:gray;text-decoration:underline;border:0}""") self.guimode.currentIndexChanged.connect(self._set_guimode) # buttons from bottom to close or proceed self.bt = QDialogButtonBox(self) self.bt.setStandardButtons( QDialogButtonBox.Ok | QDialogButtonBox.Close) self.bt.rejected.connect(self.close) self.bt.accepted.connect(self.run) if not sys.platform.startswith('lin'): self.scr.setChecked(False) self.chrt.setChecked(False) self.ionice.setChecked(False) self.scr.hide() self.chrt.hide() self.ionice.hide() if not sys.platform.startswith('win'): self.l_icon.hide() self.t_icon.hide() self.clear_2.hide() self.open_2.hide() if sys.platform.startswith('win'): self.display_tree.hide() # container for all groups of widgets container = QWidget() container_layout = QGridLayout(container) # Y, X container_layout.addWidget(self.guimode, 0, 1) container_layout.addWidget(self.group0, 1, 1) container_layout.addWidget(self.group1, 2, 1) container_layout.addWidget(self.group4, 1, 2) container_layout.addWidget(self.group5, 2, 2) container_layout.addWidget(self.bt, 3, 1) self.setCentralWidget(container) def check_paths(self): """Check that the paths are valid.""" if not os.path.isfile(self.target.text()): log.error("Target File not found or not valid.") QMessageBox.warning(self, __doc__.title(), "Target File not found or not valid.") return False if not str(self.target.text()).endswith((".py", ".pyw")): log.error("Target File not valid.") QMessageBox.warning(self, __doc__.title(), "Target File not valid.") return False if not os.path.isdir(self.outdir.text()): log.error("Target Folder not found or not valid.") QMessageBox.warning(self, __doc__.title(), "Target Folder not found or not valid.") return False if self.t_icon.text() and not os.path.isfile(self.t_icon.text()): log.warning("Target Icon File not found or not valid.") QMessageBox.warning(self, __doc__.title(), "Target Icon File not found or not valid.") return True else: return True def generate_build_command(self): """Generate a build command.""" return re.sub(r"\s+", " ", " ".join(( 'chrt --verbose --idle 0' if self.chrt.isChecked() else '', 'ionice --ignore --class 3' if self.ionice.isChecked() else '', NUITKA, '--debug --verbose' if self.debug.isChecked() else '', '--show-progress' if self.show_progress.isChecked() else '', '--show-scons --show-modules' if self.show_scons.isChecked() else '', '--unstriped' if self.keep_debug.isChecked() else '', '--trace-execution' if self.traced.isChecked() else '', '--remove-output' if self.rmbuilddir.isChecked() else '', '--code-gen-no-statement-lines' if self.nolineno.isChecked() else '', '--execute' if self.execute.isChecked() else '', '--recurse-none' if self.recurse_not.isChecked() else '--recurse-all', '--recurse-stdlib' if self.recurse_std.isChecked() else '', '--clang' if self.force_clang.isChecked() else '', '--lto' if self.force_lto.isChecked() else '', '--c++-only' if self.plusplus.isChecked() else '', '--windows-disable-console' if self.disable_console.isChecked() else '', '--experimental' if self.experimental.isChecked() else '', '--python-debug' if self.python_debug.isChecked() else '', '--module' if self.module.isChecked() else '--standalone', '--nofreeze-stdlib' if self.nofreeze.isChecked() else '', '--mingw' if self.force_mingw.isChecked() else '', '--warn-implicit-exceptions' if self.warning.isChecked() else '', '--execute-with-pythonpath' if self.pythonpath.isChecked() else '', '--enhanced' if self.enhaced.isChecked() else '', '--icon="{}"'.format(self.t_icon.text()) if self.t_icon.text() else '', '--python-version={}'.format(self.python_version.currentText()), '--jobs={}'.format(self.jobs.value()), '--output-dir="{}"'.format(self.outdir.text()), '"{}"'.format(self.target.text())))) def run(self): """Run the main method and run Nuitka.""" self.statusBar().showMessage('Working...') log.debug("Working...") if not self.check_paths(): return command_to_run_nuitka = self.generate_build_command() log.debug(command_to_run_nuitka) self.process.start(command_to_run_nuitka) if not self.process.waitForStarted(): log.error(self._read_errors()) return # ERROR if self.scr.isChecked() and sys.platform.startswith("lin"): script_file = str(self.target.text()).replace(".py", "-nuitka-compile.sh") log.debug("Writing Script {}".format(script_file)) with open(script_file, "w", encoding="utf-8") as script: script.write("#!/usr/bin/env bash\n" + command_to_run_nuitka) os.chmod(script_file, 0o755) self.statusBar().showMessage(__doc__.title()) def _process_finished(self): """Finished sucessfuly.""" log.debug("Finished.") self.showNormal() def _read_output(self): """Read and return output.""" return str(self.process.readAllStandardOutput()).strip() def _read_errors(self): """Read and return errors.""" log.debug(self.process.readAllStandardError()) return str(self.process.readAllStandardError()).strip() def _process_failed(self): """Read and return errors.""" self.showNormal() self.statusBar().showMessage(" ERROR: Failed ! ") log.warning(str(self.process.readAllStandardError()).strip().lower()) return str(self.process.readAllStandardError()).strip().lower() def _set_guimode(self): """Switch between simple and full UX.""" for widget in (self.group0, self.group4, self.group5, self.statusBar(), self.menuBar()): widget.hide() if self.guimode.currentIndex() else widget.show() self.resize(self.minimumSize() if self.guimode.currentIndex() else self.maximumSize()) self.center() def skin(self, filename=None): """Open QSS from filename,if no QSS return None,if no filename ask.""" if not filename: filename = str(QFileDialog.getOpenFileName( self, __doc__ + "-Open QSS Skin file", os.path.expanduser("~"), "CSS Cascading Style Sheet for Qt 5 (*.qss);;All (*.*)")[0]) if filename and os.path.isfile(filename): log.debug(filename) with open(filename, 'r') as file_to_read: text = file_to_read.read().strip() if text: log.debug(text) return text def center(self): """Center the Window on the Current Screen,with Multi-Monitor support. >>> MainWindow().center() True """ window_geometry = self.frameGeometry() mousepointer_position = QApplication.desktop().cursor().pos() screen = QApplication.desktop().screenNumber(mousepointer_position) centerPoint = QApplication.desktop().screenGeometry(screen).center() window_geometry.moveCenter(centerPoint) return bool(not self.move(window_geometry.topLeft())) def move_to_mouse_position(self): """Center the Window on the Current Mouse position. >>> MainWindow().move_to_mouse_position() True """ window_geometry = self.frameGeometry() window_geometry.moveCenter(QApplication.desktop().cursor().pos()) return bool(not self.move(window_geometry.topLeft())) def closeEvent(self, event): """Ask to Quit.""" the_conditional_is_true = QMessageBox.question( self, __doc__.title(), 'Quit ?.', QMessageBox.Yes | QMessageBox.No, QMessageBox.No) == QMessageBox.Yes event.accept() if the_conditional_is_true else event.ignore()
class SeedLayout(QVBoxLayout): def seed_options(self): dialog = QDialog() vbox = QVBoxLayout(dialog) if 'ext' in self.options: cb_ext = QCheckBox(_('Extend this seed with custom words')) cb_ext.setChecked(self.is_ext) vbox.addWidget(cb_ext) if 'bip39' in self.options: def f(b): self.is_seed = (lambda x: bool(x)) if b else self.saved_is_seed self.is_bip39 = b self.on_edit() if b: msg = ' '.join([ '<b>' + _('Warning') + ':</b> ', _('BIP39 seeds can be imported in Electrum, so that users can access funds locked in other wallets.' ), _('However, we do not generate BIP39 seeds, because they do not meet our safety standard.' ), _('BIP39 seeds do not include a version number, which compromises compatibility with future software.' ), _('We do not guarantee that BIP39 imports will always be supported in Electrum.' ), ]) else: msg = '' self.seed_warning.setText(msg) cb_bip39 = QCheckBox(_('BIP39 seed')) cb_bip39.toggled.connect(f) cb_bip39.setChecked(self.is_bip39) vbox.addWidget(cb_bip39) vbox.addLayout(Buttons(OkButton(dialog))) if not dialog.exec_(): return None self.is_ext = cb_ext.isChecked() if 'ext' in self.options else False self.is_bip39 = cb_bip39.isChecked( ) if 'bip39' in self.options else False def __init__(self, seed=None, title=None, icon=True, msg=None, options=None, is_seed=None, passphrase=None, parent=None, for_seed_words=True): QVBoxLayout.__init__(self) self.parent = parent self.options = options if title: self.addWidget(WWLabel(title)) if seed: # "read only", we already have the text if for_seed_words: self.seed_e = ButtonsTextEdit() else: # e.g. xpub self.seed_e = ShowQRTextEdit() self.seed_e.setReadOnly(True) self.seed_e.setText(seed) else: # we expect user to enter text assert for_seed_words self.seed_e = CompletionTextEdit() self.seed_e.setTabChangesFocus(False) # so that tab auto-completes self.is_seed = is_seed self.saved_is_seed = self.is_seed self.seed_e.textChanged.connect(self.on_edit) self.initialize_completer() self.seed_e.setMaximumHeight(75) hbox = QHBoxLayout() if icon: logo = QLabel() logo.setPixmap( QPixmap(icon_path("seed.png")).scaledToWidth( 64, mode=Qt.SmoothTransformation)) logo.setMaximumWidth(60) hbox.addWidget(logo) hbox.addWidget(self.seed_e) self.addLayout(hbox) hbox = QHBoxLayout() hbox.addStretch(1) self.seed_type_label = QLabel('') hbox.addWidget(self.seed_type_label) # options self.is_bip39 = False self.is_ext = False if options: opt_button = EnterButton(_('Options'), self.seed_options) hbox.addWidget(opt_button) self.addLayout(hbox) if passphrase: hbox = QHBoxLayout() passphrase_e = QLineEdit() passphrase_e.setText(passphrase) passphrase_e.setReadOnly(True) hbox.addWidget(QLabel(_("Your seed extension is") + ':')) hbox.addWidget(passphrase_e) self.addLayout(hbox) self.addStretch(1) self.seed_warning = WWLabel('') if msg: self.seed_warning.setText(seed_warning_msg(seed)) self.addWidget(self.seed_warning) def initialize_completer(self): bip39_english_list = Mnemonic('en').wordlist old_list = electrum_ltc.old_mnemonic.words only_old_list = set(old_list) - set(bip39_english_list) self.wordlist = list(bip39_english_list) + list( only_old_list) # concat both lists self.wordlist.sort() class CompleterDelegate(QStyledItemDelegate): def initStyleOption(self, option, index): super().initStyleOption(option, index) # Some people complained that due to merging the two word lists, # it is difficult to restore from a metal backup, as they planned # to rely on the "4 letter prefixes are unique in bip39 word list" property. # So we color words that are only in old list. if option.text in only_old_list: # yellow bg looks ~ok on both light/dark theme, regardless if (un)selected option.backgroundBrush = ColorScheme.YELLOW.as_color( background=True) self.completer = QCompleter(self.wordlist) delegate = CompleterDelegate(self.seed_e) self.completer.popup().setItemDelegate(delegate) self.seed_e.set_completer(self.completer) def get_seed(self): text = self.seed_e.text() return ' '.join(text.split()) def on_edit(self): s = self.get_seed() b = self.is_seed(s) if not self.is_bip39: t = seed_type(s) label = _('Seed Type') + ': ' + t if t else '' else: from electrum_ltc.keystore import bip39_is_checksum_valid is_checksum, is_wordlist = bip39_is_checksum_valid(s) status = ('checksum: ' + ('ok' if is_checksum else 'failed') ) if is_wordlist else 'unknown wordlist' label = 'BIP39' + ' (%s)' % status self.seed_type_label.setText(label) self.parent.next_button.setEnabled(b) # disable suggestions if user already typed an unknown word for word in self.get_seed().split(" ")[:-1]: if word not in self.wordlist: self.seed_e.disable_suggestions() return self.seed_e.enable_suggestions()
class SeedLayout(QVBoxLayout): def __init__( self, seed=None, title=None, icon=True, msg=None, options=None, is_seed=None, passphrase=None, parent=None, for_seed_words=True, *, config: 'SimpleConfig', ): QVBoxLayout.__init__(self) self.parent = parent self.options = options self.config = config self.seed_type = 'bip39' if title: self.addWidget(WWLabel(title)) if seed: # "read only", we already have the text if for_seed_words: self.seed_e = ButtonsTextEdit() else: # e.g. xpub self.seed_e = ShowQRTextEdit(config=self.config) self.seed_e.setReadOnly(True) self.seed_e.setText(seed) else: # we expect user to enter text assert for_seed_words self.seed_e = CompletionTextEdit() self.seed_e.setTabChangesFocus(False) # so that tab auto-completes self.is_seed = is_seed self.saved_is_seed = self.is_seed self.seed_e.textChanged.connect(self.on_edit) self.initialize_completer() self.seed_e.setMaximumHeight(75) hbox = QHBoxLayout() if icon: logo = QLabel() logo.setPixmap(QPixmap(icon_path("seed.png")) .scaledToWidth(64, mode=Qt.SmoothTransformation)) logo.setMaximumWidth(60) hbox.addWidget(logo) hbox.addWidget(self.seed_e) self.addLayout(hbox) hbox = QHBoxLayout() hbox.addStretch(1) self.seed_type_label = QLabel('') hbox.addWidget(self.seed_type_label) seed_types = [ (value, title) for value, title in ( ('bip39', _('BIP39 seed')), ('electrum', _('Electrum')), # ('slip39', _('SLIP39 seed')), ) #if value in self.options or value == 'electrum' ] seed_type_values = [t[0] for t in seed_types] if len(seed_types) >= 2: def f(choices_layout): self.seed_type = seed_type_values[choices_layout.selected_index()] self.is_seed = (lambda x: bool(x)) if self.seed_type != 'bip39' else self.saved_is_seed self.slip39_current_mnemonic_invalid = None self.seed_status.setText('') #self.on_edit() self.update_share_buttons() self.initialize_completer() self.seed_warning.setText(msg) checked_index = seed_type_values.index(self.seed_type) titles = [t[1] for t in seed_types] clayout = ChoicesLayout(_('Seed type'), titles, on_clicked=f, checked_index=checked_index) hbox.addLayout(clayout.layout()) # options self.is_ext = False if options: opt_button = EnterButton(_('Options'), self.seed_options) hbox.addWidget(opt_button) self.addLayout(hbox) if passphrase: hbox = QHBoxLayout() passphrase_e = QLineEdit() passphrase_e.setText(passphrase) passphrase_e.setReadOnly(True) hbox.addWidget(QLabel(_("Your seed extension is") + ':')) hbox.addWidget(passphrase_e) self.addLayout(hbox) # slip39 shares self.slip39_mnemonic_index = 0 self.slip39_mnemonics = [""] self.slip39_seed = None self.slip39_current_mnemonic_invalid = None hbox = QHBoxLayout() hbox.addStretch(1) self.prev_share_btn = QPushButton(_("Previous share")) self.prev_share_btn.clicked.connect(self.on_prev_share) hbox.addWidget(self.prev_share_btn) self.next_share_btn = QPushButton(_("Next share")) self.next_share_btn.clicked.connect(self.on_next_share) hbox.addWidget(self.next_share_btn) self.update_share_buttons() self.addLayout(hbox) self.addStretch(1) self.seed_status = WWLabel('') self.addWidget(self.seed_status) self.seed_warning = WWLabel('') if msg: self.seed_warning.setText(seed_warning_msg(seed)) self.addWidget(self.seed_warning) self.lang = 'en' def initialize_completer(self): if self.seed_type != 'slip39': bip39_english_list = Mnemonic('en').wordlist old_list = old_mnemonic.wordlist only_old_list = set(old_list) - set(bip39_english_list) self.wordlist = list(bip39_english_list) + list(only_old_list) # concat both lists self.wordlist.sort() class CompleterDelegate(QStyledItemDelegate): def initStyleOption(self, option, index): super().initStyleOption(option, index) # Some people complained that due to merging the two word lists, # it is difficult to restore from a metal backup, as they planned # to rely on the "4 letter prefixes are unique in bip39 word list" property. # So we color words that are only in old list. if option.text in only_old_list: # yellow bg looks ~ok on both light/dark theme, regardless if (un)selected option.backgroundBrush = ColorScheme.YELLOW.as_color(background=True) delegate = CompleterDelegate(self.seed_e) else: self.wordlist = list(slip39.get_wordlist()) delegate = None self.completer = QCompleter(self.wordlist) if delegate: self.completer.popup().setItemDelegate(delegate) self.seed_e.set_completer(self.completer) def get_seed_words(self): return self.seed_e.text().split() def get_seed(self): if self.seed_type != 'slip39': return ' '.join(self.get_seed_words()) else: return self.slip39_seed def on_edit(self): s = ' '.join(self.get_seed_words()) b = self.is_seed(s) if self.seed_type == 'bip39': from electrum.keystore import bip39_is_checksum_valid from electrum.mnemonic import Wordlist, filenames lang = '' for type, file in filenames.items(): word_list = Wordlist.from_file(file) is_checksum, is_wordlist = bip39_is_checksum_valid(s, wordlist=word_list) if is_wordlist: lang = type break status = ('checksum: ' + ('ok' if is_checksum else 'failed')) if is_wordlist else 'unknown wordlist' label = 'BIP39 - ' + lang + ' (%s)'%status if lang and lang != self.lang: if lang == 'en': bip39_english_list = Mnemonic('en').wordlist old_list = old_mnemonic.wordlist only_old_list = set(old_list) - set(bip39_english_list) self.wordlist = list(bip39_english_list) + list(only_old_list) # concat both lists self.wordlist.sort() self.completer.model().setStringList(self.wordlist) self.lang = 'en' else: self.wordlist = list(Mnemonic(lang).wordlist) self.wordlist.sort() self.completer.model().setStringList(self.wordlist) self.lang = lang elif self.seed_type == 'slip39': self.slip39_mnemonics[self.slip39_mnemonic_index] = s try: slip39.decode_mnemonic(s) except slip39.Slip39Error as e: share_status = str(e) current_mnemonic_invalid = True else: share_status = _('Valid.') current_mnemonic_invalid = False label = _('SLIP39 share') + ' #%d: %s' % (self.slip39_mnemonic_index + 1, share_status) # No need to process mnemonics if the current mnemonic remains invalid after editing. if not (self.slip39_current_mnemonic_invalid and current_mnemonic_invalid): self.slip39_seed, seed_status = slip39.process_mnemonics(self.slip39_mnemonics) self.seed_status.setText(seed_status) self.slip39_current_mnemonic_invalid = current_mnemonic_invalid b = self.slip39_seed is not None self.update_share_buttons() else: t = seed_type(s) label = _('Seed Type') + ': ' + t if t else '' self.seed_type_label.setText(label) self.parent.next_button.setEnabled(b) # disable suggestions if user already typed an unknown word for word in self.get_seed_words()[:-1]: if word not in self.wordlist: self.seed_e.disable_suggestions() return self.seed_e.enable_suggestions() def update_share_buttons(self): if self.seed_type != 'slip39': self.prev_share_btn.hide() self.next_share_btn.hide() return finished = self.slip39_seed is not None self.prev_share_btn.show() self.next_share_btn.show() self.prev_share_btn.setEnabled(self.slip39_mnemonic_index != 0) self.next_share_btn.setEnabled( # already pressed "prev" and undoing that: self.slip39_mnemonic_index < len(self.slip39_mnemonics) - 1 # finished entering latest share and starting new one: or (bool(self.seed_e.text().strip()) and not self.slip39_current_mnemonic_invalid and not finished) ) def on_prev_share(self): if not self.slip39_mnemonics[self.slip39_mnemonic_index]: del self.slip39_mnemonics[self.slip39_mnemonic_index] self.slip39_mnemonic_index -= 1 self.seed_e.setText(self.slip39_mnemonics[self.slip39_mnemonic_index]) self.slip39_current_mnemonic_invalid = None def on_next_share(self): if not self.slip39_mnemonics[self.slip39_mnemonic_index]: del self.slip39_mnemonics[self.slip39_mnemonic_index] else: self.slip39_mnemonic_index += 1 if len(self.slip39_mnemonics) <= self.slip39_mnemonic_index: self.slip39_mnemonics.append("") self.seed_e.setFocus() self.seed_e.setText(self.slip39_mnemonics[self.slip39_mnemonic_index]) self.slip39_current_mnemonic_invalid = None
class FilteredComboBox(QComboBox): def __init__(self, options, **kwargs): super().__init__() self.setFocusPolicy(Qt.StrongFocus) self.setEditable(True) # self.dropEvent() self.completer = QCompleter(self) # always show all completions self.completer.setCompletionMode(QCompleter.UnfilteredPopupCompletion) self.pFilterModel = QSortFilterProxyModel(self) self.pFilterModel.setFilterCaseSensitivity(Qt.CaseInsensitive) self.completer.setPopup(self.view()) self.setCompleter(self.completer) the_style = "border-top-left-radius: 10px; border-bottom-left-radius: 10px; background-color: #f5f5f5;" self.lineEdit().setStyleSheet(the_style) if "height" in kwargs: self.setFixedHeight(kwargs["height"]) else: self.setFixedHeight(37) self.lineEdit().textEdited.connect( self.pFilterModel.setFilterFixedString) self.completer.activated.connect(self.setTextIfCompleterIsClicked) model = QStandardItemModel() for i, word in enumerate(options): item = QStandardItem(word) model.setItem(i, 0, item) self.setModel(model) self.setModelColumn(0) if "width" in kwargs: self.setFixedWidth(kwargs["width"]) else: self.setFixedWidth(260) style = """ QComboBox { border-radius: 10px; } QComboBox::drop-down:button{ width: 25px; background-color: #f5f5f5; border-image: url(./resources/assets/images/drop_down.png); border-bottom-right-radius: 10px; border-top-right-radius: 10px; } QComboBox::drop-down:button:hover{ background-color: #f5f5f5; } """ self.setStyleSheet(style) def setModel(self, model): super(FilteredComboBox, self).setModel(model) self.pFilterModel.setSourceModel(model) self.completer.setModel(self.pFilterModel) def setModelColumn(self, column): self.completer.setCompletionColumn(column) self.pFilterModel.setFilterKeyColumn(column) super(FilteredComboBox, self).setModelColumn(column) def view(self): return self.completer.popup() def index(self): return self.currentIndex() def setTextIfCompleterIsClicked(self, text): if text: index = self.findText(text) self.setCurrentIndex(index) def setMaximumWidth(self, p_int): self.setFixedWidth(p_int)
def __init__(self, core_args=None, core_env=None, api_port=None): QMainWindow.__init__(self) QCoreApplication.setOrganizationDomain("nl") QCoreApplication.setOrganizationName("TUDelft") QCoreApplication.setApplicationName("Tribler") QCoreApplication.setAttribute(Qt.AA_UseHighDpiPixmaps) self.gui_settings = QSettings() api_port = api_port or int( get_gui_setting(self.gui_settings, "api_port", DEFAULT_API_PORT)) dispatcher.update_worker_settings(port=api_port) self.navigation_stack = [] self.tribler_started = False self.tribler_settings = None self.debug_window = None self.core_manager = CoreManager(api_port) self.pending_requests = {} self.pending_uri_requests = [] self.download_uri = None self.dialog = None self.new_version_dialog = None self.start_download_dialog_active = False self.request_mgr = None self.search_request_mgr = None self.search_suggestion_mgr = None self.selected_torrent_files = [] self.vlc_available = True self.has_search_results = False self.last_search_query = None self.last_search_time = None self.start_time = time.time() self.exception_handler_called = False self.token_refresh_timer = None sys.excepthook = self.on_exception uic.loadUi(get_ui_file_path('mainwindow.ui'), self) TriblerRequestManager.window = self self.tribler_status_bar.hide() # Load dynamic widgets uic.loadUi(get_ui_file_path('torrent_channel_list_container.ui'), self.channel_page_container) self.channel_torrents_list = self.channel_page_container.items_list self.channel_torrents_detail_widget = self.channel_page_container.details_tab_widget self.channel_torrents_detail_widget.initialize_details_widget() self.channel_torrents_list.itemSelectionChanged.connect( self.channel_page.clicked_item) uic.loadUi(get_ui_file_path('torrent_channel_list_container.ui'), self.search_page_container) self.search_results_list = self.search_page_container.items_list self.search_torrents_detail_widget = self.search_page_container.details_tab_widget self.search_torrents_detail_widget.initialize_details_widget() self.search_results_list.itemClicked.connect( self.on_channel_item_click) self.search_results_list.itemSelectionChanged.connect( self.search_results_page.clicked_item) self.token_balance_widget.mouseReleaseEvent = self.on_token_balance_click def on_state_update(new_state): self.loading_text_label.setText(new_state) self.core_manager.core_state_update.connect(on_state_update) self.magnet_handler = MagnetHandler(self.window) QDesktopServices.setUrlHandler("magnet", self.magnet_handler, "on_open_magnet_link") self.debug_pane_shortcut = QShortcut(QKeySequence("Ctrl+d"), self) self.debug_pane_shortcut.activated.connect( self.clicked_menu_button_debug) # Remove the focus rect on OS X for widget in self.findChildren(QLineEdit) + self.findChildren( QListWidget) + self.findChildren(QTreeWidget): widget.setAttribute(Qt.WA_MacShowFocusRect, 0) self.menu_buttons = [ self.left_menu_button_home, self.left_menu_button_search, self.left_menu_button_my_channel, self.left_menu_button_subscriptions, self.left_menu_button_video_player, self.left_menu_button_downloads, self.left_menu_button_discovered ] self.video_player_page.initialize_player() self.search_results_page.initialize_search_results_page() self.settings_page.initialize_settings_page() self.subscribed_channels_page.initialize() self.edit_channel_page.initialize_edit_channel_page() self.downloads_page.initialize_downloads_page() self.home_page.initialize_home_page() self.loading_page.initialize_loading_page() self.discovering_page.initialize_discovering_page() self.discovered_page.initialize_discovered_page() self.trust_page.initialize_trust_page() self.stackedWidget.setCurrentIndex(PAGE_LOADING) # Create the system tray icon if QSystemTrayIcon.isSystemTrayAvailable(): self.tray_icon = QSystemTrayIcon() use_monochrome_icon = get_gui_setting(self.gui_settings, "use_monochrome_icon", False, is_bool=True) self.update_tray_icon(use_monochrome_icon) # Create the tray icon menu menu = self.create_add_torrent_menu() show_downloads_action = QAction('Show downloads', self) show_downloads_action.triggered.connect( self.clicked_menu_button_downloads) token_balance_action = QAction('Show token balance', self) token_balance_action.triggered.connect( lambda: self.on_token_balance_click(None)) quit_action = QAction('Quit Tribler', self) quit_action.triggered.connect(self.close_tribler) menu.addSeparator() menu.addAction(show_downloads_action) menu.addAction(token_balance_action) menu.addSeparator() menu.addAction(quit_action) self.tray_icon.setContextMenu(menu) else: self.tray_icon = None self.hide_left_menu_playlist() self.left_menu_button_debug.setHidden(True) self.top_menu_button.setHidden(True) self.left_menu.setHidden(True) self.token_balance_widget.setHidden(True) self.settings_button.setHidden(True) self.add_torrent_button.setHidden(True) self.top_search_bar.setHidden(True) # Set various icons self.top_menu_button.setIcon(QIcon(get_image_path('menu.png'))) self.search_completion_model = QStringListModel() completer = QCompleter() completer.setModel(self.search_completion_model) completer.setCompletionMode(QCompleter.UnfilteredPopupCompletion) self.item_delegate = QStyledItemDelegate() completer.popup().setItemDelegate(self.item_delegate) completer.popup().setStyleSheet(""" QListView { background-color: #404040; } QListView::item { color: #D0D0D0; padding-top: 5px; padding-bottom: 5px; } QListView::item:hover { background-color: #707070; } """) self.top_search_bar.setCompleter(completer) # Toggle debug if developer mode is enabled self.window().left_menu_button_debug.setHidden(not get_gui_setting( self.gui_settings, "debug", False, is_bool=True)) # Start Tribler self.core_manager.start(core_args=core_args, core_env=core_env) self.core_manager.events_manager.received_search_result_channel.connect( self.search_results_page.received_search_result_channel) self.core_manager.events_manager.received_search_result_torrent.connect( self.search_results_page.received_search_result_torrent) self.core_manager.events_manager.torrent_finished.connect( self.on_torrent_finished) self.core_manager.events_manager.new_version_available.connect( self.on_new_version_available) self.core_manager.events_manager.tribler_started.connect( self.on_tribler_started) self.core_manager.events_manager.events_started.connect( self.on_events_started) self.core_manager.events_manager.low_storage_signal.connect( self.on_low_storage) self.core_manager.events_manager.credit_mining_signal.connect( self.on_credit_mining_error) # Install signal handler for ctrl+c events def sigint_handler(*_): self.close_tribler() signal.signal(signal.SIGINT, sigint_handler) self.installEventFilter(self.video_player_page) # Resize the window according to the settings center = QApplication.desktop().availableGeometry(self).center() pos = self.gui_settings.value( "pos", QPoint(center.x() - self.width() * 0.5, center.y() - self.height() * 0.5)) size = self.gui_settings.value("size", self.size()) self.move(pos) self.resize(size) self.show()
class FilteringComboBox(Widget): """Combination of QCombobox and QLineEdit with autocompletionself. Line edit and completer model is taken from QSqlTable mod Parameters: table (str): db table name containing data for combobox column (str): column name containing data for combobox color (str): 'rgb(r, g, b)' used for primary color font_size (int): default text font size in pt _model (QSqlTableModel): data model _col (int): display data model source coulumn _proxy (QSortFilterProxyModel): completer data model. _proxy.sourceModel() == _model _le (QLineEdit): QCombobox LineEdit Methods: createEditor(): (Widget): returns user input widgets value(): (str): returns user input text value setValue(value(str)): sets editor widget display value style(): (str): Returns CSS stylesheet string for input widget updateModel(): updates input widget model Args: table (str): db table name containing data for combobox column (str): column name containing data for combobox """ def __init__(self, parent, placeholderText, table, column, color='rgb(0,145,234)', image=''): self.table = table self.column = column self.color = color super().__init__(parent, placeholderText, image) self.updateModel() def createEditor(self): # setup data model self._model = QSqlTableModel() self._model.setTable(self.table) self._col = self._model.fieldIndex(self.column) # setup filter model for sorting and filtering self._proxy = QSortFilterProxyModel() self._proxy.setFilterCaseSensitivity(Qt.CaseInsensitive) self._proxy.setSourceModel(self._model) self._proxy.setFilterKeyColumn(self._col) # setup completer self._completer = QCompleter() self._completer.setModel(self._proxy) self._completer.setCompletionColumn(self._col) self._completer.setCompletionMode(QCompleter.UnfilteredPopupCompletion) # setup combobox editor = QComboBox() editor.setModel(self._proxy) editor.setModelColumn(self._col) editor.setEditable(True) editor.setFocusPolicy(Qt.StrongFocus) editor.setInsertPolicy(QComboBox.NoInsert) editor.setCompleter(self._completer) # setup connections editor.currentTextChanged[str].connect(self.onActivated) # setup editor appearence style = self.style() editor.setStyleSheet(style) editor.lineEdit().setStyleSheet(style) font = editor.font() self._completer.popup().setFont(font) return editor @pyqtSlot(str) def onActivated(self, text): print('combo_box filter text', text) if not text: # placeholder text displayed and label is not visible self._editor.setCurrentIndex(-1) if self.toggle: self._label.showLabel(False) else: # selected text is displayed and label is visible # self._editor.showPopup() # self._editor.lineEdit().setFocus() print('current copmpletion string', self._completer.currentCompletion()) self._proxy.setFilterFixedString(text) if self.toggle: self._label.showLabel(True) def style(self): """Returns stylesheet for editors Returns: style (str) """ style = """ QLineEdit {{ border: none; padding-bottom: 2px; border-bottom: 1px solid rgba(0,0,0,0.42); background-color: white; color: rgba(0,0,0,0.42); font-size: {font_size}pt;}} QLineEdit:editable {{ padding-bottom: 2px; border-bottom: 1px rgba(0,0,0,0.42); color: rgba(0,0,0,0.42);}} QLineEdit:disabled {{ border: none; padding-bottom: 2px; border-bottom: 1px rgba(0,0,0,0.42); color: rgba(0,0,0,0.38);}} QLineEdit:hover {{ padding-bottom: 2px; border-bottom: 2px solid rgba(0,0,0,0.6); color: rgba(0,0,0,0.54); }} QLineEdit:focus {{ padding-bottom: 2px; border-bottom: 2px solid {color}; color: rgba(0,0,0,0.87);}} QLineEdit:pressed {{ border-bottom: 2px {color}; font: bold; color: rgba(0,0,0,0.87)}} QComboBox {{ border: none; padding-bottom: 2px; font-size: {font_size}pt; }} QComboBox::down-arrow {{ image: url('dropdown.png'); background-color: white; border: 0px white; padding: 0px; margin: 0px; height:14px; width:14px;}} QComboBox::drop-down{{ subcontrol-position: center right; border: 0px; margin: 0px; }} QComboBox QAbstractItemView {{ font: {font_size};}} """.format(color=self.color, font_size=self._editor_font_size, dropdown=DROPDOWN_PNG) return style def updateModel(self): model = self._editor.model().sourceModel() col = self._editor.modelColumn() model.select() model.sort(col, Qt.AscendingOrder) def value(self): return self._editor.currentText() def setValue(self, value): self._editor.setCurrentText(value)
class BioInfo(estimhab_GUI.StatModUseful): """ This class contains the tab with the biological information (the curves of preference). It inherites from StatModUseful. StatModuseful is a QWidget, with some practical signal (send_log and show_fig) and some functions to find path_im and path_bio (the path where to save image) and to manage lists. """ get_list_merge = pyqtSignal() """ A Pyqtsignal which indicates to chronice_GUI.py that the merge list should be changed. In Main_Windows.py, the new list of merge file is found and send to the ChonicleGui class. """ def __init__(self, path_prj, name_prj, lang='French'): super().__init__() self.lang = lang self.path_prj = path_prj self.name_prj = name_prj self.imfish = '' self.keep_data = None self.path_im_bio = 'biology/figure_pref/' # self.path_bio is defined in StatModUseful. self.data_fish = [] # all data concerning the fish # attribute from the xml which the user can search the database # the name should refect the xml attribute or bio_info.load_xml_name() should be changed # can be changed but with caution # coorect here for new language by adding an attribute in the form "langue"_common_name # stage have to be the first attribute ! self.attribute_acc = [ 'Stage', 'French_common_name', 'English_common_name', 'Code_ONEMA', 'Code_Sandre', 'LatinName', 'CdBiologicalModel' ] self.all_run_choice = [ self.tr('Coarser Substrate'), self.tr('Dominant Substrate'), self.tr('By Percentage'), self.tr('Neglect Substrate') ] self.hdf5_merge = [ ] # the list with the name and path of the hdf5 file self.text_ini = [] # the text with the tooltip #self.name_database = 'pref_bio.db' self.timer = QTimer() self.timer.setInterval(1000) self.running_time = 0 self.timer.timeout.connect(self.show_image_hab) self.plot_new = False self.tooltip = [ ] # the list with tooltip of merge file (useful for chronicle_GUI.py) self.ind_current = None self.init_iu() def init_iu(self): """ Used in the initialization by __init__() """ # the available merged data l0 = QLabel(self.tr('<b> Substrate and hydraulic data </b>')) self.m_all = QComboBox() # create lists with the possible fishes # right buttons for both QListWidget managed in the MainWindows class l1 = QLabel(self.tr('<b> Available Fish and Guild </b>')) l2 = QLabel(self.tr('<b> Selected Fish and Guild </b>')) self.list_f.setSelectionMode(QAbstractItemView.ExtendedSelection) self.list_s.setSelectionMode(QAbstractItemView.ExtendedSelection) # show information about the fish self.list_f.itemClicked.connect(self.show_info_fish_avai) self.list_s.itemClicked.connect(self.show_info_fish_sel) self.list_f.itemActivated.connect(self.show_info_fish_avai) self.list_s.itemActivated.connect(self.show_info_fish_sel) # shwo info if movement of the arrow key self.list_f.itemSelectionChanged.connect(self.show_info_fish) self.list_s.itemSelectionChanged.connect( lambda: self.show_info_fish(True)) self.list_f.setMinimumWidth(280) # run habitat value self.l9 = QLabel(' <b> Options for the computation </b>') self.l9.setAlignment(Qt.AlignBottom) self.choice_run = QComboBox() self.choice_run.addItems(self.all_run_choice) self.runhab = QPushButton(self.tr('Compute Habitat Value')) self.runhab.setStyleSheet( "background-color: #47B5E6; color: white; font: bold") self.runhab.clicked.connect(self.run_habitat_value) self.butfig = QPushButton(self.tr("Create figure again")) self.butfig.clicked.connect(self.recreate_fig) if not self.keep_data: self.butfig.setDisabled(True) # find the path bio try: try: docxml = ET.parse( os.path.join(self.path_prj, self.name_prj + '.xml')) root = docxml.getroot() except IOError: # self.send_log.emit("Warning: the xml p file does not exist \n") return except ET.ParseError: self.send_log.emit("Warning: the xml file is not well-formed.\n") return pathbio_child = root.find(".//Path_Bio") if pathbio_child is not None: if os.path.isdir(pathbio_child.text): self.path_bio = pathbio_child.text # info on preference curve l4 = QLabel( self. tr('<b> Information on the suitability curve</b> (Right click on fish name)' )) l5 = QLabel(self.tr('Latin Name: ')) self.com_name = QLabel() l7 = QLabel(self.tr('ONEMA fish code: ')) self.fish_code = QLabel('') l8 = QLabel(self.tr('Description:')) self.descr = QLabel() self.pref_curve = QPushButton(self.tr('Show suitability curve')) self.pref_curve.clicked.connect(self.show_pref) # get a scollable area for the decription which might be long self.scroll = QScrollArea() self.scroll.setFrameStyle(QFrame.NoFrame) self.vbar = self.scroll.verticalScrollBar() self.descr.setWordWrap(True) self.descr.setMaximumSize(200, 210) self.descr.setAlignment(Qt.AlignTop) self.descr.setSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.MinimumExpanding) self.descr.setTextFormat(Qt.RichText) self.scroll.setWidget(self.descr) # to have the Qlabel at the right size self.scroll.setWidgetResizable(True) self.scroll.setStyleSheet('background-color: white') self.vbar.setStyleSheet('background-color: lightGrey') # insist on white background color (for linux, mac) self.setAutoFillBackground(True) p = self.palette() p.setColor(self.backgroundRole(), Qt.white) self.setPalette(p) # image fish self.pic = QLabel() # hydrosignature self.hs = QPushButton( self.tr('Show Measurement Conditions (Hydrosignature)')) self.hs.clicked.connect(self.show_hydrosignature) # fill in list of fish sys.stdout = self.mystdout = StringIO() self.data_fish = bio_info.load_xml_name(self.path_bio, self.attribute_acc) sys.stdout = sys.__stdout__ self.send_err_log() # order data fish by alphabetical order on the first column ind = self.data_fish[:, 0].argsort() self.data_fish = self.data_fish[ind, :] self.list_f.addItems(self.data_fish[:, 0]) # erase fish selection self.butdel = QPushButton(self.tr("Erase All Selection")) self.butdel.clicked.connect(self.remove_all_fish) # fish selected fish self.add_sel_fish() if self.list_s.count() == 0: self.runhab.setDisabled(True) self.runhab.setStyleSheet("background-color: #47B5E6") else: self.runhab.setStyleSheet( "background-color: #47B5E6; color: white; font: bold") # search possibility l3 = QLabel(self.tr('<b> Search biological models </b>')) self.keys = QComboBox() self.keys.addItems(self.attribute_acc[:-1]) self.keys.currentIndexChanged.connect(self.get_autocompletion) l02 = QLabel('is equal to') l02.setAlignment(Qt.AlignCenter) self.cond1 = QLineEdit() self.cond1.returnPressed.connect(self.next_completion) #self.cond1.returnPressed.connect(self.select_fish) self.bs = QPushButton(self.tr('Select suitability curve')) self.bs.clicked.connect(self.select_fish) # add auto-completion self.completer = QCompleter() self.model = QStringListModel() self.completer.setModel(self.model) self.cond1.setCompleter(self.completer) self.get_autocompletion() # fill hdf5 list self.update_merge_list() # layout self.layout4 = QGridLayout() self.layout4.addWidget(l0, 0, 0) self.layout4.addWidget(self.m_all, 0, 1, 1, 2) self.layout4.addWidget(l1, 2, 0) self.layout4.addWidget(l2, 2, 2) self.layout4.addWidget(self.list_f, 3, 0, 3, 2) self.layout4.addWidget(self.list_s, 3, 2, 3, 1) self.layout4.addWidget(l4, 6, 0, 1, 3) self.layout4.addWidget(l5, 7, 0) self.layout4.addWidget(self.com_name, 7, 1) self.layout4.addWidget(l7, 8, 0) self.layout4.addWidget(self.fish_code, 8, 1) self.layout4.addWidget(l8, 9, 0) self.layout4.addWidget(self.scroll, 9, 1, 3, 2) # in fact self.descr is in self.scoll self.layout4.addWidget(self.pic, 11, 0) self.layout4.addWidget(self.l9, 3, 3) self.layout4.addWidget(self.choice_run, 4, 3) self.layout4.addWidget(self.runhab, 5, 3) self.layout4.addWidget(self.butfig, 6, 3) self.layout4.addWidget(self.pref_curve, 9, 3) self.layout4.addWidget(self.hs, 10, 3) self.layout4.addWidget(self.butdel, 2, 3) self.layout4.addWidget(l3, 12, 0) self.layout4.addWidget(self.keys, 13, 0) self.layout4.addWidget(l02, 13, 1) self.layout4.addWidget(self.cond1, 13, 2) self.layout4.addWidget(self.bs, 13, 3) # self.layout4.addItem(spacer1, 0, 2) # self.layout4.addItem(spacer2, 3, 3) self.setLayout(self.layout4) def next_completion(self): """ A small function to use the enter key to select the fish with auto-completion. Adapted from https://stackoverflow.com/questions/9044001/qcompleter-and-tab-key It would be nice to make it work with tab also but it is quite complcated because the tab key is already used by PyQt to go from one windows to the next. """ index = self.completer.currentIndex() self.completer.popup().setCurrentIndex(index) start = self.completer.currentRow() if not self.completer.setCurrentRow(start + 1): self.completer.setCurrentRow(0) self.select_fish() def get_autocompletion(self): """ This function updates the auto-complexton model as a function of the QComboxBox next to it with support for upper and lower case. """ ind = self.keys.currentIndex() if ind == 0: string_all = list( set( list(self.data_fish[:, 1]) + list([item.lower() for item in self.data_fish[:, 1]]) + list([item.upper() for item in self.data_fish[:, 1]]))) elif ind == 1: string_all = list( set( list(self.data_fish[:, 3]) + [item.lower() for item in self.data_fish[:, 3]] + [item.upper() for item in self.data_fish[:, 3]])) elif ind == 2: string_all = list( set( list(self.data_fish[:, 4]) + [item.lower() for item in self.data_fish[:, 4]] + [item.upper() for item in self.data_fish[:, 4]])) elif ind == 3: string_all = list( set( list(self.data_fish[:, 5]) + [item.lower() for item in self.data_fish[:, 5]] + [item.upper() for item in self.data_fish[:, 5]])) elif ind == 4: string_all = list( set( list(self.data_fish[:, 6]) + [item.lower() for item in self.data_fish[:, 6]] + [item.upper() for item in self.data_fish[:, 6]])) elif ind == 5: string_all = list( set( list(self.data_fish[:, 7]) + [item.lower() for item in self.data_fish[:, 7]] + [item.upper() for item in self.data_fish[:, 7]])) else: string_all = '' self.model.setStringList(string_all) def show_info_fish(self, select=False): """ This function shows the useful information concerning the selected fish on the GUI. :param select:If False, the selected items comes from the QListWidgetcontaining the available fish. If True, the items comes the QListWidget with the selected fish """ # get the file if not select: i1 = self.list_f.currentItem( ) # show the info concerning the one selected fish if not i1: return self.ind_current = self.list_f.currentRow() else: found_it = False i1 = self.list_s.currentItem() if not i1: return for m in range(0, self.list_f.count()): self.list_f.setCurrentRow(m) if i1.text() == self.list_f.currentItem().text(): self.ind_current = m found_it = True break if not found_it: self.ind_current = None if i1 is None: return name_fish = i1.text() name_fish = name_fish.split(':')[0] i = np.where(self.data_fish[:, 7] == name_fish)[0] if len(i) > 0: xmlfile = os.path.join(self.path_bio, self.data_fish[i[0], 2]) else: return # open the file try: try: docxml = ET.parse(xmlfile) root = docxml.getroot() except IOError: print("Warning: the xml file does not exist \n") return except ET.ParseError: print("Warning: the xml file is not well-formed.\n") return # get the data code ONEMA # for the moment only one code alternativ possible data = root.find('.//CdAlternative') if data is not None: if data.attrib['OrgCdAlternative']: if data.attrib['OrgCdAlternative'] == 'ONEMA': self.fish_code.setText(data.text) # get the latin name data = root.find('.//LatinName') if data is not None: self.com_name.setText(data.text) # get the description data = root.findall('.//Description') if len(data) > 0: found = False for d in data: if d.attrib['Language'] == self.lang: self.descr.setText(d.text) found = True if not found: self.descr.setText(data[0].text) # get the image fish data = root.find('.//Image') if data is not None: self.imfish = os.path.join(os.getcwd(), self.path_im_bio, data.text) name_imhere = os.path.join(os.getcwd(), self.path_im_bio, data.text) if os.path.isfile(name_imhere): # use full ABSOLUTE path to the image, not relative self.pic.setPixmap( QPixmap(name_imhere).scaled(200, 90, Qt.KeepAspectRatio)) # 800 500 else: self.pic.clear() else: self.pic.clear() def show_info_fish_sel(self): """ This function shows the useful information concerning the already selected fish on the GUI and remove fish from the list of selected fish. This is what happens when the user click on the second QListWidget (the one called selected fish and guild). """ self.show_info_fish(True) self.remove_fish() # Enable the button if self.list_s.count() > 0: self.runhab.setEnabled(True) else: self.runhab.setEnabled(False) def show_info_fish_avai(self): """ This function shows the useful information concerning the available fish on the GUI and add the fish to the selected fish This is what happens when the user click on the first QListWidget (the one called available fish). """ self.show_info_fish(False) self.add_fish() if self.list_s.count() > 0: self.runhab.setEnabled(True) else: self.runhab.setEnabled(False) def show_hydrosignature(self): """ This function make the link with function in bio_info.py which allows to load and plot the data related to the hydrosignature. """ # get the file i = self.list_f.currentRow() xmlfile = os.path.join(self.path_bio, self.data_fish[i, 2]) # do the plot sys.stdout = self.mystdout = StringIO() bio_info.plot_hydrosignature(xmlfile) sys.stdout = sys.__stdout__ self.send_err_log() # show the plot self.show_fig.emit() def select_fish(self): """ This function selects the fish which corresponds at the chosen criteria by the user. The type of criteria is given in the list self.keys and the criteria is given in self.cond1. The condition should exactly match the criteria. Signs such as * do not work. """ # get item s to be selected i = self.keys.currentIndex() # item type cond = self.cond1.text() if i == 0: i = -1 # i +2=1 for the key called 'stage' which is on the second colum of self.data_type data_fish_here = [] for f in self.data_fish[:, i + 2]: data_fish_here.append(f.lower()) data_fish_here = np.array(data_fish_here) if cond.lower() in data_fish_here: inds = np.where(data_fish_here == cond.lower())[0] self.runhab.setEnabled(True) else: self.send_log.emit( self. tr('Warning: No suitability curve found for the last selection. \n' )) return # get the new selection for ind in inds: for i in range(0, self.list_f.count()): item = self.list_f.item(i) if item.text() == self.data_fish[ind, 0]: break self.list_f.setCurrentRow(i) # add the fish to the QListView self.add_fish() def update_merge_list(self): """ This function goes in the projet xml file and gets all available merged data. Usually, it is called by Substrate() (when finished to merge some data) or at the start of HABBY. We add a "tooltip" which indicates the orginal hydraulic and substrate files. """ xmlfile = os.path.join(self.path_prj, self.name_prj + '.xml') # open the file try: try: docxml = ET.parse(xmlfile) root = docxml.getroot() except IOError: self.send_log.emit( "Warning: the xml project file does not exist \n") return except ET.ParseError: self.send_log.emit( "Warning: the xml project file is not well-formed.\n") return self.m_all.clear() self.tooltip = [] self.hdf5_merge = [] # get filename files = root.findall('.//hdf5_mergedata') files = reversed(files) # get the newest first path_hdf5 = self.find_path_hdf5_est() # add it to the list if files is not None: for idx, f in enumerate(files): if os.path.isfile(os.path.join(path_hdf5, f.text)): [sub_ini, hydro_ini ] = load_hdf5.get_initial_files(path_hdf5, f.text) hydro_ini = os.path.basename(hydro_ini) textini = 'Hydraulic: ' + hydro_ini + '\nSubstrate :' + sub_ini if len(f.text) < 55: self.m_all.addItem(f.text) else: blob = f.text[:55] + '...' self.m_all.addItem(blob) self.m_all.setItemData(idx, textini, Qt.ToolTipRole) self.tooltip.append(textini) name = f.text self.hdf5_merge.append(name) else: self.send_log.emit( "Warning: One merge hdf5 file was not found by calc_hab \n" ) # a signal to indicates to Chronicle_GUI.py to update the merge file self.get_list_merge.emit() def show_pref(self): """ This function shows the image of the preference curve of the selected xml file. For this it calls, the functions read_pref and figure_pref of bio_info.py. Hence, this function justs makes the link between the GUI and the functions effectively doing the image. """ if self.ind_current is None: self.send_log.emit( "Warning: No fish selected to create suitability curves \n") return # get the file i = self.ind_current # show the info concerning the one selected fish xmlfile = os.path.join(self.path_bio, self.data_fish[i, 2]) # open the pref sys.stdout = self.mystdout = StringIO() [h_all, vel_all, sub_all, code_fish, name_fish, stages] = bio_info.read_pref(xmlfile) sys.stdout = sys.__stdout__ self.send_err_log() # plot the pref fig_dict = output_fig_GUI.load_fig_option(self.path_prj, self.name_prj) bio_info.figure_pref(h_all, vel_all, sub_all, code_fish, name_fish, stages, fig_opt=fig_dict) # show the image self.show_fig.emit() def run_habitat_value(self): """ This function runs HABBY to get the habitat value based on the data in a "merged" hdf5 file and the chosen preference files. We should not add a comma in the name of the selected fish. """ # disable the button self.runhab.setDisabled(True) self.send_log.emit(" Calculating habitat value... \n") # get the figure options and the type of output to be created fig_dict = output_fig_GUI.load_fig_option(self.path_prj, self.name_prj) # erase the memory of the data for the figure self.keep_data = None # get the name of the xml biological file of the selected fish and the stages to be analyzed pref_list = [] stages_chosen = [] name_fish = [] name_fish_sh = [ ] # because max 10 characters in attribute table of shapefile name_fish_sel = '' # for the xml project file xmlfiles = [] for i in range(0, self.list_s.count()): fish_item = self.list_s.item(i) for j in range(0, self.list_f.count()): if self.data_fish[j][0] == fish_item.text(): pref_list.append(self.data_fish[j][2]) stages_chosen.append(self.data_fish[j][1]) if int(fig_dict['fish_name_type']) == 0: # latin name name_fish.append(self.data_fish[j][7]) elif int(fig_dict['fish_name_type'] ) == 1: # french common name name_fish.append(self.data_fish[j][3]) elif int(fig_dict['fish_name_type'] ) == 2: # english common name name_fish.append(self.data_fish[j][4]) elif int(fig_dict['fish_name_type']) == 3: # code onema name_fish.append(self.data_fish[j][5]) else: name_fish.append(self.data_fish[j][5]) name_fish_sh.append(self.data_fish[j][5][:3] + self.data_fish[j][1][:3]) name_fish_sel += fish_item.text() + ',' xmlfiles.append(self.data_fish[j][2]) # save the selected fish in the xml project file try: try: filename_path_pro = os.path.join(self.path_prj, self.name_prj + '.xml') docxml = ET.parse(filename_path_pro) root = docxml.getroot() except IOError: print("Warning: the xml project file does not exist \n") return except ET.ParseError: print("Warning: the xml project file is not well-formed.\n") return hab_child = root.find(".//Habitat") if hab_child is None: blob = ET.SubElement(root, "Habitat") hab_child = root.find(".//Habitat") fish_child = root.find(".//Habitat/Fish_Selected") if fish_child is None: blob = ET.SubElement(hab_child, "Fish_Selected") fish_child = root.find(".//Habitat/Fish_Selected") fish_child.text = name_fish_sel[:-1] # last comma docxml.write(filename_path_pro) # get the name of the merged file path_hdf5 = self.find_path_hdf5_est() ind = self.m_all.currentIndex() if len(self.hdf5_merge) > 0: hdf5_file = self.hdf5_merge[ind] else: self.runhab.setDisabled(False) self.send_log.emit('Error: No merged hydraulic files available \n') return # get the path where to save the different outputs (function in estimhab_GUI.py) path_txt = self.find_path_text_est() path_im = self.find_path_im_est() path_shp = self.find_path_output_est("Path_Shape") path_para = self.find_path_output_est("Path_Paraview") # get the type of option choosen for the habitat calculation run_choice = self.choice_run.currentIndex() # only useful if we want to also show the 2d figure in the GUI self.hdf5_file = hdf5_file self.path_hdf5 = path_hdf5 path_im_bioa = os.path.join(os.getcwd(), self.path_im_bio) # send the calculation of habitat and the creation of output self.timer.start(1000) # to know when to show image self.q4 = Queue() self.p4 = Process(target=calcul_hab.calc_hab_and_output, args=(hdf5_file, path_hdf5, pref_list, stages_chosen, name_fish, name_fish_sh, run_choice, self.path_bio, path_txt, path_shp, path_para, path_im, self.q4, False, fig_dict, path_im_bioa, xmlfiles)) self.p4.start() # log self.send_log.emit("py file1='" + hdf5_file + "'") self.send_log.emit( "py path1= os.path.join(path_prj, 'fichier_hdf5')") self.send_log.emit("py pref_list= ['" + "', '".join(pref_list) + "']") self.send_log.emit("py stages= ['" + "', '".join(stages_chosen) + "']") self.send_log.emit("py type=" + str(run_choice)) self.send_log.emit("py name_fish1 = ['" + "', '".join(name_fish) + "']") self.send_log.emit("py name_fish2 = ['" + "', '".join(name_fish_sh) + "']") self.send_log.emit( "py calcul_hab.calc_hab_and_output(file1, path1 ,pref_list, stages, name_fish1, name_fish2, type, " "path_bio, path_prj, path_prj, path_prj, path_prj, [], True, [])") self.send_log.emit("restart RUN_HABITAT") self.send_log.emit("restart file1: " + hdf5_file) self.send_log.emit("restart list of preference file: " + ",".join(pref_list)) self.send_log.emit("restart stages chosen: " + ",".join(stages_chosen)) self.send_log.emit("restart type of calculation: " + str(run_choice)) def recreate_fig(self): """ This function use show_image_hab() to recreate the habitat figures shown before """ self.plot_new = True self.show_image_hab() def show_image_hab(self): """ This function is linked with the timer started in run_habitat_value. It is run regulary and check if the function on the second thread have finised created the figures. If yes, this function create the 1d figure for the HABBY GUI. """ # say in the Stauts bar that the processus is alive if self.p4.is_alive(): self.running_time += 1 # this is useful for GUI to update the running, should be logical with self.Timer() # get the langugage fig_dict = output_fig_GUI.load_fig_option(self.path_prj, self.name_prj) # send the message if fig_dict['language'] == str(1): # it is necssary to start this string with Process to see it in the Statusbar self.send_log.emit("Processus 'Habitat' fonctionne depuis " + str(self.running_time) + " sec") else: # it is necssary to start this string with Process to see it in the Statusbar self.send_log.emit( "Process 'Habitat' is alive and run since " + str(self.running_time) + " sec") # when the loading is finished if not self.q4.empty() or (self.keep_data is not None and self.plot_new): if self.keep_data is None: self.timer.stop() data_second = self.q4.get() self.mystdout = data_second[0] area_all = data_second[1] spu_all = data_second[2] name_fish = data_second[3] name_base = data_second[4] vh_all_t_sp = data_second[5] self.send_err_log() self.keep_data = data_second else: self.timer.stop() area_all = self.keep_data[1] spu_all = self.keep_data[2] name_fish = self.keep_data[3] name_base = self.keep_data[4] vh_all_t_sp = self.keep_data[5] # give the possibility of sending a new simulation self.runhab.setDisabled(False) if len(name_fish) > 0: if isinstance(name_fish[0], int): self.running_time = 0 self.send_log.emit("clear status bar") return # show one image (relatively quick to create) #sys.stdout = self.mystdout = StringIO() path_im = self.find_path_im_est() fig_dict = output_fig_GUI.load_fig_option(self.path_prj, self.name_prj) sim_name = load_hdf5.load_timestep_name(self.hdf5_file, self.path_hdf5) if fig_dict['erase_id'] == 'True': erase_id = True else: erase_id = False calcul_hab.save_hab_fig_spu(area_all, spu_all, name_fish, path_im, name_base, fig_dict, sim_name, erase_id) for t in fig_dict['time_step']: # if print last and first time step and one time step only, only print it once if t == -1 and len( vh_all_t_sp[0]) == 2 and 1 in fig_dict['time_step']: pass else: calcul_hab.save_vh_fig_2d(self.hdf5_file, self.path_hdf5, [vh_all_t_sp[0]], path_im, name_fish, name_base, fig_dict, [t], save_fig=False) # sys.stdout = sys.__stdout__ #self.send_err_log() # show figure self.show_fig.emit() # enable the button to call this function directly again to redo the figure self.butfig.setEnabled(True) # put the timer back to zero and clear status bar self.running_time = 0 self.send_log.emit("clear status bar") self.plot_new = False if not self.p4.is_alive(): # enable the button to call this functin directly again self.butfig.setEnabled(True) self.timer.stop() # put the timer back to zero self.running_time = 0 self.send_log.emit("clear status bar")
def __init__(self, core_args=None, core_env=None, api_port=None): QMainWindow.__init__(self) QCoreApplication.setOrganizationDomain("nl") QCoreApplication.setOrganizationName("TUDelft") QCoreApplication.setApplicationName("Tribler") QCoreApplication.setAttribute(Qt.AA_UseHighDpiPixmaps) self.gui_settings = QSettings() api_port = api_port or int(get_gui_setting(self.gui_settings, "api_port", DEFAULT_API_PORT)) dispatcher.update_worker_settings(port=api_port) self.navigation_stack = [] self.tribler_started = False self.tribler_settings = None self.debug_window = None self.core_manager = CoreManager(api_port) self.pending_requests = {} self.pending_uri_requests = [] self.download_uri = None self.dialog = None self.new_version_dialog = None self.start_download_dialog_active = False self.request_mgr = None self.search_request_mgr = None self.search_suggestion_mgr = None self.selected_torrent_files = [] self.vlc_available = True self.has_search_results = False self.last_search_query = None self.last_search_time = None self.start_time = time.time() self.exception_handler_called = False self.token_refresh_timer = None self.shutdown_timer = None self.add_torrent_url_dialog_active = False sys.excepthook = self.on_exception uic.loadUi(get_ui_file_path('mainwindow.ui'), self) TriblerRequestManager.window = self self.tribler_status_bar.hide() self.token_balance_widget.mouseReleaseEvent = self.on_token_balance_click def on_state_update(new_state): self.loading_text_label.setText(new_state) self.core_manager.core_state_update.connect(on_state_update) self.magnet_handler = MagnetHandler(self.window) QDesktopServices.setUrlHandler("magnet", self.magnet_handler, "on_open_magnet_link") self.debug_pane_shortcut = QShortcut(QKeySequence("Ctrl+d"), self) self.debug_pane_shortcut.activated.connect(self.clicked_menu_button_debug) self.import_torrent_shortcut = QShortcut(QKeySequence("Ctrl+o"), self) self.import_torrent_shortcut.activated.connect(self.on_add_torrent_browse_file) self.add_torrent_url_shortcut = QShortcut(QKeySequence("Ctrl+i"), self) self.add_torrent_url_shortcut.activated.connect(self.on_add_torrent_from_url) # Remove the focus rect on OS X for widget in self.findChildren(QLineEdit) + self.findChildren(QListWidget) + self.findChildren(QTreeWidget): widget.setAttribute(Qt.WA_MacShowFocusRect, 0) self.menu_buttons = [self.left_menu_button_home, self.left_menu_button_search, self.left_menu_button_my_channel, self.left_menu_button_subscriptions, self.left_menu_button_video_player, self.left_menu_button_downloads, self.left_menu_button_discovered] self.video_player_page.initialize_player() self.search_results_page.initialize_search_results_page(self.gui_settings) self.settings_page.initialize_settings_page() self.subscribed_channels_page.initialize() self.edit_channel_page.initialize_edit_channel_page(self.gui_settings) self.downloads_page.initialize_downloads_page() self.home_page.initialize_home_page() self.loading_page.initialize_loading_page() self.discovering_page.initialize_discovering_page() self.discovered_page.initialize_discovered_page(self.gui_settings) self.channel_page.initialize_channel_page(self.gui_settings) self.trust_page.initialize_trust_page() self.token_mining_page.initialize_token_mining_page() self.stackedWidget.setCurrentIndex(PAGE_LOADING) # Create the system tray icon if QSystemTrayIcon.isSystemTrayAvailable(): self.tray_icon = QSystemTrayIcon() use_monochrome_icon = get_gui_setting(self.gui_settings, "use_monochrome_icon", False, is_bool=True) self.update_tray_icon(use_monochrome_icon) # Create the tray icon menu menu = self.create_add_torrent_menu() show_downloads_action = QAction('Show downloads', self) show_downloads_action.triggered.connect(self.clicked_menu_button_downloads) token_balance_action = QAction('Show token balance', self) token_balance_action.triggered.connect(lambda: self.on_token_balance_click(None)) quit_action = QAction('Quit Tribler', self) quit_action.triggered.connect(self.close_tribler) menu.addSeparator() menu.addAction(show_downloads_action) menu.addAction(token_balance_action) menu.addSeparator() menu.addAction(quit_action) self.tray_icon.setContextMenu(menu) else: self.tray_icon = None self.hide_left_menu_playlist() self.left_menu_button_debug.setHidden(True) self.top_menu_button.setHidden(True) self.left_menu.setHidden(True) self.token_balance_widget.setHidden(True) self.settings_button.setHidden(True) self.add_torrent_button.setHidden(True) self.top_search_bar.setHidden(True) # Set various icons self.top_menu_button.setIcon(QIcon(get_image_path('menu.png'))) self.search_completion_model = QStringListModel() completer = QCompleter() completer.setModel(self.search_completion_model) completer.setCompletionMode(QCompleter.UnfilteredPopupCompletion) self.item_delegate = QStyledItemDelegate() completer.popup().setItemDelegate(self.item_delegate) completer.popup().setStyleSheet(""" QListView { background-color: #404040; } QListView::item { color: #D0D0D0; padding-top: 5px; padding-bottom: 5px; } QListView::item:hover { background-color: #707070; } """) self.top_search_bar.setCompleter(completer) # Toggle debug if developer mode is enabled self.window().left_menu_button_debug.setHidden( not get_gui_setting(self.gui_settings, "debug", False, is_bool=True)) # Start Tribler self.core_manager.start(core_args=core_args, core_env=core_env) self.core_manager.events_manager.torrent_finished.connect(self.on_torrent_finished) self.core_manager.events_manager.new_version_available.connect(self.on_new_version_available) self.core_manager.events_manager.tribler_started.connect(self.on_tribler_started) self.core_manager.events_manager.events_started.connect(self.on_events_started) self.core_manager.events_manager.low_storage_signal.connect(self.on_low_storage) self.core_manager.events_manager.credit_mining_signal.connect(self.on_credit_mining_error) self.core_manager.events_manager.tribler_shutdown_signal.connect(self.on_tribler_shutdown_state_update) self.core_manager.events_manager.upgrader_tick.connect( lambda text: self.show_status_bar("Upgrading Tribler database: " + text)) self.core_manager.events_manager.upgrader_finished.connect( lambda _: self.hide_status_bar()) self.core_manager.events_manager.received_search_result.connect( self.search_results_page.received_search_result) # Install signal handler for ctrl+c events def sigint_handler(*_): self.close_tribler() signal.signal(signal.SIGINT, sigint_handler) self.installEventFilter(self.video_player_page) # Resize the window according to the settings center = QApplication.desktop().availableGeometry(self).center() pos = self.gui_settings.value("pos", QPoint(center.x() - self.width() * 0.5, center.y() - self.height() * 0.5)) size = self.gui_settings.value("size", self.size()) self.move(pos) self.resize(size) self.show()
class MainWindow(QMainWindow): def __init__(self, parent=None): super(MainWindow, self).__init__() self.setGeometry(300, 300, 400, 300) fid = open('example.db', 'w') fid.close() db = QSqlDatabase.addDatabase("QSQLITE") db.setDatabaseName('example.db') db.open() query = QSqlQuery(db) query.exec_('CREATE TABLE "airports" ("city" TEXT, "code" TEXT)') airports = [('Aberdeen, SD', 'ABR'), ('Abilene, TX', 'ABI'), ('Adak Island, AK', 'ADK'), ('Akiachak, AK', 'KKI'), ('Akiak, AK', 'AKI'), ('Akron/Canton, OH', 'CAK'), ('Akuton, AK', 'KQA'), ('Alakanuk, AK', 'AUK'), ('Alamogordo, NM', 'ALM'), ('Alamosa, CO', 'ALS'), ('Albany, NY', 'ALB'), ('Albuquerque, NM', 'ABQ')] for item in airports: sql = 'INSERT INTO airports(city, code) VALUES(?, ?)' query.prepare(sql) query.addBindValue(item[0]) query.addBindValue(item[1]) query.exec_() query.exec_('SELECT * FROM airports') model = QSqlQueryModel() model.setQuery(query) self.cb = QComboBox(parent=self) self.cb.setModel(model) self.cb.setModelColumn(1) self.cb.setView(QTableView(self.cb)) self.cb.setGeometry(50, 50, 250, 50) self.cb.currentIndexChanged.connect(self.indexer) self.label = QLabel(parent=self) self.label.setGeometry(20, 200, 250, 50) self.cb.view().setMinimumWidth(500) self.cb.setEditable(True) self.completer = QCompleter() self.completer.setCaseSensitivity(False) self.cb.setCompleter(self.completer) self.completer.setModel(model) self.completer.setCompletionColumn(1) self.completer.setPopup(QTableView()) self.completer.popup().setMinimumWidth(500) self.completer.popup().setMinimumHeight(500) self.completer.activated.connect(self.activatedHandler) #@pyqtSlot(QObject) def activatedHandler(self, arg): pass def indexer(self, idx): self.label.setText('%d' % idx)
class TextEditTechnique(QTextEdit): def __init__(self, text_file, input_trigger=None, active=True): super(TextEditTechnique, self).__init__() self._completer = None self.isActive = active word_list = self.get_suggestion_words(text_file) self.input_trigger = input_trigger # connects the text textChanged event self.textChanged.connect(self.handle_input) self.char_length = 1 self.set_completer(word_list) self.technique_used = False self.no_suggestion_input = "~!@#$%^&*()_+{}|:\"<>?,./;'[]\\-= " # gets words for suggestion def get_suggestion_words(self, text_file): words = [] file = open(text_file) for line in file: for word in line.split(' '): word = word.strip().lower().replace('.', '').replace( ',', '').replace(':', '').replace('?', '') if word and word not in words: words.append(word) file.close() return words # initializes the completer def set_completer(self, words): if self._completer is not None: self._completer.activated.disconnect() self._completer = QCompleter(words, self) self._completer.setCaseSensitivity(Qt.CaseInsensitive) self._completer.setWrapAround(False) self._completer.setWidget(self) self._completer.setCompletionMode(QCompleter.PopupCompletion) self._completer.activated.connect(self.insert_sel_suggestion) # insert the selected suggestion and moves the text cursor def insert_sel_suggestion(self, suggestion): if self._completer.widget() is not self: return text_cursor = self.textCursor() extra = len(suggestion) - len(self._completer.completionPrefix()) text_cursor.movePosition(QTextCursor.Left) text_cursor.movePosition(QTextCursor.EndOfWord) text_cursor.insertText(suggestion[-extra:]) self.setTextCursor(text_cursor) # returns the current typed word def text_under_cursor(self): text_cursor = self.textCursor() text_cursor.select(QTextCursor.WordUnderCursor) return text_cursor.selectedText() # sets the widget def focusInEvent(self, e): if self._completer is not None and self.isActive: self._completer.setWidget(self) super(TextEditTechnique, self).focusInEvent(e) def keyPressEvent(self, e): # behaves like a normal input field! if not self.isActive or self._completer is None: super(TextEditTechnique, self).keyPressEvent(e) return # ignore events funktion keys on Textedit if popup is visible --> popup behaves default if self._completer.popup().isVisible(): if e.key() in (Qt.Key_Enter, Qt.Key_Return, Qt.Key_Escape, Qt.Key_Tab, Qt.Key_Backtab): e.ignore() self.technique_used = True return # writes text in the editfield super(TextEditTechnique, self).keyPressEvent(e) ctrl_or_shift = e.modifiers() & (Qt.ControlModifier | Qt.ShiftModifier) if self._completer is None or (ctrl_or_shift and len(e.text()) == 0): return has_modifier = (e.modifiers() != Qt.NoModifier) and not ctrl_or_shift completion_prefix = self.text_under_cursor() # hide the popup for no chars, modifiers, shift and no text if (has_modifier or len(e.text()) == 0 or len(completion_prefix) < self.char_length or e.text()[-1] in self.no_suggestion_input): self._completer.popup().hide() return # shows the popup with the suggested words if completion_prefix != self._completer.completionPrefix(): self._completer.setCompletionPrefix(completion_prefix) self._completer.popup().setCurrentIndex( self._completer.completionModel().index(0, 0)) cursor = self.cursorRect() cursor.setWidth( self._completer.popup().sizeHintForColumn(0) + self._completer.popup().verticalScrollBar().sizeHint().width()) self._completer.complete(cursor) # event called if textedit input has changed def handle_input(self): timestamp = time.time() self.input_trigger.emit(self.toPlainText(), timestamp) # clears the text input def clear_input(self): self.setHtml('') # deactivates the completer def deactivate_completer(self): self.isActive = False # activates the completer def activate_completer(self): self.isActive = True
class MainWindow(QWidget): def __init__(self, app): super().__init__() self.ticket = None self.app = app self.comboxBox = QComboBox(self) self.comboxBox.setEditable(True) # self.comboxBox.setCom self.comboxBox.addItems(slurp_lines(CANDIDATES_FILENAME)) self.comboxBox.setMaximumWidth(WINDOW_WIDTH) self.comboxBox.setCurrentText('') # self.completer.setCaseSensitivity(Qt.CaseInsensitive) self.custom_filter = ExactMultipartFilterModel(self) self.custom_filter.setSourceModel(self.comboxBox.model()) self.comboxBox.lineEdit().textEdited.connect( self.custom_filter.setFilterString) font = QFont() font.setPointSize(font.pointSize() + ADD_TO_FONT_SIZE) self.comboxBox.setFont(font) self.completer = QCompleter(self.comboxBox.model(), self.comboxBox) self.completer.setModel(self.custom_filter) self.completer.setCompletionMode(QCompleter.UnfilteredPopupCompletion) self.completer.popup().setFont(font) self.comboxBox.setCompleter(self.completer) mainLayout = QVBoxLayout(self) # mainLayout.addStretch(1) mainLayout.setSpacing(0) # mainLayout.setMargin(0) mainLayout.setContentsMargins(0, 0, 0, 0) # mainLayout.setMar mainLayout.addWidget(self.comboxBox) self.setLayout(mainLayout) self.setWindowTitle('CompleteBox') self.resize(WINDOW_WIDTH, WINDOW_HEIGHT) self.setWindowIcon(QIcon(ICON_FILENAME)) self.center() self.show() def center(self): qr = self.frameGeometry() cp = QDesktopWidget().availableGeometry().center() qr.moveCenter(cp) self.move(qr.topLeft()) def extractTicketNumber(self): return self.comboxBox.currentText()[:MAX_TICKET_LEN] def keyPressEvent(self, e): if e.key() == Qt.Key_Escape: self.close() elif e.key() == Qt.Key_Return: self.ticket = self.extractTicketNumber() logging.info('ticket: %s', self.ticket) self.close()
class LineEditWithHistory(QLineEdit): elementSelected = pyqtSignal(str) nowTime = 0 oldTime = 0 def __init__(self, parent, title='历史记录', configPath='', qssFilePath="./Resources/stylesheet/style.qss"): super(LineEditWithHistory, self).__init__(parent) self.qssFilePath = qssFilePath if "" == configPath: self.configPath = "./config/{parentName}/LineEditWithHistory.ini".format( parentName=type(parent).__name__) else: if 0 <= os.path.basename(configPath).find("."): self.configPath = configPath else: self.configPath = "{basePath}/LineEditWithHistory.ini".format( basePath=configPath) self.title = title self.sectionName = '{title}'.format(title=self.title) self.HistoryListChanged = False self.commandHasSent = False # 用于存放历史记录的List self.inputList = [] self.__loadHistory() self.__widgetInit() def Exit(self): self.__saveHistory() def __widgetInit(self): # LineEdit设置QCompleter,用于显示历史记录 self.completer = QCompleter(self) self.listModel = QStringListModel(self.inputList, self) self.completer.setCaseSensitivity(Qt.CaseInsensitive) self.completer.setModel(self.listModel) self.completer.activated.connect(self.Slot_completer_activated, type=Qt.QueuedConnection) try: with open(self.qssFilePath, "r") as fh: self.completer.popup().setStyleSheet(fh.read()) except Exception as e: logger.info('读取QSS文件失败' + str(e)) self.setCompleter(self.completer) # 输入完成按下回车后去重添加到历史记录中 self.returnPressed.connect(self.Slot_updateHistoryModule) def __loadHistory(self): historyList = self.inputList historyOp = Public.Public_ConfigOp(self.configPath) rt = historyOp.ReadAllBySection(self.sectionName) if True is rt[0]: for item in rt[1]: if (item[1] not in historyList) and ("" != item[1]): historyList.append(item[1]) else: logger.info(rt[1]) def __saveHistory(self): ipOp = Public.Public_ConfigOp(self.configPath) if True is self.HistoryListChanged: ipOp.RemoveSection(self.sectionName) ipOp.SaveAll() for index, item in enumerate(self.inputList): ipOp.SaveConfig(self.sectionName, str(index), item) def updateHistory(self): content = self.text() if content != "": if content not in self.inputList: self.inputList.append(content) self.listModel.setStringList(self.inputList) self.completer.setCompletionMode(QCompleter.PopupCompletion) self.HistoryListChanged = True self.__sendElement() def Slot_updateHistoryModule(self): self.updateHistory() # 按下回车或单击后恢复显示模式 https://doc.qt.io/qt-5/qcompleter.html#activated def Slot_completer_activated(self, text): self.completer.setCompletionMode(QCompleter.PopupCompletion) self.__sendElement() def __sendElement(self): self.elementSelected.emit(self.text()) def deleteEle(self, ele): if ele in self.inputList: self.inputList.remove(ele) self.listModel.setStringList(self.inputList) self.completer.setCompletionMode(QCompleter.PopupCompletion) self.HistoryListChanged = True def event(self, event): # 按下Tab键时弹出所有记录 if event.type() == QEvent.KeyPress: if event.key() == Qt.Key_Tab: # 设置不过滤显示 https://doc.qt.io/qt-5/qcompleter.html#completionMode-prop if self.completer.completionCount() > 0: self.completer.setCompletionMode( QCompleter.UnfilteredPopupCompletion) self.listModel.setStringList(self.inputList) self.completer.complete() self.completer.popup().show() return True elif event.key() == Qt.Key_Delete: self.deleteEle(self.text()) return super().event(event)
class SeedLayout(QVBoxLayout): def seed_options(self, import_gw_option): vbox = QVBoxLayout() # order matters when we align checkboxes to right if import_gw_option: cb_gw = QCheckBox(_('Gold Wallet seed')) cb_gw.toggled.connect(self.toggle_cb_gold_wallet) cb_gw.setChecked(self.is_gold_wallet_import) vbox.addWidget(cb_gw, alignment=Qt.AlignLeft) self.checkboxes['gw'] = cb_gw self.is_gold_wallet_import = cb_gw.isChecked() if 'bip39' in self.options: def f(b): if b: msg = ' '.join([ '<b>' + _('Warning') + ':</b> ', _('BIP39 seeds can be imported in Electrum, so that users can access funds locked in other wallets.' ), _('We do not guarantee that BIP39 imports will always be supported in Electrum.' ), ]) else: msg = '' self.seed_warning.setText(msg) cb_bip39 = QCheckBox(_('BIP39 seed')) cb_bip39.toggled.connect(f) cb_bip39.toggled.connect(self.toggle_cb_bip39) cb_bip39.setChecked(self.is_bip39) vbox.addWidget(cb_bip39, alignment=Qt.AlignLeft) self.checkboxes['bip39'] = cb_bip39 if 'ext' in self.options: cb_ext = QCheckBox(_('Extend this seed with custom words')) cb_ext.toggled.connect(self.toggle_cb_ext) cb_ext.setChecked(self.is_ext) vbox.addWidget(cb_ext, alignment=Qt.AlignLeft) self.checkboxes['ext'] = cb_ext self.addLayout(vbox) self.is_ext = cb_ext.isChecked() if 'ext' in self.options else False self.is_bip39 = cb_bip39.isChecked( ) if 'bip39' in self.options else False def toggle_cb_ext(self, flag: bool): self.is_ext = flag def toggle_cb_gold_wallet(self, flag: bool): self.is_gold_wallet_import = flag bip39 = self.checkboxes.get('bip39', None) if bip39 and bip39.isChecked() and flag: bip39.setChecked(False) self.on_edit() def toggle_cb_bip39(self, flag: bool): self.is_bip39 = flag gw = self.checkboxes.get('gw', None) if gw and gw.isChecked() and flag: gw.setChecked(False) self.on_edit() def show_default_options(self): for key, cb in self.checkboxes.items(): if key == 'gw': cb.setVisible(True) continue cb.setVisible(False) self.seed_warning.setVisible(False) def show_advanced_options(self): for cb in self.checkboxes.values(): cb.setVisible(True) self.seed_warning.setVisible(True) def __init__(self, seed=None, title=None, icon=True, msg=None, options=None, is_seed=None, passphrase=None, parent=None, for_seed_words=True, imported_wallet=None): QVBoxLayout.__init__(self) self.parent = parent self.options = options if title: self.addWidget(WWLabel(title)) if seed: # "read only", we already have the text if for_seed_words: self.seed_e = ButtonsTextEdit() else: # e.g. xpub self.seed_e = ShowQRTextEdit() self.seed_e.setReadOnly(True) self.seed_e.setText(seed) else: # we expect user to enter text assert for_seed_words self.seed_e = CompletionTextEdit() self.seed_e.setTabChangesFocus(False) # so that tab auto-completes self.is_seed = is_seed self.saved_is_seed = self.is_seed self.seed_e.textChanged.connect(self.on_edit) self.initialize_completer() self.seed_e.setContextMenuPolicy(Qt.PreventContextMenu) self.seed_e.setMaximumHeight(75) hbox = QHBoxLayout() hbox.addWidget(self.seed_e) if icon: logo = QLabel() logo.setPixmap( QPixmap(icon_path("seed.png")).scaledToWidth( 64, mode=Qt.SmoothTransformation)) logo.setMaximumWidth(60) hbox.addWidget(logo) self.addLayout(hbox) self.seed_warning = WWLabel('') # options self.is_bip39 = False self.is_ext = False self.is_gold_wallet_import = False self.checkboxes = {} import_gw = False if imported_wallet in ['standard', '2-key', '3-key']: self.is_gold_wallet_import = True import_gw = True if imported_wallet in ['2-key', '3-key']: self.options = options = ['ext'] if options: self.seed_options(import_gw) if passphrase: hbox = QHBoxLayout() passphrase_e = QLineEdit() passphrase_e.setText(passphrase) passphrase_e.setReadOnly(True) hbox.addWidget(QLabel(_("Your seed extension is") + ':')) hbox.addWidget(passphrase_e) self.addLayout(hbox) self.addStretch(1) if msg: self.seed_warning.setText(seed_warning_msg(seed)) self.addWidget(self.seed_warning) def initialize_completer(self): bip39_english_list = Mnemonic('en').wordlist old_list = electrum.old_mnemonic.words only_old_list = set(old_list) - set(bip39_english_list) self.wordlist = list(bip39_english_list) + list( only_old_list) # concat both lists self.wordlist.sort() class CompleterDelegate(QStyledItemDelegate): def initStyleOption(self, option, index): super().initStyleOption(option, index) # Some people complained that due to merging the two word lists, # it is difficult to restore from a metal backup, as they planned # to rely on the "4 letter prefixes are unique in bip39 word list" property. # So we color words that are only in old list. if option.text in only_old_list: # yellow bg looks ~ok on both light/dark theme, regardless if (un)selected option.backgroundBrush = ColorScheme.YELLOW.as_color( background=True) self.completer = QCompleter(self.wordlist) delegate = CompleterDelegate(self.seed_e) self.completer.popup().setItemDelegate(delegate) self.seed_e.set_completer(self.completer) def get_seed(self): text = self.seed_e.text() seed = ' '.join(text.split()) del text return seed def on_edit(self): s = self.get_seed() if self.is_bip39 or self.is_gold_wallet_import: b = bip39_is_checksum_valid(s)[0] else: b = bool(seed_type(s)) self.parent.next_button.setEnabled(b) # disable suggestions if user already typed an unknown word for word in self.get_seed().split(" ")[:-1]: if word not in self.wordlist: self.seed_e.disable_suggestions() return self.seed_e.enable_suggestions()
class SeedLayout(QVBoxLayout): def seed_options(self): dialog = QDialog() vbox = QVBoxLayout(dialog) seed_types = [(value, title) for value, title in ( ('electrum', _('Electrum')), ('bip39', _('BIP39 seed')), ('slip39', _('SLIP39 seed')), ) if value in self.options or value == 'electrum'] seed_type_values = [t[0] for t in seed_types] if 'ext' in self.options: cb_ext = QCheckBox(_('Extend this seed with custom words')) cb_ext.setChecked(self.is_ext) vbox.addWidget(cb_ext) if len(seed_types) >= 2: def f(choices_layout): self.seed_type = seed_type_values[ choices_layout.selected_index()] self.is_seed = (lambda x: bool( x)) if self.seed_type != 'electrum' else self.saved_is_seed self.slip39_current_mnemonic_invalid = None self.seed_status.setText('') self.on_edit() if self.seed_type == 'bip39': msg = ' '.join([ '<b>' + _('Warning') + ':</b> ', _('BIP39 seeds can be imported in Electrum, so that users can access funds locked in other wallets.' ), _('However, we do not generate BIP39 seeds, because they do not meet our safety standard.' ), _('BIP39 seeds do not include a version number, which compromises compatibility with future software.' ), _('We do not guarantee that BIP39 imports will always be supported in Electrum.' ), ]) elif self.seed_type == 'slip39': msg = ' '.join([ '<b>' + _('Warning') + ':</b> ', _('SLIP39 seeds can be imported in Electrum, so that users can access funds locked in other wallets.' ), _('However, we do not generate SLIP39 seeds.'), ]) else: msg = '' self.update_share_buttons() self.initialize_completer() self.seed_warning.setText(msg) checked_index = seed_type_values.index(self.seed_type) titles = [t[1] for t in seed_types] clayout = ChoicesLayout(_('Seed type'), titles, on_clicked=f, checked_index=checked_index) vbox.addLayout(clayout.layout()) vbox.addLayout(Buttons(OkButton(dialog))) if not dialog.exec_(): return None self.is_ext = cb_ext.isChecked() if 'ext' in self.options else False self.seed_type = seed_type_values[ clayout.selected_index()] if len(seed_types) >= 2 else 'electrum' def __init__( self, seed=None, title=None, icon=True, msg=None, options=None, is_seed=None, passphrase=None, parent=None, for_seed_words=True, *, config: 'SimpleConfig', ): QVBoxLayout.__init__(self) self.parent = parent self.options = options self.config = config self.seed_type = 'electrum' if title: self.addWidget(WWLabel(title)) if seed: # "read only", we already have the text if for_seed_words: self.seed_e = ButtonsTextEdit() else: # e.g. xpub self.seed_e = ShowQRTextEdit(config=self.config) self.seed_e.setReadOnly(True) self.seed_e.setText(seed) else: # we expect user to enter text assert for_seed_words self.seed_e = CompletionTextEdit() self.seed_e.setTabChangesFocus(False) # so that tab auto-completes self.is_seed = is_seed self.saved_is_seed = self.is_seed self.seed_e.textChanged.connect(self.on_edit) self.initialize_completer() self.seed_e.setMaximumHeight(75) hbox = QHBoxLayout() if icon: logo = QLabel() logo.setPixmap( QPixmap(icon_path("seed.png")).scaledToWidth( 64, mode=Qt.SmoothTransformation)) logo.setMaximumWidth(60) hbox.addWidget(logo) hbox.addWidget(self.seed_e) self.addLayout(hbox) hbox = QHBoxLayout() hbox.addStretch(1) self.seed_type_label = QLabel('') hbox.addWidget(self.seed_type_label) # options self.is_ext = False if options: opt_button = EnterButton(_('Options'), self.seed_options) hbox.addWidget(opt_button) self.addLayout(hbox) if passphrase: hbox = QHBoxLayout() passphrase_e = QLineEdit() passphrase_e.setText(passphrase) passphrase_e.setReadOnly(True) hbox.addWidget(QLabel(_("Your seed extension is") + ':')) hbox.addWidget(passphrase_e) self.addLayout(hbox) # slip39 shares self.slip39_mnemonic_index = 0 self.slip39_mnemonics = [""] self.slip39_seed = None self.slip39_current_mnemonic_invalid = None hbox = QHBoxLayout() hbox.addStretch(1) self.prev_share_btn = QPushButton(_("Previous share")) self.prev_share_btn.clicked.connect(self.on_prev_share) hbox.addWidget(self.prev_share_btn) self.next_share_btn = QPushButton(_("Next share")) self.next_share_btn.clicked.connect(self.on_next_share) hbox.addWidget(self.next_share_btn) self.update_share_buttons() self.addLayout(hbox) self.addStretch(1) self.seed_status = WWLabel('') self.addWidget(self.seed_status) self.seed_warning = WWLabel('') if msg: self.seed_warning.setText(seed_warning_msg(seed)) self.addWidget(self.seed_warning) def initialize_completer(self): if self.seed_type != 'slip39': bip39_english_list = Mnemonic('en').wordlist old_list = old_mnemonic.wordlist only_old_list = set(old_list) - set(bip39_english_list) self.wordlist = list(bip39_english_list) + list( only_old_list) # concat both lists self.wordlist.sort() class CompleterDelegate(QStyledItemDelegate): def initStyleOption(self, option, index): super().initStyleOption(option, index) # Some people complained that due to merging the two word lists, # it is difficult to restore from a metal backup, as they planned # to rely on the "4 letter prefixes are unique in bip39 word list" property. # So we color words that are only in old list. if option.text in only_old_list: # yellow bg looks ~ok on both light/dark theme, regardless if (un)selected option.backgroundBrush = ColorScheme.YELLOW.as_color( background=True) delegate = CompleterDelegate(self.seed_e) else: self.wordlist = list(slip39.get_wordlist()) delegate = None self.completer = QCompleter(self.wordlist) if delegate: self.completer.popup().setItemDelegate(delegate) self.seed_e.set_completer(self.completer) def get_seed_words(self): return self.seed_e.text().split() def get_seed(self): if self.seed_type != 'slip39': return ' '.join(self.get_seed_words()) else: return self.slip39_seed def on_edit(self): s = ' '.join(self.get_seed_words()) b = self.is_seed(s) if self.seed_type == 'bip39': from electrum.keystore import bip39_is_checksum_valid is_checksum, is_wordlist = bip39_is_checksum_valid(s) status = ('checksum: ' + ('ok' if is_checksum else 'failed') ) if is_wordlist else 'unknown wordlist' label = 'BIP39' + ' (%s)' % status elif self.seed_type == 'slip39': self.slip39_mnemonics[self.slip39_mnemonic_index] = s try: slip39.decode_mnemonic(s) except slip39.Slip39Error as e: share_status = str(e) current_mnemonic_invalid = True else: share_status = _('Valid.') current_mnemonic_invalid = False label = _('SLIP39 share') + ' #%d: %s' % ( self.slip39_mnemonic_index + 1, share_status) # No need to process mnemonics if the current mnemonic remains invalid after editing. if not (self.slip39_current_mnemonic_invalid and current_mnemonic_invalid): self.slip39_seed, seed_status = slip39.process_mnemonics( self.slip39_mnemonics) self.seed_status.setText(seed_status) self.slip39_current_mnemonic_invalid = current_mnemonic_invalid b = self.slip39_seed is not None self.update_share_buttons() else: t = seed_type(s) label = _('Seed Type') + ': ' + t if t else '' if t and not b: # electrum seed, but does not conform to dialog rules msg = ' '.join([ '<b>' + _('Warning') + ':</b> ', _("Looks like you have entered a valid seed of type '{}' but this dialog does not support such seeds." ).format(t), _("If unsure, try restoring as '{}'.").format( _("Standard wallet")), ]) self.seed_warning.setText(msg) else: self.seed_warning.setText("") self.seed_type_label.setText(label) self.parent.next_button.setEnabled(b) # disable suggestions if user already typed an unknown word for word in self.get_seed_words()[:-1]: if word not in self.wordlist: self.seed_e.disable_suggestions() return self.seed_e.enable_suggestions() def update_share_buttons(self): if self.seed_type != 'slip39': self.prev_share_btn.hide() self.next_share_btn.hide() return finished = self.slip39_seed is not None self.prev_share_btn.show() self.next_share_btn.show() self.prev_share_btn.setEnabled(self.slip39_mnemonic_index != 0) self.next_share_btn.setEnabled( # already pressed "prev" and undoing that: self.slip39_mnemonic_index < len(self.slip39_mnemonics) - 1 # finished entering latest share and starting new one: or (bool(self.seed_e.text().strip()) and not self.slip39_current_mnemonic_invalid and not finished)) def on_prev_share(self): if not self.slip39_mnemonics[self.slip39_mnemonic_index]: del self.slip39_mnemonics[self.slip39_mnemonic_index] self.slip39_mnemonic_index -= 1 self.seed_e.setText(self.slip39_mnemonics[self.slip39_mnemonic_index]) self.slip39_current_mnemonic_invalid = None def on_next_share(self): if not self.slip39_mnemonics[self.slip39_mnemonic_index]: del self.slip39_mnemonics[self.slip39_mnemonic_index] else: self.slip39_mnemonic_index += 1 if len(self.slip39_mnemonics) <= self.slip39_mnemonic_index: self.slip39_mnemonics.append("") self.seed_e.setFocus() self.seed_e.setText(self.slip39_mnemonics[self.slip39_mnemonic_index]) self.slip39_current_mnemonic_invalid = None
class WebBrowserWebSearchWidget(E5ClearableLineEdit): """ Class implementing a web search widget for the web browser. @signal search(QUrl) emitted when the search should be done """ search = pyqtSignal(QUrl) def __init__(self, mainWindow, parent=None): """ Constructor @param mainWindow reference to the browser main window @type WebBrowserWindow @param parent reference to the parent widget @type QWidget """ super(WebBrowserWebSearchWidget, self).__init__(parent) from E5Gui.E5LineEdit import E5LineEdit from E5Gui.E5LineEditButton import E5LineEditButton from .OpenSearch.OpenSearchManager import OpenSearchManager self.__mw = mainWindow self.__openSearchManager = OpenSearchManager(self) self.__openSearchManager.currentEngineChanged.connect( self.__currentEngineChanged) self.__currentEngine = "" self.__enginesMenu = QMenu(self) self.__enginesMenu.triggered.connect( self.__handleEnginesMenuActionTriggered) self.__engineButton = E5LineEditButton(self) self.__engineButton.setMenu(self.__enginesMenu) self.addWidget(self.__engineButton, E5LineEdit.LeftSide) self.__searchButton = E5LineEditButton(self) self.__searchButton.setIcon(UI.PixmapCache.getIcon("webSearch.png")) self.addWidget(self.__searchButton, E5LineEdit.LeftSide) self.__model = QStandardItemModel(self) self.__completer = QCompleter() self.__completer.setModel(self.__model) self.__completer.setCompletionMode( QCompleter.UnfilteredPopupCompletion) self.__completer.setWidget(self) self.__searchButton.clicked.connect(self.__searchButtonClicked) self.textEdited.connect(self.__textEdited) self.returnPressed.connect(self.__searchNow) self.__completer.activated[QModelIndex].connect( self.__completerActivated) self.__completer.highlighted[QModelIndex].connect( self.__completerHighlighted) self.__enginesMenu.aboutToShow.connect(self.__showEnginesMenu) self.__suggestionsItem = None self.__suggestions = [] self.__suggestTimer = None self.__suggestionsEnabled = Preferences.getWebBrowser( "WebSearchSuggestions") self.__recentSearchesItem = None self.__recentSearches = [] self.__maxSavedSearches = 10 self.__engine = None self.__loadSearches() self.__setupCompleterMenu() self.__currentEngineChanged() def __searchNow(self): """ Private slot to perform the web search. """ searchText = self.text() if not searchText: return import WebBrowser.WebBrowserWindow if WebBrowser.WebBrowserWindow.WebBrowserWindow.isPrivate(): return if searchText in self.__recentSearches: self.__recentSearches.remove(searchText) self.__recentSearches.insert(0, searchText) if len(self.__recentSearches) > self.__maxSavedSearches: self.__recentSearches = self.__recentSearches[:self. __maxSavedSearches] self.__setupCompleterMenu() self.__mw.currentBrowser().setFocus() self.__mw.currentBrowser().load( self.__openSearchManager.currentEngine().searchUrl(searchText)) def __setupCompleterMenu(self): """ Private method to create the completer menu. """ if (not self.__suggestions or (self.__model.rowCount() > 0 and self.__model.item(0) != self.__suggestionsItem)): self.__model.clear() self.__suggestionsItem = None else: self.__model.removeRows(1, self.__model.rowCount() - 1) boldFont = QFont() boldFont.setBold(True) if self.__suggestions: if self.__model.rowCount() == 0: if not self.__suggestionsItem: self.__suggestionsItem = QStandardItem( self.tr("Suggestions")) self.__suggestionsItem.setFont(boldFont) self.__model.appendRow(self.__suggestionsItem) for suggestion in self.__suggestions: self.__model.appendRow(QStandardItem(suggestion)) if not self.__recentSearches: self.__recentSearchesItem = QStandardItem( self.tr("No Recent Searches")) self.__recentSearchesItem.setFont(boldFont) self.__model.appendRow(self.__recentSearchesItem) else: self.__recentSearchesItem = QStandardItem( self.tr("Recent Searches")) self.__recentSearchesItem.setFont(boldFont) self.__model.appendRow(self.__recentSearchesItem) for recentSearch in self.__recentSearches: self.__model.appendRow(QStandardItem(recentSearch)) view = self.__completer.popup() view.setFixedHeight( view.sizeHintForRow(0) * self.__model.rowCount() + view.frameWidth() * 2) self.__searchButton.setEnabled( bool(self.__recentSearches or self.__suggestions)) def __completerActivated(self, index): """ Private slot handling the selection of an entry from the completer. @param index index of the item (QModelIndex) """ if (self.__suggestionsItem and self.__suggestionsItem.index().row() == index.row()): return if (self.__recentSearchesItem and self.__recentSearchesItem.index().row() == index.row()): return self.__searchNow() def __completerHighlighted(self, index): """ Private slot handling the highlighting of an entry of the completer. @param index index of the item (QModelIndex) @return flah indicating a successful highlighting (boolean) """ if (self.__suggestionsItem and self.__suggestionsItem.index().row() == index.row()): return False if (self.__recentSearchesItem and self.__recentSearchesItem.index().row() == index.row()): return False self.setText(index.data()) return True def __textEdited(self, txt): """ Private slot to handle changes of the search text. @param txt search text (string) """ if self.__suggestionsEnabled: if self.__suggestTimer is None: self.__suggestTimer = QTimer(self) self.__suggestTimer.setSingleShot(True) self.__suggestTimer.setInterval(200) self.__suggestTimer.timeout.connect(self.__getSuggestions) self.__suggestTimer.start() else: self.__completer.setCompletionPrefix(txt) self.__completer.complete() def __getSuggestions(self): """ Private slot to get search suggestions from the configured search engine. """ searchText = self.text() if searchText: self.__openSearchManager.currentEngine().requestSuggestions( searchText) def __newSuggestions(self, suggestions): """ Private slot to receive a new list of suggestions. @param suggestions list of suggestions (list of strings) """ self.__suggestions = suggestions self.__setupCompleterMenu() self.__completer.complete() def __showEnginesMenu(self): """ Private slot to handle the display of the engines menu. """ self.__enginesMenu.clear() from .OpenSearch.OpenSearchEngineAction import OpenSearchEngineAction engineNames = self.__openSearchManager.allEnginesNames() for engineName in engineNames: engine = self.__openSearchManager.engine(engineName) action = OpenSearchEngineAction(engine, self.__enginesMenu) action.setData(engineName) self.__enginesMenu.addAction(action) if self.__openSearchManager.currentEngineName() == engineName: action.setCheckable(True) action.setChecked(True) cb = self.__mw.currentBrowser() from .Tools import Scripts script = Scripts.getOpenSearchLinks() cb.page().runJavaScript(script, WebBrowserPage.SafeJsWorld, self.__showEnginesMenuCallback) def __showEnginesMenuCallback(self, res): """ Private method handling the open search links callback. @param res result of the JavaScript @type list of dict """ cb = self.__mw.currentBrowser() if res: self.__enginesMenu.addSeparator() for entry in res: url = cb.url().resolved(QUrl(entry["url"])) title = entry["title"] if url.isEmpty(): continue if not title: title = cb.title() action = self.__enginesMenu.addAction( self.tr("Add '{0}'").format(title)) action.setData(url) action.setIcon(cb.icon()) self.__enginesMenu.addSeparator() self.__enginesMenu.addAction(self.__mw.searchEnginesAction()) if self.__recentSearches: act = self.__enginesMenu.addAction( self.tr("Clear Recent Searches")) act.setData("@@CLEAR@@") def __handleEnginesMenuActionTriggered(self, action): """ Private slot to handle an action of the menu being triggered. @param action reference to the action that triggered @type QAction """ actData = action.data() if isinstance(actData, QUrl): # add search engine self.__openSearchManager.addEngine(actData) elif isinstance(actData, str): # engine name or special action if actData == "@@CLEAR@@": self.clear() else: self.__openSearchManager.setCurrentEngineName(actData) def __searchButtonClicked(self): """ Private slot to show the search menu via the search button. """ self.__setupCompleterMenu() self.__completer.complete() def clear(self): """ Public method to clear all private data. """ self.__recentSearches = [] self.__setupCompleterMenu() super(WebBrowserWebSearchWidget, self).clear() self.clearFocus() def preferencesChanged(self): """ Public method to handle the change of preferences. """ self.__suggestionsEnabled = Preferences.getWebBrowser( "WebSearchSuggestions") if not self.__suggestionsEnabled: self.__suggestions = [] self.__setupCompleterMenu() def saveSearches(self): """ Public method to save the recently performed web searches. """ Preferences.Prefs.settings.setValue('WebBrowser/WebSearches', self.__recentSearches) def __loadSearches(self): """ Private method to load the recently performed web searches. """ searches = Preferences.Prefs.settings.value('WebBrowser/WebSearches') if searches is not None: self.__recentSearches = searches def openSearchManager(self): """ Public method to get a reference to the opensearch manager object. @return reference to the opensearch manager object (OpenSearchManager) """ return self.__openSearchManager def __currentEngineChanged(self): """ Private slot to track a change of the current search engine. """ if self.__openSearchManager.engineExists(self.__currentEngine): oldEngine = self.__openSearchManager.engine(self.__currentEngine) oldEngine.imageChanged.disconnect(self.__engineImageChanged) if self.__suggestionsEnabled: oldEngine.suggestions.disconnect(self.__newSuggestions) newEngine = self.__openSearchManager.currentEngine() if newEngine.networkAccessManager() is None: newEngine.setNetworkAccessManager(self.__mw.networkManager()) newEngine.imageChanged.connect(self.__engineImageChanged) if self.__suggestionsEnabled: newEngine.suggestions.connect(self.__newSuggestions) self.setInactiveText(self.__openSearchManager.currentEngineName()) self.__currentEngine = self.__openSearchManager.currentEngineName() self.__engineButton.setIcon( QIcon( QPixmap.fromImage( self.__openSearchManager.currentEngine().image()))) self.__suggestions = [] self.__setupCompleterMenu() def __engineImageChanged(self): """ Private slot to handle a change of the current search engine icon. """ self.__engineButton.setIcon( QIcon( QPixmap.fromImage( self.__openSearchManager.currentEngine().image()))) def mousePressEvent(self, evt): """ Protected method called by a mouse press event. @param evt reference to the mouse event (QMouseEvent) """ if evt.button() == Qt.XButton1: self.__mw.currentBrowser().triggerPageAction(QWebEnginePage.Back) elif evt.button() == Qt.XButton2: self.__mw.currentBrowser().triggerPageAction( QWebEnginePage.Forward) else: super(WebBrowserWebSearchWidget, self).mousePressEvent(evt)
class ExtendedComboBox(QComboBox): isHidden = pyqtSignal() def __init__(self, gtoAction, parent=None): super(ExtendedComboBox, self).__init__(parent) self.gtomain = gtoAction.gtomain self.debug = gtoAction.debug self.info = gtoAction.info self.config = gtoAction.config self.tools = [] self.doQueryOnShow = False # speed up # // For performance reasons use this policy on large models # // or AdjustToMinimumContentsLengthWithIcon self.setSizeAdjustPolicy(QComboBox.AdjustToMinimumContentsLength) # // Improving Performance: It is possible to give the view hints # // about the data it is handling in order to improve its performance # // when displaying large numbers of items. One approach that can be taken # // for views that are intended to display items with equal sizes # // is to set the uniformItemSizes property to true. self.view().setUniformItemSizes(True) # // This property holds the layout mode for the items. When the mode is Batched, # // the items are laid out in batches of batchSize items, while processing events. # // This makes it possible to instantly view and interact with the visible items # // while the rest are being laid out. self.view().setLayoutMode(QListView.Batched) # // batchSize : int # // This property holds the number of items laid out in each batch # // if layoutMode is set to Batched. The default value is 100." try: self.setInsertPolicy(self.NoInsert) self.setFocusPolicy(Qt.StrongFocus) self.setEditable(True) # add a filter model to filter matching items self.pFilterModel = QSortFilterProxyModel(self) self.pFilterModel.setFilterCaseSensitivity(Qt.CaseInsensitive) self.pFilterModel.setSourceModel(self.model()) # add a completer, which uses the filter model self.completer = QCompleter(self.pFilterModel, self) # always show all (filtered) completions self.completer.setCompletionMode( QCompleter.UnfilteredPopupCompletion) self.setCompleter(self.completer) # connect signals self.lineEdit().textEdited.connect( self.pFilterModel.setFilterFixedString) self.lineEdit().textEdited.connect(self.correctInput) # corrct qt bug: style = "QWidget{font-size: " + str(self.fontInfo().pointSize( )) + "pt;font-family: " + self.fontInfo().family() + ";}" self.completer.popup().setStyleSheet(style) # stat vars self.query = None self.layer = None self.doQueryOnShow = False self.masterExpression = None self.child = None except Exception as e: self.info.err(e) def hideEvent(self, e): self.isHidden.emit() def showEvent(self, e): try: if self.doQueryOnShow: self.buildList() except: pass # on model change, update the models of the filter and completer as well def setModel(self, model): super(ExtendedComboBox, self).setModel(model) self.pFilterModel.setSourceModel(model) self.completer.setModel(self.pFilterModel) # on model column change, update the model column of the filter and completer as well def setModelColumn(self, column): self.completer.setCompletionColumn(column) self.pFilterModel.setFilterKeyColumn(column) super(ExtendedComboBox, self).setModelColumn(column) def setQuery(self, query): self.query = query self.tools = self.query.get('tools', []) self.doQueryOnShow = query.get('load', True) def Layer(self): return self.layer def setLayer(self, layer): self.layer = layer def Expression(self): return self.currentData() def Query(self): return self.query def setChild(self, cbo): try: self.child = cbo except Exception as e: self.info.err(e) # on selection of an item from the completer, select the corresponding item from combobox def on_completer_activated(self, text): try: if text is not None: index = self.findText(text, Qt.MatchContains) self.setCurrentIndex(index) if self.debug: self.info.log(self.objectName(), "index:", index) except Exception as e: self.info.err(e) def correctInput(self, text=None): try: if self.debug: self.info.log("correctInput", text) if text is not None: index = self.findText(text, Qt.MatchContains) if self.debug: self.info.log(self.objectName(), "index:", index) if index == -1: self.setCurrentText(text[:-1]) self.on_completer_activated(text[:-1]) index = self.findText(self.currentText(), Qt.MatchExactly) if index == -1: self.resetFilter(False) else: if index == self.currentIndex(): self.indexChanged(index) else: self.setCurrentIndex(index) except Exception as e: self.info.err(e) def resetFilter(self, resetSelf): try: if self.debug: self.info.log(self.objectName(), "reset") if resetSelf: self.setMasterExpression(None) if self.child: self.child.resetFilter(True) except Exception as e: self.info.err(e) def setMasterExpression(self, masterExpression): try: if self.debug: self.info.log(self.objectName(), "setMasterExpression", self.masterExpression, "to", masterExpression) self.masterExpression = masterExpression if self.doQueryOnShow: self.buildList() else: if self.masterExpression is None: self.clear() else: self.buildList() except Exception as e: self.info.err(e) def buildList(self): if self.debug: self.info.log(self.objectName(), "buildList", self.query) self.setCurrentText('Lade...') self.setEnabled(False) self.repaint() if self.debug: self.info.log(self.objectName(), "excecuteQuery") try: self.currentIndexChanged.disconnect() except: pass try: self.clear() req = qgis.core.QgsFeatureRequest() req.setFlags(qgis.core.QgsFeatureRequest.NoGeometry) req.setSubsetOfAttributes(self.query['fields'], self.layer.fields()) req.addOrderBy(self.query['fields'][0], True, True) if self.masterExpression is not None: req.setFilterExpression(self.masterExpression) values = [] features = self.layer.getFeatures(req) for feat in features: displayStr = '' expr = '' for fn in self.query['fields']: idx = self.layer.fields().indexFromName(fn) if idx != -1: value = feat[fn] if isinstance(value, str): value = "'" + value + "'" if expr == '': expr = expr + '"{0}"='.format(fn) + str(value) else: expr = expr + ' AND ' + '"{0}"='.format(fn) + str( value) displayStr = displayStr + str(feat[fn]) else: displayStr = displayStr + str(fn) if not displayStr in values and displayStr.strip() != '': values.append(displayStr) self.addItem(displayStr, expr) if self.debug: self.info.log(self.objectName(), "items:", self.count()) self.setCurrentIndex(-1) self.setSizeAdjustPolicy(self.AdjustToContents) self.setEnabled(True) self.completer.activated.connect(self.on_completer_activated) self.currentIndexChanged.connect(self.indexChanged) if self.masterExpression is not None: if self.count() == 1: if self.debug: self.info.log(self.objectName(), "self.setCurrentIndex(0)", "self.masterExpression", self.masterExpression) self.setCurrentIndex(0) else: self.setCurrentIndex(-1) except Exception as e: self.info.err(e) def indexChanged(self, index): try: expr = self.currentData() if self.debug: self.info.log(self.objectName(), "cbo Expr:", expr) if self.masterExpression is not None: if expr is not None: expr = self.masterExpression + ' AND ' + expr else: expr = self.masterExpression self.info.log(self.objectName(), "Expr:", expr) if expr is not None: self.layer.selectByExpression(expr) self.gtomain.runcmd(self.tools) if self.child is not None: if self.debug: self.child.objectName() self.child.setMasterExpression(expr) except Exception as e: self.info.err(e)
def __init__(self, core_args=None, core_env=None, api_port=None, api_key=None): QMainWindow.__init__(self) self._logger = logging.getLogger(self.__class__.__name__) QCoreApplication.setOrganizationDomain("nl") QCoreApplication.setOrganizationName("TUDelft") QCoreApplication.setApplicationName("Tribler") self.setWindowIcon(QIcon(QPixmap(get_image_path('tribler.png')))) self.gui_settings = QSettings('nl.tudelft.tribler') api_port = api_port or int( get_gui_setting(self.gui_settings, "api_port", DEFAULT_API_PORT)) api_key = api_key or get_gui_setting( self.gui_settings, "api_key", hexlify(os.urandom(16)).encode('utf-8')) self.gui_settings.setValue("api_key", api_key) api_port = get_first_free_port(start=api_port, limit=100) request_manager.port, request_manager.key = api_port, api_key self.tribler_started = False self.tribler_settings = None # TODO: move version_id to tribler_common and get core version in the core crash message self.tribler_version = version_id self.debug_window = None self.error_handler = ErrorHandler(self) self.core_manager = CoreManager(api_port, api_key, self.error_handler) self.pending_requests = {} self.pending_uri_requests = [] self.download_uri = None self.dialog = None self.create_dialog = None self.chosen_dir = None self.new_version_dialog = None self.start_download_dialog_active = False self.selected_torrent_files = [] self.has_search_results = False self.last_search_query = None self.last_search_time = None self.start_time = time.time() self.token_refresh_timer = None self.shutdown_timer = None self.add_torrent_url_dialog_active = False sys.excepthook = self.error_handler.gui_error uic.loadUi(get_ui_file_path('mainwindow.ui'), self) TriblerRequestManager.window = self self.tribler_status_bar.hide() self.token_balance_widget.mouseReleaseEvent = self.on_token_balance_click def on_state_update(new_state): self.loading_text_label.setText(new_state) connect(self.core_manager.core_state_update, on_state_update) self.magnet_handler = MagnetHandler(self.window) QDesktopServices.setUrlHandler("magnet", self.magnet_handler, "on_open_magnet_link") self.debug_pane_shortcut = QShortcut(QKeySequence("Ctrl+d"), self) connect(self.debug_pane_shortcut.activated, self.clicked_menu_button_debug) self.import_torrent_shortcut = QShortcut(QKeySequence("Ctrl+o"), self) connect(self.import_torrent_shortcut.activated, self.on_add_torrent_browse_file) self.add_torrent_url_shortcut = QShortcut(QKeySequence("Ctrl+i"), self) connect(self.add_torrent_url_shortcut.activated, self.on_add_torrent_from_url) connect(self.top_search_bar.clicked, self.clicked_search_bar) # Remove the focus rect on OS X for widget in self.findChildren(QLineEdit) + self.findChildren( QListWidget) + self.findChildren(QTreeWidget): widget.setAttribute(Qt.WA_MacShowFocusRect, 0) self.menu_buttons = [ self.left_menu_button_downloads, self.left_menu_button_discovered, self.left_menu_button_trust_graph, self.left_menu_button_popular, ] hide_xxx = get_gui_setting(self.gui_settings, "family_filter", True, is_bool=True) self.search_results_page.initialize_content_page(hide_xxx=hide_xxx) self.search_results_page.channel_torrents_filter_input.setHidden(True) self.settings_page.initialize_settings_page() self.downloads_page.initialize_downloads_page() self.loading_page.initialize_loading_page() self.discovering_page.initialize_discovering_page() self.discovered_page.initialize_content_page(hide_xxx=hide_xxx) self.popular_page.initialize_content_page( hide_xxx=hide_xxx, controller_class=PopularContentTableViewController) self.trust_page.initialize_trust_page() self.trust_graph_page.initialize_trust_graph() self.stackedWidget.setCurrentIndex(PAGE_LOADING) # Create the system tray icon if QSystemTrayIcon.isSystemTrayAvailable(): self.tray_icon = QSystemTrayIcon() use_monochrome_icon = get_gui_setting(self.gui_settings, "use_monochrome_icon", False, is_bool=True) self.update_tray_icon(use_monochrome_icon) # Create the tray icon menu menu = self.create_add_torrent_menu() show_downloads_action = QAction('Show downloads', self) connect(show_downloads_action.triggered, self.clicked_menu_button_downloads) token_balance_action = QAction('Show token balance', self) connect(token_balance_action.triggered, lambda _: self.on_token_balance_click(None)) quit_action = QAction('Quit Tribler', self) connect(quit_action.triggered, self.close_tribler) menu.addSeparator() menu.addAction(show_downloads_action) menu.addAction(token_balance_action) menu.addSeparator() menu.addAction(quit_action) self.tray_icon.setContextMenu(menu) else: self.tray_icon = None self.left_menu_button_debug.setHidden(True) self.top_menu_button.setHidden(True) self.left_menu.setHidden(True) self.token_balance_widget.setHidden(True) self.settings_button.setHidden(True) self.add_torrent_button.setHidden(True) self.top_search_bar.setHidden(True) # Set various icons self.top_menu_button.setIcon(QIcon(get_image_path('menu.png'))) self.search_completion_model = QStringListModel() completer = QCompleter() completer.setModel(self.search_completion_model) completer.setCompletionMode(QCompleter.UnfilteredPopupCompletion) self.item_delegate = QStyledItemDelegate() completer.popup().setItemDelegate(self.item_delegate) completer.popup().setStyleSheet(""" QListView { background-color: #404040; } QListView::item { color: #D0D0D0; padding-top: 5px; padding-bottom: 5px; } QListView::item:hover { background-color: #707070; } """) self.top_search_bar.setCompleter(completer) # Toggle debug if developer mode is enabled self.window().left_menu_button_debug.setHidden(not get_gui_setting( self.gui_settings, "debug", False, is_bool=True)) # Start Tribler self.core_manager.start(core_args=core_args, core_env=core_env) connect(self.core_manager.events_manager.torrent_finished, self.on_torrent_finished) connect(self.core_manager.events_manager.new_version_available, self.on_new_version_available) connect(self.core_manager.events_manager.tribler_started, self.on_tribler_started) connect(self.core_manager.events_manager.low_storage_signal, self.on_low_storage) connect(self.core_manager.events_manager.tribler_shutdown_signal, self.on_tribler_shutdown_state_update) connect(self.core_manager.events_manager.config_error_signal, self.on_config_error_signal) # Install signal handler for ctrl+c events def sigint_handler(*_): self.close_tribler() signal.signal(signal.SIGINT, sigint_handler) # Resize the window according to the settings center = QApplication.desktop().availableGeometry(self).center() pos = self.gui_settings.value( "pos", QPoint(center.x() - self.width() * 0.5, center.y() - self.height() * 0.5)) size = self.gui_settings.value("size", self.size()) self.move(pos) self.resize(size) self.show() self.add_to_channel_dialog = AddToChannelDialog(self.window()) self.add_torrent_menu = self.create_add_torrent_menu() self.add_torrent_button.setMenu(self.add_torrent_menu) self.channels_menu_list = self.findChild(ChannelsMenuListWidget, "channels_menu_list") connect(self.channels_menu_list.itemClicked, self.open_channel_contents_page) # The channels content page is only used to show subscribed channels, so we always show xxx # contents in it. connect( self.core_manager.events_manager.node_info_updated, lambda data: self.channels_menu_list.reload_if_necessary([data]), ) connect(self.left_menu_button_new_channel.clicked, self.create_new_channel)
class HelpWebSearchWidget(E5ClearableLineEdit): """ Class implementing a web search widget for the web browser. @signal search(QUrl) emitted when the search should be done """ search = pyqtSignal(QUrl) def __init__(self, parent=None): """ Constructor @param parent reference to the parent widget (QWidget) """ super(HelpWebSearchWidget, self).__init__(parent) from E5Gui.E5LineEdit import E5LineEdit from E5Gui.E5LineEditButton import E5LineEditButton from .OpenSearch.OpenSearchManager import OpenSearchManager self.__mw = parent self.__openSearchManager = OpenSearchManager(self) self.__openSearchManager.currentEngineChanged.connect( self.__currentEngineChanged) self.__currentEngine = "" self.__enginesMenu = QMenu(self) self.__engineButton = E5LineEditButton(self) self.__engineButton.setMenu(self.__enginesMenu) self.addWidget(self.__engineButton, E5LineEdit.LeftSide) self.__searchButton = E5LineEditButton(self) self.__searchButton.setIcon(UI.PixmapCache.getIcon("webSearch.png")) self.addWidget(self.__searchButton, E5LineEdit.LeftSide) self.__model = QStandardItemModel(self) self.__completer = QCompleter() self.__completer.setModel(self.__model) self.__completer.setCompletionMode( QCompleter.UnfilteredPopupCompletion) self.__completer.setWidget(self) self.__searchButton.clicked.connect(self.__searchButtonClicked) self.textEdited.connect(self.__textEdited) self.returnPressed.connect(self.__searchNow) self.__completer.activated[QModelIndex].connect( self.__completerActivated) self.__completer.highlighted[QModelIndex].connect( self.__completerHighlighted) self.__enginesMenu.aboutToShow.connect(self.__showEnginesMenu) self.__suggestionsItem = None self.__suggestions = [] self.__suggestTimer = None self.__suggestionsEnabled = Preferences.getHelp("WebSearchSuggestions") self.__recentSearchesItem = None self.__recentSearches = [] self.__maxSavedSearches = 10 self.__engine = None self.__loadSearches() self.__setupCompleterMenu() self.__currentEngineChanged() def __searchNow(self): """ Private slot to perform the web search. """ searchText = self.text() if not searchText: return globalSettings = QWebSettings.globalSettings() if not globalSettings.testAttribute( QWebSettings.PrivateBrowsingEnabled): if searchText in self.__recentSearches: self.__recentSearches.remove(searchText) self.__recentSearches.insert(0, searchText) if len(self.__recentSearches) > self.__maxSavedSearches: self.__recentSearches = \ self.__recentSearches[:self.__maxSavedSearches] self.__setupCompleterMenu() url = self.__openSearchManager.currentEngine().searchUrl(searchText) self.search.emit(url) def __setupCompleterMenu(self): """ Private method to create the completer menu. """ if not self.__suggestions or \ (self.__model.rowCount() > 0 and self.__model.item(0) != self.__suggestionsItem): self.__model.clear() self.__suggestionsItem = None else: self.__model.removeRows(1, self.__model.rowCount() - 1) boldFont = QFont() boldFont.setBold(True) if self.__suggestions: if self.__model.rowCount() == 0: if not self.__suggestionsItem: self.__suggestionsItem = QStandardItem( self.tr("Suggestions")) self.__suggestionsItem.setFont(boldFont) self.__model.appendRow(self.__suggestionsItem) for suggestion in self.__suggestions: self.__model.appendRow(QStandardItem(suggestion)) if not self.__recentSearches: self.__recentSearchesItem = QStandardItem( self.tr("No Recent Searches")) self.__recentSearchesItem.setFont(boldFont) self.__model.appendRow(self.__recentSearchesItem) else: self.__recentSearchesItem = QStandardItem( self.tr("Recent Searches")) self.__recentSearchesItem.setFont(boldFont) self.__model.appendRow(self.__recentSearchesItem) for recentSearch in self.__recentSearches: self.__model.appendRow(QStandardItem(recentSearch)) view = self.__completer.popup() view.setFixedHeight( view.sizeHintForRow(0) * self.__model.rowCount() + view.frameWidth() * 2) self.__searchButton.setEnabled( bool(self.__recentSearches or self.__suggestions)) def __completerActivated(self, index): """ Private slot handling the selection of an entry from the completer. @param index index of the item (QModelIndex) """ if self.__suggestionsItem and \ self.__suggestionsItem.index().row() == index.row(): return if self.__recentSearchesItem and \ self.__recentSearchesItem.index().row() == index.row(): return self.__searchNow() def __completerHighlighted(self, index): """ Private slot handling the highlighting of an entry of the completer. @param index index of the item (QModelIndex) @return flah indicating a successful highlighting (boolean) """ if self.__suggestionsItem and \ self.__suggestionsItem.index().row() == index.row(): return False if self.__recentSearchesItem and \ self.__recentSearchesItem.index().row() == index.row(): return False self.setText(index.data()) return True def __textEdited(self, txt): """ Private slot to handle changes of the search text. @param txt search text (string) """ if self.__suggestionsEnabled: if self.__suggestTimer is None: self.__suggestTimer = QTimer(self) self.__suggestTimer.setSingleShot(True) self.__suggestTimer.setInterval(200) self.__suggestTimer.timeout.connect(self.__getSuggestions) self.__suggestTimer.start() else: self.__completer.setCompletionPrefix(txt) self.__completer.complete() def __getSuggestions(self): """ Private slot to get search suggestions from the configured search engine. """ searchText = self.text() if searchText: self.__openSearchManager.currentEngine()\ .requestSuggestions(searchText) def __newSuggestions(self, suggestions): """ Private slot to receive a new list of suggestions. @param suggestions list of suggestions (list of strings) """ self.__suggestions = suggestions self.__setupCompleterMenu() self.__completer.complete() def __showEnginesMenu(self): """ Private slot to handle the display of the engines menu. """ self.__enginesMenu.clear() from .OpenSearch.OpenSearchEngineAction import OpenSearchEngineAction engineNames = self.__openSearchManager.allEnginesNames() for engineName in engineNames: engine = self.__openSearchManager.engine(engineName) action = OpenSearchEngineAction(engine, self.__enginesMenu) action.setData(engineName) action.triggered.connect(self.__changeCurrentEngine) self.__enginesMenu.addAction(action) if self.__openSearchManager.currentEngineName() == engineName: action.setCheckable(True) action.setChecked(True) ct = self.__mw.currentBrowser() linkedResources = ct.linkedResources("search") if len(linkedResources) > 0: self.__enginesMenu.addSeparator() for linkedResource in linkedResources: url = QUrl(linkedResource.href) title = linkedResource.title mimetype = linkedResource.type_ if mimetype != "application/opensearchdescription+xml": continue if url.isEmpty(): continue if url.isRelative(): url = ct.url().resolved(url) if not title: if not ct.title(): title = url.host() else: title = ct.title() action = self.__enginesMenu.addAction( self.tr("Add '{0}'").format(title), self.__addEngineFromUrl) action.setData(url) action.setIcon(ct.icon()) self.__enginesMenu.addSeparator() self.__enginesMenu.addAction(self.__mw.searchEnginesAction()) if self.__recentSearches: self.__enginesMenu.addAction(self.tr("Clear Recent Searches"), self.clear) def __changeCurrentEngine(self): """ Private slot to handle the selection of a search engine. """ action = self.sender() if action is not None: name = action.data() self.__openSearchManager.setCurrentEngineName(name) def __addEngineFromUrl(self): """ Private slot to add a search engine given its URL. """ action = self.sender() if action is not None: url = action.data() if not isinstance(url, QUrl): return self.__openSearchManager.addEngine(url) def __searchButtonClicked(self): """ Private slot to show the search menu via the search button. """ self.__setupCompleterMenu() self.__completer.complete() def clear(self): """ Public method to clear all private data. """ self.__recentSearches = [] self.__setupCompleterMenu() super(HelpWebSearchWidget, self).clear() self.clearFocus() def preferencesChanged(self): """ Public method to handle the change of preferences. """ self.__suggestionsEnabled = Preferences.getHelp("WebSearchSuggestions") if not self.__suggestionsEnabled: self.__suggestions = [] self.__setupCompleterMenu() def saveSearches(self): """ Public method to save the recently performed web searches. """ Preferences.Prefs.settings.setValue('Help/WebSearches', self.__recentSearches) def __loadSearches(self): """ Private method to load the recently performed web searches. """ searches = Preferences.Prefs.settings.value('Help/WebSearches') if searches is not None: self.__recentSearches = searches def openSearchManager(self): """ Public method to get a reference to the opensearch manager object. @return reference to the opensearch manager object (OpenSearchManager) """ return self.__openSearchManager def __currentEngineChanged(self): """ Private slot to track a change of the current search engine. """ if self.__openSearchManager.engineExists(self.__currentEngine): oldEngine = self.__openSearchManager.engine(self.__currentEngine) oldEngine.imageChanged.disconnect(self.__engineImageChanged) if self.__suggestionsEnabled: oldEngine.suggestions.disconnect(self.__newSuggestions) newEngine = self.__openSearchManager.currentEngine() if newEngine.networkAccessManager() is None: newEngine.setNetworkAccessManager(self.__mw.networkAccessManager()) newEngine.imageChanged.connect(self.__engineImageChanged) if self.__suggestionsEnabled: newEngine.suggestions.connect(self.__newSuggestions) self.setInactiveText(self.__openSearchManager.currentEngineName()) self.__currentEngine = self.__openSearchManager.currentEngineName() self.__engineButton.setIcon( QIcon( QPixmap.fromImage( self.__openSearchManager.currentEngine().image()))) self.__suggestions = [] self.__setupCompleterMenu() def __engineImageChanged(self): """ Private slot to handle a change of the current search engine icon. """ self.__engineButton.setIcon( QIcon( QPixmap.fromImage( self.__openSearchManager.currentEngine().image()))) def mousePressEvent(self, evt): """ Protected method called by a mouse press event. @param evt reference to the mouse event (QMouseEvent) """ if evt.button() == Qt.XButton1: self.__mw.currentBrowser().pageAction(QWebPage.Back).trigger() elif evt.button() == Qt.XButton2: self.__mw.currentBrowser().pageAction(QWebPage.Forward).trigger() else: super(HelpWebSearchWidget, self).mousePressEvent(evt)
def __init__(self): QMainWindow.__init__(self) self.navigation_stack = [] self.feedback_dialog_is_open = False self.tribler_started = False self.tribler_settings = None self.debug_window = None self.core_manager = CoreManager() self.pending_requests = {} self.pending_uri_requests = [] self.download_uri = None self.dialog = None self.start_download_dialog_active = False self.request_mgr = None self.search_request_mgr = None self.search_suggestion_mgr = None self.selected_torrent_files = [] self.vlc_available = True self.has_search_results = False self.start_time = time.time() sys.excepthook = self.on_exception uic.loadUi(get_ui_file_path('mainwindow.ui'), self) TriblerRequestManager.window = self self.tribler_status_bar.hide() self.magnet_handler = MagnetHandler(self.window) QDesktopServices.setUrlHandler("magnet", self.magnet_handler, "on_open_magnet_link") QCoreApplication.setOrganizationDomain("nl") QCoreApplication.setOrganizationName("TUDelft") QCoreApplication.setApplicationName("Tribler") QCoreApplication.setAttribute(Qt.AA_UseHighDpiPixmaps) self.read_settings() # Remove the focus rect on OS X for widget in self.findChildren(QLineEdit) + self.findChildren( QListWidget) + self.findChildren(QTreeWidget): widget.setAttribute(Qt.WA_MacShowFocusRect, 0) self.menu_buttons = [ self.left_menu_button_home, self.left_menu_button_search, self.left_menu_button_my_channel, self.left_menu_button_subscriptions, self.left_menu_button_video_player, self.left_menu_button_downloads, self.left_menu_button_discovered ] self.video_player_page.initialize_player() self.search_results_page.initialize_search_results_page() self.settings_page.initialize_settings_page() self.subscribed_channels_page.initialize() self.edit_channel_page.initialize_edit_channel_page() self.downloads_page.initialize_downloads_page() self.home_page.initialize_home_page() self.loading_page.initialize_loading_page() self.discovering_page.initialize_discovering_page() self.discovered_page.initialize_discovered_page() self.trust_page.initialize_trust_page() self.stackedWidget.setCurrentIndex(PAGE_LOADING) # Create the system tray icon if QSystemTrayIcon.isSystemTrayAvailable(): self.tray_icon = QSystemTrayIcon() use_monochrome_icon = get_gui_setting(self.gui_settings, "use_monochrome_icon", False, is_bool=True) self.update_tray_icon(use_monochrome_icon) self.hide_left_menu_playlist() self.left_menu_button_debug.setHidden(True) self.top_menu_button.setHidden(True) self.left_menu.setHidden(True) self.trust_button.setHidden(True) self.settings_button.setHidden(True) self.add_torrent_button.setHidden(True) self.top_search_bar.setHidden(True) # Set various icons self.top_menu_button.setIcon(QIcon(get_image_path('menu.png'))) self.search_completion_model = QStringListModel() completer = QCompleter() completer.setModel(self.search_completion_model) completer.setCompletionMode(QCompleter.UnfilteredPopupCompletion) self.item_delegate = QStyledItemDelegate() completer.popup().setItemDelegate(self.item_delegate) completer.popup().setStyleSheet(""" QListView { background-color: #404040; } QListView::item { color: #D0D0D0; padding-top: 5px; padding-bottom: 5px; } QListView::item:hover { background-color: #707070; } """) self.top_search_bar.setCompleter(completer) # Toggle debug if developer mode is enabled self.window().left_menu_button_debug.setHidden(not get_gui_setting( self.gui_settings, "debug", False, is_bool=True)) self.core_manager.start() self.core_manager.events_manager.received_search_result_channel.connect( self.search_results_page.received_search_result_channel) self.core_manager.events_manager.received_search_result_torrent.connect( self.search_results_page.received_search_result_torrent) self.core_manager.events_manager.torrent_finished.connect( self.on_torrent_finished) self.core_manager.events_manager.new_version_available.connect( self.on_new_version_available) self.core_manager.events_manager.tribler_started.connect( self.on_tribler_started) # Install signal handler for ctrl+c events def sigint_handler(*_): self.close_tribler() signal.signal(signal.SIGINT, sigint_handler) self.installEventFilter(self.video_player_page) self.show()
class ElectrumAIRWindow(ElectrumMultikeyWalletWindow): class TX_TYPES(enum.IntEnum): Secure = 0 Secure_Fast = 1 def __init__(self, gui_object: 'ElectrumGui', wallet: 'Abstract_Wallet'): self.wordlist = load_wordlist("english.txt") super().__init__(gui_object=gui_object, wallet=wallet) def create_recovery_tab(self, wallet: 'Abstract_Wallet', config): return RecoveryTabAIR(self, wallet, config) def create_send_tab(self): # A 4-column grid layout. All the stretch is in the last column. # The exchange rate plugin adds a fiat widget in column 2 self.send_grid = grid = QGridLayout() grid.setSpacing(8) grid.setColumnStretch(3, 1) blocking_warning_text = _( 'Warning: Please be aware that in the process of using the Secure \ Transaction feature, a part of the funds left in your wallet might be blocked. This is a normal procedure linked to UTXO \ and the blockchain parameters of the Bitcoin Vault wallet. Your funds will be unblocked once the transaction is verified \ (after approximately 24 hrs) or canceled (within 24 hrs).') self.label_transaction_limitations = QLabel(blocking_warning_text) self.label_transaction_limitations.setStyleSheet( ColorScheme.RED.as_stylesheet(True)) self.label_transaction_limitations.setWordWrap(True) from .paytoedit import PayToEdit self.amount_e = BTCAmountEdit(self.get_decimal_point) self.payto_e = PayToEdit(self) msg = _('Recipient of the funds.') + '\n\n' \ + _( 'You may enter a Bitcoin address, a label from your list of contacts (a list of completions will be proposed), or an alias (email-like address that forwards to a Bitcoin address)') payto_label = HelpLabel(_('Pay to'), msg) grid.addWidget(payto_label, 1, 0) grid.addWidget(self.payto_e, 1, 1, 1, -1) completer = QCompleter() completer.setCaseSensitivity(False) self.payto_e.set_completer(completer) completer.setModel(self.completions) msg = _('Description of the transaction (not mandatory).') + '\n\n' \ + _( 'The description is not sent to the recipient of the funds. It is stored in your wallet file, and displayed in the \'History\' tab.') description_label = HelpLabel(_('Description'), msg) grid.addWidget(description_label, 2, 0) self.message_e = MyLineEdit() self.message_e.setMinimumWidth(700) grid.addWidget(self.message_e, 2, 1, 1, -1) msg = _('Amount to be sent.') + '\n\n' \ + _('The amount will be displayed in red if you do not have enough funds in your wallet.') + ' ' \ + _( 'Note that if you have frozen some of your addresses, the available funds will be lower than your total balance.') + '\n\n' \ + _('Keyboard shortcut: type "!" to send all your coins.') amount_label = HelpLabel(_('Amount'), msg) grid.addWidget(amount_label, 3, 0) grid.addWidget(self.amount_e, 3, 1) self.fiat_send_e = AmountEdit(self.fx.get_currency if self.fx else '') if not self.fx or not self.fx.is_enabled(): self.fiat_send_e.setVisible(False) grid.addWidget(self.fiat_send_e, 3, 2) self.amount_e.frozen.connect( lambda: self.fiat_send_e.setFrozen(self.amount_e.isReadOnly())) self.max_button = EnterButton(_("Max"), self.spend_max) self.max_button.setFixedWidth(100) self.max_button.setCheckable(True) grid.addWidget(self.max_button, 3, 3) def on_tx_type(index): if not self.is_2fa: if self.tx_type_combo.currentIndex() == self.TX_TYPES.Secure: self.instant_privkey_line.setEnabled(False) self.instant_privkey_line.clear() self.label_transaction_limitations.show() elif self.tx_type_combo.currentIndex( ) == self.TX_TYPES.Secure_Fast: self.instant_privkey_line.setEnabled(True) self.label_transaction_limitations.hide() else: if self.tx_type_combo.currentIndex() == self.TX_TYPES.Secure: description_label.setEnabled(True) self.message_e.setEnabled(True) self.label_transaction_limitations.setText( blocking_warning_text) self.label_transaction_limitations.show() elif self.tx_type_combo.currentIndex( ) == self.TX_TYPES.Secure_Fast: self.message_e.setText('') self.label_transaction_limitations.setText( _('Description is not supported for Secure Fast transaction' )) description_label.setEnabled(False) self.message_e.setEnabled(False) msg = _('Choose transaction type.') + '\n\n' + \ _('Secure - confirmed after 24 hours. Can be canceled within that time.') + '\n' + \ _('Secure Fast - confirmed immediately. Cannot be canceled. Requires an additional seed phrase.') tx_type_label = HelpLabel(_('Transaction type'), msg) self.tx_type_combo = QComboBox() self.tx_type_combo.addItems( [_(tx_type.name.replace('_', ' ')) for tx_type in self.TX_TYPES]) self.tx_type_combo.setCurrentIndex(self.TX_TYPES.Secure) self.tx_type_combo.currentIndexChanged.connect(on_tx_type) grid.addWidget(tx_type_label, 4, 0) grid.addWidget(self.tx_type_combo, 4, 1, 1, -1) if not self.is_2fa: instant_privkey_label = HelpLabel(_('Secure Fast Tx seed'), msg) self.instant_privkey_line = CompletionTextEdit() self.instant_privkey_line.setTabChangesFocus(False) self.instant_privkey_line.setEnabled(False) self.instant_privkey_line.textChanged.connect( self.on_instant_priv_key_line_edit) self.instant_privkey_line.setContextMenuPolicy( Qt.PreventContextMenu) # complete line edit with suggestions class CompleterDelegate(QStyledItemDelegate): def initStyleOption(self, option, index): super().initStyleOption(option, index) delegate = CompleterDelegate(self.instant_privkey_line) self.completer = QCompleter(self.wordlist) self.completer.popup().setItemDelegate(delegate) self.instant_privkey_line.set_completer(self.completer) height = self.payto_e.height() self.instant_privkey_line.setMaximumHeight(2 * height) grid.addWidget(instant_privkey_label, 5, 0) grid.addWidget(self.instant_privkey_line, 5, 1, 1, -1) self.save_button = EnterButton(_("Save"), self.do_save_invoice) self.send_button = EnterButton(_("Pay"), self.do_pay) self.clear_button = EnterButton(_("Clear"), self.do_clear) buttons = QHBoxLayout() buttons.addStretch(1) buttons.addWidget(self.clear_button) buttons.addWidget(self.save_button) buttons.addWidget(self.send_button) grid.addLayout(buttons, 6, 1, 1, 4) hbox_transaction_limits = QHBoxLayout() hbox_transaction_limits.addWidget(self.label_transaction_limitations) grid.addLayout(hbox_transaction_limits, 6, 1, 1, 2) self.amount_e.shortcut.connect(self.spend_max) def reset_max(text): self.max_button.setChecked(False) enable = not bool(text) and not self.amount_e.isReadOnly() # self.max_button.setEnabled(enable) self.amount_e.textEdited.connect(reset_max) self.fiat_send_e.textEdited.connect(reset_max) self.set_onchain(False) self.invoices_label = QLabel(_('Outgoing payments')) from .invoice_list import InvoiceList self.invoice_list = InvoiceList(self) vbox0 = QVBoxLayout() vbox0.addLayout(grid) hbox = QHBoxLayout() hbox.addLayout(vbox0) hbox.addStretch(1) w = QWidget() vbox = QVBoxLayout(w) vbox.addLayout(hbox) vbox.addStretch(1) vbox.addWidget(self.invoices_label) vbox.addWidget(self.invoice_list) vbox.setStretchFactor(self.invoice_list, 1000) w.searchable_list = self.invoice_list run_hook('create_send_tab', grid) return w def on_instant_priv_key_line_edit(self): for word in self.get_instant_seed()[:-1]: if word not in self.wordlist: self.instant_privkey_line.disable_suggestions() return self.instant_privkey_line.enable_suggestions() def get_instant_seed(self): text = self.instant_privkey_line.text() words = text.split() del text return words def get_instant_keypair(self): stored_instant_pubkey = self.wallet.storage.get('instant_pubkey') seed = self.get_instant_seed() if not short_mnemonic.is_valid(seed): raise ValueError(_("Invalid Secure Fast Tx seed")) privkey, pubkey = short_mnemonic.seed_to_keypair(seed) del seed if pubkey != stored_instant_pubkey: raise Exception( _("Secure Fast Tx seed not matching any key in this wallet")) return {pubkey: (privkey, True)} def do_pay(self): invoice = self.read_invoice() if not invoice: return keypair = None if self.tx_type_combo.currentIndex() == self.TX_TYPES.Secure_Fast: invoice['txtype'] = TxType.INSTANT.name try: if not self.is_2fa: keypair = self.get_instant_keypair() self.wallet.set_instant() except Exception as e: self.on_error([0, str(e)]) return else: invoice['txtype'] = TxType.ALERT_PENDING.name self.wallet.set_alert() self.wallet.save_invoice(invoice) self.invoice_list.update() self.do_clear() self.do_pay_invoice(invoice, external_keypairs=keypair) def do_pay_invoice(self, invoice, external_keypairs=None): if invoice['type'] == PR_TYPE_ONCHAIN: self.wallet.set_alert() if invoice['txtype'] == TxType.INSTANT.name: try: if not self.is_2fa and external_keypairs == None: external_keypairs = self.get_instant_keypair() self.wallet.set_instant() except Exception as e: self.on_error([0, str(e)]) return outputs = invoice['outputs'] self.pay_onchain_dialog(self.get_coins(), outputs, invoice=invoice, external_keypairs=external_keypairs) else: raise Exception('unknown invoice type') def do_save_invoice(self): invoice = self.read_invoice() if not invoice: return if self.tx_type_combo.currentIndex() == self.TX_TYPES.Secure_Fast: invoice['txtype'] = TxType.INSTANT.name else: invoice['txtype'] = TxType.ALERT_PENDING.name self.wallet.save_invoice(invoice) self.do_clear() self.invoice_list.update() def do_clear(self): self.max_button.setChecked(False) self.payment_request = None self.payto_URI = None self.payto_e.is_pr = False self.is_onchain = False self.set_onchain(False) for e in [self.payto_e, self.message_e, self.amount_e]: e.setText('') e.setFrozen(False) if not self.is_2fa: self.instant_privkey_line.clear() self.tx_type_combo.setCurrentIndex(self.TX_TYPES.Secure) self.update_status() run_hook('do_clear', self) def pay_onchain_dialog(self, inputs, outputs, invoice=None, external_keypairs=None): # trustedcoin requires this if run_hook('abort_send', self): return is_sweep = False make_tx = lambda fee_est: self.wallet.make_unsigned_transaction( coins=inputs, outputs=outputs, fee=fee_est, is_sweep=is_sweep) if self.config.get('advanced_preview'): self.preview_tx_dialog(make_tx, outputs, external_keypairs=external_keypairs, invoice=invoice) return output_values = [x.value for x in outputs] output_value = '!' if '!' in output_values else sum(output_values) d = ConfirmTxDialog(self, make_tx, output_value, is_sweep) d.update_tx() if d.not_enough_funds: self.show_message(_('Not Enough Funds')) return cancelled, is_send, password, tx = d.run() if cancelled: return if is_send: def sign_done(success): if success: if self.is_2fa and (self.wallet.is_instant_mode() or self.wallet.is_recovery_mode()): self.show_psbt_qrcode(tx, invoice=invoice) else: self.broadcast_or_show(tx, invoice=invoice) self.sign_tx_with_password(tx, sign_done, password, external_keypairs) else: self.preview_tx_dialog(make_tx, outputs, external_keypairs=external_keypairs, invoice=invoice) def preview_tx_dialog(self, make_tx, outputs, external_keypairs=None, invoice=None): dialog_class = PreviewPsbtTxDialog \ if self.is_2fa and (self.wallet.is_instant_mode() or self.wallet.is_recovery_mode()) \ else PreviewTxDialog d = dialog_class(make_tx, outputs, external_keypairs, window=self, invoice=invoice) d.show()
class TextStatusEditComplete(TextStatusEdit): """ Adds Completion functions to the base class This class extends 'TextStatusEdit' by: 1. providing a QCompleter to validate lines for the 'fixupText' and 'lineChanged' signals 2. providing a popup for suggested completions as the user is typing 3. auto-completing the line when the user selects a suggestion. The task of auto completion and providing suggestions is provided directly by this class. The task validating and cleaning up text is provided by the PluginFinder. """ def __init__(self, parent: QWidget = None): super().__init__(parent) self._dataModel = None self._monitorDbChanges = False self._enableAutoCompletion = False self._completedAndSelected = False self._completer = QCompleter(self) self._completer.setWidget(self) self._completer.setWrapAround(False) self._completer.setCompletionMode(QCompleter.PopupCompletion) self._completer.setCaseSensitivity(Qt.CaseInsensitive) self._completer.setFilterMode(Qt.MatchStartsWith) self._completer.setModelSorting( QCompleter.CaseInsensitivelySortedModel) self._completer.activated.connect(self.replaceLine) self._pluginFinder = PluginFinder(self._completer, self) self.fixupText.connect(self._pluginFinder.fixupText) self.lineChanged.connect(self._pluginFinder.setRowForLine) QShortcut(Qt.CTRL + Qt.Key_E, self, self.toggleAutoCompletion) QShortcut(Qt.CTRL + Qt.Key_T, self, self.suggestCompletions) # --- Methods related to the completer's underlying data model def setModel(self, model: QAbstractItemModel): self._completer.setModel(model) def _updateModelSignals(self): """ We do not need to check for column changes due to the way our PluginModel is structured. """ if self._dataModel is not None: self._dataModel.rowsMoved.disconnect(self.resetData) self._dataModel.rowsInserted.disconnect(self.resetData) self._dataModel.rowsRemoved.disconnect(self.resetData) self._dataModel.modelReset.disconnect(self.resetData) self._dataModel.dataChanged.disconnect(self.resetData) self._dataModel.layoutChanged.disconnect(self.resetData) if self._monitorDbChanges: self._dataModel = self._completer.model() if self._dataModel is not None: self._dataModel.rowsMoved.connect(self.resetData) self._dataModel.rowsInserted.connect(self.resetData) self._dataModel.rowsRemoved.connect(self.resetData) self._dataModel.modelReset.connect(self.resetData) self._dataModel.dataChanged.connect(self.resetData) self._dataModel.layoutChanged.connect(self.resetData) else: self._dataModel = None def monitorDbChanges(self, enable: bool): """ Enable invalidating line status when the data model changes. Depending on the underlying data model, it may be unnecessary to monitor these changes, or, a higher level class can monitor specific signals more efficiently. So, this is not enabled by default. """ if self._monitorDbChanges == enable: return self._monitorDbChanges = enable if enable: self._dataModel = self._completer.model() self._completer.completionModel().sourceModelChanged.connect( self._updateModelSignals) else: self._completer.completionModel().sourceModelChanged.disconnect( self._updateModelSignals) self._updateModelSignals() # ---- Methods related to line completion def completer(self): return self._completer def enableAutoCompletion(self, enable: bool): self._enableAutoCompletion = enable def toggleAutoCompletion(self): self.enableAutoCompletion(not self._enableAutoCompletion) def _textUnderCursor(self): tc = self.textCursor() if tc.positionInBlock() == 0 and len(tc.block().text()) > 1: tc.movePosition(QTextCursor.NextCharacter) tc.movePosition(QTextCursor.StartOfLine, QTextCursor.KeepAnchor) return tc.selectedText().lstrip() def suggestCompletions(self): if self.isLineInvalid(self.textCursor().blockNumber()): self._suggestCompletionsForText(self._textUnderCursor()) def _suggestCompletionsForText(self, prefix: str): if not prefix: return if prefix != self._completer.completionPrefix(): self._completer.setCompletionPrefix(prefix) self._completer.popup().setCurrentIndex( self._completer.completionModel().index(0, 0)) if self._completer.completionCount() == 1: self._insertSuggestion(self._completer.currentCompletion()) else: rect = self.cursorRect() rect.moveRight(self.statusAreaWidth()) rect.setWidth( self._completer.popup().sizeHintForColumn( self._completer.completionColumn()) + self._completer.popup().verticalScrollBar().sizeHint().width()) self._completer.complete(rect) def _insertSuggestion(self, text: str): """ Only one suggestion matched, prefill line """ cursor = self.textCursor() # handle when cursor is in middle of line if not cursor.atBlockEnd(): cursor.beginEditBlock() cursor.select(QTextCursor.LineUnderCursor) cursor.removeSelectedText() cursor.insertText(text) cursor.movePosition(QTextCursor.StartOfLine) cursor.movePosition(QTextCursor.EndOfLine, QTextCursor.KeepAnchor) self._completedAndSelected = True self.setTextCursor(cursor) cursor.endEditBlock() return # handle when cursor at end of line cursor.beginEditBlock() numCharsToComplete = len(text) - len( self._completer.completionPrefix()) insertionPosition = cursor.position() cursor.movePosition(QTextCursor.EndOfLine, QTextCursor.KeepAnchor) cursor.removeSelectedText() cursor.insertText(text[-numCharsToComplete:]) cursor.setPosition(insertionPosition) cursor.movePosition(QTextCursor.EndOfLine, QTextCursor.KeepAnchor) self._completedAndSelected = True self.setTextCursor(cursor) cursor.endEditBlock() def keyPressEvent(self, event: QKeyEvent): if self._completedAndSelected and self.handledCompletedAndSelected( event): return self._completedAndSelected = False if self._completer.popup().isVisible(): ignoredKeys = [ Qt.Key_Up, Qt.Key_Down, Qt.Key_Enter, Qt.Key_Return, Qt.Key_Tab, Qt.Key_Escape, ] if event.key() in ignoredKeys: event.ignore() return self._completer.popup().hide() super().keyPressEvent(event) if not self._enableAutoCompletion: return ctrlOrShift = (event.modifiers() & Qt.ShiftModifier == Qt.ShiftModifier or event.modifiers() & Qt.ControlModifier == Qt.ControlModifier) if ctrlOrShift and not event.text(): return if self.textCursor().atBlockEnd(): self.suggestCompletions() def mousePressEvent(self, event: QMouseEvent): if self._completedAndSelected: self._completedAndSelected = False self.document().undo() super().mousePressEvent(event) def handledCompletedAndSelected(self, event: QKeyEvent): """ The line is prefilled when only one completion matches. The user can accept the suggestion by pressing 'Enter'. The user can reject the suggestion by pressing 'Esc' or by continuing to type. """ self._completedAndSelected = False cursor = self.textCursor() acceptKeys = [Qt.Key_Enter, Qt.Key_Return, Qt.Key_Tab] if event.key() in acceptKeys: self.replaceLine(self._completer.currentCompletion()) elif event.key() == Qt.Key_Escape: self.document().undo() else: self.document().undo() return False self.setTextCursor(cursor) event.accept() return True def replaceLine(self, text: str): cursor = self.textCursor() cursor.beginEditBlock() cursor.select(QTextCursor.LineUnderCursor) cursor.removeSelectedText() cursor.insertText(text) cursor.movePosition(QTextCursor.EndOfLine) self.setTextCursor(cursor) cursor.endEditBlock() # ---- Methods related to Context Menu def createStandardContextMenu(self, pos: QPoint): menu = super().createStandardContextMenu(pos) menu.addSeparator() autoCompletionAction = menu.addAction( QIcon(), self.tr("Enable Auto Complete"), self.toggleAutoCompletion, QKeySequence(Qt.CTRL + Qt.Key_E), ) autoCompletionAction.setCheckable(True) autoCompletionAction.setChecked(self._enableAutoCompletion) completionAction = menu.addAction( QIcon(), self.tr("Suggest Completions"), self.suggestCompletions, QKeySequence(Qt.CTRL + Qt.Key_T), ) completionAction.setEnabled( self.isLineInvalid(self.textCursor().blockNumber())) return menu
class MySearch(QToolBar): updateBookViewSignal = pyqtSignal(list) def __init__(self, db: MyDb): super(MySearch, self).__init__() self.db = db self.setFont(QFont("", 13)) self.setToolButtonStyle(Qt.ToolButtonTextBesideIcon) self.searchBy = QComboBox() self.searchBy.addItems(['按名称', '按标签', '按内容']) self.searchAttr = self.searchBy.currentText() self.searchBy.currentTextChanged.connect(self.changeAttr) self.searchMode = QComboBox() self.searchMode.addItems(['模糊匹配', '准确匹配', '正则匹配']) self.searchAttrMode = self.searchMode.currentText() self.searchMode.currentTextChanged.connect(self.changeAttrMode) self.inputLine = QLineEdit() self.inputLine.returnPressed.connect(self.onSearch) # 开始搜索 self.inputCompleter = QCompleter() self.inputCompleter.setCaseSensitivity(Qt.CaseInsensitive) # 初始化 model = QStringListModel() notenames = self.db.getAllNoteNames() model.setStringList(notenames) self.inputCompleter.setModel(model) self.inputCompleter.setFilterMode(Qt.MatchExactly) self.inputLine.setCompleter(self.inputCompleter) self.inputCompleter.popup().setFont(QFont("", 14)) self.inputLine.setPlaceholderText("搜索") self.searchAct = QAction(QIcon("img/search-4.png"), "搜索", self) self.searchAct.triggered.connect(self.onSearch) self.addWidget(self.searchBy) self.addWidget(self.searchMode) self.addSeparator() self.addWidget(self.inputLine) self.addAction(self.searchAct) def onSearch(self): notes = self.db.getAllNotes() keyword = self.inputLine.text() if not keyword: notes = [note.name for note in notes] self.updateBookViewSignal.emit(notes) return if self.searchAttr == '按名称': if self.searchAttrMode == '准确匹配': notes = [note.name for note in notes if keyword == note.name] elif self.searchAttrMode == '模糊匹配': notes = [note.name for note in notes if keyword in note.name] else: notes = [ note.name for note in notes if re.match(keyword, note.name) ] elif self.searchAttr == '按标签': if self.searchAttrMode == '准确匹配': notes = [note.name for note in notes if keyword in note.tags] elif self.searchAttrMode == '模糊匹配': notes = [ note.name for note in notes if note.hasTagFuzzy(keyword) ] else: notes = [ note.name for note in notes if note.hasTagReg(keyword) ] elif self.searchAttr == '按内容': notes = [note.name for note in notes if note.textInFile(keyword)] self.updateBookViewSignal.emit(notes) def changeAttr(self, attr): self.searchAttr = attr model = QStringListModel() if attr == '按名称': notenames = self.db.getAllNoteNames() model.setStringList(notenames) elif attr == '按标签': tags = self.db.getAllTags() model.setStringList(tags) else: # 按内容 model.setStringList([]) self.inputCompleter.setModel(model) def changeAttrMode(self, attrMode): self.searchAttrMode = attrMode if attrMode == '准确匹配': self.inputCompleter.setFilterMode(Qt.MatchExactly) elif attrMode == '模糊匹配': self.inputCompleter.setFilterMode(Qt.MatchContains)
class Spdom(WizardWidget): drag_label = "Spatial Domain <spdom>" acceptable_tags = ["spdom", "bounding"] ui_class = UI_spdom.Ui_fgdc_spdom def __init__(self, root_widget=None): self.east = 180 self.west = -180 self.north = 90 self.south = -90 self.valid = True super(self.__class__, self).__init__() self.schema = "bdp" self.root_widget = root_widget self.after_load = False self.in_xml_load = False self.has_rect = True self.completer = QCompleter() self.ui.fgdc_descgeog.setCompleter(self.completer) self.model = QStringListModel() self.completer.setModel(self.model) self.completer.setCaseSensitivity(0) fname = utils.get_resource_path("spatial/BNDCoords.csv") self.bnds_df = pd.read_csv(fname) self.model.setStringList(self.bnds_df["Name"]) self.completer.popup().clicked.connect(self.on_completer_activated) self.completer.popup().selectionModel().selectionChanged.connect( self.on_completer_activated) # self.completer.popup().activated.connect(self.on_completer_activated) def build_ui(self): """ Build and modify this widget's GUI Returns ------- None """ self.ui = self.ui_class() self.ui.setupUi(self) if platform.system() == "Darwin": map_fname = utils.get_resource_path("leaflet/map_mac.html") else: map_fname = utils.get_resource_path("leaflet/map.html") try: self.view = QWebView() self.view.page().mainFrame().addToJavaScriptWindowObject( "Spdom", self) self.view.setUrl(QUrl.fromLocalFile(map_fname)) self.frame = self.view.page().mainFrame() self.view.load( QUrl.fromLocalFile(QtCore.QDir.current().filePath(map_fname))) except AttributeError: self.view = QWebView() self.view.load( QUrl.fromLocalFile(QtCore.QDir.current().filePath(map_fname))) channel = QWebChannel(self.view.page()) jstr = """ var spdom; new QWebChannel(qt.webChannelTransport, function (channel) { spdom = channel.objects.spdom; });""" self.view.page().setWebChannel(channel) self.evaluate_js(jstr) channel.registerObject("spdom", self) self.ui.verticalLayout_3.addWidget(self.view) # setup drag-drop functionality for this widget and all it's children. self.setup_dragdrop(self) self.add_rect() self.raise_() def connect_events(self): self.ui.fgdc_eastbc.editingFinished.connect(self.coord_updated) self.ui.fgdc_westbc.editingFinished.connect(self.coord_updated) self.ui.fgdc_northbc.editingFinished.connect(self.coord_updated) self.ui.fgdc_southbc.editingFinished.connect(self.coord_updated) def on_completer_activated(self, model_index): try: cur_descgeog = model_index.data() except AttributeError: try: cur_descgeog = model_index.indexes()[0].data() except: return try: if self.bnds_df["Name"].str.contains(cur_descgeog).any(): self.ui.fgdc_eastbc.setText( str( float(self.bnds_df[self.bnds_df["Name"] == cur_descgeog]["east"]))) self.ui.fgdc_westbc.setText( str( float(self.bnds_df[self.bnds_df["Name"] == cur_descgeog]["west"]))) self.ui.fgdc_northbc.setText( str( float(self.bnds_df[self.bnds_df["Name"] == cur_descgeog]["north"]))) self.ui.fgdc_southbc.setText( str( float(self.bnds_df[self.bnds_df["Name"] == cur_descgeog]["south"]))) self.add_rect() self.update_map() except: pass # this is a convenience function. # If anything at all happens pass silently def complete_name(self): self.view.page().runJavaScript("addRect();", js_callback) def coord_updated(self): good_coords = self.all_good_coords() try: cur_name = self.sender().objectName() if "fgdc" not in cur_name: return cur_value = self.sender().text() except AttributeError: cur_name = "" cur_value = "" try: cur_value = float(cur_value) except ValueError: pass msg = "" if type(cur_value) != float and cur_value != "": msg = "number entered must be numeric only" elif cur_value == "": msg = "" elif cur_name in ["fgdc_westbc", "fgdc_eastbc" ] and -180 >= cur_value >= 180: msg = "East or West coordinate must be within -180 and 180" elif cur_name in ["fgdc_southbc", "fgdc_northbc" ] and -90 >= cur_value >= 90: msg = "North and South coordinates must be within -90 and 90" elif cur_name == "fgdc_southbc": try: north = float(self.ui.fgdc_northbc.text()) if north <= cur_value: msg = "North coordinate must be greater than South coordinate" except ValueError: pass elif cur_name == "fgdc_northbc": try: south = float(self.ui.fgdc_southbc.text()) if south >= cur_value: msg = "North coordinate must be greater than South coordinate" except ValueError: pass if msg: QMessageBox.warning(self, "Problem bounding coordinates", msg) if good_coords: self.add_rect() else: self.remove_rect() return self.update_map() def update_map(self): jstr = """east = {eastbc}; west = {westbc}; south = {southbc}; north = {northbc}; updateMap(); fitMap(); """.format( **{ "eastbc": self.ui.fgdc_eastbc.text(), "westbc": self.ui.fgdc_westbc.text(), "northbc": self.ui.fgdc_northbc.text(), "southbc": self.ui.fgdc_southbc.text(), }) self.evaluate_js(jstr) def add_rect(self): jstr = """addRect();""" self.evaluate_js(jstr) def remove_rect(self): if self.has_rect: self.has_rect = False jstr = """removeRect();""" self.evaluate_js(jstr) def evaluate_js(self, jstr): """ :param jstr: :return: """ try: self.frame.evaluateJavaScript(jstr) except: self.view.page().runJavaScript(jstr, js_callback) @pyqtSlot(float, float) def on_ne_move(self, lat, lng): if self.in_xml_load: n, e = lat, lng try: s = float(self.ui.fgdc_southbc.text()) w = float(self.ui.fgdc_westbc.text()) bounds = spatial_utils.format_bounding((w, e, n, s)) self.ui.fgdc_eastbc.setText(bounds[1]) self.ui.fgdc_northbc.setText(bounds[2]) except: pass @pyqtSlot(float, float) def on_nw_move(self, lat, lng): if self.in_xml_load: n, w = lat, lng try: s = float(self.ui.fgdc_southbc.text()) e = float(self.ui.fgdc_eastbc.text()) bounds = spatial_utils.format_bounding((w, e, n, s)) self.ui.fgdc_westbc.setText(bounds[0]) self.ui.fgdc_northbc.setText(bounds[2]) except: pass @pyqtSlot(float, float) def on_se_move(self, lat, lng): if self.in_xml_load: s, e = lat, lng try: n = float(self.ui.fgdc_northbc.text()) w = float(self.ui.fgdc_westbc.text()) bounds = spatial_utils.format_bounding((w, e, n, s)) self.ui.fgdc_eastbc.setText(bounds[1]) self.ui.fgdc_southbc.setText(bounds[3]) except: pass @pyqtSlot(float, float) def on_sw_move(self, lat, lng): if self.in_xml_load: s, w = lat, lng try: n = float(self.ui.fgdc_northbc.text()) e = float(self.ui.fgdc_eastbc.text()) bounds = spatial_utils.format_bounding((w, e, n, s)) self.ui.fgdc_westbc.setText(bounds[0]) self.ui.fgdc_southbc.setText(bounds[3]) except: pass def switch_schema(self, schema): self.schema = schema if schema == "bdp": self.ui.fgdc_descgeog.show() self.ui.descgeog_label.show() self.ui.descgeog_star.show() else: self.ui.fgdc_descgeog.hide() self.ui.descgeog_label.hide() self.ui.descgeog_star.hide() def all_good_coords(self): try: if -180 > float(self.ui.fgdc_westbc.text()) > 180: return False if -180 > float(self.ui.fgdc_eastbc.text()) > 180: return False if -90 > float(self.ui.fgdc_southbc.text()) > 90: return False if -90 > float(self.ui.fgdc_northbc.text()) > 90: return False if float(self.ui.fgdc_northbc.text()) <= float( self.ui.fgdc_southbc.text()): return False return True except: return False def clear_widget(self): super(self.__class__, self).clear_widget() # self.view.page().mainFrame().addToJavaScriptWindowObject("Spdom", self) # map_fname = utils.get_resource_path('leaflet/map.html') # self.view.setUrl(QUrl.fromLocalFile(map_fname)) def showEvent(self, e): if not self.after_load: self.add_rect() self.update_map() jstr = "sw_marker.openPopup();" self.evaluate_js(jstr) self.after_load = True def to_xml(self): spdom = xml_node("spdom") if self.schema == "bdp": descgeog = xml_node("descgeog", text=self.ui.fgdc_descgeog.text(), parent_node=spdom) bounding = xml_node("bounding", parent_node=spdom) westbc = xml_node("westbc", text=self.ui.fgdc_westbc.text(), parent_node=bounding) eastbc = xml_node("eastbc", text=self.ui.fgdc_eastbc.text(), parent_node=bounding) northbc = xml_node("northbc", text=self.ui.fgdc_northbc.text(), parent_node=bounding) southbc = xml_node("southbc", text=self.ui.fgdc_southbc.text(), parent_node=bounding) if self.original_xml is not None: boundalt = xml_utils.search_xpath(self.original_xml, "bounding/boundalt") if boundalt is not None: spdom.append(deepcopy(boundalt)) dsgpoly_list = xml_utils.search_xpath(self.original_xml, "dsgpoly", only_first=False) for dsgpoly in dsgpoly_list: spdom.append(deepcopy(dsgpoly)) return spdom def from_xml(self, spdom): self.in_xml_load = False self.original_xml = spdom self.clear_widget() utils.populate_widget(self, spdom) contents = xml_utils.node_to_dict(spdom, add_fgdc=False) if "bounding" in contents: contents = contents["bounding"] try: if self.all_good_coords(): self.add_rect() self.update_map() else: self.remove_rect() except KeyError: self.remove_rect() self.in_xml_load = True
class DiscoveryPlugin: def __init__(self, _iface): # Save reference to the QGIS interface self.iface = _iface # initialize plugin directory self.plugin_dir = os.path.dirname(__file__) # Variables to facilitate delayed queries and database connection management self.icon_loading = ':plugins/_Discovery_sqlite/icons/loading.gif' self.db_timer = QTimer() self.line_edit_timer = QTimer() self.line_edit_timer.setSingleShot(True) self.line_edit_timer.timeout.connect(self.reset_line_edit_after_move) self.next_query_time = None self.last_query_time = time.time() self.db_conn = None self.search_delay = 0.5 # s self.query_sql = '' self.query_text = '' self.query_dict = {} self.db_idle_time = 60.0 # s self.search_results = [] self.tool_bar = None self.search_line_edit = None self.completer = None self.marker = QgsVertexMarker(iface.mapCanvas()) self.marker.setIconSize(15) self.marker.setPenWidth(2) self.marker.setColor(QColor(226, 27, 28)) #51,160,44)) self.marker.setZValue(11) self.marker.setVisible(False) self.marker2 = QgsVertexMarker(iface.mapCanvas()) self.marker2.setIconSize(16) self.marker2.setPenWidth(4) self.marker2.setColor(QColor(255, 255, 255, 200)) self.marker2.setZValue(10) self.marker2.setVisible(False) def initGui(self): # Create a new toolbar self.tool_bar = self.iface.addToolBar(u'Панель поиска') self.tool_bar.setObjectName('Discovery_sqlite_Plugin') # Add search edit box self.search_line_edit = QgsFilterLineEdit() self.search_line_edit.setSelectOnFocus(True) self.search_line_edit.setShowSearchIcon(True) self.search_line_edit.setPlaceholderText( u'Поиск адреса или участка...') # self.search_line_edit.setMaximumWidth(768) self.tool_bar.addWidget(self.search_line_edit) # loading indicator self.load_movie = QMovie() self.label_load = QLabel() self.tool_bar.addWidget(self.label_load) # Set up the completer model = QStandardItemModel() self.completer = QCompleter([]) # Initialise with en empty list self.completer.setCaseSensitivity(Qt.CaseInsensitive) self.completer.setMaxVisibleItems(30) self.completer.setModelSorting( QCompleter.UnsortedModel) # Sorting done in PostGIS self.completer.setCompletionMode(QCompleter.UnfilteredPopupCompletion ) # Show all fetched possibilities self.completer.setModel(model) tableView = QTableView() tableView.verticalHeader().setVisible(False) tableView.horizontalHeader().setVisible(False) tableView.setSelectionBehavior(QTableView.SelectRows) tableView.setShowGrid(False) # tableView.verticalHeader().setSectionResizeMode(QHeaderView.ResizeToContents) tableView.verticalHeader().setSectionResizeMode(QHeaderView.Fixed) fontsize = QFontMetrics( tableView.verticalHeader().font()).height() + 2 #font size tableView.verticalHeader().setDefaultSectionSize( fontsize) #font size 15 tableView.horizontalHeader().setSectionResizeMode( QHeaderView.ResizeToContents) tableView.horizontalHeader().setStretchLastSection(True) tableView.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff) self.completer.setCompletionColumn(0) self.completer.setPopup(tableView) self.completer.activated[QModelIndex].connect(self.on_result_selected) self.completer.highlighted[QModelIndex].connect( self.on_result_highlighted) self.search_line_edit.setCompleter(self.completer) # Connect any signals self.search_line_edit.textEdited.connect(self.on_search_text_changed) self.search_line_edit.returnPressed.connect(self.returnPressed) self.read_config() # Search results self.search_results = [] # Set up a timer to periodically perform db queries as required self.db_timer.timeout.connect(self.schedule_search) def read_config(self): qstring = '' self.data = self.settings_path() try: for line in self.data[1:]: words = line.split(';') postgissearchcolumn = words[2].strip() postgistable = words[0].strip() geomcolumn = words[3].strip() layername = words[4].strip() isSelect = int(words[5].strip()) connection = sqlite3.connect(os.path.join(self.dbfile.strip())) cur = connection.cursor() qstring = u'select {2} from {0} where length({1})>0 LIMIT 1'.format( postgistable, postgissearchcolumn, geomcolumn) cur.execute(qstring) connection.close() self.make_enabled(True) # assume the config is invalid first except Exception as E: print(E) self.make_enabled(False) # включить или выключить поисковую строку в зависимости от результата проверки настроек def make_enabled(self, enabled): self.search_line_edit.setEnabled(enabled) self.search_line_edit.setPlaceholderText( u"Поиск адреса или участка..." if enabled else u"Поиск отключен: проверьте конфигурацию") def settings_path(self): p = os.path.join(self.plugin_dir, 'layers.ini') f = codecs.open(p, 'r', encoding='cp1251') data = f.readlines() dbfileline = data[0] if dbfileline[:2] == u'\\\\': self.dbfile = dbfileline elif dbfileline[1] == u':': self.dbfile = dbfileline else: self.dbfile = os.path.join(self.plugin_dir, dbfileline) f.close() return data def unload(self): self.db_timer.stop() self.db_timer.timeout.disconnect(self.schedule_search) self.completer.highlighted[QModelIndex].disconnect( self.on_result_highlighted) self.completer.activated[QModelIndex].disconnect( self.on_result_selected) self.search_line_edit.textEdited.disconnect( self.on_search_text_changed) self.search_line_edit.returnPressed.disconnect(self.returnPressed) self.tool_bar.clear() # Clear all actions self.iface.mainWindow().removeToolBar(self.tool_bar) def clear_suggestions(self): model = self.completer.model() model.clear() # model.setStringList([]) def returnPressed(self): if self.completer.popup().isHidden(): self.do_search(self.search_line_edit.text()) # def setLoading(self, isLoading): # if self.label_load is None: # return # if isLoading: # load_movie = QMovie() # load_movie.setFileName(self.icon_loading) # self.label_load.setMovie(load_movie) # load_movie.start() # else: # load_movie = QMovie() # load_movie.stop() # self.label_load.setMovie(load_movie) def schedule_search(self): if self.next_query_time is not None and self.next_query_time < time.time( ): self.next_query_time = None # Prevent this query from being repeated self.last_query_time = time.time() self.do_search(self.search_line_edit.text()) self.db_timer.stop() # self.setLoading(False) self.search_line_edit.setShowSpinner(False) else: # self.setLoading(True) self.search_line_edit.setShowSpinner(True) if time.time() > self.last_query_time + self.db_idle_time: self.db_conn = None # def on_search_text_changed(self, new_search_text): def on_search_text_changed(self, new_search_text): # self.setLoading(False) self.search_line_edit.setShowSpinner(False) if len(new_search_text) < 3: self.db_timer.stop() self.clear_suggestions() return self.db_timer.start(300) self.next_query_time = time.time() + self.search_delay def do_search(self, new_search_text): if len(new_search_text) < 3: self.clear_suggestions() return self.clear_suggestions() self.query_text = new_search_text self.search_results = [] self.suggestions = [] for index, line in enumerate(self.data[1:]): curline_layer = line words = curline_layer.split(';') searchcolumn = words[2].strip() # поле со значением для поиска postgistable = words[0].strip() # таблица geomcolumn = words[3].strip() # поле с геометрией layername = words[4].strip( ) # имя слоя в легенде для соответствий и выделения isSelect = int( words[5].strip()) # выделять ли объект в слое layername descript = words[1].strip( ) # описание. Выводится в списке результатов query_text, query_dict = self.get_search_sql( new_search_text, searchcolumn, postgistable) query_sql = query_text query_dict = query_dict self.perform_search(query_sql, query_dict, descript, postgistable, layername, isSelect, searchcolumn) # QStringList - просто одна строка в выводе # if len(self.suggestions) > 0: # model = self.completer.model() # model.setStringList(self.suggestions) # print(model) # self.completer.complete() if len(self.suggestions) > 0: # model = self.completer.model() model = QStandardItemModel() font = QFont() font.setItalic(True) font.setPointSize(7) # заполняем модель for i, line in enumerate(self.suggestions): #icon pixmap = QPixmap(':plugins/_Discovery_sqlite/icons/' + line[2] + '.png') pixmap = pixmap.scaledToHeight(10) pixmap = pixmap.scaledToWidth(10) # itemImage = QStandardItem() # itemImage.setData(pixmap, Qt.DecorationRole) # model.setItem(i, 0, itemImage) itemLayer = QStandardItem(u"{1}[{0}]".format( line[1], u' ' * 50)) itemLayer.setFont(font) itemValue = QStandardItem(line[0]) itemValue.setData(pixmap, Qt.DecorationRole) model.setItem(i, 0, itemValue) model.setItem(i, 1, itemLayer) self.completer.setModel(model) self.completer.complete() else: model = self.completer.model() # self.suggestions.append(u"<Не найдено>") # для QStringList # model.setStringList(self.suggestions) # для QStringList model.setItem( 0, 0, QStandardItem('<Не найдено>')) # для QStandardItemModel self.completer.complete() def perform_search(self, query_sql, query_dict, descript, tablename, layername, isSelect, searchcolumn): cur = self.get_db_cur() cur.execute(query_sql, query_dict) for row in cur.fetchall(): geom, suggestion_text = row[0], row[1] self.search_results.append(geom) self.suggestions.append([ suggestion_text, descript, tablename, layername, isSelect, searchcolumn ]) # self.suggestions.append(suggestion_text) # для QStringList def get_search_sql(self, search_text, search_column, table): wildcarded_search_string = '' for part in search_text.split(): wildcarded_search_string += '%' + part #.lower() wildcarded_search_string += '%' wildcarded_search_string = wildcarded_search_string query_dict = {'search_text': wildcarded_search_string} # wildcarded_search_string = wildcarded_search_string.encode('cp1251') query_text = u"SELECT WKT_GEOMETRY AS geom, {0} AS suggestion_string FROM {1} WHERE ({0}) LIKE '{2}' ORDER BY {0} LIMIT 1000".format( search_column, table, wildcarded_search_string) # query_text = query_text.decode('cp1251') return query_text, query_dict def on_result_selected(self, result_index): resultIndexRow = result_index.row() if len(self.search_results) < 1: self.search_line_edit.setPlaceholderText(u'') return # What to do when the user makes a selection geometry_text = self.search_results[resultIndexRow] location_geom = QgsGeometry.fromWkt(geometry_text) canvas = self.iface.mapCanvas() # dst_srid = canvas.mapRenderer().destinationCrs().authid() # Ensure the geometry from the DB is reprojected to the same SRID as the map canvas location_centroid = location_geom.centroid().asPoint() result_text = self.completer.completionModel().index( resultIndexRow, 0).data() if self.suggestions[resultIndexRow][2] in ( u"adres_nd") and location_geom.type() == 0: # point self.show_marker(location_centroid) self.iface.mapCanvas().setExtent(location_geom.boundingBox()) self.iface.mapCanvas().zoomScale(1000) layer_build = self.find_layer(u"Здания") if layer_build != None: layer_build.selectByIds([]) for feat in layer_build.getFeatures( QgsFeatureRequest().setFilterRect( QgsRectangle(self.iface.mapCanvas().extent()))): if location_geom.intersects(feat.geometry()): # self.show_marker_feature(feat.geometry()) self.iface.setActiveLayer(layer_build) layer_build.selectByIds([feat.id()]) layer_build.triggerRepaint() return else: #not point layername = self.suggestions[resultIndexRow][3] isSelect = self.suggestions[resultIndexRow][4] searchcolumn = self.suggestions[resultIndexRow][5] box = location_geom.boundingBox() if box.height() > box.width(): max = box.height() else: max = box.width() box.grow(max * 0.10) self.iface.mapCanvas().setExtent(box) if isSelect == 1: selLayer = self.find_layer(layername) if selLayer is not None: for feat in selLayer.getFeatures( QgsFeatureRequest().setFilterRect(box)): # print(feat[searchcolumn], str(result_text).strip()) try: if str(feat[searchcolumn]) == str( result_text).strip(): self.iface.setActiveLayer(selLayer) selLayer.selectByIds([feat.id()]) selLayer.triggerRepaint() break except Exception as E: print(E) break self.show_marker_feature(location_geom) canvas.refresh() self.line_edit_timer.start(0) # self.db_timer.stop() def get_db_cur(self): # Create a new new connection if required if self.db_conn is None: self.db_conn = sqlite3.connect(os.path.join(self.dbfile.strip())) return self.db_conn.cursor() def on_result_highlighted(self, result_idx): self.line_edit_timer.start(0) def reset_line_edit_after_move(self): self.search_line_edit.setText(self.query_text) def find_layer(self, layer_name): for search_layer in self.iface.mapCanvas().layers(): if search_layer.name() == layer_name: return search_layer return None def show_marker(self, point): for m in [self.marker, self.marker2]: m.setCenter(point) m.setOpacity(1.0) m.setVisible(True) QTimer.singleShot(4000, self.hide_marker) def hide_marker(self): opacity = self.marker.opacity() if opacity > 0.: # produce a fade out effect opacity -= 0.1 self.marker.setOpacity(opacity) self.marker2.setOpacity(opacity) QTimer.singleShot(100, self.hide_marker) else: self.marker.setVisible(False) self.marker2.setVisible(False) def show_marker_feature(self, geom): if geom.type() == 2: #poly self.r = QgsRubberBand(iface.mapCanvas(), True) elif geom.type() == 1: #line self.r = QgsRubberBand(iface.mapCanvas(), False) self.r.setToGeometry(geom, None) self.r.setColor(QColor(255, 0, 0, 200)) self.r.setFillColor(QColor(255, 0, 0, 50)) self.r.setWidth(2) self.r.setZValue(9) QTimer.singleShot(4000, self.hide_marker_feature) def hide_marker_feature(self): opacity = self.r.opacity() if opacity > 0.: # produce a fade out effect opacity -= 0.1 self.r.setOpacity(opacity) QTimer.singleShot(100, self.hide_marker_feature) else: iface.mapCanvas().scene().removeItem(self.r)
class CompletingTextEdit(QTextEdit): def __init__(self, parent=None): super().__init__(parent) self.completerSequence = QKeySequence("Ctrl+Space") self._completer = QCompleter() self._completer.setWidget(self) self._completer.activated.connect(self.insertCompletion) def insertCompletion(self, completion): if self._completer.widget() is not self: return tc = self.textCursor() extra = len(completion) - len(self._completer.completionPrefix()) tc.movePosition(QTextCursor.Left) tc.movePosition(QTextCursor.EndOfWord) tc.insertText(completion[-extra:]) self.setTextCursor(tc) def textUnderCursor(self): tc = self.textCursor() tc.select(QTextCursor.WordUnderCursor) return tc.selectedText() def loadFromFile(self, fileName): f = QFile(fileName) if not f.open(QFile.ReadOnly): model = QStringListModel() self._completer.setModel(model) QApplication.setOverrideCursor(QCursor(Qt.WaitCursor)) words = [] while not f.atEnd(): line = f.readLine().trimmed() if line.length() != 0: try: line = str(line, encoding='utf-8') except TypeError: line = str(line) words.append(line) QApplication.restoreOverrideCursor() model = QStringListModel(words) self._completer.setModel(model) def loadFromList(self, words): model = QStringListModel(words) self._completer.setModel(model) def keyPressEvent(self, e): if self._completer.popup().isVisible(): # The following keys are forwarded by the completer to the widget. if e.key() in (Qt.Key_Enter, Qt.Key_Return, Qt.Key_Escape, Qt.Key_Tab, Qt.Key_Backtab): e.ignore() # Let the completer do default behavior. return newSeq = QKeySequence(e.modifiers() | e.key()) isShortcut = newSeq == self.completerSequence if not isShortcut: # Do not process the shortcut when we have a completer. super().keyPressEvent(e) return ctrlOrShift = e.modifiers() & (Qt.ControlModifier | Qt.ShiftModifier) if ctrlOrShift and not e.text(): return eow = "~!@#$%^&*()_+{}|:\"<>?,./;'[]\\-=" hasModifier = (e.modifiers() != Qt.NoModifier) and not ctrlOrShift completionPrefix = self.textUnderCursor() if not isShortcut and (hasModifier or not e.text() or len(completionPrefix) < 3 or e.text()[-1] in eow): self._completer.popup().hide() return if completionPrefix != self._completer.completionPrefix(): self._completer.setCompletionPrefix(completionPrefix) self._completer.popup().setCurrentIndex( self._completer.completionModel().index(0, 0)) cr = self.cursorRect() cr.setWidth( self._completer.popup().sizeHintForColumn(0) + self._completer.popup().verticalScrollBar().sizeHint().width()) self._completer.complete(cr)
class MainWindow(QMainWindow): """Main window class.""" def __init__(self, parent=None): """Init class.""" super(MainWindow, self).__init__() QNetworkProxyFactory.setUseSystemConfiguration(True) self.statusBar().showMessage(__doc__ + get_nuitka_version()) self.setWindowTitle(__doc__.strip().capitalize()) self.setMinimumSize(480, 400) self.setMaximumSize(1024, 800) self.resize(self.minimumSize()) self.setWindowIcon(QIcon.fromTheme("python")) self.center() QShortcut("Ctrl+q", self, activated=lambda: self.close()) self.menuBar().addMenu("&File").addAction("Exit", lambda: self.close()) windowMenu = self.menuBar().addMenu("&Window") windowMenu.addAction("Minimize", lambda: self.showMinimized()) windowMenu.addAction("Maximize", lambda: self.showMaximized()) windowMenu.addAction("Restore", lambda: self.showNormal()) windowMenu.addAction("FullScreen", lambda: self.showFullScreen()) windowMenu.addAction("Center", lambda: self.center()) windowMenu.addAction("Top-Left", lambda: self.move(0, 0)) windowMenu.addAction("To Mouse", lambda: self.move_to_mouse_position()) windowMenu.addSeparator() windowMenu.addAction( "Increase size", lambda: self.resize(self.size().width() * 1.4, self.size().height() * 1.4)) windowMenu.addAction( "Decrease size", lambda: self.resize(self.size().width() // 1.4, self.size().height() // 1.4)) windowMenu.addAction("Minimum size", lambda: self.resize(self.minimumSize())) windowMenu.addAction("Maximum size", lambda: self.resize(self.maximumSize())) windowMenu.addAction( "Horizontal Wide", lambda: self.resize(self.maximumSize().width(), self.minimumSize().height())) windowMenu.addAction( "Vertical Tall", lambda: self.resize(self.minimumSize().width(), self.maximumSize().height())) windowMenu.addSeparator() windowMenu.addAction("Disable Resize", lambda: self.setFixedSize(self.size())) windowMenu.addAction("Set Interface Font...", lambda: self.setFont(QFontDialog.getFont()[0])) windowMenu.addAction("Load .qss Skin", lambda: self.setStyleSheet(self.skin())) helpMenu = self.menuBar().addMenu("&Help") helpMenu.addAction("About Qt 5", lambda: QMessageBox.aboutQt(self)) helpMenu.addAction("About Python 3", lambda: open_new_tab('https://www.python.org')) helpMenu.addAction("About " + __doc__, lambda: QMessageBox.about(self, __doc__, HELP)) helpMenu.addSeparator() helpMenu.addAction( "Keyboard Shortcut", lambda: QMessageBox.information(self, __doc__, "<b>Quit = CTRL+Q")) if sys.platform.startswith('linux'): helpMenu.addAction( "View Source Code", lambda: call('xdg-open ' + __file__, shell=True)) helpMenu.addAction("View GitHub Repo", lambda: open_new_tab(__url__)) helpMenu.addAction("Check Updates", lambda: Downloader(self)) # process self.process = QProcess() self.process.readyReadStandardOutput.connect(self._read_output) self.process.readyReadStandardError.connect(self._read_errors) self.process.finished.connect(self._process_finished) self.process.error.connect(self._process_failed) # widgets self.group0, self.group1 = QGroupBox("Options"), QGroupBox("Paths") self.group4, self.group5 = QGroupBox("Details"), QGroupBox("Miscs") g0grid, g1vlay = QGridLayout(self.group0), QGridLayout(self.group1) g5vlay, g4vlay = QVBoxLayout(self.group5), QVBoxLayout(self.group4) # group 0 the options self.module = QCheckBox("Create compiled extension module") self.standalone = QCheckBox("Standalone executable binary output") self.nofreeze = QCheckBox("No freeze all modules of standard library") self.python_debug = QCheckBox("Use Python Debug") self.warning = QCheckBox("Warnings for implicit exceptions at compile") self.recurse_std = QCheckBox("Recursive compile the standard library") self.recurse_not = QCheckBox("Force No recursive compiling") self.execute = QCheckBox("Execute the created binary after compiling") self.pythonpath = QCheckBox("Keep pythonpath when executing") self.enhaced = QCheckBox("Enhaced compile, Not CPython compatible") self.nolineno = QCheckBox("No Statements line numbers on compile") self.rmbuilddir = QCheckBox("Remove build directory after compile.") self.nuitka_debug = QCheckBox("Use Nuitka Debug") self.keep_debug = QCheckBox("Keep debug info on compile for GDB") self.traced = QCheckBox("Traced execution output") self.plusplus = QCheckBox("Compile C++ Only on generated source files") self.experimental = QCheckBox("Experimental features") self.force_clang = QCheckBox("Force use of CLang") self.force_mingw = QCheckBox("Force use of MinGW on MS Windows") self.force_lto = QCheckBox("Use link time optimizations LTO") self.show_scons = QCheckBox("Show Scons executed commands") self.show_progress = QCheckBox("Show progress info and statistics") self.show_summary = QCheckBox("Show final summary of included modules") self.disable_console = QCheckBox("Disable the Console on MS Windows") for i, widget in enumerate( (self.module, self.standalone, self.nofreeze, self.python_debug, self.warning, self.recurse_std, self.recurse_not, self.execute, self.pythonpath, self.enhaced, self.nolineno, self.rmbuilddir, self.nuitka_debug, self.keep_debug, self.traced, self.plusplus, self.experimental, self.force_clang, self.force_mingw, self.force_lto, self.show_scons, self.show_progress, self.show_summary, self.disable_console)): widget.setToolTip(widget.text()) g0grid.addWidget(widget, i if i < i + 1 else i - (i - 1), i % 2) # group 1 paths self.target = QLineEdit() self.outdir = QLineEdit(os.path.expanduser("~")) self.t_icon = QLineEdit() self.target.setToolTip("Python App file you want to Compile to Binary") self.outdir.setToolTip("Folder to write Compiled Output Binary files") self.t_icon.setToolTip("Icon image file to embed for your Python App") self.target.setPlaceholderText("/full/path/to/target/python_app.py") self.outdir.setPlaceholderText("/full/path/to/output/folder/") self.t_icon.setPlaceholderText("/full/path/to/python_app/icon.png") self.completer, self.dirs = QCompleter(self), QDirModel(self) self.completer.setModel(self.dirs) self.completer.setCaseSensitivity(Qt.CaseInsensitive) self.completer.setCompletionMode(QCompleter.PopupCompletion) self.completer.popup().setStyleSheet("border: 1px solid gray") self.completer.popup().setVerticalScrollBarPolicy( Qt.ScrollBarAlwaysOff) self.outdir.setCompleter(self.completer) self.t_icon.setCompleter(self.completer) self.target.setCompleter(self.completer) self.clear_1 = QPushButton(QIcon.fromTheme("edit-clear"), "", self, clicked=lambda: self.target.clear()) self.clear_2 = QPushButton(QIcon.fromTheme("edit-clear"), "", self, clicked=lambda: self.t_icon.clear()) self.clear_3 = QPushButton(QIcon.fromTheme("edit-clear"), "", self, clicked=lambda: self.outdir.clear()) self.open_1 = QPushButton( QIcon.fromTheme("folder-open"), "", self, clicked=lambda: self.target.setText( str( QFileDialog.getOpenFileName( self, __doc__, os.path.expanduser("~"), """Python (*.py);; Python for Windows (*.pyw);;All (*.*)""")[0]))) self.open_2 = QPushButton( QIcon.fromTheme("folder-open"), "", self, clicked=lambda: self.t_icon.setText( str( QFileDialog.getOpenFileName( self, __doc__, os.path.expanduser("~"), "PNG (*.png);;JPG (*.jpg);;ICO (*.ico);;All (*.*)")[0]) )) self.open_3 = QPushButton( QIcon.fromTheme("folder-open"), "", self, clicked=lambda: self.outdir.setText( str( QFileDialog.getExistingDirectory( self, __doc__, os.path.expanduser("~"))))) self.l_icon = QLabel("Target Icon") g1vlay.addWidget(QLabel("<b>Target Python"), 0, 0) g1vlay.addWidget(self.target, 0, 1) g1vlay.addWidget(self.clear_1, 0, 2) g1vlay.addWidget(self.open_1, 0, 3) g1vlay.addWidget(self.l_icon, 1, 0) g1vlay.addWidget(self.t_icon, 1, 1) g1vlay.addWidget(self.clear_2, 1, 2) g1vlay.addWidget(self.open_2, 1, 3) g1vlay.addWidget(QLabel("<b>Output Folder"), 2, 0) g1vlay.addWidget(self.outdir, 2, 1) g1vlay.addWidget(self.clear_3, 2, 2) g1vlay.addWidget(self.open_3, 2, 3) # group 4 the dome view mode self.jobs = QSpinBox() self.jobs.setRange(1, cpu_count()) self.jobs.setValue(cpu_count()) self.jobs.setToolTip("Backend Worker Jobs Processes") self.python_version = QComboBox() self.python_version.addItems(["2.7", "3.2", "3.3", "3.4"]) self.python_version.setToolTip("Python version to use with Nuitka") self.display_tree = QPushButton("Display Tree") self.display_tree.clicked.connect(lambda: call( NUITKA + " --display-tree {}".format(self.target.text()), shell=True)) self.dump_tree = QPushButton( "View Docs", clicked=lambda: open_new_tab( "http://nuitka.net/doc/user-manual.html")) self.open_log = QPushButton("View Logs") _log = os.path.join(gettempdir(), "nuitka-gui.log") _open = "xdg-open " if sys.platform.startswith("lin") else "open " self.open_log.clicked.connect(lambda: call(_open + _log, shell=True)) self.open_folder = QPushButton("Open Build Folder") self.open_folder.clicked.connect( lambda: call(_open + str(self.outdir.text()).strip(), shell=True)) # self.display_tree.clicked.connect(self._display_tree) g4vlay.addWidget(QLabel("<b>Worker Jobs")) g4vlay.addWidget(self.jobs) g4vlay.addWidget(QLabel("<b>Python Version")) g4vlay.addWidget(self.python_version) g4vlay.addWidget(QLabel("<b>Actions")) g4vlay.addWidget(self.display_tree) g4vlay.addWidget(self.dump_tree) g4vlay.addWidget(self.open_log) g4vlay.addWidget(self.open_folder) # group 5 miscelaneous stuff self.debug, self.scr = QCheckBox("Use Debug"), QCheckBox("Make Script") self.chrt, self.ionice = QCheckBox("Slow CPU"), QCheckBox("Slow HDD") self.minimi = QCheckBox("Auto Minimize") self.chrt.setToolTip("Use Low CPU speed priority (Linux only)") self.ionice.setToolTip("Use Low HDD speed priority (Linux only)") self.scr.setToolTip("Generate a Bash Script to Compile with Nuitka") self.debug.setToolTip("Use Debug Verbose mode") self.minimi.setToolTip("Automatically Minimize when compiling starts") self.scr.setChecked(True) self.chrt.setChecked(True) self.ionice.setChecked(True) self.minimi.setChecked(True) g5vlay.addWidget(self.debug) g5vlay.addWidget(self.scr) g5vlay.addWidget(self.chrt) g5vlay.addWidget(self.ionice) g5vlay.addWidget(self.minimi) # option to show or hide some widgets on the gui self.guimode = QComboBox() self.guimode.addItems(('Full UX / UI', 'Simple UX / UI')) self.guimode.setCurrentIndex(1) self._set_guimode() self.guimode.setStyleSheet("""QComboBox{background:transparent; margin-left:25px;color:gray;text-decoration:underline;border:0}""") self.guimode.currentIndexChanged.connect(self._set_guimode) # buttons from bottom to close or proceed self.bt = QDialogButtonBox(self) self.bt.setStandardButtons(QDialogButtonBox.Ok | QDialogButtonBox.Close) self.bt.rejected.connect(self.close) self.bt.accepted.connect(self.run) if not sys.platform.startswith('lin'): self.scr.setChecked(False) self.chrt.setChecked(False) self.ionice.setChecked(False) self.scr.hide() self.chrt.hide() self.ionice.hide() if not sys.platform.startswith('win'): self.l_icon.hide() self.t_icon.hide() self.clear_2.hide() self.open_2.hide() if sys.platform.startswith('win'): self.display_tree.hide() # container for all groups of widgets container = QWidget() container_layout = QGridLayout(container) # Y, X container_layout.addWidget(self.guimode, 0, 1) container_layout.addWidget(self.group0, 1, 1) container_layout.addWidget(self.group1, 2, 1) container_layout.addWidget(self.group4, 1, 2) container_layout.addWidget(self.group5, 2, 2) container_layout.addWidget(self.bt, 3, 1) self.setCentralWidget(container) def check_paths(self): """Check that the paths are valid.""" if not os.path.isfile(self.target.text()): log.error("Target File not found or not valid.") QMessageBox.warning(self, __doc__.title(), "Target File not found or not valid.") return False if not str(self.target.text()).endswith((".py", ".pyw")): log.error("Target File not valid.") QMessageBox.warning(self, __doc__.title(), "Target File not valid.") return False if not os.path.isdir(self.outdir.text()): log.error("Target Folder not found or not valid.") QMessageBox.warning(self, __doc__.title(), "Target Folder not found or not valid.") return False if self.t_icon.text() and not os.path.isfile(self.t_icon.text()): log.warning("Target Icon File not found or not valid.") QMessageBox.warning(self, __doc__.title(), "Target Icon File not found or not valid.") return True else: return True def generate_build_command(self): """Generate a build command.""" return re.sub( r"\s+", " ", " ".join( ('chrt --verbose --idle 0' if self.chrt.isChecked() else '', 'ionice --ignore --class 3' if self.ionice.isChecked() else '', NUITKA, '--debug --verbose' if self.debug.isChecked() else '', '--show-progress' if self.show_progress.isChecked() else '', '--show-scons --show-modules' if self.show_scons.isChecked() else '', '--unstriped' if self.keep_debug.isChecked() else '', '--trace-execution' if self.traced.isChecked() else '', '--remove-output' if self.rmbuilddir.isChecked() else '', '--code-gen-no-statement-lines' if self.nolineno.isChecked() else '', '--execute' if self.execute.isChecked() else '', '--recurse-none' if self.recurse_not.isChecked() else '--recurse-all', '--recurse-stdlib' if self.recurse_std.isChecked() else '', '--clang' if self.force_clang.isChecked() else '', '--lto' if self.force_lto.isChecked() else '', '--c++-only' if self.plusplus.isChecked() else '', '--windows-disable-console' if self.disable_console.isChecked() else '', '--experimental' if self.experimental.isChecked() else '', '--python-debug' if self.python_debug.isChecked() else '', '--module' if self.module.isChecked() else '--standalone', '--nofreeze-stdlib' if self.nofreeze.isChecked() else '', '--mingw' if self.force_mingw.isChecked() else '', '--warn-implicit-exceptions' if self.warning.isChecked() else '', '--execute-with-pythonpath' if self.pythonpath.isChecked() else '', '--enhanced' if self.enhaced.isChecked() else '', '--icon="{}"'.format(self.t_icon.text()) if self.t_icon.text() else '', '--python-version={}'.format( self.python_version.currentText()), '--jobs={}'.format( self.jobs.value()), '--output-dir="{}"'.format( self.outdir.text()), '"{}"'.format( self.target.text())))) def run(self): """Run the main method and run Nuitka.""" self.statusBar().showMessage('Working...') log.debug("Working...") if not self.check_paths(): return command_to_run_nuitka = self.generate_build_command() log.debug(command_to_run_nuitka) self.process.start(command_to_run_nuitka) if not self.process.waitForStarted(): log.error(self._read_errors()) return # ERROR if self.scr.isChecked() and sys.platform.startswith("lin"): script_file = str(self.target.text()).replace( ".py", "-nuitka-compile.sh") log.debug("Writing Script {}".format(script_file)) with open(script_file, "w", encoding="utf-8") as script: script.write("#!/usr/bin/env bash\n" + command_to_run_nuitka) os.chmod(script_file, 0o755) self.statusBar().showMessage(__doc__.title()) def _process_finished(self): """Finished sucessfuly.""" log.debug("Finished.") self.showNormal() def _read_output(self): """Read and return output.""" return str(self.process.readAllStandardOutput()).strip() def _read_errors(self): """Read and return errors.""" log.debug(self.process.readAllStandardError()) return str(self.process.readAllStandardError()).strip() def _process_failed(self): """Read and return errors.""" self.showNormal() self.statusBar().showMessage(" ERROR: Failed ! ") log.warning(str(self.process.readAllStandardError()).strip().lower()) return str(self.process.readAllStandardError()).strip().lower() def _set_guimode(self): """Switch between simple and full UX.""" for widget in (self.group0, self.group4, self.group5, self.statusBar(), self.menuBar()): widget.hide() if self.guimode.currentIndex() else widget.show() self.resize(self.minimumSize() if self.guimode.currentIndex( ) else self.maximumSize()) self.center() def skin(self, filename=None): """Open QSS from filename,if no QSS return None,if no filename ask.""" if not filename: filename = str( QFileDialog.getOpenFileName( self, __doc__ + "-Open QSS Skin file", os.path.expanduser("~"), "CSS Cascading Style Sheet for Qt 5 (*.qss);;All (*.*)") [0]) if filename and os.path.isfile(filename): log.debug(filename) with open(filename, 'r') as file_to_read: text = file_to_read.read().strip() if text: log.debug(text) return text def center(self): """Center the Window on the Current Screen,with Multi-Monitor support. >>> MainWindow().center() True """ window_geometry = self.frameGeometry() mousepointer_position = QApplication.desktop().cursor().pos() screen = QApplication.desktop().screenNumber(mousepointer_position) centerPoint = QApplication.desktop().screenGeometry(screen).center() window_geometry.moveCenter(centerPoint) return bool(not self.move(window_geometry.topLeft())) def move_to_mouse_position(self): """Center the Window on the Current Mouse position. >>> MainWindow().move_to_mouse_position() True """ window_geometry = self.frameGeometry() window_geometry.moveCenter(QApplication.desktop().cursor().pos()) return bool(not self.move(window_geometry.topLeft())) def closeEvent(self, event): """Ask to Quit.""" the_conditional_is_true = QMessageBox.question( self, __doc__.title(), 'Quit ?.', QMessageBox.Yes | QMessageBox.No, QMessageBox.No) == QMessageBox.Yes event.accept() if the_conditional_is_true else event.ignore()
class MainWindow(QMainWindow, Ui_MainWindow): def __init__(self, parent=None): super(MainWindow, self).__init__(parent) self.initUI() def initUI(self): self.setupUi(self) self.setupCSSStyle() self.utility = Utility() #实例化工具类 # self.setFixedSize(QSize(self.width(),self.height())) #禁止拉伸窗口大小 self.winHeight = self.height() # 获取窗体高度,便用 self.setWindowIcon(QIcon('Pictures/train.png')) #设置图标 self.logoLabel.resize(150,150) self.logoLabel.setScaledContents(True) #图片填满label self.logoLabel.setPixmap(QPixmap('Pictures/train3.png')) #设置label上的图片 self.exchangeButton.setIcon(QIcon('Pictures/exchange.png')) # 设置Icon self.exchangeButton.setIconSize(QSize(50, 50)) # 设置交换按钮icon size self.exchangeButton.clicked.connect(self.exchangePlace) self.timeButton.setText(self.utility.getDepartureDate()) # 设置出发日期 self.timeButton.setIcon(QIcon('Pictures/calendar.png')) # 设置Icon self.timeButton.clicked[bool].connect(self.selectDate) self.isTimeButtonPressed = False #按钮点初始状态 self.studentCheckBox.stateChanged.connect(lambda: self.iSBoxChecked(self.studentCheckBox)) self.highSpeedCheckBox.stateChanged.connect(lambda: self.iSBoxChecked(self.highSpeedCheckBox)) self.stations = StationCodes().getStations() self.setupLineEdit() self.initCalendar() def initCalendar(self): x = self.timeButton.x() # 获取时间按钮的坐标x y = self.timeButton.y() + self.timeButton.height() w = self.timeButton.width() h = calHeight self.cal = MyCalendar(self) self.cal.setGeometry(x, y, w, h) self.cal.clicked[QDate].connect(self.showDate) # clicked[参数],即定义showDate是传入的参数类型设置 self.cal.close() def setupLineEdit(self): # 增加自动补全 self.completer = QCompleter(self.stations) self.completer.setFilterMode(Qt.MatchStartsWith) # 起始位置 delegate = CompleterDelegate(self) self.completer.popup().setStyleSheet("QScrollBar{background:skyBlue;}") self.completer.popup().setItemDelegate(delegate) self.setupEditIcon(self.startLineEdit,'始发地') self.setupEditIcon(self.destinationLineEdit,'目的地') # def selectStation(self): # # self.placeLists = QListWidget(self) # self.placeLists.setGeometry(QRect(self.startButton.x(),self.startButton.y()+self.startButton.height(),self.startButton.width(),self.height()-self.startButton.y()-self.startButton.height())) # self.placeLists.addItems(self.stations) # # self.placeLists.setSortingEnabled(True) # # self.placeLists.sortItems() # 排序 # self.placeLists.setCurrentRow(0) # self.placeLists.setStyleSheet("QListWidget{color:black; background:white}" # "QListWidget::Item{padding-top:2.5px; padding-bottom:2.5px; }" # "QListWidget::Item:hover{background:skyblue; }" #下拉滑动背景色 # "QListWidget::item:selected{ color:red; background:skyblue;}" #当前被选择的item背景,颜色 # "QListWidget::item:selected:!active{ background:skyblue; }") # # self.placeLists.currentItemChanged.connect(self.selectPlaceItem) # # self.placeLists.show() # # # def selectPlaceItem(self): # # self.startButton.setText(self.placeLists.currentItem().text()) # self.placeLists.close() def setupEditIcon(self,lineEdit,placeHolderText): if lineEdit==self.startLineEdit: iconPath = 'Pictures/start.png' else: iconPath = 'Pictures/destination.png' lineEdit.setCompleter(self.completer) lineEdit.setPlaceholderText(placeHolderText) action = QAction(lineEdit) action.setIcon(QIcon(iconPath)) lineEdit.addAction(action, QLineEdit.LeadingPosition) def selectDate(self,pressed): if self.isTimeButtonPressed == False: self.cal.show() self.startRect = QRect(self.geometry().x(), self.geometry().y(), self.width(), self.height()) self.endRect = QRect(self.geometry().x(), self.geometry().y(), self.width(),self.cal.y() + self.cal.height() + bottomSpace) self.setFrameAnimation(self.startRect, self.endRect) self.isTimeButtonPressed = True else: self.setFrameAnimation(self.endRect, self.startRect) self.cal.close() self.isTimeButtonPressed = False def setFrameAnimation(self, startRect, endRect): self.animation = QPropertyAnimation(self, b'geometry') self.animation.setDuration(250) self.animation.setStartValue(startRect) self.animation.setEndValue(endRect) self.animation.start() def showDate(self,date): dateStr = date.toString('yyyy-MM-dd') dateTime = self.utility.stringToDatetime(dateStr) self.timeButton.setText(self.utility.getDepartureDate(dateTime)) #"yyyy-MM-dd ddd(星期)" self.setFrameAnimation(self.endRect, self.startRect) self.isTimeButtonPressed = False self.cal.close() # 关闭日期控件 def iSBoxChecked(self,checkBox): if checkBox.isChecked(): checkBox.setStyleSheet(checkBoxQSS + "QCheckBox{color:#d81e06}") else: checkBox.setStyleSheet(checkBoxQSS) if self.studentCheckBox.isChecked(): self.queryButton.setText('查询学生票') else: self.queryButton.setText('查询车票') def exchangePlace(self): startPlace = self.startLineEdit.text() self.startLineEdit.setText(self.destinationLineEdit.text()) self.destinationLineEdit.setText(startPlace) def setupCSSStyle(self): # self.setStyleSheet("QMainWindow{background-color:white}") self.setStyleSheet("QMainWindow{border-image: url(Pictures/bg3.jpg)}") #设置背景图 self.exchangeButton.setStyleSheet("QPushButton{background-color:transparent}") self.queryButton.setStyleSheet('QPushButton{color:white;background-color:#d81e06;border:1px;border-radius:5px}') self.timeButton.setStyleSheet('QPushButton{text-align:left;color:white;background-color:transparent;qproperty-iconSize: 25px}') for lineEdit in (self.startLineEdit,self.destinationLineEdit): lineEdit.setStyleSheet("QLineEdit{border-width:5px;border-radius:5px;font-size:12pt;padding-left:2px;" "background-color:transparent;color:white;font-familiy:黑体;font-weight:bold;" "border: 1px solid lightGray;}") self.highSpeedCheckBox.setStyleSheet(checkBoxQSS) self.studentCheckBox.setStyleSheet(checkBoxQSS)
class SeedLayout(QVBoxLayout): def __init__(self, seed=None, title=None, icon=True, msg=None, options=None, is_seed=None, passphrase=None, parent=None, for_seed_words=True): QVBoxLayout.__init__(self) self.parent = parent self.options = options if title: self.addWidget(WWLabel(title)) if seed: # "read only", we already have the text if for_seed_words: self.seed_e = ButtonsTextEdit() else: # e.g. xpub self.seed_e = ShowQRTextEdit() self.seed_e.setReadOnly(True) self.seed_e.setText(seed) else: # we expect user to enter text assert for_seed_words self.seed_e = ButtonsTextEdit() self.seed_e.addButton("paste.png",self.paste_text,_("paste seed")) #self.seed_e.setTabChangesFocus(False) # so that tab auto-completes self.is_seed = is_seed self.saved_is_seed = self.is_seed self.seed_e.textChanged.connect(self.on_edit) #self.initialize_completer() self.seed_e.setMaximumHeight(75) hbox = QHBoxLayout() if icon: logo = QLabel() logo.setPixmap(QPixmap(icon_path("seed.png")) .scaledToWidth(64, mode=Qt.SmoothTransformation)) logo.setMaximumWidth(60) hbox.addWidget(logo) hbox.addWidget(self.seed_e) self.addLayout(hbox) hbox = QHBoxLayout() hbox.addStretch(1) self.seed_type_label = QLabel('') hbox.addWidget(self.seed_type_label) # options self.is_bip39 = False if options: vbox = QVBoxLayout() hbox.addLayout(vbox) if 'bip39' in self.options: def f(b): self.is_seed = (lambda x: bool(x)) if b else self.saved_is_seed self.is_bip39 = b self.on_edit() cb_bip39 = QCheckBox(_('Import mobile wallet seed phrase.')) cb_bip39.toggled.connect(f) cb_bip39.setChecked(self.is_bip39) vbox.addWidget(cb_bip39) self.addLayout(hbox) self.addStretch(1) self.seed_warning = WWLabel('') if msg: self.seed_warning.setText(seed_warning_msg(seed)) self.addWidget(self.seed_warning) def paste_text(self): self.seed_e.setText(self.parent.app.clipboard().text()) def initialize_completer(self): bip39_english_list = Mnemonic('en').wordlist old_list = electrum.old_mnemonic.words only_old_list = set(old_list) - set(bip39_english_list) self.wordlist = bip39_english_list + list(only_old_list) # concat both lists self.wordlist.sort() class CompleterDelegate(QStyledItemDelegate): def initStyleOption(self, option, index): super().initStyleOption(option, index) # Some people complained that due to merging the two word lists, # it is difficult to restore from a metal backup, as they planned # to rely on the "4 letter prefixes are unique in bip39 word list" property. # So we color words that are only in old list. if option.text in only_old_list: # yellow bg looks ~ok on both light/dark theme, regardless if (un)selected option.backgroundBrush = ColorScheme.YELLOW.as_color(background=True) self.completer = QCompleter(self.wordlist) delegate = CompleterDelegate(self.seed_e) self.completer.popup().setItemDelegate(delegate) self.seed_e.set_completer(self.completer) def get_seed(self): text = self.seed_e.text() return ' '.join(text.split()) def on_edit(self): s = self.get_seed() b = self.is_seed(s) if not self.is_bip39: t = seed_type(s) label = _('Seed Type') + ': ' + t if t else '' else: from electrum.keystore import bip39_is_checksum_valid is_checksum, is_wordlist = bip39_is_checksum_valid(s) status = ('checksum: ' + ('ok' if is_checksum else 'failed')) if is_wordlist else 'unknown wordlist' label = 'BIP39' + ' (%s)'%status self.seed_type_label.setText(label) self.parent.next_button.setEnabled(b)
class SeedConfirmDisplay(QVBoxLayout): def __init__( self, title=None, icon=True, options=None, is_seed=None, parent=None, for_seed_words=True, full_check=True, *, config: 'SimpleConfig', ): QVBoxLayout.__init__(self) self.parent = parent self.options = options self.config = config self.seed_type = 'electrum' if title: self.addWidget(WWLabel(title)) assert for_seed_words self.seed_e = CompletionTextEdit() self.seed_e.setTabChangesFocus(False) # so that tab auto-completes self.is_seed = is_seed self.saved_is_seed = self.is_seed self.seed_e.textChanged.connect(self.on_edit) self.initialize_completer() self.seed_e.setMaximumHeight(75) hbox = QHBoxLayout() if icon: logo = QLabel() logo.setPixmap(QPixmap(icon_path("seed.png")) .scaledToWidth(64, mode=Qt.SmoothTransformation)) logo.setMaximumWidth(60) hbox.addWidget(logo) hbox.addWidget(self.seed_e) self.addLayout(hbox) hbox = QHBoxLayout() hbox.addStretch(1) self.seed_type_label = QLabel('') if full_check: hbox.addWidget(self.seed_type_label) # options self.is_ext = False self.opt_button = None if options: self.opt_button = EnterButton(_('Options'), self.seed_options) seed_types = [ (value, title) for value, title in ( ('electrum', _('Electrum')), ('bip39', _('BIP39 seed')), ) ] seed_type_values = [t[0] for t in seed_types] def f(choices_layout): self.seed_type = seed_type_values[choices_layout.selected_index()] self.is_seed = (lambda x: bool(x)) if self.seed_type != 'bip39' else self.saved_is_seed self.seed_status.setText('') self.on_edit(from_click=True) self.initialize_completer() self.seed_warning.setText(None) if self.seed_type == 'bip39': if self.opt_button: self.opt_button.setVisible(False) else: if self.opt_button: self.opt_button.setVisible(True) if options and full_check: hbox.addWidget(self.opt_button) self.addLayout(hbox) checked_index = seed_type_values.index(self.seed_type) titles = [t[1] for t in seed_types] self.clayout = ChoicesLayout(_('Seed type'), titles, on_clicked=f, checked_index=checked_index) if full_check: hbox.addLayout(self.clayout.layout()) self.addStretch(1) self.seed_status = WWLabel('') self.addWidget(self.seed_status) self.seed_warning = WWLabel('') self.addWidget(self.seed_warning) self.lang = 'en' def seed_options(self): dialog = QDialog() vbox = QVBoxLayout(dialog) if 'ext' in self.options: cb_ext = QCheckBox(_('Extend this seed with custom words')) cb_ext.setChecked(self.is_ext) vbox.addWidget(cb_ext) vbox.addLayout(Buttons(OkButton(dialog))) if not dialog.exec_(): return None self.is_ext = cb_ext.isChecked() if 'ext' in self.options else False #self.seed_type = 'electrum' def initialize_completer(self): if self.seed_type != 'slip39': bip39_english_list = Mnemonic('en').wordlist old_list = old_mnemonic.wordlist only_old_list = set(old_list) - set(bip39_english_list) self.wordlist = list(bip39_english_list) + list(only_old_list) # concat both lists self.wordlist.sort() class CompleterDelegate(QStyledItemDelegate): def initStyleOption(self, option, index): super().initStyleOption(option, index) # Some people complained that due to merging the two word lists, # it is difficult to restore from a metal backup, as they planned # to rely on the "4 letter prefixes are unique in bip39 word list" property. # So we color words that are only in old list. if option.text in only_old_list: # yellow bg looks ~ok on both light/dark theme, regardless if (un)selected option.backgroundBrush = ColorScheme.YELLOW.as_color(background=True) delegate = CompleterDelegate(self.seed_e) else: self.wordlist = list(slip39.get_wordlist()) delegate = None self.completer = QCompleter(self.wordlist) if delegate: self.completer.popup().setItemDelegate(delegate) self.seed_e.set_completer(self.completer) def get_seed_words(self): return self.seed_e.text().split() def get_seed(self): if self.seed_type != 'slip39': return ' '.join(self.get_seed_words()) else: return self.slip39_seed def on_edit(self, *, from_click=False): s = ' '.join(self.get_seed_words()) b = self.is_seed(s) from electrum.keystore import bip39_is_checksum_valid from electrum.mnemonic import Wordlist, filenames lang = '' for type, file in filenames.items(): word_list = Wordlist.from_file(file) is_checksum, is_wordlist = bip39_is_checksum_valid(s, wordlist=word_list) if is_wordlist: lang = type break if self.seed_type == 'bip39': status = ('checksum: ' + ('ok' if is_checksum else 'failed')) if is_wordlist else 'unknown wordlist' label = 'BIP39 - ' + lang + ' (%s)'%status if lang and lang != self.lang: if lang == 'en': bip39_english_list = Mnemonic('en').wordlist old_list = old_mnemonic.wordlist only_old_list = set(old_list) - set(bip39_english_list) self.wordlist = list(bip39_english_list) + list(only_old_list) # concat both lists self.wordlist.sort() self.completer.model().setStringList(self.wordlist) self.lang = 'en' else: self.wordlist = list(Mnemonic(lang).wordlist) self.wordlist.sort() self.completer.model().setStringList(self.wordlist) self.lang = lang b = is_checksum else: t = seed_type(s) label = _('Seed Type') + ': ' + t if t else '' if is_checksum and is_wordlist and not from_click: # This is a valid bip39 and this method was called from typing # Emulate selecting the bip39 option self.clayout.group.buttons()[1].click() return self.seed_type_label.setText(label) self.parent.next_button.setEnabled(b) # disable suggestions if user already typed an unknown word for word in self.get_seed_words()[:-1]: if word not in self.wordlist: self.seed_e.disable_suggestions() return self.seed_e.enable_suggestions()
class MainWindow(QWidget): def __init__(self, parent=None, path_config='config.ini', path_style='./styles/style.qss', logger=None): super(MainWindow, self).__init__(parent) self.path_config = path_config self.config = Config(self.path_config) self.settings = self.config.getConfig() self.logger = logger self.drawInitialMenu(path_style) def drawInitialMenu(self, path_style='./styles/style.qss'): self.path_to_data = self.settings.get('path', 'data') self.path_to_library = self.settings.get('path', 'library') self.path_to_playlist = self.settings.get('path', 'playlist') self.path_to_database = self.settings.get('path', 'database') self.language = self.settings.get('general', 'language') self.style = self.settings.get('general', 'style') try: with open(path_style, "r") as f: stylesheet = f.read() except: stylesheet = ''' QWidget{background-color: #f0f8ff; color:#0f0f7f} QPushButton{color: #ffffff; background-color: #7070ff; border: 2px; border-radius: 5px; font-weight:bold} QPushButton:hover,QPushButton:pressed{color: #ffffff; background-color:#9f9f9f} QPushButton:disabled{color: #ffffff; background-color:#dfdfdf} QLineEdit{color:#0f0f7f; border:1px solid #a0a0a0; border-radius: 5px; background-color:#dff3ff} QLineEdit:hover{color:#0f0f7f; border:1px solid #7070ff; border-radius: 5px} QLineEdit:disabled{color:#d0d0d0; border:1px solid #d0d0d0; border-radius: 5px} QSpinBox{color:#0f0f7f; border:1px solid #a0a0a0; border-radius: 5px; background-color:#dff3ff} QSpinBox:hover{color:#0f0f7f; border:1px solid #7070ff; border-radius: 5px} QProgressBar{text-align:center; color:#7f7faf; font-weight:bold} QProgressBar::chunk{background-color: #50ff50; width: 10px; margin: 1px;} ''' self.setStyleSheet(stylesheet) if self.language == "en": self.sentences = LANG_ENG else: self.sentences = LANG_JA self.apg = APG(self.path_to_database, self.logger) self.width = int(self.settings.get('screen', 'width')) self.height = int(self.settings.get('screen', 'height')) self.label_data = QLabel(self.sentences["path_to_data"], self) self.label_data.setGeometry(self.width * 0.08, self.height * 0.10, self.width * 0.2, 30) self.label_library = QLabel(self.sentences["path_to_library"], self) self.label_library.setGeometry(self.width * 0.08, self.height * 0.20, self.width * 0.2, 30) self.label_playlist = QLabel(self.sentences["path_to_playlist"], self) self.label_playlist.setGeometry(self.width * 0.08, self.height * 0.30, self.width * 0.2, 30) self.label_advanced = QLabel(self.sentences["advanced_settings"], self) self.label_advanced.setGeometry(self.width * 0.08, self.height * 0.40, self.width * 0.22, 30) self.setFixedSize(self.width, self.height) self.button_data = QPushButton(self.sentences["select"], self) self.button_data.setObjectName('path_data') self.button_data.setGeometry(self.width * 0.78, self.height * 0.10, self.width * 0.15, 30) self.button_data.clicked.connect(self.buttonClicked) self.button_library = QPushButton(self.sentences["select"], self) self.button_library.setObjectName('path_library') self.button_library.setGeometry(self.width * 0.78, self.height * 0.20, self.width * 0.15, 30) self.button_library.clicked.connect(self.buttonClicked) self.button_playlist = QPushButton(self.sentences["select"], self) self.button_playlist.setObjectName('path_playlist') self.button_playlist.setGeometry(self.width * 0.78, self.height * 0.30, self.width * 0.15, 30) self.button_playlist.clicked.connect(self.buttonClicked) self.line_data = QLineEdit(self.path_to_data, self) self.line_data.setGeometry(self.width * 0.27, self.height * 0.10, self.width * 0.5, 30) self.line_data.setToolTip(self.sentences["tips_data"]) self.line_library = QLineEdit(self.path_to_library, self) self.line_library.setGeometry(self.width * 0.27, self.height * 0.20, self.width * 0.5, 30) self.line_library.setToolTip(self.sentences["tips_library"]) self.line_playlist = QLineEdit(self.path_to_playlist, self) self.line_playlist.setGeometry(self.width * 0.27, self.height * 0.30, self.width * 0.5, 30) self.line_playlist.setToolTip(self.sentences["tips_playlist"]) self.line_keyword = QLineEdit(self) self.line_keyword.setGeometry(self.width * 0.25, self.height * 0.55, self.width * 0.3, 30) self.line_keyword.setEnabled(False) self.line_keyword.setToolTip(self.sentences["tips_line_keyword"]) self.completer = QCompleter(self.apg.getCandidate(target="anime"), self) self.completer.setCaseSensitivity(Qt.CaseInsensitive) self.completer.setCompletionMode(QCompleter.PopupCompletion) self.completer.popup().setStyleSheet( "background-color:#3c3c3c; color:#cccccc") self.line_keyword.setCompleter(self.completer) self.logger.info("Completer was called.") self.label_category = QLabel(self.sentences["category"], self) self.label_category.setGeometry(self.width * 0.1, self.height * 0.47, self.width * 0.2, 30) self.check_anime = QCheckBox(self.sentences["anime"], self) self.check_anime.setObjectName("anime") self.check_anime.toggle() self.check_anime.setGeometry(self.width * 0.22, self.height * 0.47, self.width * 0.15, 30) self.check_anime.clicked.connect(self.checkClicked) self.check_anime.setToolTip(self.sentences["tips_cat_anime"]) self.check_game = QCheckBox(self.sentences["game"], self) self.check_game.setObjectName("game") self.check_game.toggle() self.check_game.setGeometry(self.width * 0.33, self.height * 0.47, self.width * 0.15, 30) self.check_game.clicked.connect(self.checkClicked) self.check_game.setToolTip(self.sentences["tips_cat_game"]) self.check_sf = QCheckBox(self.sentences["sf"], self) self.check_sf.setObjectName("sf") self.check_sf.toggle() self.check_sf.setGeometry(self.width * 0.44, self.height * 0.47, self.width * 0.15, 30) self.check_sf.clicked.connect(self.checkClicked) self.check_sf.setToolTip(self.sentences["tips_cat_sf"]) self.keyword = QCheckBox(self.sentences["anime_title"], self) self.keyword.setObjectName("keyword") self.keyword.setGeometry(self.width * 0.10, self.height * 0.55, self.width * 0.15, 30) self.keyword.clicked.connect(self.checkClicked) self.keyword.setToolTip(self.sentences["tips_keyword"]) # Define the push button for start the process self.run = QPushButton(self.sentences["run"], self) self.run.setObjectName('run') self.run.setGeometry(self.width * 0.35, self.height * 0.85, self.width * 0.15, 30) self.run.clicked.connect(self.buttonClicked) self.run.setEnabled(True) self.run.setToolTip(self.sentences["tips_run"]) # Define the push button for making the database self.db_update = QPushButton(self.sentences["db_update"], self) self.db_update.setObjectName('db_update') self.db_update.setGeometry(self.width * 0.75, self.height * 0.65, self.width * 0.15, 30) self.db_update.clicked.connect(self.buttonClicked) self.db_update.setEnabled(True) self.db_update.setToolTip(self.sentences["tips_database"]) # Define the push button for stop the process self.stop = QPushButton(self.sentences["stop"], self) self.stop.setObjectName('stop') self.stop.setGeometry(self.width * 0.55, self.height * 0.85, self.width * 0.15, 30) self.stop.clicked.connect(self.buttonClicked) self.stop.setEnabled(False) self.stop.setToolTip(self.sentences["tips_stop"]) # Define the progress bar self.progress = QProgressBar(self) self.progress.setGeometry(self.width * 0.55, self.height * 0.95, self.width * 0.4, 20) self.progress.setMaximum(100) # Define the status bar self.status = QStatusBar(self) self.status.setGeometry(self.width * 0.01, self.height * 0.95, self.width * 0.5, 20) self.status.showMessage(self.sentences["init"]) self.setWindowTitle('Anison Playlist Generator') self.setWindowTitle self.thread_prog = SubProgress() self.thread_prog.signal.connect(self.updateProgress) self.thread_updateDB = None self.thread_genPlaylist = None def __del__(self): self.config.saveConfig(name=self.path_config, path_library=self.path_to_library, path_data=self.path_to_data, path_playlist=self.path_to_playlist, path_database=self.path_to_database, width=800, height=480, path_style="./styles/style.qss", language=self.language) def paintEvent(self, event): self.painter = QPainter(self) self.painter.setBrush(QColor(30, 30, 30)) self.painter.setPen(QColor(160, 160, 160)) rect = QRect(int(self.width * 0.05), int(self.height * 0.43), int(self.width * 0.90), int(self.height * 0.40)) self.painter.drawRoundedRect(rect, 20.0, 20.0) self.painter.end() def updateText(self, var, folder=True, ext=".m3u"): updated_path = "" if folder: updated_path = QFileDialog.getExistingDirectory( None, 'rootpath', var.text()) else: updated_path = QFileDialog.getOpenFileName(None, 'rootpath', var.text())[0] if updated_path == "" or (os.path.isfile(updated_path) and os.path.splitext(updated_path)[1] != ext): var.setText(var.text()) else: var.setText(updated_path) return updated_path def checkClicked(self): sender = self.sender() if sender.objectName() == "keyword": if self.keyword.checkState(): self.status.showMessage(sender.text().replace(":", "") + self.sentences["enable"]) self.line_keyword.setEnabled(True) else: self.status.showMessage(sender.text().replace(":", "") + self.sentences["disable"]) self.line_keyword.setEnabled(False) elif sender.objectName() == "anime": if self.check_anime.checkState(): self.status.showMessage(self.check_anime.text() + self.sentences["enable"]) else: self.status.showMessage(self.check_anime.text() + self.sentences["disable"]) elif sender.objectName() == "game": if self.check_game.checkState(): self.status.showMessage(self.check_game.text() + self.sentences["enable"]) else: self.status.showMessage(self.check_game.text() + self.sentences["disable"]) elif sender.objectName() == "sf": if self.check_sf.checkState(): self.status.showMessage(self.check_sf.text() + self.sentences["enable"]) else: self.status.showMessage(self.check_sf.text() + self.sentences["disable"]) def buttonClicked(self): sender = self.sender() self.status.showMessage(sender.text() + self.sentences["pressed"]) if sender.objectName() == "path_data": self.path_to_data = self.updateText(self.line_data) elif sender.objectName() == "path_library": self.path_to_library = self.updateText(self.line_library) elif sender.objectName() == "path_playlist": self.path_to_playlist = self.updateText(self.line_playlist, False, ".m3u") elif sender.objectName() == "run": self.logger.info("Started to making the playlist.") # 既に実行しているスレッドがある場合は処理を中止 if self.checkThreadRunning(): self.logger.info( "GenPlaylist: The previous thread is running.") QMessageBox.warning(None, self.sentences["warn_overwrite"], "直前の処理を中断しています.", QMessageBox.Ok) return # ファイルの拡張子の確認 if os.path.splitext(self.line_playlist.text())[1] != ".m3u": self.status.showMessage(self.sentences["warn_ext"]) return # プレイリストが存在するか確認 if os.path.exists(self.line_playlist.text()): react = QMessageBox.warning( None, self.sentences["warn_overwrite"], self.sentences["message_overwrite"], QMessageBox.Yes, QMessageBox.No) if react == QMessageBox.No: self.lockInput(enabled=True) return # チェックボックスの状態の確認 check_categories = { "anison": self.check_anime.checkState(), "game": self.check_game.checkState(), "sf": self.check_sf.checkState() } # プレイリスト作成用のスレッドの呼び出し self.thread_genPlaylist = GenPlaylist( apg=self.apg, keyword=self.line_keyword.text() if self.keyword.checkState() else "", use_key=1 if self.keyword.checkState() else 0, path_playlist=self.line_playlist.text(), check_categories=check_categories) self.thread_genPlaylist.signal.connect(self.generatePlaylist) # マルチスレッドによる処理の開始 self.lockInput(enabled=False) self.thread_prog.start() self.thread_genPlaylist.start() elif sender.objectName() == "db_update": self.logger.info("Started to updating the database.") # 既に実行しているスレッドがある場合は処理を中止 if self.checkThreadRunning(): self.logger.info("UpdateDB: The previous thread is running.") QMessageBox.warning(None, self.sentences["warn_overwrite"], "直前の処理を中断中です", QMessageBox.Ok) return # データベース更新処理の確認 react = QMessageBox.warning(None, self.sentences["warn_overwrite"], "データベースを更新しますか?", QMessageBox.Yes, QMessageBox.No) # Noなら処理を中止 if react == QMessageBox.No: self.lockInput(enabled=True) return # スレッドの実行準備 self.thread_updateDB = UpdateDB( apg=self.apg, path_data=self.line_data.text(), path_library=self.line_library.text()) self.thread_updateDB.signal.connect(self.updateDB) # ボタンなどの入力が出来ないようにする self.lockInput(enabled=False) # マルチスレッドによる処理の開始 self.thread_prog.start() self.thread_updateDB.start() elif sender.objectName() == "stop": self.apg.stop() self.apg.reset() self.thread_prog.wait() self.thread_prog.quit() if self.thread_updateDB != None: self.thread_updateDB.wait() self.thread_updateDB.quit() elif self.thread_genPlaylist != None: self.thread_genPlaylist.wait() self.thread_genPlaylist.quit() self.status.showMessage(self.sentences["warn_stop"]) self.lockInput(enabled=True) def checkThreadRunning(self): """ マルチスレッド処理が行われているかを返す関数 """ check_updateDB = (self.thread_updateDB != None) and self.thread_updateDB.isRunning() check_genPlaylist = (self.thread_genPlaylist != None) and self.thread_genPlaylist.isRunning() return check_updateDB or check_genPlaylist def lockInput(self, enabled=True): """ ボタン入力などの有効/無効を切り替える関数 """ self.stop.setEnabled(not enabled) self.run.setEnabled(enabled) self.db_update.setEnabled(enabled) self.check_anime.setEnabled(enabled) self.check_game.setEnabled(enabled) self.check_sf.setEnabled(enabled) self.keyword.setEnabled(enabled) self.line_keyword.setEnabled(enabled) self.button_data.setEnabled(enabled) self.button_library.setEnabled(enabled) self.button_playlist.setEnabled(enabled) self.line_data.setEnabled(enabled) self.line_playlist.setEnabled(enabled) self.line_library.setEnabled(enabled) return def updateProgress(self, signal): """ プログレスバーの値を更新する関数 """ db, library, playlist = self.apg.getProgress() value = 0 if 0 < playlist and playlist < 100: value = playlist elif 0 < db and db < 100: value = db else: value = library self.progress.setValue(value) def resetProgress(self): """ ProgressBarの値を0にする関数 """ self.progress.setValue(0) def generatePlaylist(self, signal): """ GenPlaylistスレッド実行時に呼び出される関数 空白を受信したらスレッドを終了する """ if signal != "": self.status.showMessage(signal) else: self.apg.reset() self.thread_genPlaylist.wait() self.thread_genPlaylist.quit() self.thread_prog.wait() self.thread_prog.quit() self.resetProgress() self.lockInput(enabled=True) # メッセージの表示 self.status.showMessage(self.sentences["fin_making_playlist"]) def updateDB(self, signal): """ UpdateDBスレッド実行時に呼び出される関数 空白を受信したらスレッドを終了する """ if signal != "": self.status.showMessage(signal) else: self.apg.reset() self.thread_updateDB.wait() self.thread_updateDB.quit() self.thread_prog.wait() self.thread_prog.quit() self.resetProgress() self.lockInput(enabled=True) # 検索候補の更新 self.completer.model().setStringList( self.apg.getCandidate(target="anime")) # メッセージの表示 self.status.showMessage(self.sentences["fin_updating_database"])
class HelpWebSearchWidget(E5ClearableLineEdit): """ Class implementing a web search widget for the web browser. @signal search(QUrl) emitted when the search should be done """ search = pyqtSignal(QUrl) def __init__(self, parent=None): """ Constructor @param parent reference to the parent widget (QWidget) """ super(HelpWebSearchWidget, self).__init__(parent) from E5Gui.E5LineEdit import E5LineEdit from E5Gui.E5LineEditButton import E5LineEditButton from .OpenSearch.OpenSearchManager import OpenSearchManager self.__mw = parent self.__openSearchManager = OpenSearchManager(self) self.__openSearchManager.currentEngineChanged.connect( self.__currentEngineChanged) self.__currentEngine = "" self.__enginesMenu = QMenu(self) self.__engineButton = E5LineEditButton(self) self.__engineButton.setMenu(self.__enginesMenu) self.addWidget(self.__engineButton, E5LineEdit.LeftSide) self.__searchButton = E5LineEditButton(self) self.__searchButton.setIcon(UI.PixmapCache.getIcon("webSearch.png")) self.addWidget(self.__searchButton, E5LineEdit.LeftSide) self.__model = QStandardItemModel(self) self.__completer = QCompleter() self.__completer.setModel(self.__model) self.__completer.setCompletionMode( QCompleter.UnfilteredPopupCompletion) self.__completer.setWidget(self) self.__searchButton.clicked.connect(self.__searchButtonClicked) self.textEdited.connect(self.__textEdited) self.returnPressed.connect(self.__searchNow) self.__completer.activated[QModelIndex].connect( self.__completerActivated) self.__completer.highlighted[QModelIndex].connect( self.__completerHighlighted) self.__enginesMenu.aboutToShow.connect(self.__showEnginesMenu) self.__suggestionsItem = None self.__suggestions = [] self.__suggestTimer = None self.__suggestionsEnabled = Preferences.getHelp("WebSearchSuggestions") self.__recentSearchesItem = None self.__recentSearches = [] self.__maxSavedSearches = 10 self.__engine = None self.__loadSearches() self.__setupCompleterMenu() self.__currentEngineChanged() def __searchNow(self): """ Private slot to perform the web search. """ searchText = self.text() if not searchText: return globalSettings = QWebSettings.globalSettings() if not globalSettings.testAttribute( QWebSettings.PrivateBrowsingEnabled): if searchText in self.__recentSearches: self.__recentSearches.remove(searchText) self.__recentSearches.insert(0, searchText) if len(self.__recentSearches) > self.__maxSavedSearches: self.__recentSearches = \ self.__recentSearches[:self.__maxSavedSearches] self.__setupCompleterMenu() url = self.__openSearchManager.currentEngine().searchUrl(searchText) self.search.emit(url) def __setupCompleterMenu(self): """ Private method to create the completer menu. """ if not self.__suggestions or \ (self.__model.rowCount() > 0 and self.__model.item(0) != self.__suggestionsItem): self.__model.clear() self.__suggestionsItem = None else: self.__model.removeRows(1, self.__model.rowCount() - 1) boldFont = QFont() boldFont.setBold(True) if self.__suggestions: if self.__model.rowCount() == 0: if not self.__suggestionsItem: self.__suggestionsItem = QStandardItem( self.tr("Suggestions")) self.__suggestionsItem.setFont(boldFont) self.__model.appendRow(self.__suggestionsItem) for suggestion in self.__suggestions: self.__model.appendRow(QStandardItem(suggestion)) if not self.__recentSearches: self.__recentSearchesItem = QStandardItem( self.tr("No Recent Searches")) self.__recentSearchesItem.setFont(boldFont) self.__model.appendRow(self.__recentSearchesItem) else: self.__recentSearchesItem = QStandardItem( self.tr("Recent Searches")) self.__recentSearchesItem.setFont(boldFont) self.__model.appendRow(self.__recentSearchesItem) for recentSearch in self.__recentSearches: self.__model.appendRow(QStandardItem(recentSearch)) view = self.__completer.popup() view.setFixedHeight(view.sizeHintForRow(0) * self.__model.rowCount() + view.frameWidth() * 2) self.__searchButton.setEnabled( bool(self.__recentSearches or self.__suggestions)) def __completerActivated(self, index): """ Private slot handling the selection of an entry from the completer. @param index index of the item (QModelIndex) """ if self.__suggestionsItem and \ self.__suggestionsItem.index().row() == index.row(): return if self.__recentSearchesItem and \ self.__recentSearchesItem.index().row() == index.row(): return self.__searchNow() def __completerHighlighted(self, index): """ Private slot handling the highlighting of an entry of the completer. @param index index of the item (QModelIndex) @return flah indicating a successful highlighting (boolean) """ if self.__suggestionsItem and \ self.__suggestionsItem.index().row() == index.row(): return False if self.__recentSearchesItem and \ self.__recentSearchesItem.index().row() == index.row(): return False self.setText(index.data()) return True def __textEdited(self, txt): """ Private slot to handle changes of the search text. @param txt search text (string) """ if self.__suggestionsEnabled: if self.__suggestTimer is None: self.__suggestTimer = QTimer(self) self.__suggestTimer.setSingleShot(True) self.__suggestTimer.setInterval(200) self.__suggestTimer.timeout.connect(self.__getSuggestions) self.__suggestTimer.start() else: self.__completer.setCompletionPrefix(txt) self.__completer.complete() def __getSuggestions(self): """ Private slot to get search suggestions from the configured search engine. """ searchText = self.text() if searchText: self.__openSearchManager.currentEngine()\ .requestSuggestions(searchText) def __newSuggestions(self, suggestions): """ Private slot to receive a new list of suggestions. @param suggestions list of suggestions (list of strings) """ self.__suggestions = suggestions self.__setupCompleterMenu() self.__completer.complete() def __showEnginesMenu(self): """ Private slot to handle the display of the engines menu. """ self.__enginesMenu.clear() from .OpenSearch.OpenSearchEngineAction import OpenSearchEngineAction engineNames = self.__openSearchManager.allEnginesNames() for engineName in engineNames: engine = self.__openSearchManager.engine(engineName) action = OpenSearchEngineAction(engine, self.__enginesMenu) action.setData(engineName) action.triggered.connect(self.__changeCurrentEngine) self.__enginesMenu.addAction(action) if self.__openSearchManager.currentEngineName() == engineName: action.setCheckable(True) action.setChecked(True) ct = self.__mw.currentBrowser() linkedResources = ct.linkedResources("search") if len(linkedResources) > 0: self.__enginesMenu.addSeparator() for linkedResource in linkedResources: url = QUrl(linkedResource.href) title = linkedResource.title mimetype = linkedResource.type_ if mimetype != "application/opensearchdescription+xml": continue if url.isEmpty(): continue if url.isRelative(): url = ct.url().resolved(url) if not title: if not ct.title(): title = url.host() else: title = ct.title() action = self.__enginesMenu.addAction( self.tr("Add '{0}'").format(title), self.__addEngineFromUrl) action.setData(url) action.setIcon(ct.icon()) self.__enginesMenu.addSeparator() self.__enginesMenu.addAction(self.__mw.searchEnginesAction()) if self.__recentSearches: self.__enginesMenu.addAction(self.tr("Clear Recent Searches"), self.clear) def __changeCurrentEngine(self): """ Private slot to handle the selection of a search engine. """ action = self.sender() if action is not None: name = action.data() self.__openSearchManager.setCurrentEngineName(name) def __addEngineFromUrl(self): """ Private slot to add a search engine given its URL. """ action = self.sender() if action is not None: url = action.data() if not isinstance(url, QUrl): return self.__openSearchManager.addEngine(url) def __searchButtonClicked(self): """ Private slot to show the search menu via the search button. """ self.__setupCompleterMenu() self.__completer.complete() def clear(self): """ Public method to clear all private data. """ self.__recentSearches = [] self.__setupCompleterMenu() super(HelpWebSearchWidget, self).clear() self.clearFocus() def preferencesChanged(self): """ Public method to handle the change of preferences. """ self.__suggestionsEnabled = Preferences.getHelp("WebSearchSuggestions") if not self.__suggestionsEnabled: self.__suggestions = [] self.__setupCompleterMenu() def saveSearches(self): """ Public method to save the recently performed web searches. """ Preferences.Prefs.settings.setValue( 'Help/WebSearches', self.__recentSearches) def __loadSearches(self): """ Private method to load the recently performed web searches. """ searches = Preferences.Prefs.settings.value('Help/WebSearches') if searches is not None: self.__recentSearches = searches def openSearchManager(self): """ Public method to get a reference to the opensearch manager object. @return reference to the opensearch manager object (OpenSearchManager) """ return self.__openSearchManager def __currentEngineChanged(self): """ Private slot to track a change of the current search engine. """ if self.__openSearchManager.engineExists(self.__currentEngine): oldEngine = self.__openSearchManager.engine(self.__currentEngine) oldEngine.imageChanged.disconnect(self.__engineImageChanged) if self.__suggestionsEnabled: oldEngine.suggestions.disconnect(self.__newSuggestions) newEngine = self.__openSearchManager.currentEngine() if newEngine.networkAccessManager() is None: newEngine.setNetworkAccessManager(self.__mw.networkAccessManager()) newEngine.imageChanged.connect(self.__engineImageChanged) if self.__suggestionsEnabled: newEngine.suggestions.connect(self.__newSuggestions) self.setInactiveText(self.__openSearchManager.currentEngineName()) self.__currentEngine = self.__openSearchManager.currentEngineName() self.__engineButton.setIcon(QIcon(QPixmap.fromImage( self.__openSearchManager.currentEngine().image()))) self.__suggestions = [] self.__setupCompleterMenu() def __engineImageChanged(self): """ Private slot to handle a change of the current search engine icon. """ self.__engineButton.setIcon(QIcon(QPixmap.fromImage( self.__openSearchManager.currentEngine().image()))) def mousePressEvent(self, evt): """ Protected method called by a mouse press event. @param evt reference to the mouse event (QMouseEvent) """ if evt.button() == Qt.XButton1: self.__mw.currentBrowser().pageAction(QWebPage.Back).trigger() elif evt.button() == Qt.XButton2: self.__mw.currentBrowser().pageAction(QWebPage.Forward).trigger() else: super(HelpWebSearchWidget, self).mousePressEvent(evt)
def setCategories(self, categories): completer = QCompleter(categories) mCompleterItemDelegate = QStyledItemDelegate(self) completer.popup().setItemDelegate(mCompleterItemDelegate) Utilities.Style.applyStyle(completer.popup()) self.category.setCompleter(completer)