Ejemplo n.º 1
0
class CodeEditor(QWidget):
    def __init__(self, parent):
        super().__init__()
        self.parent = parent
        # Layouts
        self.layout = QVBoxLayout()
        self.top_layout = QHBoxLayout()

        # Code Editor
        self.text_code_editor = QPlainTextEdit()
        self.text_code_editor.setStyleSheet(
            """ background-color: #162F3E; font-size:16px; color:white;""")
        self.editor_font = QFont()
        self.editor_font.setFamily("Courier")
        self.editor_font.setStyleHint(QFont().Monospace)
        self.editor_font.setPointSize(16)
        self.editor_font.setFixedPitch(True)

        # Set tab metric of self.text_code_editor
        metrics = QFontMetrics(self.editor_font)
        self.text_code_editor.setTabStopWidth(4 * metrics.width(' '))
        self.text_code_editor.setFont(self.editor_font)

        # Results
        self.plaintext_results = QPlainTextEdit()
        self.plaintext_results.setFont(self.editor_font)
        self.plaintext_results.setMaximumHeight(300)
        self.plaintext_results.setReadOnly(True)
        self.plaintext_results.setStyleSheet(
            """ background-color: #162F3E; font-size:13px; color:white;""")

        # Rub button
        self.button_run = QPushButton("Run Code (CTRL+Enter)")
        self.button_run.clicked.connect(self.run_code)

        # Kill kernel button
        self.button_kill = QPushButton("Kill Kernel")
        self.button_kill.clicked.connect(self.stop_thread)

        # Start kernel button
        self.button_start = QPushButton("Start Kernel")
        self.button_start.clicked.connect(self.start_kernel)

        # adding layout and widgets
        self.top_layout.addWidget(self.button_run)
        self.top_layout.addWidget(self.button_start)
        self.top_layout.addWidget(self.button_kill)
        self.layout.addLayout(self.top_layout)
        self.layout.addWidget(self.text_code_editor)
        self.layout.addWidget(self.plaintext_results)

        self.setLayout(self.layout)
        self.start_kernel()

    def keyPressEvent(self, key):
        if self.text_code_editor.hasFocus():
            # Ctrl+Enter Pressed
            if key.key() == 16777220:
                self.run_code()

    def start_kernel(self):
        self.button_start.setDisabled(True)
        self.button_kill.setEnabled(True)
        self.button_run.setEnabled(True)
        self.parent.toolbarBox.setDisabled(True)
        self.plaintext_results.clear()
        self.text_code_editor.clear()
        # start thread
        self.codeThread = CodeThread(parent=self)
        self.codeThread.sgn_message.connect(self.update_results)
        self.codeThread.sgn_status.connect(self.update_elements)
        self.codeThread.start()

    def run_code(self):
        cursor = self.text_code_editor.textCursor()
        selected = cursor.selectedText().replace(u"\u2029", "\n")
        if len(selected) > 0:
            self.codeThread.code = selected
        else:
            self.codeThread.code = self.text_code_editor.toPlainText()

    def update_elements(self, status):
        if status == 1:
            self.parent.toolbarBox.setDisabled(True)
            self.text_code_editor.setDisabled(True)
        else:
            self.text_code_editor.setEnabled(True)

    def update_results(self, message):
        all_messages = self.plaintext_results.toPlainText() + '\n' + message
        self.plaintext_results.setPlainText(all_messages)
        self.plaintext_results.verticalScrollBar().setValue(
            self.plaintext_results.verticalScrollBar().maximum())

    def stop_thread(self):
        self.codeThread.terminate()
        self.update_elements(0)
        self.update_results("Kernel terminated")
        self.button_start.setEnabled(True)
        self.button_run.setDisabled(True)
        self.button_kill.setDisabled(True)
        self.parent.toolbarBox.setEnabled(True)
