Ejemplo n.º 1
0
class PlainView(QFrame):

    textChanged = pyqtSignal()

    def __init__(self, text):
        super(PlainView, self).__init__()
        layout = QVBoxLayout()
        self._plain_text = QPlainTextEdit(text)
        self._plain_text.textChanged.connect(self._on_plain_text_changed_event)
        self._search_field = SearchField()
        self._search_field.setClosable(True)
        self._search_field.setIcon(qtawesome.icon("fa.search"))
        self._search_field.setPlaceholderText("Search text")
        self._search_field.escapePressed.connect(
            self._on_search_field_escape_pressed_event)
        self._search_field.textChanged.connect(self._do_highlight_text)
        self._search_field.closeEvent.connect(self._do_close_search_field)
        self._search_field.setVisible(False)
        layout.addWidget(self._plain_text)
        layout.addWidget(self._search_field)
        layout.setContentsMargins(0, 0, 0, 0)
        self.setLayout(layout)

    def _on_plain_text_changed_event(self):
        self.textChanged.emit()
        if self._search_field.isVisible():
            self._do_highlight_text()

    def _on_search_field_escape_pressed_event(self):
        if self._search_field.hasFocus() and self._search_field.isVisible():
            self._do_close_search_field()

    def _do_highlight_clear(self):
        self._plain_text.blockSignals(True)
        format = QTextCharFormat()
        format.setForeground(QBrush(QColor("black")))
        cursor = self._plain_text.textCursor()
        cursor.setPosition(0)
        cursor.movePosition(QTextCursor.End, QTextCursor.KeepAnchor, 1)
        cursor.mergeCharFormat(format)
        self._plain_text.blockSignals(False)

    def _do_highlight_text(self):
        def highlight_text(text, format):
            cursor = self._plain_text.textCursor()
            regex = QRegExp(QRegExp.escape(text))

            # Process the displayed document
            pos = 0
            index = regex.indexIn(self._plain_text.toPlainText(), pos)
            while index != -1:
                # Select the matched text and apply the desired format
                cursor.setPosition(index)
                cursor.movePosition(QTextCursor.NextCharacter,
                                    QTextCursor.KeepAnchor, len(text))
                cursor.mergeCharFormat(format)
                # Move to the next match
                pos = index + regex.matchedLength()
                index = regex.indexIn(self.toPlainText(), pos)

        self._do_highlight_clear()
        self._plain_text.blockSignals(True)
        searchString = self._search_field.text()
        if searchString:
            format = QTextCharFormat()
            format.setForeground(QBrush(QColor("red")))
            highlight_text(searchString, format)
        self._plain_text.blockSignals(False)

    def _do_open_search_field(self):
        self._search_field.setVisible(True)
        self._do_highlight_text()
        self._search_field.setFocus()

    def _do_close_search_field(self):
        self._do_highlight_clear()
        self._plain_text.setFocus()
        self._search_field.setVisible(False)

    def toggleSearchField(self):
        if self._search_field.hasFocus() and self._search_field.isVisible():
            self._do_close_search_field()
        else:
            self._do_open_search_field()

    def toPlainText(self):
        return self._plain_text.toPlainText()

    def setPlainText(self, text):
        return self._plain_text.setPlainText(text)

    def setFocus(self, Qt_FocusReason=None):
        self._plain_text.setFocus()
