openprocess 0.7.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- cpnpy/__init__.py +46 -0
- openprocess/__init__.py +57 -0
- openprocess/analysis/__init__.py +0 -0
- openprocess/analysis/state_space.py +521 -0
- openprocess/analysis/state_space_process.py +251 -0
- openprocess/cli.py +742 -0
- openprocess/exercises/1 Petri nets/Exercise 1.1 Order handling/answer.pnml +31 -0
- openprocess/exercises/1 Petri nets/Exercise 1.1 Order handling/question.md +38 -0
- openprocess/exercises/2 Soundness/Exercise 2.1 Spot the flaw/answer.pnml +27 -0
- openprocess/exercises/2 Soundness/Exercise 2.1 Spot the flaw/net.pnml +29 -0
- openprocess/exercises/2 Soundness/Exercise 2.1 Spot the flaw/question.md +65 -0
- openprocess/exercises/3 Discovery/Exercise 3.1 The alpha-algorithm/log.txt +1 -0
- openprocess/exercises/3 Discovery/Exercise 3.1 The alpha-algorithm/question.md +67 -0
- openprocess/exercises/4 Regions/Exercise 4.1 Regions of a transition system/question.md +75 -0
- openprocess/exercises/4 Regions/Exercise 4.1 Regions of a transition system/ts.txt +5 -0
- openprocess/exercises/5 Markings/Exercise 5.1 Markings and matrices/net.pnml +27 -0
- openprocess/exercises/5 Markings/Exercise 5.1 Markings and matrices/question.md +65 -0
- openprocess/exercises/6 Inductive Miner/Exercise 6.1 Cuts and trees/log.txt +1 -0
- openprocess/exercises/6 Inductive Miner/Exercise 6.1 Cuts and trees/question.md +62 -0
- openprocess/exercises/7 Conformance/Exercise 7.1 Replay, alignments and workflows/log.txt +1 -0
- openprocess/exercises/7 Conformance/Exercise 7.1 Replay, alignments and workflows/m1.pnml +36 -0
- openprocess/exercises/7 Conformance/Exercise 7.1 Replay, alignments and workflows/m2.pnml +28 -0
- openprocess/exercises/7 Conformance/Exercise 7.1 Replay, alignments and workflows/m3.pnml +30 -0
- openprocess/exercises/7 Conformance/Exercise 7.1 Replay, alignments and workflows/net.pnml +36 -0
- openprocess/exercises/7 Conformance/Exercise 7.1 Replay, alignments and workflows/question.md +69 -0
- openprocess/exercises/pack.md +14 -0
- openprocess/flow/__init__.py +50 -0
- openprocess/flow/box.py +466 -0
- openprocess/flow/boxes/__init__.py +7 -0
- openprocess/flow/boxes/check.py +119 -0
- openprocess/flow/boxes/compare.py +16 -0
- openprocess/flow/boxes/cpn.py +53 -0
- openprocess/flow/boxes/discover.py +124 -0
- openprocess/flow/boxes/filter.py +80 -0
- openprocess/flow/boxes/input.py +124 -0
- openprocess/flow/boxes/output.py +52 -0
- openprocess/flow/boxes/predict.py +186 -0
- openprocess/flow/boxes/science.py +159 -0
- openprocess/flow/boxes/sweeps.py +18 -0
- openprocess/flow/convert.py +187 -0
- openprocess/flow/datasets.py +198 -0
- openprocess/flow/explain.py +115 -0
- openprocess/flow/library.py +222 -0
- openprocess/flow/record.py +385 -0
- openprocess/flow/runner.py +357 -0
- openprocess/flow/sweep.py +92 -0
- openprocess/flow/types.py +290 -0
- openprocess/flow/workflow.py +628 -0
- openprocess/gui/__init__.py +0 -0
- openprocess/gui/app.py +90 -0
- openprocess/gui/arc_editing.py +295 -0
- openprocess/gui/canvas.py +1414 -0
- openprocess/gui/flow/__init__.py +8 -0
- openprocess/gui/flow/canvas.py +854 -0
- openprocess/gui/flow/page.py +972 -0
- openprocess/gui/flow/templates.py +131 -0
- openprocess/gui/flow/viewers.py +665 -0
- openprocess/gui/items.py +1275 -0
- openprocess/gui/learn/answer_boxes.py +978 -0
- openprocess/gui/learn/concealment.py +91 -0
- openprocess/gui/learn/mode.py +1181 -0
- openprocess/gui/panning.py +241 -0
- openprocess/gui/resources/openprocess-icon.png +0 -0
- openprocess/gui/studio/__init__.py +1 -0
- openprocess/gui/studio/__main__.py +3 -0
- openprocess/gui/studio/app.py +4031 -0
- openprocess/gui/studio/charts.py +115 -0
- openprocess/gui/studio/compare_page.py +487 -0
- openprocess/gui/studio/cpn_page.py +1858 -0
- openprocess/gui/studio/definition_view.py +284 -0
- openprocess/gui/studio/derivation_view.py +421 -0
- openprocess/gui/studio/documents.py +152 -0
- openprocess/gui/studio/dotted_chart.py +1401 -0
- openprocess/gui/studio/file_dialogs.py +143 -0
- openprocess/gui/studio/filter_dialog.py +247 -0
- openprocess/gui/studio/graph_builders.py +176 -0
- openprocess/gui/studio/graph_view.py +682 -0
- openprocess/gui/studio/instances.py +413 -0
- openprocess/gui/studio/log_editor.py +675 -0
- openprocess/gui/studio/log_page.py +800 -0
- openprocess/gui/studio/markdown_view.py +127 -0
- openprocess/gui/studio/mathtext.py +260 -0
- openprocess/gui/studio/ml_highlighter.py +75 -0
- openprocess/gui/studio/model_page.py +760 -0
- openprocess/gui/studio/net_comparison.py +124 -0
- openprocess/gui/studio/notes_overlay.py +275 -0
- openprocess/gui/studio/petri_page.py +844 -0
- openprocess/gui/studio/regions_view.py +502 -0
- openprocess/gui/studio/sidebar.py +149 -0
- openprocess/gui/studio/style.py +503 -0
- openprocess/gui/studio/tool_icons.py +134 -0
- openprocess/gui/studio/updates.py +439 -0
- openprocess/gui/studio/widgets.py +899 -0
- openprocess/gui/studio/workers.py +60 -0
- openprocess/gui/studio/workspace.py +447 -0
- openprocess/gui/theme.py +394 -0
- openprocess/gui/tidy.py +86 -0
- openprocess/io/__init__.py +0 -0
- openprocess/io/cpn_reader.py +389 -0
- openprocess/io/cpn_writer.py +357 -0
- openprocess/learn/__init__.py +23 -0
- openprocess/learn/answers.py +188 -0
- openprocess/learn/checks.py +953 -0
- openprocess/learn/computed.py +1180 -0
- openprocess/learn/context.py +145 -0
- openprocess/learn/exam.py +169 -0
- openprocess/learn/exercise-packs.md +325 -0
- openprocess/learn/importer.py +216 -0
- openprocess/learn/notation.py +474 -0
- openprocess/learn/pack.py +511 -0
- openprocess/learn/sheet.py +296 -0
- openprocess/mining/__init__.py +73 -0
- openprocess/mining/analysis.py +689 -0
- openprocess/mining/columns.py +282 -0
- openprocess/mining/compare_nets.py +246 -0
- openprocess/mining/conformance/__init__.py +0 -0
- openprocess/mining/conformance/alignments.py +263 -0
- openprocess/mining/conformance/quality.py +145 -0
- openprocess/mining/conformance/token_replay.py +252 -0
- openprocess/mining/csv_import.py +222 -0
- openprocess/mining/definitions.py +584 -0
- openprocess/mining/dfg.py +187 -0
- openprocess/mining/discovery/__init__.py +0 -0
- openprocess/mining/discovery/alpha.py +168 -0
- openprocess/mining/discovery/heuristics.py +332 -0
- openprocess/mining/discovery/inductive.py +477 -0
- openprocess/mining/discovery/state_regions.py +62 -0
- openprocess/mining/filtering.py +237 -0
- openprocess/mining/footprint.py +183 -0
- openprocess/mining/invariants.py +191 -0
- openprocess/mining/layout.py +279 -0
- openprocess/mining/log.py +364 -0
- openprocess/mining/petrinet.py +354 -0
- openprocess/mining/playout.py +75 -0
- openprocess/mining/pm4py_bridge.py +82 -0
- openprocess/mining/pnml.py +223 -0
- openprocess/mining/processtree.py +216 -0
- openprocess/mining/regions.py +476 -0
- openprocess/mining/stats.py +160 -0
- openprocess/mining/structure.py +374 -0
- openprocess/mining/transition_system.py +409 -0
- openprocess/mining/xes.py +399 -0
- openprocess/ml/__init__.py +0 -0
- openprocess/ml/ast_nodes.py +332 -0
- openprocess/ml/builtins.py +364 -0
- openprocess/ml/colorsets.py +522 -0
- openprocess/ml/errors.py +60 -0
- openprocess/ml/evaluator.py +754 -0
- openprocess/ml/lexer.py +277 -0
- openprocess/ml/multiset.py +417 -0
- openprocess/ml/parser.py +737 -0
- openprocess/ml/values.py +319 -0
- openprocess/model/__init__.py +0 -0
- openprocess/model/declarations.py +617 -0
- openprocess/model/examples.py +98 -0
- openprocess/model/net.py +701 -0
- openprocess/model/plain.py +192 -0
- openprocess/references.py +280 -0
- openprocess/sim/__init__.py +0 -0
- openprocess/sim/binding.py +620 -0
- openprocess/sim/export.py +66 -0
- openprocess/sim/simulator.py +315 -0
- openprocess/teaching/__init__.py +4 -0
- openprocess/teaching/answers.py +4 -0
- openprocess/teaching/checks.py +5 -0
- openprocess/teaching/pack.py +4 -0
- openprocess/teaching/sheet.py +4 -0
- openprocess-0.7.0.dist-info/METADATA +927 -0
- openprocess-0.7.0.dist-info/RECORD +173 -0
- openprocess-0.7.0.dist-info/WHEEL +5 -0
- openprocess-0.7.0.dist-info/entry_points.txt +6 -0
- openprocess-0.7.0.dist-info/licenses/LICENSE +21 -0
- openprocess-0.7.0.dist-info/top_level.txt +2 -0
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
"""An endless canvas: pan anywhere, zoom about the pointer.
|
|
2
|
+
|
|
3
|
+
Earlier versions sized the scrollable area to the drawing. With the whole
|
|
4
|
+
net in view there was then nothing to scroll: two-finger scrolling did
|
|
5
|
+
nothing, zooming could not keep the point under the pointer still (the view
|
|
6
|
+
re-centred the drawing instead), and to start a new part of the net beside
|
|
7
|
+
the old one you first had to zoom in. Middle-drag panning grew the area on
|
|
8
|
+
the fly, but only that one gesture did.
|
|
9
|
+
|
|
10
|
+
This mixin gives the view a very large scene rect and no scroll bars, so the
|
|
11
|
+
canvas behaves like a sheet of paper that goes on in every direction:
|
|
12
|
+
|
|
13
|
+
* two-finger scroll (or the mouse wheel; Shift + wheel goes sideways) pans,
|
|
14
|
+
whatever is in view;
|
|
15
|
+
* dragging with the middle button, or with the left button while Space is
|
|
16
|
+
held, pans too -- whatever the tool or the item under the pointer;
|
|
17
|
+
* ⌘/Ctrl + scroll and trackpad pinch zoom about the pointer
|
|
18
|
+
(:meth:`CanvasPanning.zoom_about`).
|
|
19
|
+
|
|
20
|
+
Zooming to fit just frames the drawing; nothing has to be reset.
|
|
21
|
+
|
|
22
|
+
The drawing can never be lost, but it can be panned out of view: then a
|
|
23
|
+
hint at the top of the canvas says so, with an arrow pointing to where it
|
|
24
|
+
is, and clicking it frames the drawing again (as *Fit* does).
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
from __future__ import annotations
|
|
28
|
+
|
|
29
|
+
import math
|
|
30
|
+
|
|
31
|
+
from PySide6.QtCore import QPointF, QRectF, Qt, QTimer
|
|
32
|
+
from PySide6.QtGui import QCursor
|
|
33
|
+
from PySide6.QtWidgets import QGraphicsView, QPushButton
|
|
34
|
+
|
|
35
|
+
#: Arrows pointing from the middle of the view towards the drawing, by
|
|
36
|
+
#: eighths of a turn, starting east and going clockwise (screen y points down).
|
|
37
|
+
ARROWS = "→↘↓↙←↖↑↗"
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
#: Half the side of the canvas, in scene units: far more than any net needs,
|
|
41
|
+
#: small enough that the scroll range fits in an int at the largest zoom.
|
|
42
|
+
EXTENT = 50_000.0
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
class CanvasPanning:
|
|
46
|
+
"""Mix into a ``QGraphicsView`` subclass (before it in the bases) and call
|
|
47
|
+
:meth:`init_canvas` at the end of ``__init__``."""
|
|
48
|
+
|
|
49
|
+
_pan_last = None # the pointer position while a pan drag is on
|
|
50
|
+
_pan_cursor = None # the (tool's) cursor to put back afterwards
|
|
51
|
+
_space_held = False # Space is down: a left drag pans
|
|
52
|
+
_space_cursor = None
|
|
53
|
+
#: What the off-screen hint calls the drawing.
|
|
54
|
+
drawing_name = "The drawing"
|
|
55
|
+
|
|
56
|
+
def init_canvas(self) -> None:
|
|
57
|
+
self.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
|
|
58
|
+
self.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
|
|
59
|
+
self.setSceneRect(QRectF(-EXTENT, -EXTENT, 2 * EXTENT, 2 * EXTENT))
|
|
60
|
+
# The anchor for zooming needs to know where the pointer is.
|
|
61
|
+
self.viewport().setMouseTracking(True)
|
|
62
|
+
# The hint shown when the drawing is out of view.
|
|
63
|
+
self.offscreen_hint = QPushButton(self)
|
|
64
|
+
self.offscreen_hint.setObjectName("offscreenHint")
|
|
65
|
+
self.offscreen_hint.setCursor(Qt.PointingHandCursor)
|
|
66
|
+
self.offscreen_hint.setToolTip("Frame the whole drawing again (Fit)")
|
|
67
|
+
self.offscreen_hint.clicked.connect(self.show_drawing)
|
|
68
|
+
self.offscreen_hint.hide()
|
|
69
|
+
self._hint_timer = QTimer(self)
|
|
70
|
+
self._hint_timer.setSingleShot(True)
|
|
71
|
+
self._hint_timer.setInterval(30)
|
|
72
|
+
self._hint_timer.timeout.connect(self.update_offscreen_hint)
|
|
73
|
+
self.horizontalScrollBar().valueChanged.connect(lambda _value: self._hint_timer.start())
|
|
74
|
+
self.verticalScrollBar().valueChanged.connect(lambda _value: self._hint_timer.start())
|
|
75
|
+
if self.scene() is not None:
|
|
76
|
+
self.scene().changed.connect(lambda _rects: self._hint_timer.start())
|
|
77
|
+
|
|
78
|
+
# -- the drawing out of view ------------------------------------------------------
|
|
79
|
+
def show_drawing(self) -> None:
|
|
80
|
+
"""Frame the whole drawing (the view's own Fit)."""
|
|
81
|
+
fit = getattr(self, "zoom_to_fit", None) or getattr(self, "fit", None)
|
|
82
|
+
if fit is not None:
|
|
83
|
+
fit()
|
|
84
|
+
self.update_offscreen_hint()
|
|
85
|
+
|
|
86
|
+
def update_offscreen_hint(self) -> None:
|
|
87
|
+
"""Show the hint, pointing the right way, while none of the drawing is in view."""
|
|
88
|
+
hint = getattr(self, "offscreen_hint", None)
|
|
89
|
+
if hint is None or self.scene() is None:
|
|
90
|
+
return
|
|
91
|
+
drawing = self.scene().itemsBoundingRect()
|
|
92
|
+
visible = self.mapToScene(self.viewport().rect()).boundingRect()
|
|
93
|
+
if drawing.isEmpty() or visible.intersects(drawing):
|
|
94
|
+
hint.hide()
|
|
95
|
+
return
|
|
96
|
+
offset = self.mapFromScene(drawing.center()) - self.viewport().rect().center()
|
|
97
|
+
angle = math.degrees(math.atan2(offset.y(), offset.x())) % 360
|
|
98
|
+
arrow = ARROWS[round(angle / 45) % 8]
|
|
99
|
+
hint.setText(f"{arrow} {self.drawing_name} is out of view — show it")
|
|
100
|
+
hint.adjustSize()
|
|
101
|
+
area = self.viewport().geometry()
|
|
102
|
+
hint.move(area.center().x() - hint.width() // 2, area.top() + 12)
|
|
103
|
+
hint.show()
|
|
104
|
+
hint.raise_()
|
|
105
|
+
|
|
106
|
+
def resizeEvent(self, event) -> None: # noqa: N802
|
|
107
|
+
super().resizeEvent(event)
|
|
108
|
+
timer = getattr(self, "_hint_timer", None)
|
|
109
|
+
if timer is not None:
|
|
110
|
+
timer.start()
|
|
111
|
+
|
|
112
|
+
# -- dragging -------------------------------------------------------------------
|
|
113
|
+
def _starts_pan(self, event) -> bool:
|
|
114
|
+
return event.button() == Qt.MiddleButton or (
|
|
115
|
+
event.button() == Qt.LeftButton and self._space_held)
|
|
116
|
+
|
|
117
|
+
def mousePressEvent(self, event) -> None: # noqa: N802
|
|
118
|
+
if self._starts_pan(event):
|
|
119
|
+
self._pan_last = event.position()
|
|
120
|
+
self._pan_button = event.button()
|
|
121
|
+
if self._pan_cursor is None:
|
|
122
|
+
self._pan_cursor = self.viewport().cursor()
|
|
123
|
+
self.viewport().setCursor(Qt.ClosedHandCursor)
|
|
124
|
+
event.accept()
|
|
125
|
+
return
|
|
126
|
+
super().mousePressEvent(event)
|
|
127
|
+
|
|
128
|
+
def mouseMoveEvent(self, event) -> None: # noqa: N802
|
|
129
|
+
if self._pan_last is not None:
|
|
130
|
+
if not event.buttons() & self._pan_button: # released outside the window
|
|
131
|
+
self._end_pan()
|
|
132
|
+
else:
|
|
133
|
+
delta = event.position() - self._pan_last
|
|
134
|
+
self._pan_last = event.position()
|
|
135
|
+
self.pan_by(round(delta.x()), round(delta.y()))
|
|
136
|
+
event.accept()
|
|
137
|
+
return
|
|
138
|
+
super().mouseMoveEvent(event)
|
|
139
|
+
|
|
140
|
+
def mouseReleaseEvent(self, event) -> None: # noqa: N802
|
|
141
|
+
if self._pan_last is not None and event.button() == self._pan_button:
|
|
142
|
+
self._end_pan()
|
|
143
|
+
event.accept()
|
|
144
|
+
return
|
|
145
|
+
super().mouseReleaseEvent(event)
|
|
146
|
+
|
|
147
|
+
def _end_pan(self) -> None:
|
|
148
|
+
self._pan_last = None
|
|
149
|
+
if self._space_held:
|
|
150
|
+
self.viewport().setCursor(Qt.OpenHandCursor)
|
|
151
|
+
else:
|
|
152
|
+
self._restore_cursor()
|
|
153
|
+
|
|
154
|
+
def _restore_cursor(self) -> None:
|
|
155
|
+
if self._pan_cursor is not None:
|
|
156
|
+
self.viewport().setCursor(self._pan_cursor)
|
|
157
|
+
self._pan_cursor = None
|
|
158
|
+
|
|
159
|
+
def keyPressEvent(self, event) -> None: # noqa: N802
|
|
160
|
+
if event.key() == Qt.Key_Space and not event.isAutoRepeat() \
|
|
161
|
+
and event.modifiers() == Qt.NoModifier:
|
|
162
|
+
self._space_held = True
|
|
163
|
+
if self._pan_cursor is None:
|
|
164
|
+
self._pan_cursor = self.viewport().cursor()
|
|
165
|
+
self.viewport().setCursor(Qt.OpenHandCursor)
|
|
166
|
+
event.accept()
|
|
167
|
+
return
|
|
168
|
+
if event.key() == Qt.Key_Space:
|
|
169
|
+
event.accept() # auto-repeat while held
|
|
170
|
+
return
|
|
171
|
+
super().keyPressEvent(event)
|
|
172
|
+
|
|
173
|
+
def keyReleaseEvent(self, event) -> None: # noqa: N802
|
|
174
|
+
if event.key() == Qt.Key_Space and not event.isAutoRepeat():
|
|
175
|
+
self._space_held = False
|
|
176
|
+
if self._pan_last is None:
|
|
177
|
+
self._restore_cursor()
|
|
178
|
+
event.accept()
|
|
179
|
+
return
|
|
180
|
+
super().keyReleaseEvent(event)
|
|
181
|
+
|
|
182
|
+
def focusOutEvent(self, event) -> None: # noqa: N802
|
|
183
|
+
if self._space_held: # Space released somewhere else
|
|
184
|
+
self._space_held = False
|
|
185
|
+
if self._pan_last is None:
|
|
186
|
+
self._restore_cursor()
|
|
187
|
+
super().focusOutEvent(event)
|
|
188
|
+
|
|
189
|
+
# -- moving the view ----------------------------------------------------------------
|
|
190
|
+
def pan_by(self, dx: int, dy: int) -> None:
|
|
191
|
+
"""Move the drawing ``dx``, ``dy`` pixels (with the pointer)."""
|
|
192
|
+
# Panning is taking charge of the view, like zooming by hand: resizing
|
|
193
|
+
# the window must not re-fit it.
|
|
194
|
+
self.auto_fit = False
|
|
195
|
+
horizontal, vertical = self.horizontalScrollBar(), self.verticalScrollBar()
|
|
196
|
+
horizontal.setValue(horizontal.value() - dx)
|
|
197
|
+
vertical.setValue(vertical.value() - dy)
|
|
198
|
+
|
|
199
|
+
def wheel_pan(self, event) -> None:
|
|
200
|
+
"""Scroll: pan by the trackpad's pixels, or a step per wheel notch."""
|
|
201
|
+
pixels = event.pixelDelta()
|
|
202
|
+
if not pixels.isNull():
|
|
203
|
+
dx, dy = pixels.x(), pixels.y()
|
|
204
|
+
else:
|
|
205
|
+
angle = event.angleDelta()
|
|
206
|
+
dx, dy = angle.x() / 3, angle.y() / 3 # 40 px per notch
|
|
207
|
+
if event.modifiers() & Qt.ShiftModifier and not dx:
|
|
208
|
+
dx, dy = dy, 0 # Shift + wheel: sideways
|
|
209
|
+
self.pan_by(round(dx), round(dy))
|
|
210
|
+
event.accept()
|
|
211
|
+
|
|
212
|
+
def zoom_about(self, ratio: float, point: QPointF | None = None) -> None:
|
|
213
|
+
"""Scale by ``ratio`` keeping the scene point under ``point`` (viewport
|
|
214
|
+
coordinates; the pointer, else the centre) where it is on screen."""
|
|
215
|
+
if point is None:
|
|
216
|
+
pointer = self.viewport().mapFromGlobal(QCursor.pos())
|
|
217
|
+
point = QPointF(pointer if self.viewport().rect().contains(pointer)
|
|
218
|
+
else self.viewport().rect().center())
|
|
219
|
+
anchor = self.transformationAnchor()
|
|
220
|
+
self.setTransformationAnchor(QGraphicsView.NoAnchor)
|
|
221
|
+
before = self.mapToScene(point.toPoint())
|
|
222
|
+
self.scale(ratio, ratio)
|
|
223
|
+
if getattr(self, "_hint_timer", None) is not None:
|
|
224
|
+
self._hint_timer.start()
|
|
225
|
+
after = self.mapFromScene(before)
|
|
226
|
+
self.horizontalScrollBar().setValue(self.horizontalScrollBar().value()
|
|
227
|
+
+ after.x() - round(point.x()))
|
|
228
|
+
self.verticalScrollBar().setValue(self.verticalScrollBar().value()
|
|
229
|
+
+ after.y() - round(point.y()))
|
|
230
|
+
self.setTransformationAnchor(anchor)
|
|
231
|
+
|
|
232
|
+
# Older names, kept for callers outside this module.
|
|
233
|
+
def reset_pan_area(self) -> None:
|
|
234
|
+
"""Nothing to reset: the canvas is endless."""
|
|
235
|
+
|
|
236
|
+
def hold_still(self) -> None:
|
|
237
|
+
"""The drawing never moves by itself on an endless canvas."""
|
|
238
|
+
|
|
239
|
+
|
|
240
|
+
#: The previous name of the mixin.
|
|
241
|
+
MiddleButtonPan = CanvasPanning
|
|
Binary file
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""OpenProcess Studio: the process-mining workspace (logs, discovery, conformance)."""
|