Ejemplo n.º 2
0
class UI(QMainWindow):
  ''' GUI '''
  def __init__(self, model_dir: str, model_language: str, model_status: str):
      super().__init__()
      self.setAcceptDrops(True)

      # create menu actions
      menubar = self.menuBar()
      windowMenu = menubar.addMenu('&Window')
      exitAction = QAction(' &Minimize', self)
      exitAction.setShortcut('Ctrl+M')
      exitAction.triggered.connect(self.minimize)
      windowMenu.addAction(exitAction)
      zoomInAction = QAction(' &Zoom in', self)
      zoomInAction.setShortcut(QKeySequence.ZoomIn)
      zoomInAction.triggered.connect(self.zoomIn)
      windowMenu.addAction(zoomInAction)
      zoomOutAction = QAction(' &Zoom out', self)
      zoomOutAction.setShortcut(QKeySequence.ZoomOut)
      zoomOutAction.triggered.connect(self.zoomOut)
      windowMenu.addAction(zoomOutAction)
      originalSizeAction = QAction(' &Original Size', self)
      originalSizeAction.setShortcut('Ctrl+0')
      originalSizeAction.triggered.connect(self.originalSize)
      windowMenu.addAction(originalSizeAction)

      central_layout = QVBoxLayout()

      # # label row
      header_layout = QHBoxLayout()
      text_label = QLabel('Source')
      text_label.setAlignment(Qt.AlignCenter)
      header_layout.addWidget(text_label)
      summarizer_label = QLabel('T5 Summarizer')
      summarizer_label.setAlignment(Qt.AlignCenter)
      header_layout.addWidget(summarizer_label)
      header_layout.setContentsMargins(0, 0, 0, 0)
      self.header_frame = QFrame()
      self.header_frame.setLayout(header_layout)
      central_layout.addWidget(self.header_frame)
      self.header_frame.hide()

      # summarization rows
      self.summarizationLayout = QGridLayout()
      self.input = QPlainTextEdit(placeholderText='Insert text to summarize')
      self.input.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
      self.input.installEventFilter(self)  # press Enter to summarize
      self.summarizationLayout.addWidget(self.input, 1, 0)  # widget, position --> row index, column index
      setup_layout = QVBoxLayout()
      self.summarize_button = QPushButton('Summarize text box')
      self.summarize_button.clicked.connect(self.summarize_input_field)
      setup_layout.addStretch(1)
      setup_layout.addWidget(self.summarize_button)
      uploadFileButton = QPushButton('Upload text file')
      uploadFileButton.clicked.connect(self.upload_file_event)
      setup_layout.addWidget(uploadFileButton)
      slider_layout = QHBoxLayout()
      slider_layout.addWidget(QLabel('Summary length:'))
      self.sl = QSlider(Qt.Horizontal)
      self.sl.setMinimum(1)
      self.sl.setMaximum(3)
      self.sl.setValue(1)
      self.sl.setTickInterval(1)
      self.slider_len_settings = {
        1: (0.05, 0.15),
        2: (0.25, 0.4),
        3: (0.5, 0.65)
      }
      self.lower_token_ratio = self.slider_len_settings[1][0]
      self.upper_token_ratio = self.slider_len_settings[1][1]
      self.sl.valueChanged.connect(self.slider_value_change)
      slider_layout.addWidget(self.sl)
      setup_layout.addLayout(slider_layout)
      setup_layout.addStretch(1)
      setup_layout.setContentsMargins(0, 0, 0, 0)
      self.setup_widget = QWidget()
      self.setup_widget.setLayout(setup_layout)
      self.summarizationLayout.addWidget(self.setup_widget, 1, 1)  # widget, position --> row index, column index

      self.scroll = QScrollArea()
      self.scroll.setFrameStyle(QFrame.NoFrame)
      self.scroll.setWidgetResizable(True)
      self.central_frame = QFrame()
      self.central_frame.setLayout(self.summarizationLayout)
      self.summarizationLayout.setContentsMargins(0, 0, 0, 0)
      self.scroll.setWidget(self.central_frame)

      central_layout.addWidget(self.scroll)
      self.central_widget = QWidget()
      self.central_widget.setLayout(central_layout)

      self.setCentralWidget(self.central_widget)
      self.setWindowTitle(f"Text Summarizer: {model_dir} {model_language} {model_status}")

      if model_dir != 'dev':
        self.summarizer = AbstractiveSummarizer(
                                                                      model_dir,
                                                                      model_language,
                                                                      status=model_status
                                                                  )
      self.next_summary_position = 1

  def slider_value_change(self):
    setting_nr = self.sl.value()
    len_setting = self.slider_len_settings[setting_nr]
    self.lower_token_ratio = len_setting[0]
    self.upper_token_ratio = len_setting[1]

  def eventFilter(self, obj, event):
    ''' activates self.summarize when Enter is pressed '''
    if event.type() == QEvent.KeyPress and obj is self.input:
      if event.key() == Qt.Key_Return and self.input.hasFocus():
        self.summarize_input_field()
        return True
    return False

  def originalSize(self):
    ''' resets font size '''
    self.central_widget.setFont(QFont(".AppleSystemUIFont", 13))

  def minimize(self):
    ''' minimize window in operating system '''
    self.setWindowState(self.windowState() | QWindow.Minimized)

  def summarize_input_field(self):
    text = self.read_input_field()
    if text:
      self.summarize_string(text, self.next_summary_position)
      self.next_summary_position += 1
      self.append_new_row_to_layout()

  def read_input_field(self):
    textString = self.input.toPlainText()
    return textString.strip()

  def summarize_string(self, text: str, position_in_layout: int):
    ''' summarize the text in the input field '''
    text_input_field = QTextEdit(text)
    text_input_field.setReadOnly(True)
    self.summarizationLayout.addWidget(text_input_field, position_in_layout, 0)
    summarize_label = QLabel('Summarizing...')
    summarize_label.setAlignment(Qt.AlignCenter)
    self.summarizationLayout.addWidget(summarize_label, position_in_layout, 1)
    modelRunner = ModelRunner(self.summarizer, text, position_in_layout, window=self)
    modelRunner.start()
    modelRunner.summary_output.connect(self.summarize_finished)
    self.input.setFocus()

  def summarize_finished(self, summary_output):
    self.header_frame.show()
    summary_text_field = QTextEdit(summary_output['summary'])
    summary_text_field.setReadOnly(True)
    position_in_layout = summary_output['position_in_layout']
    self.summarizationLayout.addWidget(summary_text_field, position_in_layout, 1)
    self.summarizationLayout.setRowMinimumHeight(position_in_layout, 120)

  def append_new_row_to_layout(self):
    nrRows = self.next_summary_position + 1
    self.summarizationLayout.addWidget(self.input, nrRows, 0)
    self.input.clear()
    self.summarizationLayout.addWidget(self.setup_widget, nrRows, 1)

  def upload_file_event(self):
    fileProps = QFileDialog.getOpenFileName(self, 'Open File')
    filename = fileProps[0]
    if filename:
      self.summarize_file(filename)

  def read_lines_from_file_path(self, file_path):
    with open(file_path, 'r') as f:
      texts = f.read()
    return texts.split('\n')

  def summarize_file(self, file_path):
    texts = self.read_lines_from_file_path(file_path)
    for i, text in enumerate(texts):
      self.summarize_string(text, self.next_summary_position + i)
      if i == 10:
        break
    self.next_summary_position += len(texts)
    self.append_new_row_to_layout()

  def dragEnterEvent(self, event):
      if event.mimeData().hasUrls():
          event.accept()
      else:
          event.ignore()

  def dropEvent(self, event):
      files = [u.toLocalFile() for u in event.mimeData().urls()]
      for f in files:
          self.summarize_file(f)

  def zoomIn(self):
    ''' increases font size '''
    size = self.central_widget.font().pointSize()
    self.central_widget.setFont(QFont(".AppleSystemUIFont", size + 2))

  def zoomOut(self):
    ''' decreases font size '''
    size = self.central_widget.font().pointSize()
    self.central_widget.setFont(QFont(".AppleSystemUIFont", size - 2))
