From 7aac07d808f61a6f3c37f61aea1f2f4a2bc99a71 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Math=C3=AFs=20F=C3=A9d=C3=A9rico?= Date: Sun, 28 Nov 2021 17:05:10 +0100 Subject: [PATCH 1/4] :hammer: Refactor OCBView Prevent drag_scene on blocks --- opencodeblocks/graphics/view.py | 43 +++++++++++++++++---------------- 1 file changed, 22 insertions(+), 21 deletions(-) diff --git a/opencodeblocks/graphics/view.py b/opencodeblocks/graphics/view.py index aebd6ef0..11881192 100644 --- a/opencodeblocks/graphics/view.py +++ b/opencodeblocks/graphics/view.py @@ -18,25 +18,25 @@ from opencodeblocks.graphics.edge import OCBEdge from opencodeblocks.graphics.blocks import OCBBlock -MODE_NOOP = 0 -MODE_EDGE_DRAG = 1 -MODE_EDITING = 2 - -MODES = { - 'MODE_NOOP': MODE_NOOP, - 'MODE_EDGE_DRAG': MODE_EDGE_DRAG, - 'MODE_EDITING': MODE_EDITING, -} - class OCBView(QGraphicsView): """ View for the OCB Window. """ + MODE_NOOP = 0 + MODE_EDGE_DRAG = 1 + MODE_EDITING = 2 + + MODES = { + 'NOOP': MODE_NOOP, + 'EDGE_DRAG': MODE_EDGE_DRAG, + 'EDITING': MODE_EDITING, + } + def __init__(self, scene: OCBScene, parent=None, zoom_step: float = 1.25, zoom_min: float = 0.2, zoom_max: float = 5): super().__init__(parent=parent) - self.mode = MODE_NOOP + self.mode = self.MODE_NOOP self.zoom = 1 self.zoom_step, self.zoom_min, self.zoom_max = zoom_step, zoom_min, zoom_max @@ -125,7 +125,8 @@ def leftMouseButtonRelease(self, event: QMouseEvent): def middleMouseButtonPress(self, event: QMouseEvent): """ OCBView reaction to middleMouseButtonPress event. """ - event = self.drag_scene(event, "press") + if self.itemAt(event.pos()) is None: + event = self.drag_scene(event, "press") super().mousePressEvent(event) def middleMouseButtonRelease(self, event: QMouseEvent): @@ -219,9 +220,9 @@ def drag_edge(self, event: QMouseEvent, action="press"): scene = self.scene() if action == "press": if isinstance(item_at_click, OCBSocket) \ - and self.mode != MODE_EDGE_DRAG\ + and self.mode != self.MODE_EDGE_DRAG\ and item_at_click.socket_type != 'input': - self.mode = MODE_EDGE_DRAG + self.mode = self.MODE_EDGE_DRAG self.edge_drag = OCBEdge( source_socket=item_at_click, destination=self.mapToScene(event.pos()) @@ -229,7 +230,7 @@ def drag_edge(self, event: QMouseEvent, action="press"): scene.addItem(self.edge_drag) return elif action == "release": - if self.mode == MODE_EDGE_DRAG: + if self.mode == self.MODE_EDGE_DRAG: if isinstance(item_at_click, OCBSocket) \ and item_at_click is not self.edge_drag.source_socket \ and item_at_click.socket_type != 'output': @@ -239,9 +240,9 @@ def drag_edge(self, event: QMouseEvent, action="press"): else: self.edge_drag.remove() self.edge_drag = None - self.mode = MODE_NOOP + self.mode = self.MODE_NOOP elif action == "move": - if self.mode == MODE_EDGE_DRAG: + if self.mode == self.MODE_EDGE_DRAG: self.edge_drag.destination = self.mapToScene(event.pos()) return event @@ -249,16 +250,16 @@ def set_mode(self, mode: str): """ Change the view mode. Args: - mode: Mode key to change to, must in present in knowed MODES. + mode: Mode key to change to, must in present in MODES. """ - self.mode = MODES[mode] + self.mode = self.MODES[mode] def is_mode(self, mode: str): """ Return True if the view is in the given mode. Args: - mode: Mode key to compare to, must in present in knowed MODES. + mode: Mode key to compare to, must in present in MODES. """ - return self.mode == MODES[mode] + return self.mode == self.MODES[mode] From 9b568d1e4a86ea53059ba3c9027a2f97b6ef8ca7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Math=C3=AFs=20F=C3=A9d=C3=A9rico?= Date: Sun, 28 Nov 2021 17:05:28 +0100 Subject: [PATCH 2/4] :wrench: Refactor pyeditor to reduce duplications --- opencodeblocks/graphics/window.py | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/opencodeblocks/graphics/window.py b/opencodeblocks/graphics/window.py index 7e0b134e..d50f1b7e 100644 --- a/opencodeblocks/graphics/window.py +++ b/opencodeblocks/graphics/window.py @@ -10,8 +10,6 @@ from PyQt5.QtWidgets import QDockWidget, QListWidget, QWidget, QAction, QFileDialog, QMainWindow,\ QMessageBox, QMdiArea -from opencodeblocks import __appname__ as application_name -from opencodeblocks.graphics.view import MODE_EDITING from opencodeblocks.graphics.widget import OCBWidget from opencodeblocks.graphics.theme_manager import theme_manager @@ -269,40 +267,45 @@ def onFileSaveAs(self) -> bool: return True return False + @staticmethod + def is_not_editing(current_window: OCBWidget): + """ True if current_window exists and is not in editing mode. """ + return current_window is not None and not current_window.view.is_mode('EDITING') + def onEditUndo(self): """ Undo last operation if not in edit mode. """ current_window = self.activeMdiChild() - if current_window is not None and current_window.view.mode != MODE_EDITING: + if self.is_not_editing(current_window): current_window.scene.history.undo() def onEditRedo(self): """ Redo last operation if not in edit mode. """ current_window = self.activeMdiChild() - if current_window is not None and current_window.view.mode != MODE_EDITING: + if self.is_not_editing(current_window): current_window.scene.history.redo() def onEditCut(self): """ Cut the selected items if not in edit mode. """ current_window = self.activeMdiChild() - if current_window is not None and current_window.view.mode != MODE_EDITING: + if self.is_not_editing(current_window): current_window.scene.clipboard.cut() def onEditCopy(self): """ Copy the selected items if not in edit mode. """ current_window = self.activeMdiChild() - if current_window is not None and current_window.view.mode != MODE_EDITING: + if self.is_not_editing(current_window): current_window.scene.clipboard.copy() def onEditPaste(self): """ Paste the selected items if not in edit mode. """ current_window = self.activeMdiChild() - if current_window is not None and current_window.view.mode != MODE_EDITING: + if self.is_not_editing(current_window): current_window.scene.clipboard.paste() def onEditDelete(self): """ Delete the selected items if not in edit mode. """ current_window = self.activeMdiChild() - if current_window is not None and current_window.view.mode != MODE_EDITING: + if self.is_not_editing(current_window): current_window.view.deleteSelected() # def closeEvent(self, event:QEvent): From 62469d3db19491b6b4ac2e9a556c06052469c03d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Math=C3=AFs=20F=C3=A9d=C3=A9rico?= Date: Sun, 28 Nov 2021 17:12:52 +0100 Subject: [PATCH 3/4] :wrench: Forced 'Empty' block to be first in list --- opencodeblocks/graphics/view.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/opencodeblocks/graphics/view.py b/opencodeblocks/graphics/view.py index 11881192..df5211e6 100644 --- a/opencodeblocks/graphics/view.py +++ b/opencodeblocks/graphics/view.py @@ -136,6 +136,7 @@ def middleMouseButtonRelease(self, event: QMouseEvent): self.setDragMode(QGraphicsView.DragMode.RubberBandDrag) def retreiveBlockTypes(self) -> List[Tuple[str]]: + """ Retreive the list of stored blocks. """ block_type_files = os.listdir("blocks") block_types = [] for b in block_type_files: @@ -145,7 +146,10 @@ def retreiveBlockTypes(self) -> List[Tuple[str]]: title = "New Block" if "title" in data: title = f"New {data['title']} Block" - block_types.append((filepath, title)) + if data['title'] == "Empty": + block_types[:0] = [(filepath, title)] + else: + block_types.append((filepath, title)) return block_types def contextMenuEvent(self, event: QContextMenuEvent): From 4266ea1ed75338008eab7e90f427a6c403acc99a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Math=C3=AFs=20F=C3=A9d=C3=A9rico?= Date: Sun, 28 Nov 2021 17:14:48 +0100 Subject: [PATCH 4/4] :tada: Filtered wheelEvent in PyEditor Scroll can only be done in edit mode and only vertical scroll is allowed --- opencodeblocks/graphics/pyeditor.py | 33 ++++++++++++++++++++--------- 1 file changed, 23 insertions(+), 10 deletions(-) diff --git a/opencodeblocks/graphics/pyeditor.py b/opencodeblocks/graphics/pyeditor.py index dce0d055..4cad260d 100644 --- a/opencodeblocks/graphics/pyeditor.py +++ b/opencodeblocks/graphics/pyeditor.py @@ -5,7 +5,7 @@ from typing import TYPE_CHECKING, List from PyQt5.QtCore import QThreadPool, Qt -from PyQt5.QtGui import QFocusEvent, QFont, QFontMetrics, QColor +from PyQt5.QtGui import QFocusEvent, QFont, QFontMetrics, QColor, QMouseEvent, QWheelEvent from PyQt5.Qsci import QsciScintilla, QsciLexerPython from opencodeblocks.graphics.theme_manager import theme_manager @@ -31,6 +31,7 @@ def __init__(self, block: OCBBlock): """ super().__init__(None) + self._mode = "NOOP" self.block = block self.kernel = kernel self.threadpool = threadpool @@ -90,19 +91,31 @@ def views(self) -> List['OCBView']: """ Get the views in which the python_editor is present. """ return self.block.scene().views() - def set_views_mode(self, mode: str): - """ Set the views in which the python_editor is present to editing mode. """ + def wheelEvent(self, event: QWheelEvent) -> None: + """ How PythonEditor handles wheel events """ + if self.mode == "EDITING" and event.angleDelta().x() == 0: + event.accept() + return super().wheelEvent(event) + + @property + def mode(self) -> int: + """ PythonEditor current mode """ + return self._mode + + @mode.setter + def mode(self, value: str): + self._mode = value for view in self.views(): - if mode == "MODE_EDITING" or view.is_mode("MODE_EDITING"): - view.set_mode(mode) + view.set_mode(value) - def focusInEvent(self, event: QFocusEvent): - """ PythonEditor reaction to PyQt focusIn events. """ - self.set_views_mode("MODE_EDITING") - return super().focusInEvent(event) + def mousePressEvent(self, event: QMouseEvent) -> None: + """ PythonEditor reaction to PyQt mousePressEvent events. """ + if event.buttons() & Qt.MouseButton.LeftButton: + self.mode = "EDITING" + return super().mousePressEvent(event) def focusOutEvent(self, event: QFocusEvent): """ PythonEditor reaction to PyQt focusOut events. """ - self.set_views_mode("MODE_NOOP") + self.mode = "NOOP" self.block.source = self.text() return super().focusOutEvent(event)