Ejemplo n.º 2
0
class ApplicationPage(QWidget):
    """ The GUI for the application page of a project. """

    # The page's label.
    label = "Application Source"

    @property
    def project(self):
        """ The project property getter. """

        return self._project

    @project.setter
    def project(self, value):
        """ The project property setter. """

        if self._project != value:
            self._project = value
            self._script_edit.set_project(value)
            self._package_edit.set_project(value)
            self._update_page()

    def __init__(self):
        """ Initialise the page. """

        super().__init__()

        self._project = None

        # Create the page's GUI.
        layout = QGridLayout()

        form = BetterForm()

        self._name_edit = QLineEdit(
            placeholderText="Application name",
            whatsThis="The name of the application. It will default to "
            "the base name of the application script without any "
            "extension.",
            textEdited=self._name_changed)
        form.addRow("Name", self._name_edit)

        self._script_edit = FilenameEditor(
            "Application Script",
            placeholderText="Application script",
            whatsThis="The name of the application's optional main script "
            "file.",
            textEdited=self._script_changed)
        form.addRow("Main script file", self._script_edit)

        self._entry_point_edit = QLineEdit(
            placeholderText="Entry point in application package",
            whatsThis="The name of the optional entry point in the "
            "application's package.",
            textEdited=self._entry_point_changed)
        form.addRow("Entry point", self._entry_point_edit)

        self._sys_path_edit = QLineEdit(
            placeholderText="Additional sys.path directories",
            whatsThis="A space separated list of additional directories, "
            "ZIP files and eggs to add to <tt>sys.path</tt>. Only "
            "set this if you want to allow external packages to "
            "be imported.",
            textEdited=self._sys_path_changed)
        form.addRow("sys.path", self._sys_path_edit)

        layout.addLayout(form, 0, 0)

        options_layout = BetterForm()

        self._console_edit = QCheckBox(
            "Use console (Windows)",
            whatsThis="Enable console output for Windows applications. "
            "Console output will be enabled automatically if no "
            "graphical PyQt modules are used.",
            stateChanged=self._console_changed)
        options_layout.addRow(self._console_edit)

        self._bundle_edit = QCheckBox(
            "Application bundle (macOS)",
            whatsThis="Build an application bundle on macOS. If it is not "
            "checked then the application will be built as a "
            "simple executable.",
            stateChanged=self._bundle_changed)
        options_layout.addRow(self._bundle_edit)

        layout.addLayout(options_layout, 0, 1)

        # Extra space is needed before the application package editor.
        layout.setRowMinimumHeight(
            1, 1.4 * QFontInfo(QGuiApplication.font()).pixelSize())

        self._package_edit = _ApplicationPackageEditor()
        self._package_edit.package_changed.connect(self._package_changed)
        package_edit_gb = QGroupBox(self._package_edit.title)
        package_edit_gb.setFlat(True)
        package_edit_gb.setLayout(self._package_edit)
        layout.addWidget(package_edit_gb, 2, 0, 1, 2)

        qmake = CollapsibleWidget("Additional qmake Configuration")
        self._qmake_edit = QPlainTextEdit(
            whatsThis="Any text entered here will be appended to the "
            "generated <tt>.pro</tt> that will be processed by "
            "<tt>qmake</tt>.",
            textChanged=self._qmake_changed)
        qmake.setWidget(self._qmake_edit)
        layout.addWidget(qmake, 3, 0, 1, 2)

        self.setLayout(layout)

    def _update_page(self):
        """ Update the page using the current project. """

        project = self.project

        self._name_edit.setText(project.application_name)
        self._script_edit.setText(project.application_script)
        self._entry_point_edit.setText(project.application_entry_point)
        self._sys_path_edit.setText(project.sys_path)
        self._package_edit.configure(project.application_package, project)

        blocked = self._console_edit.blockSignals(True)
        self._console_edit.setCheckState(
            Qt.Checked if project.application_is_console else Qt.Unchecked)
        self._console_edit.blockSignals(blocked)

        blocked = self._bundle_edit.blockSignals(True)
        self._bundle_edit.setCheckState(
            Qt.Checked if project.application_is_bundle else Qt.Unchecked)
        self._bundle_edit.blockSignals(blocked)

        blocked = self._qmake_edit.blockSignals(True)
        self._qmake_edit.setPlainText(self._project.qmake_configuration)
        self._qmake_edit.blockSignals(blocked)

    def _console_changed(self, state):
        """ Invoked when the user changes the console state. """

        self.project.application_is_console = (state == Qt.Checked)
        self.project.modified = True

    def _bundle_changed(self, state):
        """ Invoked when the user changes the bundle state. """

        self.project.application_is_bundle = (state == Qt.Checked)
        self.project.modified = True

    def _name_changed(self, value):
        """ Invoked when the user edits the application name. """

        self.project.application_name = value
        self.project.modified = True

    def _script_changed(self, value):
        """ Invoked when the user edits the application script name. """

        self.project.application_script = value
        self.project.modified = True

    def _entry_point_changed(self, value):
        """ Invoked when the user edits the entry point. """

        self.project.application_entry_point = value
        self.project.modified = True

    def _sys_path_changed(self, value):
        """ Invoked when the user edits the sys.path directories. """

        self.project.sys_path = value.strip()
        self.project.modified = True

    def _package_changed(self):
        """ Invoked when the user edits the application package. """

        self.project.modified = True

    def _qmake_changed(self):
        """ Invoked when the user edits the qmake configuration. """

        self.project.qmake_configuration = self._qmake_edit.toPlainText()
        self.project.modified = True