Ejemplo n.º 3
0
class DebugWindow(QMainWindow):
    """A main window to edit text and examine the generated token structure.

    Example::

        from PyQt5.Qt import *
        a=QApplication([])

        from parceqt.debug import DebugWindow
        w = DebugWindow()
        w.resize(1200,900)
        w.show()

        w.set_theme("default", True)

        from parce.lang.css import *
        w.set_root_lexicon(Css.root)
        w.set_text(open("path/to/parce/themes/default.css").read())

    In the debug window you can edit the text at the left and directly at the
    right examine the tree structure. Along the top of the window the path to
    the token at the current cursor position is displayed, from the root
    lexicon upto the token, from which the action is displayed.

    Clicking a button selects the associated range of the context or token in
    the text view. Clicking an item in the tree also selects that range in the
    text.

    Moving the cursor in the text updates the current item in the tree,
    and the displayed ancestor path.

    """

    show_updated_region_enabled = False

    def __init__(self, parent=None):
        super().__init__(parent, windowTitle="parceqt debugger")

        f = self._updated_format = QTextCharFormat()
        c = QColor("palegreen")
        c.setAlpha(64)
        f.setBackground(c)
        f = self._currentline_format = QTextCharFormat()
        f.setProperty(QTextCharFormat.FullWidthSelection, True)

        self._actions = Actions(self)
        self._actions.add_menus(self.menuBar())

        widget = QWidget(self)
        self.setCentralWidget(widget)
        layout = QVBoxLayout(margin=4, spacing=2)
        widget.setLayout(layout)

        top_layout = QHBoxLayout(margin=0, spacing=0)

        self.guessButton = QToolButton(self,
                                       clicked=self.guess_root_lexicon,
                                       toolTip="Guess Language",
                                       icon=self.style().standardIcon(
                                           QStyle.SP_BrowserReload))
        self.lexiconChooser = LexiconChooser(self)
        self.ancestorView = AncestorView(self)
        top_layout.addWidget(self.guessButton)
        top_layout.addWidget(self.lexiconChooser)
        top_layout.addWidget(self.ancestorView)
        top_layout.addStretch(10)
        layout.addLayout(top_layout)
        self.guessButton.setFixedHeight(
            self.lexiconChooser.sizeHint().height())

        splitter = QSplitter(self, orientation=Qt.Horizontal)
        layout.addWidget(splitter, 100)

        self.textEdit = QPlainTextEdit(lineWrapMode=QPlainTextEdit.NoWrap,
                                       cursorWidth=2)
        self.treeView = QTreeView()

        splitter.addWidget(self.textEdit)
        splitter.addWidget(self.treeView)
        splitter.setStretchFactor(0, 3)
        splitter.setStretchFactor(1, 2)

        self.extraSelectionManager = ExtraSelectionManager(self.textEdit)

        self.document = d = self.textEdit.document()
        self.textEdit.setDocument(self.document)

        self.worker = w = parceqt.worker(d)
        self.builder = b = w.builder()
        w.debugging = True

        self.setStatusBar(QStatusBar())
        self.create_model()

        # signal connections
        self.textEdit.viewport().installEventFilter(self)
        self.textEdit.installEventFilter(self)
        self.lexiconChooser.lexicon_changed.connect(
            self.slot_root_lexicon_changed)
        self.ancestorView.node_clicked.connect(self.slot_node_clicked)
        w.started.connect(self.slot_build_started)
        w.tree_updated.connect(self.slot_build_updated)
        self.textEdit.cursorPositionChanged.connect(
            self.slot_cursor_position_changed)
        self.treeView.clicked.connect(self.slot_item_clicked)

        self.textEdit.setFocus()
        self.set_theme()

        # somewhat larger font by default
        font = self.textEdit.font()
        font.setPointSizeF(11)
        self.textEdit.setFont(font)

    def create_model(self):
        """Instantiate a tree model for the tree view."""
        m = self.treeView.model()
        if not m:
            m = parceqt.treemodel.TreeModel(self.builder.root)
            m.connect_debugging_builder(self.builder)
            self.treeView.setModel(m)

    def delete_model(self):
        """Delete the model and remove it from the tree."""
        m = self.treeView.model()
        if m:
            m.disconnect_debugging_builder(self.builder)
            self.treeView.setModel(None)
            m.deleteLater()

    def set_text(self, text):
        """Set the text in the text edit."""
        self.document.setPlainText(text)

    def set_root_lexicon(self, lexicon):
        """Set the root lexicon to use."""
        self.lexiconChooser.set_root_lexicon(lexicon)

    def guess_root_lexicon(self):
        """Again choose the root lexicon based on the text."""
        text = self.document.toPlainText()
        if text:
            self.set_root_lexicon(parce.find(contents=text))

    def open_file(self, filename):
        """Read a file from disk and guess the language."""
        text = read_file(filename)
        root_lexicon = parce.find(filename=filename, contents=text)
        self.set_text(text)
        self.set_root_lexicon(root_lexicon)
        c = self.textEdit.textCursor()
        c.setPosition(0)
        self.textEdit.setTextCursor(c)

    def set_theme(self, theme="default", adjust_widget=True):
        """Set the theme to use for the text edit."""
        if isinstance(theme, str):
            theme = parce.theme_by_name(theme)
        formatter = parceqt.formatter.Formatter(theme) if theme else None
        if adjust_widget:
            if formatter:
                font = formatter.font(self)
                self.textEdit.setPalette(formatter.palette(self))
            else:
                font = QApplication.font(self)
                self.textEdit.setPalette(QApplication.palette(self))
            font.setPointSizeF(self.textEdit.font().pointSizeF())  # keep size
            self.textEdit.setFont(font)
            self.highlight_current_line()
        h = parceqt.highlighter.SyntaxHighlighter.instance(self.worker)
        h.set_formatter(formatter)

    def slot_build_started(self):
        """Called when the tree builder has started a build."""
        self.treeView.setCursor(Qt.BusyCursor)

    def slot_build_updated(self):
        """Called when the tree builder has finished a build."""
        self.treeView.unsetCursor()
        self.slot_cursor_position_changed()
        self.statusBar().showMessage(", ".join(
            lexicon_names(self.builder.lexicons)))
        tree = self.worker.get_root()
        self.lexiconChooser.setToolTip(
            parceqt.treemodel.TreeModel.node_tooltip(tree))
        if self.show_updated_region_enabled:
            self.show_updated_region()

    def slot_cursor_position_changed(self):
        """Called when the text cursor moved."""
        tree = self.worker.get_root()
        if tree:
            pos = self.textEdit.textCursor().position()
            doc = parceqt.document.Document(self.document)
            token = doc.token(pos)
            if token:
                self.ancestorView.set_token_path(token)
                model = self.treeView.model()
                if model:
                    index = model.get_model_index(token)
                    self.treeView.setCurrentIndex(index)
        elif tree is not None:
            self.ancestorView.clear()
        self.highlight_current_line()

    def slot_item_clicked(self, index):
        """Called when a node in the tree view is clicked."""
        tree = self.worker.get_root()
        if tree:
            model = self.treeView.model()
            if model:
                node = self.treeView.model().get_node(index)
                cursor = self.textEdit.textCursor()
                cursor.setPosition(node.end)
                cursor.setPosition(node.pos, QTextCursor.KeepAnchor)
                self.textEdit.setTextCursor(cursor)
        self.textEdit.setFocus()

    def slot_node_clicked(self, node):
        """Called when a button in the ancestor view is clicked."""
        tree = self.worker.get_root()
        if tree and node.root() is tree:
            cursor = self.textEdit.textCursor()
            cursor.setPosition(node.end)
            cursor.setPosition(node.pos, QTextCursor.KeepAnchor)
            self.textEdit.setTextCursor(cursor)
            self.textEdit.setFocus()
            model = self.treeView.model()
            if model:
                index = model.get_model_index(node)
                self.treeView.expand(index)
                self.treeView.setCurrentIndex(index)

    def slot_root_lexicon_changed(self, lexicon):
        """Called when the root lexicon is changed."""
        parceqt.set_root_lexicon(self.document, lexicon)

    def highlight_current_line(self):
        """Highlight the current line."""
        group = QPalette.Active if self.textEdit.hasFocus(
        ) else QPalette.Inactive
        p = self.textEdit.palette()
        color = p.color(group, QPalette.AlternateBase)
        self._currentline_format.setBackground(color)
        if color != p.color(group, QPalette.Base):
            c = self.textEdit.textCursor()
            c.clearSelection()
            self.extraSelectionManager.highlight(self._currentline_format, [c])
        else:
            self.extraSelectionManager.clear(self._currentline_format)

    def show_updated_region(self):
        """Highlight the updated region for 2 seconds."""
        end = self.builder.end
        if end >= self.document.characterCount() - 1:
            end = self.document.characterCount() - 1
            if self.builder.start == 0:
                return
        c = QTextCursor(self.document)
        c.setPosition(end)
        c.setPosition(self.builder.start, QTextCursor.KeepAnchor)
        self.extraSelectionManager.highlight(self._updated_format, [c],
                                             msec=2000)

    def clear_updated_region(self):
        self.extraSelectionManager.clear(self._updated_format)

    def eventFilter(self, obj, ev):
        """Implemented to support Ctrl+wheel zooming and keybfocus handling."""
        if obj == self.textEdit:
            if ev.type() in (QEvent.FocusIn, QEvent.FocusOut):
                self.highlight_current_line()
        else:  # viewport
            if ev.type() == QEvent.Wheel and ev.modifiers(
            ) == Qt.ControlModifier:
                if ev.angleDelta().y() > 0:
                    self.textEdit.zoomIn()
                elif ev.angleDelta().y() < 0:
                    self.textEdit.zoomOut()
                return True
        return False