示例#1
0
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()
示例#2
0
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
示例#4
0
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)
示例#5
0
    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()
示例#6
0
文件: widgets.py 项目: mchal821/dars
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)
示例#7
0
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")
示例#8
0
    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()
示例#9
0
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)
示例#10
0
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
示例#11
0
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()
示例#12
0
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)
示例#13
0
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()
示例#14
0
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)
示例#17
0
    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)
示例#18
0
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)
示例#19
0
    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()
示例#21
0
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
示例#22
0
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)
示例#23
0
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)
示例#25
0
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)
示例#26
0
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()
示例#27
0
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)
示例#28
0
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()
示例#30
0
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"])
示例#31
0
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)