Ejemplo n.º 3
0
class LogViewer(QWidget):
    def __init__(self,
                 show_indicators=False,
                 indicator='',
                 file_name='',
                 parent=None):
        super(LogViewer, self).__init__(parent)

        # Setup layout
        global_layout = QHBoxLayout(self)
        global_layout.setContentsMargins(0, 0, 0, 0)

        widget = QWidget(self)
        widget.setObjectName('logview')
        global_layout.addWidget(widget)

        layout = QVBoxLayout(widget)
        layout.setContentsMargins(0, 0, 0, 0)

        self.project_path = ''
        self.log_path = ''
        self.error_path = ''
        self.indicator_names = ('log', 'error', 'sys_log', 'notes')
        self.indicator = indicator

        self.text = QPlainTextEdit(widget)
        if file_name:
            self.text.setObjectName('dialog')
        else:
            self.text.setObjectName('status')
        self.text.setPlaceholderText('Welcome to TranSPHIRE!')
        self.text.setToolTip(
            'Double click after starting TranSPHIRE in order to show more information'
        )
        self.text.setReadOnly(True)
        self.text.setWordWrapMode(QTextOption.WrapAnywhere)
        layout.addWidget(self.text, stretch=1)

        self.file_name = file_name

        self.buttons = {}
        if show_indicators:

            layout_h1 = QHBoxLayout()

            for entry in self.indicator_names:
                template = '{0}: {{0}}'.format(entry)
                self.buttons[entry] = [QPushButton(self), template]
                self.buttons[entry][0].setObjectName('button_entry')
                self.buttons[entry][0].clicked.connect(self.my_click_event)

                layout_h1.addWidget(self.buttons[entry][0])
                self.increment_indicator(entry, '0')

            layout_h1.addStretch(1)
            layout.addLayout(layout_h1)

            self.change_state(False)

        self.update_plain_text(force=True)
        self.timer = QTimer(self)
        self.timer.setInterval(1000)
        self.timer.timeout.connect(self.update_plain_text)
        self.timer.start()

        if self.indicator == 'notes':
            layout_h = QHBoxLayout()
            layout_h.setContentsMargins(0, 0, 0, 0)
            self.input_edit = QLineEdit('', self)
            submit_button = QPushButton('Submit', self)
            submit_button.clicked.connect(self.submit_text)
            layout_h.addWidget(self.input_edit, stretch=1)
            layout_h.addWidget(submit_button)
            layout.addLayout(layout_h)

    @pyqtSlot()
    def update_plain_text(self, force=False):
        if self.file_name and os.path.exists(self.file_name):
            with open(self.file_name, 'r') as read:
                text = read.read()
            if force:
                self.reset_plain_text(text)
            elif text.replace('\n', '').replace(
                    ' ', '') != self.text.toPlainText().replace(
                        '\n', '').replace(' ', ''):
                self.reset_plain_text(text)

    def reset_plain_text(self, text):
        self.text.setPlainText(text)
        cursor = self.text.textCursor()
        cursor.movePosition(QTextCursor.End)
        self.text.setTextCursor(cursor)

    @pyqtSlot()
    def submit_text(self):
        self.appendPlainText(self.input_edit.text(),
                             indicator='notes',
                             user=True)
        self.input_edit.setText('')

    def appendPlainText(self, text, indicator='log', user=False):
        text_raw = tu.create_log(text)
        prefix, suffix = text_raw.split(' => ', 1)
        if user:
            text = '{}\n{}: {}\n'.format(prefix, getpass.getuser(), suffix)
        else:
            text = '{}\n{}\n'.format(prefix, suffix)
        try:
            with open(self.file_name, 'a+') as write:
                write.write(text)
        except IOError:
            pass
        self.text.appendPlainText(text)
        cursor = self.text.textCursor()
        cursor.movePosition(QTextCursor.End)
        self.text.setTextCursor(cursor)
        print(text)
        if self.project_path:
            self.increment_indicator(indicator)

    def increment_indicator(self, indicator, text=''):
        if indicator in self.indicator_names:
            button, template = self.buttons[indicator]
            if text:
                cur_text = text
            else:
                cur_text = str(1 + int(self.get_indicator(indicator)))

            button.setText(template.format(cur_text))
            button.setToolTip(template.format(text))
            if self.get_indicator(indicator) == '0':
                button.setStyleSheet('')
            else:
                button.setStyleSheet(tu.get_style('changed'))
        else:
            assert False, indicator

    def get_indicator(self, indicator):
        if indicator in self.indicator_names:
            return self.buttons[indicator][0].text().split(':')[-1].strip()
        else:
            assert False, indicator

    @pyqtSlot()
    def my_click_event(self, event=None):
        if not self.project_path:
            return None

        sender = self.sender()
        sender_text = sender.text().split(':')[0].strip()
        is_notes = False
        file_path = self.log_path

        if sender_text == 'log':
            file_names = ['log.txt']

        elif sender_text == 'notes':
            is_notes = True
            file_names = ['notes.txt']

        elif sender_text == 'sys_log':
            file_names = ['sys_log.txt']

        elif sender_text == 'error':
            file_names = [
                os.path.basename(entry)
                for entry in glob.glob(os.path.join(self.error_path, '*'))
            ]
            file_path = self.error_path

        else:
            assert False, sender.text()

        if not is_notes:
            self.increment_indicator(sender_text, '0')

        sender.setEnabled(False)
        QTimer.singleShot(5000, lambda: sender.setEnabled(True))

        dialog = logviewerdialog.LogViewerDialog(self)
        for file_name in file_names:
            dialog.add_tab(
                LogViewer(file_name=os.path.join(file_path, file_name),
                          indicator=sender_text,
                          parent=self),
                os.path.basename(file_name),
            )
        dialog.show()

    @pyqtSlot(str, str, str)
    def set_project_path(self, project_path, log_path, error_path):
        self.project_path = project_path
        self.log_path = log_path
        self.error_path = error_path
        state = True
        if not self.project_path:
            state = False
            self.file_name = ''
        elif not self.file_name:
            self.file_name = os.path.join(self.log_path, 'log.txt')
            self.update_plain_text(force=True)
        self.change_state(state)

    def change_state(self, state):
        self.text.blockSignals(not state)
        for button, _ in self.buttons.values():
            button.setEnabled(state)
            button.blockSignals(not state)