pygpt-net 2.6.61__py3-none-any.whl → 2.6.62__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.
- pygpt_net/CHANGELOG.txt +7 -0
- pygpt_net/__init__.py +1 -1
- pygpt_net/controller/chat/response.py +8 -2
- pygpt_net/controller/settings/profile.py +16 -4
- pygpt_net/controller/settings/workdir.py +30 -5
- pygpt_net/controller/theme/common.py +4 -2
- pygpt_net/controller/theme/markdown.py +2 -2
- pygpt_net/controller/theme/theme.py +2 -1
- pygpt_net/controller/ui/ui.py +31 -3
- pygpt_net/core/agents/custom/llama_index/runner.py +18 -3
- pygpt_net/core/agents/custom/runner.py +10 -5
- pygpt_net/core/agents/runners/llama_workflow.py +65 -5
- pygpt_net/core/agents/runners/openai_workflow.py +2 -1
- pygpt_net/core/node_editor/types.py +13 -1
- pygpt_net/core/render/web/renderer.py +76 -11
- pygpt_net/data/config/config.json +2 -2
- pygpt_net/data/config/models.json +2 -2
- pygpt_net/data/css/style.dark.css +18 -0
- pygpt_net/data/css/style.light.css +20 -1
- pygpt_net/data/locale/locale.de.ini +2 -0
- pygpt_net/data/locale/locale.en.ini +2 -0
- pygpt_net/data/locale/locale.es.ini +2 -0
- pygpt_net/data/locale/locale.fr.ini +2 -0
- pygpt_net/data/locale/locale.it.ini +2 -0
- pygpt_net/data/locale/locale.pl.ini +3 -1
- pygpt_net/data/locale/locale.uk.ini +2 -0
- pygpt_net/data/locale/locale.zh.ini +2 -0
- pygpt_net/item/ctx.py +23 -1
- pygpt_net/provider/agents/llama_index/workflow/codeact.py +9 -6
- pygpt_net/provider/agents/llama_index/workflow/openai.py +38 -11
- pygpt_net/provider/agents/llama_index/workflow/planner.py +36 -16
- pygpt_net/provider/agents/llama_index/workflow/supervisor.py +60 -10
- pygpt_net/provider/agents/openai/agent.py +3 -1
- pygpt_net/provider/agents/openai/agent_b2b.py +13 -9
- pygpt_net/provider/agents/openai/agent_planner.py +6 -2
- pygpt_net/provider/agents/openai/agent_with_experts.py +4 -1
- pygpt_net/provider/agents/openai/agent_with_experts_feedback.py +4 -2
- pygpt_net/provider/agents/openai/agent_with_feedback.py +4 -2
- pygpt_net/provider/agents/openai/evolve.py +6 -2
- pygpt_net/provider/agents/openai/supervisor.py +3 -1
- pygpt_net/provider/api/openai/agents/response.py +1 -0
- pygpt_net/provider/core/config/patch.py +8 -0
- pygpt_net/tools/agent_builder/tool.py +6 -0
- pygpt_net/tools/agent_builder/ui/dialogs.py +0 -41
- pygpt_net/ui/layout/toolbox/presets.py +14 -2
- pygpt_net/ui/main.py +2 -2
- pygpt_net/ui/widget/dialog/confirm.py +27 -3
- pygpt_net/ui/widget/draw/painter.py +90 -1
- pygpt_net/ui/widget/lists/preset.py +289 -25
- pygpt_net/ui/widget/node_editor/editor.py +53 -15
- pygpt_net/ui/widget/node_editor/node.py +82 -104
- pygpt_net/ui/widget/node_editor/view.py +4 -5
- pygpt_net/ui/widget/textarea/input.py +155 -21
- {pygpt_net-2.6.61.dist-info → pygpt_net-2.6.62.dist-info}/METADATA +17 -8
- {pygpt_net-2.6.61.dist-info → pygpt_net-2.6.62.dist-info}/RECORD +58 -58
- {pygpt_net-2.6.61.dist-info → pygpt_net-2.6.62.dist-info}/LICENSE +0 -0
- {pygpt_net-2.6.61.dist-info → pygpt_net-2.6.62.dist-info}/WHEEL +0 -0
- {pygpt_net-2.6.61.dist-info → pygpt_net-2.6.62.dist-info}/entry_points.txt +0 -0
|
@@ -392,10 +392,12 @@ class AgentBuilder(BaseTool):
|
|
|
392
392
|
# Start
|
|
393
393
|
registry.register(NodeTypeSpec(
|
|
394
394
|
type_name="Flow/Start",
|
|
395
|
+
display_name=trans("node.editor.spec.start.title"),
|
|
395
396
|
title=trans("node.editor.spec.start.title"),
|
|
396
397
|
base_id="start",
|
|
397
398
|
export_kind="start",
|
|
398
399
|
bg_color="#2D5A27",
|
|
400
|
+
max_num=1, # per-layout limit
|
|
399
401
|
properties=[
|
|
400
402
|
PropertySpec(id="output", type="flow", name=trans("node.editor.property.output.name"), editable=False,
|
|
401
403
|
allowed_inputs=0, allowed_outputs=1),
|
|
@@ -406,6 +408,7 @@ class AgentBuilder(BaseTool):
|
|
|
406
408
|
# Agent
|
|
407
409
|
registry.register(NodeTypeSpec(
|
|
408
410
|
type_name="Flow/Agent",
|
|
411
|
+
display_name=trans("node.editor.spec.agent.title"),
|
|
409
412
|
title=trans("node.editor.spec.agent.title"),
|
|
410
413
|
base_id="agent",
|
|
411
414
|
export_kind="agent",
|
|
@@ -435,6 +438,7 @@ class AgentBuilder(BaseTool):
|
|
|
435
438
|
# Memory
|
|
436
439
|
registry.register(NodeTypeSpec(
|
|
437
440
|
type_name="Flow/Memory",
|
|
441
|
+
display_name=trans("node.editor.spec.memory.title"),
|
|
438
442
|
title=trans("node.editor.spec.memory.title"),
|
|
439
443
|
base_id="mem",
|
|
440
444
|
export_kind="memory",
|
|
@@ -449,10 +453,12 @@ class AgentBuilder(BaseTool):
|
|
|
449
453
|
# End
|
|
450
454
|
registry.register(NodeTypeSpec(
|
|
451
455
|
type_name="Flow/End",
|
|
456
|
+
display_name=trans("node.editor.spec.end.title"),
|
|
452
457
|
title=trans("node.editor.spec.end.title"),
|
|
453
458
|
base_id="end",
|
|
454
459
|
export_kind="end",
|
|
455
460
|
bg_color="#6B2E2E",
|
|
461
|
+
max_num=1, # per-layout limit
|
|
456
462
|
properties=[
|
|
457
463
|
PropertySpec(id="input", type="flow", name=trans("node.editor.property.input.name"), editable=False,
|
|
458
464
|
allowed_inputs=-1, allowed_outputs=0),
|
|
@@ -77,49 +77,8 @@ class Builder:
|
|
|
77
77
|
registry=registry
|
|
78
78
|
) # parent == dialog
|
|
79
79
|
|
|
80
|
-
theme = self.window.core.config.get("theme")
|
|
81
|
-
if theme.startswith("light"):
|
|
82
|
-
style = """
|
|
83
|
-
NodeEditor {
|
|
84
|
-
qproperty-gridBackColor: #ffffff;
|
|
85
|
-
qproperty-gridPenColor: #eaeaea;
|
|
86
|
-
|
|
87
|
-
qproperty-nodeBackgroundColor: #2d2f34;
|
|
88
|
-
qproperty-nodeBorderColor: #4b4f57;
|
|
89
|
-
qproperty-nodeSelectionColor: #ff9900;
|
|
90
|
-
qproperty-nodeTitleColor: #3a3d44;
|
|
91
|
-
|
|
92
|
-
qproperty-portInputColor: #66b2ff;
|
|
93
|
-
qproperty-portOutputColor: #70e070;
|
|
94
|
-
qproperty-portConnectedColor: #ffd166;
|
|
95
|
-
|
|
96
|
-
qproperty-edgeColor: #c0c0c0;
|
|
97
|
-
qproperty-edgeSelectedColor: #ff8a5c;
|
|
98
|
-
}
|
|
99
|
-
"""
|
|
100
|
-
else:
|
|
101
|
-
style = """
|
|
102
|
-
NodeEditor {
|
|
103
|
-
qproperty-gridBackColor: #242629;
|
|
104
|
-
qproperty-gridPenColor: #3b3f46;
|
|
105
|
-
|
|
106
|
-
qproperty-nodeBackgroundColor: #2d2f34;
|
|
107
|
-
qproperty-nodeBorderColor: #4b4f57;
|
|
108
|
-
qproperty-nodeSelectionColor: #ff9900;
|
|
109
|
-
qproperty-nodeTitleColor: #3a3d44;
|
|
110
|
-
|
|
111
|
-
qproperty-portInputColor: #66b2ff;
|
|
112
|
-
qproperty-portOutputColor: #70e070;
|
|
113
|
-
qproperty-portConnectedColor: #ffd166;
|
|
114
|
-
|
|
115
|
-
qproperty-edgeColor: #c0c0c0;
|
|
116
|
-
qproperty-edgeSelectedColor: #ff8a5c;
|
|
117
|
-
}
|
|
118
|
-
"""
|
|
119
|
-
editor.setStyleSheet(style)
|
|
120
80
|
editor.on_clear = self.tool.clear
|
|
121
81
|
editor.editing_allowed = self.tool.editing_allowed
|
|
122
|
-
|
|
123
82
|
u.editor[id] = editor
|
|
124
83
|
|
|
125
84
|
layout = QVBoxLayout()
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
# ui/layout/presets.py
|
|
1
2
|
#!/usr/bin/env python3
|
|
2
3
|
# -*- coding: utf-8 -*-
|
|
3
4
|
# ================================================== #
|
|
@@ -6,7 +7,7 @@
|
|
|
6
7
|
# GitHub: https://github.com/szczyglis-dev/py-gpt #
|
|
7
8
|
# MIT License #
|
|
8
9
|
# Created By : Marcin Szczygliński #
|
|
9
|
-
# Updated Date: 2025.09.26
|
|
10
|
+
# Updated Date: 2025.09.26 13:30:00 #
|
|
10
11
|
# ================================================== #
|
|
11
12
|
|
|
12
13
|
from PySide6 import QtCore
|
|
@@ -126,6 +127,16 @@ class Presets:
|
|
|
126
127
|
models[self.id] = model
|
|
127
128
|
view.setModel(model)
|
|
128
129
|
|
|
130
|
+
# Preserve current scroll position across model rebuild to avoid a visible jump to the top.
|
|
131
|
+
# This is applied while updates are disabled, then restored just before re-enabling them.
|
|
132
|
+
try:
|
|
133
|
+
v = view.verticalScrollBar().value()
|
|
134
|
+
h = view.horizontalScrollBar().value()
|
|
135
|
+
view.set_pending_v_scroll(v)
|
|
136
|
+
view.set_pending_h_scroll(h)
|
|
137
|
+
except Exception:
|
|
138
|
+
pass
|
|
139
|
+
|
|
129
140
|
# Block user input during model rebuild to avoid crashes on quick clicks
|
|
130
141
|
view.begin_model_update()
|
|
131
142
|
|
|
@@ -193,5 +204,6 @@ class Presets:
|
|
|
193
204
|
# Force repaint in case Qt defers layout until next input
|
|
194
205
|
view.viewport().update()
|
|
195
206
|
|
|
196
|
-
#
|
|
207
|
+
# Clear one-shot pending scroll values and re-enable user interaction
|
|
208
|
+
view.clear_pending_scroll()
|
|
197
209
|
view.end_model_update()
|
pygpt_net/ui/main.py
CHANGED
|
@@ -22,7 +22,7 @@ from pygpt_net.controller import Controller
|
|
|
22
22
|
from pygpt_net.tools import Tools
|
|
23
23
|
from pygpt_net.ui import UI
|
|
24
24
|
from pygpt_net.ui.widget.textarea.web import ChatWebOutput
|
|
25
|
-
from pygpt_net.utils import get_app_meta, freeze_updates, set_env, has_env, get_env
|
|
25
|
+
from pygpt_net.utils import get_app_meta, freeze_updates, set_env, has_env, get_env, trans
|
|
26
26
|
|
|
27
27
|
|
|
28
28
|
class MainWindow(QMainWindow, QtStyleTools):
|
|
@@ -356,7 +356,7 @@ class MainWindow(QMainWindow, QtStyleTools):
|
|
|
356
356
|
self.core.presets.save_all()
|
|
357
357
|
print("Exiting...")
|
|
358
358
|
print("")
|
|
359
|
-
print("
|
|
359
|
+
print(f"{trans('exit.msg')} https://pygpt.net/#donate")
|
|
360
360
|
|
|
361
361
|
def changeEvent(self, event):
|
|
362
362
|
"""
|
|
@@ -6,9 +6,10 @@
|
|
|
6
6
|
# GitHub: https://github.com/szczyglis-dev/py-gpt #
|
|
7
7
|
# MIT License #
|
|
8
8
|
# Created By : Marcin Szczygliński #
|
|
9
|
-
# Updated Date:
|
|
9
|
+
# Updated Date: 2025.09.26 10:00:00 #
|
|
10
10
|
# ================================================== #
|
|
11
11
|
|
|
12
|
+
import sys
|
|
12
13
|
from PySide6.QtCore import Qt
|
|
13
14
|
from PySide6.QtWidgets import QDialog, QLabel, QHBoxLayout, QVBoxLayout, QPushButton
|
|
14
15
|
|
|
@@ -32,6 +33,7 @@ class ConfirmDialog(QDialog):
|
|
|
32
33
|
self.setWindowTitle(trans('dialog.confirm.title'))
|
|
33
34
|
self.setWindowFlags(self.windowFlags() | Qt.WindowStaysOnTopHint) # always on top
|
|
34
35
|
|
|
36
|
+
# Buttons
|
|
35
37
|
self.window.ui.nodes['dialog.confirm.btn.yes'] = QPushButton(trans('dialog.confirm.yes'))
|
|
36
38
|
self.window.ui.nodes['dialog.confirm.btn.yes'].clicked.connect(
|
|
37
39
|
lambda: self.window.controller.dialogs.confirm.accept(self.type, self.id, self.parent_object))
|
|
@@ -40,9 +42,24 @@ class ConfirmDialog(QDialog):
|
|
|
40
42
|
self.window.ui.nodes['dialog.confirm.btn.no'].clicked.connect(
|
|
41
43
|
lambda: self.window.controller.dialogs.confirm.dismiss(self.type, self.id))
|
|
42
44
|
|
|
45
|
+
# Always make the neutral action (No/Cancel) the default/active one.
|
|
46
|
+
# This ensures Enter triggers the safe option by default.
|
|
47
|
+
self.window.ui.nodes['dialog.confirm.btn.no'].setAutoDefault(True)
|
|
48
|
+
self.window.ui.nodes['dialog.confirm.btn.no'].setDefault(True)
|
|
49
|
+
self.window.ui.nodes['dialog.confirm.btn.no'].setFocus()
|
|
50
|
+
self.window.ui.nodes['dialog.confirm.btn.yes'].setAutoDefault(False)
|
|
51
|
+
self.window.ui.nodes['dialog.confirm.btn.yes'].setDefault(False)
|
|
52
|
+
|
|
53
|
+
# Bottom button row with platform-specific ordering
|
|
54
|
+
# Windows: affirmative on the left, neutral on the right
|
|
55
|
+
# Linux/macOS: neutral on the left, affirmative on the right
|
|
43
56
|
bottom = QHBoxLayout()
|
|
44
|
-
|
|
45
|
-
|
|
57
|
+
if self._affirmative_on_left():
|
|
58
|
+
bottom.addWidget(self.window.ui.nodes['dialog.confirm.btn.yes'])
|
|
59
|
+
bottom.addWidget(self.window.ui.nodes['dialog.confirm.btn.no'])
|
|
60
|
+
else:
|
|
61
|
+
bottom.addWidget(self.window.ui.nodes['dialog.confirm.btn.no'])
|
|
62
|
+
bottom.addWidget(self.window.ui.nodes['dialog.confirm.btn.yes'])
|
|
46
63
|
|
|
47
64
|
self.layout = QVBoxLayout()
|
|
48
65
|
self.message = QLabel("")
|
|
@@ -54,6 +71,13 @@ class ConfirmDialog(QDialog):
|
|
|
54
71
|
self.layout.addLayout(bottom)
|
|
55
72
|
self.setLayout(self.layout)
|
|
56
73
|
|
|
74
|
+
def _affirmative_on_left(self) -> bool:
|
|
75
|
+
"""
|
|
76
|
+
Decide button order depending on the platform.
|
|
77
|
+
Returns True on Windows, False otherwise (Linux/macOS).
|
|
78
|
+
"""
|
|
79
|
+
return sys.platform.startswith('win')
|
|
80
|
+
|
|
57
81
|
def closeEvent(self, event):
|
|
58
82
|
"""
|
|
59
83
|
Close event handler
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
# GitHub: https://github.com/szczyglis-dev/py-gpt #
|
|
7
7
|
# MIT License #
|
|
8
8
|
# Created By : Marcin Szczygliński #
|
|
9
|
-
# Updated Date: 2025.09.
|
|
9
|
+
# Updated Date: 2025.09.26 12:00:00 #
|
|
10
10
|
# ================================================== #
|
|
11
11
|
|
|
12
12
|
import datetime
|
|
@@ -98,6 +98,11 @@ class PainterWidget(QWidget):
|
|
|
98
98
|
self._autoScrollMinSpeed = 2 # px per tick (min)
|
|
99
99
|
self._autoScrollMaxSpeed = 18 # px per tick (max)
|
|
100
100
|
|
|
101
|
+
# Pan (middle mouse) state
|
|
102
|
+
self._panning = False
|
|
103
|
+
self._panLastGlobalPos = QPoint()
|
|
104
|
+
self._cursorBeforePan = None # store/restore cursor shape while panning
|
|
105
|
+
|
|
101
106
|
# Actions
|
|
102
107
|
self._act_undo = QAction(QIcon(":/icons/undo.svg"), trans('action.undo'), self)
|
|
103
108
|
self._act_undo.triggered.connect(self.undo)
|
|
@@ -1084,6 +1089,69 @@ class PainterWidget(QWidget):
|
|
|
1084
1089
|
if scrolled or dx != 0 or dy != 0:
|
|
1085
1090
|
self.update()
|
|
1086
1091
|
|
|
1092
|
+
# ---------- Pan (middle mouse drag) ----------
|
|
1093
|
+
|
|
1094
|
+
def _can_pan(self) -> bool:
|
|
1095
|
+
"""
|
|
1096
|
+
Return True if widget is inside a scroll area and content is scrollable.
|
|
1097
|
+
"""
|
|
1098
|
+
self._find_scroll_area()
|
|
1099
|
+
if self._scrollArea is None:
|
|
1100
|
+
return False
|
|
1101
|
+
hbar = self._scrollArea.horizontalScrollBar()
|
|
1102
|
+
vbar = self._scrollArea.verticalScrollBar()
|
|
1103
|
+
h_ok = hbar is not None and hbar.maximum() > hbar.minimum()
|
|
1104
|
+
v_ok = vbar is not None and vbar.maximum() > vbar.minimum()
|
|
1105
|
+
return h_ok or v_ok
|
|
1106
|
+
|
|
1107
|
+
def _start_pan(self, global_pos: QPoint):
|
|
1108
|
+
"""
|
|
1109
|
+
Begin view panning with middle mouse button.
|
|
1110
|
+
"""
|
|
1111
|
+
if self._panning:
|
|
1112
|
+
return
|
|
1113
|
+
self._panning = True
|
|
1114
|
+
self._panLastGlobalPos = QPoint(global_pos)
|
|
1115
|
+
# Store current cursor to restore later
|
|
1116
|
+
self._cursorBeforePan = QCursor(self.cursor())
|
|
1117
|
+
# Use a closed hand to indicate grabbing the canvas
|
|
1118
|
+
self.setCursor(QCursor(Qt.ClosedHandCursor))
|
|
1119
|
+
self.grabMouse()
|
|
1120
|
+
|
|
1121
|
+
def _update_pan(self, global_pos: QPoint):
|
|
1122
|
+
"""
|
|
1123
|
+
Update scrollbars based on mouse movement delta in global coordinates.
|
|
1124
|
+
"""
|
|
1125
|
+
if not self._panning or self._scrollArea is None:
|
|
1126
|
+
return
|
|
1127
|
+
dx = global_pos.x() - self._panLastGlobalPos.x()
|
|
1128
|
+
dy = global_pos.y() - self._panLastGlobalPos.y()
|
|
1129
|
+
self._panLastGlobalPos = QPoint(global_pos)
|
|
1130
|
+
|
|
1131
|
+
hbar = self._scrollArea.horizontalScrollBar()
|
|
1132
|
+
vbar = self._scrollArea.verticalScrollBar()
|
|
1133
|
+
|
|
1134
|
+
# Dragging the content to the right should reveal the left side -> subtract deltas
|
|
1135
|
+
if hbar is not None and hbar.maximum() > hbar.minimum():
|
|
1136
|
+
hbar.setValue(int(max(hbar.minimum(), min(hbar.maximum(), hbar.value() - dx))))
|
|
1137
|
+
if vbar is not None and vbar.maximum() > vbar.minimum():
|
|
1138
|
+
vbar.setValue(int(max(vbar.minimum(), min(vbar.maximum(), vbar.value() - dy))))
|
|
1139
|
+
|
|
1140
|
+
def _end_pan(self):
|
|
1141
|
+
"""
|
|
1142
|
+
End panning and restore previous cursor.
|
|
1143
|
+
"""
|
|
1144
|
+
if not self._panning:
|
|
1145
|
+
return
|
|
1146
|
+
self._panning = False
|
|
1147
|
+
self.releaseMouse()
|
|
1148
|
+
try:
|
|
1149
|
+
if self._cursorBeforePan is not None:
|
|
1150
|
+
# Restore previous cursor (do not guess based on mode/crop)
|
|
1151
|
+
self.setCursor(self._cursorBeforePan)
|
|
1152
|
+
finally:
|
|
1153
|
+
self._cursorBeforePan = None
|
|
1154
|
+
|
|
1087
1155
|
# ---------- Events ----------
|
|
1088
1156
|
|
|
1089
1157
|
def wheelEvent(self, event):
|
|
@@ -1109,6 +1177,14 @@ class PainterWidget(QWidget):
|
|
|
1109
1177
|
|
|
1110
1178
|
:param event: Event
|
|
1111
1179
|
"""
|
|
1180
|
+
# Middle button: start panning if scrollable
|
|
1181
|
+
if event.button() == Qt.MiddleButton:
|
|
1182
|
+
if not (self.cropping and self._selecting) and not self.drawing and self._can_pan():
|
|
1183
|
+
gp = event.globalPosition().toPoint()
|
|
1184
|
+
self._start_pan(gp)
|
|
1185
|
+
event.accept()
|
|
1186
|
+
return
|
|
1187
|
+
|
|
1112
1188
|
if event.button() == Qt.LeftButton:
|
|
1113
1189
|
self._mouseDown = True
|
|
1114
1190
|
if self.cropping:
|
|
@@ -1146,6 +1222,13 @@ class PainterWidget(QWidget):
|
|
|
1146
1222
|
|
|
1147
1223
|
:param event: Event
|
|
1148
1224
|
"""
|
|
1225
|
+
# Update panning if active
|
|
1226
|
+
if self._panning and (event.buttons() & Qt.MiddleButton):
|
|
1227
|
+
gp = event.globalPosition().toPoint()
|
|
1228
|
+
self._update_pan(gp)
|
|
1229
|
+
event.accept()
|
|
1230
|
+
return
|
|
1231
|
+
|
|
1149
1232
|
if self.cropping and self._selecting and (event.buttons() & Qt.LeftButton):
|
|
1150
1233
|
self._selectionRect = QRect(self._selectionStart, self._to_canvas_point(event.position()))
|
|
1151
1234
|
self.update()
|
|
@@ -1175,6 +1258,12 @@ class PainterWidget(QWidget):
|
|
|
1175
1258
|
|
|
1176
1259
|
:param event: Event
|
|
1177
1260
|
"""
|
|
1261
|
+
# End panning on middle button release
|
|
1262
|
+
if event.button() == Qt.MiddleButton:
|
|
1263
|
+
self._end_pan()
|
|
1264
|
+
event.accept()
|
|
1265
|
+
return
|
|
1266
|
+
|
|
1178
1267
|
if event.button() in (Qt.LeftButton, Qt.RightButton):
|
|
1179
1268
|
self._mouseDown = False
|
|
1180
1269
|
if self.cropping and self._selecting:
|