From 50a4e1059640ffded629b6d586d5d139e098a664 Mon Sep 17 00:00:00 2001 From: Hugh Sorby Date: Fri, 6 Oct 2023 17:53:22 +1300 Subject: [PATCH 1/4] Work on improving positioning steps when resizing or loading. --- src/mapclient/core/managers/undomanager.py | 1 + src/mapclient/core/workflow/workflowscene.py | 20 +++-- src/mapclient/core/workflow/workflowutils.py | 11 +++ .../view/workflow/workflowgraphicsview.py | 81 ++++++++++++------- src/mapclient/view/workflow/workflowwidget.py | 4 +- src/setup.py | 2 +- 6 files changed, 80 insertions(+), 39 deletions(-) create mode 100644 src/mapclient/core/workflow/workflowutils.py diff --git a/src/mapclient/core/managers/undomanager.py b/src/mapclient/core/managers/undomanager.py index 6f95755f..fe9a3494 100644 --- a/src/mapclient/core/managers/undomanager.py +++ b/src/mapclient/core/managers/undomanager.py @@ -18,6 +18,7 @@ along with MAP Client. If not, see .. """ + class UndoManager(object): """ This class is the undo redo manager for multiple undo stacks. It is a diff --git a/src/mapclient/core/workflow/workflowscene.py b/src/mapclient/core/workflow/workflowscene.py index 9704def2..c64766cb 100644 --- a/src/mapclient/core/workflow/workflowscene.py +++ b/src/mapclient/core/workflow/workflowscene.py @@ -23,6 +23,7 @@ from mapclient.core.workflow.workflowdependencygraph import WorkflowDependencyGraph from mapclient.core.workflow.workflowitems import MetaStep, Connection +from mapclient.core.workflow.workflowutils import convert_to_parameterised_position, revert_parameterised_position from mapclient.mountpoints.workflowstep import workflowStepFactory from mapclient.core.utils import load_configuration from mapclient.settings.general import get_configuration_file @@ -39,7 +40,8 @@ def __init__(self, manager): self._items = {} self._dependencyGraph = WorkflowDependencyGraph(self) self._main_window = None - self._view_parameters = {'rect': QtCore.QRectF(0, 0, 1024, 880)} + self._default_view_rect = QtCore.QRectF(0, 0, 1024, 880) + self._view_parameters = None def getViewParameters(self): return self._view_parameters @@ -81,6 +83,8 @@ def saveState(self, ws): ws.setValue(key, self._view_parameters[key]) ws.endGroup() + rect = self._view_parameters['rect'] + ws.remove('nodes') ws.beginGroup('nodes') ws.beginWriteArray('nodelist') @@ -103,7 +107,9 @@ def saveState(self, ws): if source_uri is not None: ws.setValue('source_uri', source_uri) ws.setValue('name', step.getName()) - ws.setValue('position', metastep.getPos()) + new_position = convert_to_parameterised_position(rect, metastep.getPos()) + ws.setValue('position', new_position) + ws.setValue('parameterised_position', True) ws.setValue('selected', metastep.getSelected()) ws.setValue('identifier', identifier) ws.setValue('unique_identifier', metastep.getUniqueIdentifier()) @@ -189,14 +195,15 @@ def load_state(self, ws): ws.beginGroup('view') loaded_view_parameters = { 'scale': float(ws.value('scale', '1.0')), - 'rect': ws.value('rect', self._view_parameters['rect']), + 'rect': ws.value('rect', self._default_view_rect), 'transform': ws.value('transform') } ws.endGroup() # Scale the WorkflowScene view-parameters: - current_rect = self._view_parameters['rect'] + current_rect = self._view_parameters['rect'] if self._view_parameters else loaded_view_parameters['rect'] loaded_rect = loaded_view_parameters['rect'] + scale_factor = loaded_view_parameters['scale'] if scale_factor != 1.0: current_rect.setWidth(current_rect.width() / scale_factor) @@ -219,8 +226,9 @@ def load_state(self, ws): uniqueIdentifier = ws.value('unique_identifier', uuid.uuid1()) # Adjust the item positions according to the scale factors. - position.setX(position.x() * sf_x) - position.setY(position.y() * sf_y) + parameterised = ws.value('parameterised_position', False) + if parameterised: + position = revert_parameterised_position(loaded_rect, position) step = workflowStepFactory(name, self._location) step.setMainWindow(self._main_window) diff --git a/src/mapclient/core/workflow/workflowutils.py b/src/mapclient/core/workflow/workflowutils.py new file mode 100644 index 00000000..3fa66a06 --- /dev/null +++ b/src/mapclient/core/workflow/workflowutils.py @@ -0,0 +1,11 @@ +from PySide6 import QtCore + + +def convert_to_parameterised_position(bounding_rect, raw_position): + print(raw_position.x() / bounding_rect.width(), raw_position.x(), bounding_rect.width(), bounding_rect.left()) + return QtCore.QPointF(raw_position.x() / bounding_rect.width(), raw_position.y() / bounding_rect.height()) + + +def revert_parameterised_position(bounding_rect, parameterised_position): + print(bounding_rect.width() * parameterised_position.x(), parameterised_position.x(), bounding_rect.width(), bounding_rect.left()) + return QtCore.QPointF(bounding_rect.width() * parameterised_position.x(), bounding_rect.height() * parameterised_position.y()) diff --git a/src/mapclient/view/workflow/workflowgraphicsview.py b/src/mapclient/view/workflow/workflowgraphicsview.py index 7280e442..00eaa134 100644 --- a/src/mapclient/view/workflow/workflowgraphicsview.py +++ b/src/mapclient/view/workflow/workflowgraphicsview.py @@ -22,6 +22,7 @@ from PySide6 import QtCore, QtWidgets, QtGui +from mapclient.core.workflow.workflowutils import convert_to_parameterised_position, revert_parameterised_position from mapclient.mountpoints.workflowstep import workflowStepFactory from mapclient.core.workflow.workflowscene import MetaStep from mapclient.view.utils import is_light_mode @@ -46,6 +47,7 @@ def __init__(self, parent=None): self._graphics_shown = False self._graphics_initialised = False self._graphics_scale_factor = 1.0 + self._margin = 10 self._undoStack = None self._location = '' @@ -192,7 +194,6 @@ def copy_steps(self, start_pos=QtCore.QPoint(0, 0)): mime_data.setData('image/x-workflow-step(s)', data) return mime_data - # TODO: Paste the arrows as well. def paste_steps(self, stream, event_position=None): """ This takes a stream of workflow items and pastes them to the workflow. See copy_steps for details on generating this stream. @@ -340,6 +341,7 @@ def mouseReleaseEvent(self, event): if self._selectionStartPos: diff = event.pos() - self._selectionStartPos if diff.x() != 0 and diff.y() != 0: + print("mouse move steps.") self._undoStack.beginMacro('Move Step(s)') for item in self.scene().selectedItems(): if item.type() == Node.Type: @@ -468,14 +470,15 @@ def resizeEvent(self, event): scene.setReady() self._graphics_initialised = True - margin = 10 + old_rect = scene.sceneRect() + print(self._margin / self._graphics_scale_factor) scene.setSceneRect( - margin / self._graphics_scale_factor, - margin / self._graphics_scale_factor, - (view_rect.width() - 2 * margin) / self._graphics_scale_factor, - (view_rect.height() - 2 * margin) / self._graphics_scale_factor + self._margin / self._graphics_scale_factor, + self._margin / self._graphics_scale_factor, + (view_rect.width() - 2 * self._margin) / self._graphics_scale_factor, + (view_rect.height() - 2 * self._margin) / self._graphics_scale_factor ) - self.reposition_steps() + self._reposition_steps(old_rect) def _unscale_view(self, scale_factor): self._graphics_scale_factor *= scale_factor @@ -487,46 +490,64 @@ def _unscale_view(self, scale_factor): def wheelEvent(self, event): if event.modifiers() == QtCore.Qt.KeyboardModifier.ControlModifier: - self.zoom(event.angleDelta().y()) + self._zoom(event.angleDelta().y()) else: super(WorkflowGraphicsView, self).wheelEvent(event) - def zoomIn(self): - self.zoom(-120) + def zoom_in(self): + self._zoom(-120) - def zoomOut(self): - self.zoom(120) + def zoom_out(self): + self._zoom(120) - def zoom(self, delta): + def _zoom(self, delta): + old_rect = self.sceneRect() + print("rect 1:", old_rect) scale_factor = math.pow(2.0, -delta / 240.0) self.scale(scale_factor, scale_factor) + print("rect 2:", self.sceneRect()) self._unscale_view(scale_factor) - self.view_all() + self._reposition_steps(old_rect) + print("rect 3:", self.sceneRect()) def reset_zoom(self): + old_rect = self.sceneRect() + print("rect 1:", self.sceneRect()) reverse_sf = 1 / self._graphics_scale_factor self.scale(reverse_sf, reverse_sf) + print("rect 2:", self.sceneRect()) self._unscale_view(reverse_sf) + self._reposition_steps(old_rect) + print("rect 3:", self.sceneRect()) self._graphics_scale_factor = 1.0 self.resetTransform() + print("rect 4:", self.sceneRect()) - def reposition_steps(self): + def _reposition_steps(self, old_rect): scene_rect = self.sceneRect() - self._undoStack.beginMacro('Reposition Step(s)') + # self._undoStack.beginMacro('Reposition Step(s)') + count = 0 for item in self.items(): if isinstance(item, Node): - x = item.x() - if x > (scene_rect.right() - 116): - x = scene_rect.right() - 116 - y = item.y() - if y > (scene_rect.bottom() - 116): - y = scene_rect.bottom() - 116 - - if x != item.x() or y != item.y(): - self._undoStack.push(CommandMove(item, item.pos(), QtCore.QPointF(x, y))) - self._undoStack.endMacro() - self.merge_macros() + parameterised_position = convert_to_parameterised_position(old_rect, item.pos()) + new_position = revert_parameterised_position(scene_rect, parameterised_position) + + item.setPos(new_position) + if count == 1: + print(new_position) + + count += 1 + # if x > (scene_rect.right() - 116): + # x = scene_rect.right() - 116 + # y = item.y() + # if y > (scene_rect.bottom() - 116): + # y = scene_rect.bottom() - 116 + # + # if x != item.x() or y != item.y(): + # self._undoStack.push(CommandMove(item, item.pos(), QtCore.QPointF(x, y))) + # self._undoStack.endMacro() + # self.merge_macros() def merge_macros(self): # If the top-most macro is successfully merged, remove it from the undo stack. @@ -587,16 +608,16 @@ def nodes_bounding_rect(self): return QtCore.QRectF(0, 0, 0, 0) def scale_workflow(self, sf_x, sf_y): - # Scale the workflow item positions. Add 12 to account for subtracting the view border before scaling. + # Scale the workflow item positions. Add self._margin to account for subtracting the view border before scaling. self._undoStack.beginMacro('Move Step(s)') for item in self.items(): if isinstance(item, Node): x = item.x() if sf_x != 1: - x = ((x - 12) * sf_x) + 12 + x = ((x - self._margin) * sf_x) + self._margin y = item.y() if sf_y != 1: - y = ((y - 12) * sf_y) + 12 + y = ((y - self._margin) * sf_y) + self._margin if x != item.x() or y != item.y(): self._undoStack.push(CommandMove(item, item.pos(), QtCore.QPointF(x, y))) diff --git a/src/mapclient/view/workflow/workflowwidget.py b/src/mapclient/view/workflow/workflowwidget.py index 1bc8a642..f6dd0ee3 100644 --- a/src/mapclient/view/workflow/workflowwidget.py +++ b/src/mapclient/view/workflow/workflowwidget.py @@ -587,10 +587,10 @@ def _set_action_properties(action, name, slot, shortcut='', statustip=''): action.setStatusTip(statustip) def zoom_in(self): - self._ui.graphicsView.zoomIn() + self._ui.graphicsView.zoom_in() def zoom_out(self): - self._ui.graphicsView.zoomOut() + self._ui.graphicsView.zoom_out() def reset_zoom(self): self._ui.graphicsView.reset_zoom() diff --git a/src/setup.py b/src/setup.py index d33018d1..136979e0 100644 --- a/src/setup.py +++ b/src/setup.py @@ -37,7 +37,7 @@ def find_version(*file_paths): 'requests', 'python-dateutil', 'dulwich', - 'pmr2.wfctrl', + 'pmr2.wfctrl >= 0.5.0', 'pmr2.client >= 0.2', 'packaging', 'filelock', From 84e0532c07ae4aae29164cfb394e57e1f87a7125 Mon Sep 17 00:00:00 2001 From: Hugh Sorby Date: Sat, 7 Oct 2023 10:53:20 +1300 Subject: [PATCH 2/4] Try and handle scene resizing better. --- .../core/managers/workflowmanager.py | 7 +- src/mapclient/core/workflow/workflowscene.py | 18 ++--- src/mapclient/core/workflow/workflowutils.py | 6 +- .../view/workflow/workflowgraphicsitems.py | 17 ++++- .../view/workflow/workflowgraphicsview.py | 72 +++++-------------- src/mapclient/view/workflow/workflowwidget.py | 2 +- 6 files changed, 46 insertions(+), 76 deletions(-) diff --git a/src/mapclient/core/managers/workflowmanager.py b/src/mapclient/core/managers/workflowmanager.py index 660532f3..58f655a4 100644 --- a/src/mapclient/core/managers/workflowmanager.py +++ b/src/mapclient/core/managers/workflowmanager.py @@ -201,10 +201,11 @@ def is_restricted(location): return is_workflow_in_use(location) - def load(self, location): + def load(self, location, scene_rect=QtCore.QPointF(0, 0)): """ Open a workflow from the given location. :param location: + :param scene_rect: Rectangle of the scene rect to load the workflow into. """ if location is None: raise WorkflowError('No location given to open Workflow.') @@ -227,7 +228,7 @@ def load(self, location): self.set_location(location) if self._scene.is_loadable(wf): if mark_workflow_in_use(location): - self._scene.load_state(wf) + self._scene.load_state(wf, scene_rect) else: logger.warning('Workflow is already in use.') raise WorkflowError('Workflow is already in use.') @@ -251,7 +252,7 @@ def load(self, location): if self._scene.is_loadable(wf): if mark_workflow_in_use(location): - self._scene.load_state(wf) + self._scene.load_state(wf, scene_rect) else: logger.warning('Workflow is already in use.') raise WorkflowError('Workflow is already in use.') diff --git a/src/mapclient/core/workflow/workflowscene.py b/src/mapclient/core/workflow/workflowscene.py index c64766cb..d526021e 100644 --- a/src/mapclient/core/workflow/workflowscene.py +++ b/src/mapclient/core/workflow/workflowscene.py @@ -23,7 +23,6 @@ from mapclient.core.workflow.workflowdependencygraph import WorkflowDependencyGraph from mapclient.core.workflow.workflowitems import MetaStep, Connection -from mapclient.core.workflow.workflowutils import convert_to_parameterised_position, revert_parameterised_position from mapclient.mountpoints.workflowstep import workflowStepFactory from mapclient.core.utils import load_configuration from mapclient.settings.general import get_configuration_file @@ -31,7 +30,7 @@ class WorkflowScene(object): """ - This is the authoratative model for the workflow scene. + This is the authoritative model for the workflow scene. """ def __init__(self, manager): @@ -83,8 +82,6 @@ def saveState(self, ws): ws.setValue(key, self._view_parameters[key]) ws.endGroup() - rect = self._view_parameters['rect'] - ws.remove('nodes') ws.beginGroup('nodes') ws.beginWriteArray('nodelist') @@ -107,9 +104,7 @@ def saveState(self, ws): if source_uri is not None: ws.setValue('source_uri', source_uri) ws.setValue('name', step.getName()) - new_position = convert_to_parameterised_position(rect, metastep.getPos()) - ws.setValue('position', new_position) - ws.setValue('parameterised_position', True) + ws.setValue('position', metastep.getPos()) ws.setValue('selected', metastep.getSelected()) ws.setValue('identifier', identifier) ws.setValue('unique_identifier', metastep.getUniqueIdentifier()) @@ -190,7 +185,7 @@ def doStepReport(self, ws): return report - def load_state(self, ws): + def load_state(self, ws, scene_rect): self.clear() ws.beginGroup('view') loaded_view_parameters = { @@ -201,7 +196,7 @@ def load_state(self, ws): ws.endGroup() # Scale the WorkflowScene view-parameters: - current_rect = self._view_parameters['rect'] if self._view_parameters else loaded_view_parameters['rect'] + current_rect = scene_rect loaded_rect = loaded_view_parameters['rect'] scale_factor = loaded_view_parameters['scale'] @@ -226,9 +221,8 @@ def load_state(self, ws): uniqueIdentifier = ws.value('unique_identifier', uuid.uuid1()) # Adjust the item positions according to the scale factors. - parameterised = ws.value('parameterised_position', False) - if parameterised: - position = revert_parameterised_position(loaded_rect, position) + position.setX(position.x() * sf_x) + position.setY(position.y() * sf_y) step = workflowStepFactory(name, self._location) step.setMainWindow(self._main_window) diff --git a/src/mapclient/core/workflow/workflowutils.py b/src/mapclient/core/workflow/workflowutils.py index 3fa66a06..60bb9dd2 100644 --- a/src/mapclient/core/workflow/workflowutils.py +++ b/src/mapclient/core/workflow/workflowutils.py @@ -2,10 +2,8 @@ def convert_to_parameterised_position(bounding_rect, raw_position): - print(raw_position.x() / bounding_rect.width(), raw_position.x(), bounding_rect.width(), bounding_rect.left()) - return QtCore.QPointF(raw_position.x() / bounding_rect.width(), raw_position.y() / bounding_rect.height()) + return QtCore.QPointF(raw_position.x() / bounding_rect.right(), raw_position.y() / bounding_rect.bottom()) def revert_parameterised_position(bounding_rect, parameterised_position): - print(bounding_rect.width() * parameterised_position.x(), parameterised_position.x(), bounding_rect.width(), bounding_rect.left()) - return QtCore.QPointF(bounding_rect.width() * parameterised_position.x(), bounding_rect.height() * parameterised_position.y()) + return QtCore.QPointF(bounding_rect.right() * parameterised_position.x(), bounding_rect.bottom() * parameterised_position.y()) diff --git a/src/mapclient/view/workflow/workflowgraphicsitems.py b/src/mapclient/view/workflow/workflowgraphicsitems.py index c8bc7e59..85bc9fa6 100644 --- a/src/mapclient/view/workflow/workflowgraphicsitems.py +++ b/src/mapclient/view/workflow/workflowgraphicsitems.py @@ -25,6 +25,7 @@ from mapclient.core.annotations import PROVIDES_ANNOTATIONS, USES_ANNOTATIONS, ANNOTATION_BASE from mapclient.core.workflow.workflowscene import Connection +from mapclient.core.workflow.workflowutils import convert_to_parameterised_position from mapclient.tools.annotation.annotationdialog import AnnotationDialog from mapclient.tools.pmr.pmrdvcshelper import repositoryIsUpToDate from mapclient.view.utils import is_light_mode @@ -253,6 +254,7 @@ def __init__(self, metastep): .scaled(self.Size, self.Size, QtCore.Qt.AspectRatioMode.KeepAspectRatio, QtCore.Qt.TransformationMode.FastTransformation) self._step_port_items = [] + self._parameterised_pos = QtCore.QPointF(0, 0) self._text = StepText(metastep.getStep().getName(), self) self._updateTextIcon() @@ -366,7 +368,7 @@ def updateDVCSIcon(self): """ :TODO: Update this for setting/saving output/input for step to repository """ - if self._metastep._step.getIdentifier(): + if self._metastep.getStep().getIdentifier(): if repositoryIsUpToDate(self._getStepLocation()): self._modified_item.hide() else: @@ -374,9 +376,18 @@ def updateDVCSIcon(self): else: self._modified_item.hide() - def setPos(self, pos): + def setPos(self, pos, modify_parameterised=True): super(Node, self).setPos(pos) - self.scene().workflowScene().setItemPos(self._metastep, pos) + scene = self.scene() + if modify_parameterised: + self._parameterised_pos = convert_to_parameterised_position(scene.sceneRect(), pos) + self._metastep.setPos(pos) + + def set_parameterised_pos(self, parameterised_pos): + self._parameterised_pos = parameterised_pos + + def parameterised_pos(self): + return self._parameterised_pos def type(self): return Node.Type diff --git a/src/mapclient/view/workflow/workflowgraphicsview.py b/src/mapclient/view/workflow/workflowgraphicsview.py index 00eaa134..6cc1c9e9 100644 --- a/src/mapclient/view/workflow/workflowgraphicsview.py +++ b/src/mapclient/view/workflow/workflowgraphicsview.py @@ -44,7 +44,6 @@ def __init__(self, parent=None): self._errorIcon = None self._main_window = None - self._graphics_shown = False self._graphics_initialised = False self._graphics_scale_factor = 1.0 self._margin = 10 @@ -456,29 +455,23 @@ def dragEnterEvent(self, event): else: event.ignore() - def showEvent(self, event): - self._graphics_shown = True - def resizeEvent(self, event): QtWidgets.QGraphicsView.resizeEvent(self, event) - if self._graphics_shown: - scene = self.scene() - event_size = event.size() - view_rect = QtCore.QRectF(0, 0, event_size.width(), event_size.height()) - if not self._graphics_initialised: - scene.setReady() - self._graphics_initialised = True - - old_rect = scene.sceneRect() - print(self._margin / self._graphics_scale_factor) - scene.setSceneRect( - self._margin / self._graphics_scale_factor, - self._margin / self._graphics_scale_factor, - (view_rect.width() - 2 * self._margin) / self._graphics_scale_factor, - (view_rect.height() - 2 * self._margin) / self._graphics_scale_factor - ) - self._reposition_steps(old_rect) + scene = self.scene() + event_size = event.size() + view_rect = QtCore.QRectF(0, 0, event_size.width(), event_size.height()) + if not self._graphics_initialised: + scene.setReady() + self._graphics_initialised = True + + scene.setSceneRect( + self._margin / self._graphics_scale_factor, + self._margin / self._graphics_scale_factor, + (view_rect.width() - 2 * self._margin) / self._graphics_scale_factor, + (view_rect.height() - 2 * self._margin) / self._graphics_scale_factor + ) + self._reposition_steps() def _unscale_view(self, scale_factor): self._graphics_scale_factor *= scale_factor @@ -501,53 +494,26 @@ def zoom_out(self): self._zoom(120) def _zoom(self, delta): - old_rect = self.sceneRect() - print("rect 1:", old_rect) scale_factor = math.pow(2.0, -delta / 240.0) self.scale(scale_factor, scale_factor) - print("rect 2:", self.sceneRect()) self._unscale_view(scale_factor) - self._reposition_steps(old_rect) - print("rect 3:", self.sceneRect()) + self._reposition_steps() def reset_zoom(self): - old_rect = self.sceneRect() - print("rect 1:", self.sceneRect()) reverse_sf = 1 / self._graphics_scale_factor self.scale(reverse_sf, reverse_sf) - print("rect 2:", self.sceneRect()) self._unscale_view(reverse_sf) - self._reposition_steps(old_rect) - print("rect 3:", self.sceneRect()) + self._reposition_steps() self._graphics_scale_factor = 1.0 self.resetTransform() - print("rect 4:", self.sceneRect()) - def _reposition_steps(self, old_rect): + def _reposition_steps(self): scene_rect = self.sceneRect() - # self._undoStack.beginMacro('Reposition Step(s)') - count = 0 for item in self.items(): if isinstance(item, Node): - parameterised_position = convert_to_parameterised_position(old_rect, item.pos()) - new_position = revert_parameterised_position(scene_rect, parameterised_position) - - item.setPos(new_position) - if count == 1: - print(new_position) - - count += 1 - # if x > (scene_rect.right() - 116): - # x = scene_rect.right() - 116 - # y = item.y() - # if y > (scene_rect.bottom() - 116): - # y = scene_rect.bottom() - 116 - # - # if x != item.x() or y != item.y(): - # self._undoStack.push(CommandMove(item, item.pos(), QtCore.QPointF(x, y))) - # self._undoStack.endMacro() - # self.merge_macros() + new_position = revert_parameterised_position(scene_rect, item.parameterised_pos()) + item.setPos(new_position, False) def merge_macros(self): # If the top-most macro is successfully merged, remove it from the undo stack. diff --git a/src/mapclient/view/workflow/workflowwidget.py b/src/mapclient/view/workflow/workflowwidget.py index f6dd0ee3..5c456065 100644 --- a/src/mapclient/view/workflow/workflowwidget.py +++ b/src/mapclient/view/workflow/workflowwidget.py @@ -447,7 +447,7 @@ def performWorkflowChecks(self, workflow_dir): def _load(self, workflow_dir): try: m = self._main_window.model().workflowManager() - m.load(workflow_dir) + m.load(workflow_dir, self._graphicsScene.sceneRect()) m.setPreviousLocation(workflow_dir) self._graphicsScene.updateModel() self._ui.graphicsView.setLocation(workflow_dir) From 16316f154a188134211d39c5f61ef0a1765e2730 Mon Sep 17 00:00:00 2001 From: Hugh Sorby Date: Sat, 7 Oct 2023 10:57:41 +1300 Subject: [PATCH 3/4] Remove print statement from mouseReleaseEvent. --- src/mapclient/view/workflow/workflowgraphicsview.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/mapclient/view/workflow/workflowgraphicsview.py b/src/mapclient/view/workflow/workflowgraphicsview.py index 6cc1c9e9..be46d623 100644 --- a/src/mapclient/view/workflow/workflowgraphicsview.py +++ b/src/mapclient/view/workflow/workflowgraphicsview.py @@ -340,7 +340,6 @@ def mouseReleaseEvent(self, event): if self._selectionStartPos: diff = event.pos() - self._selectionStartPos if diff.x() != 0 and diff.y() != 0: - print("mouse move steps.") self._undoStack.beginMacro('Move Step(s)') for item in self.scene().selectedItems(): if item.type() == Node.Type: From 838450d902bae1cc21baabc07116c52dae74329b Mon Sep 17 00:00:00 2001 From: Hugh Sorby Date: Sun, 8 Oct 2023 15:48:48 +1300 Subject: [PATCH 4/4] Update positioning of steps within the workflow panel. --- src/mapclient/core/metrics.py | 10 +++-- src/mapclient/core/workflow/workflowutils.py | 8 ++-- .../view/workflow/workflowgraphicsitems.py | 21 +++++----- .../view/workflow/workflowgraphicsscene.py | 39 ++++++++++--------- .../view/workflow/workflowgraphicsview.py | 6 +-- 5 files changed, 45 insertions(+), 39 deletions(-) diff --git a/src/mapclient/core/metrics.py b/src/mapclient/core/metrics.py index 19ef16e9..b5184cb4 100644 --- a/src/mapclient/core/metrics.py +++ b/src/mapclient/core/metrics.py @@ -101,10 +101,12 @@ def _log_event(self, event): ] } - response = requests.post(self._base_url, json=event_data) - - if not response.ok: - logger.info(f"Event response: {event['name']} - {response.status_code}") + try: + response = requests.post(self._base_url, json=event_data) + if not response.ok: + logger.info(f"Event response: {event['name']} - {response.status_code}") + except requests.ConnectionError: + logger.info(f"Event logging failed: {event['name']}") metrics_logger = MetricsLogger() diff --git a/src/mapclient/core/workflow/workflowutils.py b/src/mapclient/core/workflow/workflowutils.py index 60bb9dd2..0d01ecb8 100644 --- a/src/mapclient/core/workflow/workflowutils.py +++ b/src/mapclient/core/workflow/workflowutils.py @@ -1,9 +1,9 @@ from PySide6 import QtCore -def convert_to_parameterised_position(bounding_rect, raw_position): - return QtCore.QPointF(raw_position.x() / bounding_rect.right(), raw_position.y() / bounding_rect.bottom()) +def convert_to_parameterised_position(bounding_rect, raw_position, offset): + return QtCore.QPointF(raw_position.x() / (bounding_rect.width() - offset.x()), raw_position.y() / (bounding_rect.height() - offset.y())) -def revert_parameterised_position(bounding_rect, parameterised_position): - return QtCore.QPointF(bounding_rect.right() * parameterised_position.x(), bounding_rect.bottom() * parameterised_position.y()) +def revert_parameterised_position(bounding_rect, parameterised_position, offset): + return QtCore.QPointF((bounding_rect.width() - offset.x()) * parameterised_position.x(), (bounding_rect.height() - offset.y()) * parameterised_position.y()) diff --git a/src/mapclient/view/workflow/workflowgraphicsitems.py b/src/mapclient/view/workflow/workflowgraphicsitems.py index 85bc9fa6..4c744797 100644 --- a/src/mapclient/view/workflow/workflowgraphicsitems.py +++ b/src/mapclient/view/workflow/workflowgraphicsitems.py @@ -245,6 +245,7 @@ class Node(Item): def __init__(self, metastep): Item.__init__(self) + self._margin = 2.0 self._metastep = metastep icon = self._metastep.getStep().getIcon() if not icon: @@ -380,7 +381,7 @@ def setPos(self, pos, modify_parameterised=True): super(Node, self).setPos(pos) scene = self.scene() if modify_parameterised: - self._parameterised_pos = convert_to_parameterised_position(scene.sceneRect(), pos) + self._parameterised_pos = convert_to_parameterised_position(scene.sceneRect(), pos, self.offset()) self._metastep.setPos(pos) def set_parameterised_pos(self, parameterised_pos): @@ -425,15 +426,17 @@ def showStepName(self, show): def metaItem(self): return self._metastep + def offset(self): + return QtCore.QPointF(self._pixmap.width(), self._pixmap.height()) + def boundingRect(self): - adjust = 2.0 - return QtCore.QRectF(-adjust, -adjust, - self._pixmap.width() + 2 * adjust, - self._pixmap.height() + 2 * adjust) + return QtCore.QRectF(-self._margin, -self._margin, + self._pixmap.width() + 2 * self._margin, + self._pixmap.height() + 2 * self._margin) def paint(self, painter, option, widget): - if option.state & QtWidgets.QStyle.State_Selected: # or self.selected: - painter.setBrush(QtCore.Qt.darkGray) + if option.state & QtWidgets.QStyle.StateFlag.State_Selected: # or self.selected: + painter.setBrush(QtCore.Qt.GlobalColor.darkGray) painter.drawRoundedRect(self.boundingRect(), 5, 5) # super(Node, self).paint(painter, option, widget) @@ -444,8 +447,8 @@ def paint(self, painter, option, widget): def itemChange(self, change, value): if change == QtWidgets.QGraphicsItem.GraphicsItemChange.ItemPositionChange and self.scene(): - return self.scene().ensureItemInScene(self, value) - elif change == QtWidgets.QGraphicsItem.ItemPositionHasChanged: + return self.scene().ensure_item_in_scene(self, value) + elif change == QtWidgets.QGraphicsItem.GraphicsItemChange.ItemPositionHasChanged: for port_item in self._step_port_items: port_item.itemChange(change, value) diff --git a/src/mapclient/view/workflow/workflowgraphicsscene.py b/src/mapclient/view/workflow/workflowgraphicsscene.py index e0667802..886fefc0 100644 --- a/src/mapclient/view/workflow/workflowgraphicsscene.py +++ b/src/mapclient/view/workflow/workflowgraphicsscene.py @@ -119,30 +119,31 @@ def updateModel(self): self._previousSelection = self.selectedItems() - def ensureItemInScene(self, item, newPos): + def ensure_item_in_scene(self, item, new_pos): if self._is_ready: bRect = item.boundingRect() - xp1 = bRect.x() + newPos.x() - yp1 = bRect.y() + newPos.y() - xp2 = bRect.x() + bRect.width() + newPos.x() - yp2 = bRect.y() + bRect.height() + newPos.y() + xp1 = bRect.x() + new_pos.x() + yp1 = bRect.y() + new_pos.y() + xp2 = xp1 + bRect.width() + yp2 = yp1 + bRect.height() bRect.setCoords(xp1, yp1, xp2, yp2) + offset = item.offset() rect = self.sceneRect() if not rect.contains(bRect): - x1 = max(bRect.left(), rect.left()) + 2.0 # plus bounding rectangle adjust - x2 = min(bRect.x() + bRect.width(), rect.x() + rect.width()) - bRect.width() + 2.0 - y1 = max(bRect.top(), rect.top()) + 2.0 # plus bounding rectangle adjust - y2 = min(bRect.bottom(), rect.bottom()) - bRect.height() + 2.0 - if newPos.x() != x1: - newPos.setX(x1) - elif newPos.x() != x2: - newPos.setX(x2) - if newPos.y() != y1: - newPos.setY(y1) - elif newPos.y() != y2: - newPos.setY(y2) - - return newPos + x1 = max(bRect.left(), rect.left()) + y1 = max(bRect.top(), rect.top()) + x2 = min(bRect.right(), rect.right()) - offset.x() + y2 = min(bRect.bottom(), rect.bottom()) - offset.y() + if new_pos.x() <= x1: + new_pos.setX(x1) + elif new_pos.x() >= x2: + new_pos.setX(x2) + if new_pos.y() <= y1: + new_pos.setY(y1) + elif new_pos.y() >= y2: + new_pos.setY(y2) + + return new_pos def clear(self): QtWidgets.QGraphicsScene.clear(self) diff --git a/src/mapclient/view/workflow/workflowgraphicsview.py b/src/mapclient/view/workflow/workflowgraphicsview.py index be46d623..22e38be1 100644 --- a/src/mapclient/view/workflow/workflowgraphicsview.py +++ b/src/mapclient/view/workflow/workflowgraphicsview.py @@ -22,7 +22,7 @@ from PySide6 import QtCore, QtWidgets, QtGui -from mapclient.core.workflow.workflowutils import convert_to_parameterised_position, revert_parameterised_position +from mapclient.core.workflow.workflowutils import revert_parameterised_position from mapclient.mountpoints.workflowstep import workflowStepFactory from mapclient.core.workflow.workflowscene import MetaStep from mapclient.view.utils import is_light_mode @@ -245,7 +245,7 @@ def paste_steps(self, stream, event_position=None): node = self.create_node(scene, name) self._undoStack.push(CommandAdd(scene, node)) - self._undoStack.push(CommandMove(node, position, scene.ensureItemInScene(node, position))) + self._undoStack.push(CommandMove(node, position, scene.ensure_item_in_scene(node, position))) node.setSelected(True) @@ -511,7 +511,7 @@ def _reposition_steps(self): scene_rect = self.sceneRect() for item in self.items(): if isinstance(item, Node): - new_position = revert_parameterised_position(scene_rect, item.parameterised_pos()) + new_position = revert_parameterised_position(scene_rect, item.parameterised_pos(), item.offset()) item.setPos(new_position, False) def merge_macros(self):