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)
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))
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