pyscreeps-arena 0.5.7b0__py3-none-any.whl → 0.5.7.2__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.
- pyscreeps_arena/__init__.py +35 -3
- pyscreeps_arena/core/const.py +1 -1
- pyscreeps_arena/project.7z +0 -0
- pyscreeps_arena/ui/__init__.py +3 -1
- pyscreeps_arena/ui/map_render.py +705 -0
- pyscreeps_arena/ui/mapviewer.py +14 -0
- pyscreeps_arena/ui/qcreeplogic/qcreeplogic.py +82 -21
- pyscreeps_arena/ui/qmapv/__init__.py +3 -0
- pyscreeps_arena/ui/qmapv/qcinfo.py +567 -0
- pyscreeps_arena/ui/qmapv/qco.py +441 -0
- pyscreeps_arena/ui/qmapv/qmapv.py +728 -0
- pyscreeps_arena/ui/qmapv/test_array_drag.py +191 -0
- pyscreeps_arena/ui/qmapv/test_drag.py +107 -0
- pyscreeps_arena/ui/qmapv/test_qcinfo.py +169 -0
- pyscreeps_arena/ui/qmapv/test_qco_drag.py +7 -0
- pyscreeps_arena/ui/qmapv/test_qmapv.py +224 -0
- pyscreeps_arena/ui/qmapv/test_simple_array.py +303 -0
- {pyscreeps_arena-0.5.7b0.dist-info → pyscreeps_arena-0.5.7.2.dist-info}/METADATA +1 -1
- pyscreeps_arena-0.5.7.2.dist-info/RECORD +40 -0
- pyscreeps_arena-0.5.7b0.dist-info/RECORD +0 -28
- {pyscreeps_arena-0.5.7b0.dist-info → pyscreeps_arena-0.5.7.2.dist-info}/WHEEL +0 -0
- {pyscreeps_arena-0.5.7b0.dist-info → pyscreeps_arena-0.5.7.2.dist-info}/entry_points.txt +0 -0
- {pyscreeps_arena-0.5.7b0.dist-info → pyscreeps_arena-0.5.7.2.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,441 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
"""
|
|
3
|
+
QPSA Cell Object Component - 单元格对象组件
|
|
4
|
+
"""
|
|
5
|
+
from typing import List, Dict, Any, Optional
|
|
6
|
+
import json
|
|
7
|
+
from PyQt6.QtWidgets import (QWidget, QVBoxLayout, QHBoxLayout, QLabel,
|
|
8
|
+
QListWidget, QListWidgetItem, QFrame,
|
|
9
|
+
QSpacerItem, QSizePolicy, QPushButton)
|
|
10
|
+
from PyQt6.QtCore import Qt, pyqtSignal, pyqtSlot, QMimeData
|
|
11
|
+
from PyQt6.QtGui import QPainter, QPen, QColor, QBrush, QPixmap, QDrag
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class QPSACellObjectItem(QWidget):
|
|
15
|
+
"""List item widget with remove button."""
|
|
16
|
+
|
|
17
|
+
# Signals
|
|
18
|
+
removeClicked = pyqtSignal(str) # object_id
|
|
19
|
+
|
|
20
|
+
def __init__(self, obj_id: str, obj_type: str, obj_name: str,
|
|
21
|
+
x: int, y: int, method: str, image: Optional[QPixmap] = None, parent=None):
|
|
22
|
+
super().__init__(parent)
|
|
23
|
+
self._id = obj_id
|
|
24
|
+
self._type = obj_type
|
|
25
|
+
self._name = obj_name
|
|
26
|
+
self._x = x
|
|
27
|
+
self._y = y
|
|
28
|
+
self._method = method
|
|
29
|
+
self._image = image
|
|
30
|
+
self._init_ui()
|
|
31
|
+
|
|
32
|
+
def _init_ui(self):
|
|
33
|
+
"""Initialize UI components."""
|
|
34
|
+
# Main horizontal layout
|
|
35
|
+
layout = QHBoxLayout()
|
|
36
|
+
layout.setContentsMargins(2, 2, 2, 2) # Reduced margins
|
|
37
|
+
layout.setSpacing(2) # Reduced spacing
|
|
38
|
+
|
|
39
|
+
# Left side - shape/icon
|
|
40
|
+
self._icon_label = QLabel()
|
|
41
|
+
self._icon_label.setFixedSize(20, 20) # Reduced from 30x30
|
|
42
|
+
self._icon_label.setStyleSheet("""
|
|
43
|
+
QLabel {
|
|
44
|
+
border: 1px solid #ccc;
|
|
45
|
+
border-radius: 3px;
|
|
46
|
+
background-color: #f0f0f0;
|
|
47
|
+
}
|
|
48
|
+
""")
|
|
49
|
+
self._icon_label.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
|
50
|
+
self._update_icon()
|
|
51
|
+
layout.addWidget(self._icon_label)
|
|
52
|
+
|
|
53
|
+
# Middle section - vertical layout for type, id and coordinates
|
|
54
|
+
middle_layout = QVBoxLayout()
|
|
55
|
+
middle_layout.setSpacing(1) # Reduced spacing
|
|
56
|
+
middle_layout.setContentsMargins(0, 0, 0, 0)
|
|
57
|
+
|
|
58
|
+
# Type and name label (first row)
|
|
59
|
+
type_name_label = QLabel(f"{self._type}: {self._name}")
|
|
60
|
+
type_name_label.setStyleSheet("font-weight: bold; font-size: 9px;") # Reduced font size
|
|
61
|
+
type_name_label.setWordWrap(False)
|
|
62
|
+
middle_layout.addWidget(type_name_label)
|
|
63
|
+
|
|
64
|
+
# ID label (second row)
|
|
65
|
+
id_label = QLabel(f"ID: {self._id}")
|
|
66
|
+
id_label.setStyleSheet("font-size: 8px; color: #666;") # Reduced font size
|
|
67
|
+
id_label.setWordWrap(False)
|
|
68
|
+
middle_layout.addWidget(id_label)
|
|
69
|
+
|
|
70
|
+
# Coordinates and method label (third row)
|
|
71
|
+
coord_method_label = QLabel(f"({self._x}, {self._y}) | {self._method}")
|
|
72
|
+
coord_method_label.setStyleSheet("font-size: 8px; color: #888;") # Reduced font size
|
|
73
|
+
coord_method_label.setWordWrap(False)
|
|
74
|
+
middle_layout.addWidget(coord_method_label)
|
|
75
|
+
|
|
76
|
+
layout.addLayout(middle_layout)
|
|
77
|
+
|
|
78
|
+
# Add spacer to push button to the right
|
|
79
|
+
layout.addStretch()
|
|
80
|
+
|
|
81
|
+
# Right side - remove button with red circle and cross
|
|
82
|
+
self._remove_btn = QPushButton("✕") # Changed to simpler symbol
|
|
83
|
+
self._remove_btn.setFixedSize(16, 16) # Reduced from 24x24
|
|
84
|
+
self._remove_btn.setStyleSheet("""
|
|
85
|
+
QPushButton {
|
|
86
|
+
border: 1px solid #ff6b6b;
|
|
87
|
+
border-radius: 8px;
|
|
88
|
+
background-color: #ffe6e6;
|
|
89
|
+
color: #ff6b6b;
|
|
90
|
+
font-size: 10px;
|
|
91
|
+
font-weight: bold;
|
|
92
|
+
}
|
|
93
|
+
QPushButton:hover {
|
|
94
|
+
background-color: #ff6b6b;
|
|
95
|
+
color: white;
|
|
96
|
+
}
|
|
97
|
+
QPushButton:pressed {
|
|
98
|
+
background-color: #ff5252;
|
|
99
|
+
}
|
|
100
|
+
""")
|
|
101
|
+
self._remove_btn.clicked.connect(self._on_remove_clicked)
|
|
102
|
+
layout.addWidget(self._remove_btn)
|
|
103
|
+
|
|
104
|
+
self.setLayout(layout)
|
|
105
|
+
|
|
106
|
+
# self.setMinimumWidth(160) # Reduced from 200
|
|
107
|
+
|
|
108
|
+
def _update_icon(self):
|
|
109
|
+
"""Update the icon based on current image."""
|
|
110
|
+
if self._image and not self._image.isNull():
|
|
111
|
+
# Get the label size and use 90% of it for the image
|
|
112
|
+
label_width = self._icon_label.width()
|
|
113
|
+
label_height = self._icon_label.height()
|
|
114
|
+
|
|
115
|
+
# Calculate 90% of container size
|
|
116
|
+
target_width = int(label_width * 0.9)
|
|
117
|
+
target_height = int(label_height * 0.9)
|
|
118
|
+
|
|
119
|
+
# Scale image to fit the label with 90% size
|
|
120
|
+
scaled_image = self._image.scaled(
|
|
121
|
+
target_width, target_height, Qt.AspectRatioMode.KeepAspectRatio,
|
|
122
|
+
Qt.TransformationMode.SmoothTransformation
|
|
123
|
+
)
|
|
124
|
+
self._icon_label.setPixmap(scaled_image)
|
|
125
|
+
self._icon_label.setText("")
|
|
126
|
+
else:
|
|
127
|
+
# Use placeholder text if no image
|
|
128
|
+
self._icon_label.setPixmap(QPixmap())
|
|
129
|
+
self._icon_label.setText("◆") # Placeholder shape
|
|
130
|
+
|
|
131
|
+
@pyqtSlot()
|
|
132
|
+
def _on_remove_clicked(self):
|
|
133
|
+
"""Handle remove button click."""
|
|
134
|
+
self.removeClicked.emit(self._id)
|
|
135
|
+
|
|
136
|
+
# Properties
|
|
137
|
+
@property
|
|
138
|
+
def id(self) -> str:
|
|
139
|
+
"""Get object ID."""
|
|
140
|
+
return self._id
|
|
141
|
+
|
|
142
|
+
@property
|
|
143
|
+
def image(self) -> Optional[QPixmap]:
|
|
144
|
+
"""Get object image/screenshot."""
|
|
145
|
+
return self._image
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
class QPSACellObject(QWidget):
|
|
149
|
+
"""Cell object component that accepts drag and drop."""
|
|
150
|
+
|
|
151
|
+
# Signals
|
|
152
|
+
objectAdded = pyqtSignal(object) # Emitted when an object is added
|
|
153
|
+
objectRemoved = pyqtSignal(str) # Emitted when an object is removed
|
|
154
|
+
itemChanged = pyqtSignal() # Emitted when any item is added or removed
|
|
155
|
+
|
|
156
|
+
def __init__(self, parent=None):
|
|
157
|
+
super().__init__(parent)
|
|
158
|
+
self._objects = [] # List to store objects
|
|
159
|
+
self._init_ui()
|
|
160
|
+
self._drag_start_pos = None
|
|
161
|
+
self._dragging_item = None
|
|
162
|
+
|
|
163
|
+
# Install event filter on the list widget to capture mouse events
|
|
164
|
+
self._objects_list.viewport().installEventFilter(self)
|
|
165
|
+
|
|
166
|
+
def _init_ui(self):
|
|
167
|
+
"""Initialize UI components."""
|
|
168
|
+
# Main vertical layout
|
|
169
|
+
layout = QVBoxLayout()
|
|
170
|
+
layout.setContentsMargins(2, 2, 2, 2) # Reduced margins
|
|
171
|
+
layout.setSpacing(2) # Reduced spacing
|
|
172
|
+
|
|
173
|
+
# Title label
|
|
174
|
+
# title_label = QLabel("Cell Objects")
|
|
175
|
+
# title_label.setStyleSheet("font-weight: bold; font-size: 14px;")
|
|
176
|
+
# layout.addWidget(title_label)
|
|
177
|
+
|
|
178
|
+
# Objects list
|
|
179
|
+
self._objects_list = QListWidget()
|
|
180
|
+
self._objects_list.setFrameStyle(QFrame.Shape.Box)
|
|
181
|
+
self._objects_list.setStyleSheet("""
|
|
182
|
+
QListWidget {
|
|
183
|
+
border: 1px solid #ccc;
|
|
184
|
+
border-radius: 3px;
|
|
185
|
+
background-color: white;
|
|
186
|
+
}
|
|
187
|
+
QListWidget::item {
|
|
188
|
+
border-bottom: 1px solid #eee;
|
|
189
|
+
}
|
|
190
|
+
QListWidget::item:selected {
|
|
191
|
+
background-color: #e3f2fd;
|
|
192
|
+
}
|
|
193
|
+
""")
|
|
194
|
+
|
|
195
|
+
# Set widget to accept drops
|
|
196
|
+
self.setAcceptDrops(True)
|
|
197
|
+
|
|
198
|
+
# Disable list widget's own drag handling to use our custom implementation
|
|
199
|
+
self._objects_list.setDragEnabled(False) # Disable default drag handling
|
|
200
|
+
self._objects_list.setDropIndicatorShown(True)
|
|
201
|
+
self._objects_list.setDragDropMode(QListWidget.DragDropMode.NoDragDrop) # No default drag drop
|
|
202
|
+
self._objects_list.setAcceptDrops(False)
|
|
203
|
+
|
|
204
|
+
layout.addWidget(self._objects_list)
|
|
205
|
+
|
|
206
|
+
self.setLayout(layout)
|
|
207
|
+
|
|
208
|
+
# Set minimum size - reduced from original
|
|
209
|
+
self.setMinimumWidth(175) # Reduced from 200
|
|
210
|
+
self.setMaximumWidth(175) # Reduced from 300
|
|
211
|
+
self.setMinimumHeight(80) # Reduced from 150
|
|
212
|
+
self.setMaximumHeight(120) # Reduced from 150
|
|
213
|
+
|
|
214
|
+
# Drag and drop functionality
|
|
215
|
+
def dragEnterEvent(self, event):
|
|
216
|
+
"""Handle drag enter event."""
|
|
217
|
+
print(f"[DEBUG] Drag enter: hasText={event.mimeData().hasText()}, hasJSON={event.mimeData().hasFormat('application/json')}")
|
|
218
|
+
if event.mimeData().hasText() or event.mimeData().hasFormat("application/json"):
|
|
219
|
+
event.acceptProposedAction()
|
|
220
|
+
|
|
221
|
+
def dragMoveEvent(self, event):
|
|
222
|
+
"""Handle drag move event."""
|
|
223
|
+
event.acceptProposedAction()
|
|
224
|
+
|
|
225
|
+
def dropEvent(self, event):
|
|
226
|
+
"""Handle drop event to add object."""
|
|
227
|
+
mime_data = event.mimeData()
|
|
228
|
+
json_data = None
|
|
229
|
+
image = None
|
|
230
|
+
|
|
231
|
+
print(f"[DEBUG] Drop received: hasText={event.mimeData().hasText()}, hasJSON={event.mimeData().hasFormat('application/json')}, hasImage={event.mimeData().hasImage()}")
|
|
232
|
+
|
|
233
|
+
# Try to get JSON data from different sources
|
|
234
|
+
if mime_data.hasFormat("application/json"):
|
|
235
|
+
try:
|
|
236
|
+
json_bytes = mime_data.data("application/json")
|
|
237
|
+
json_str = json_bytes.data().decode('utf-8')
|
|
238
|
+
print(f"[DEBUG] JSON data: {json_str}")
|
|
239
|
+
json_data = json.loads(json_str)
|
|
240
|
+
except Exception as e:
|
|
241
|
+
print(f"[DEBUG] Failed to parse JSON data: {e}")
|
|
242
|
+
elif mime_data.hasText():
|
|
243
|
+
try:
|
|
244
|
+
json_str = mime_data.text()
|
|
245
|
+
print(f"[DEBUG] Text data: {json_str}")
|
|
246
|
+
json_data = json.loads(json_str)
|
|
247
|
+
except Exception as e:
|
|
248
|
+
print(f"[DEBUG] Failed to parse text as JSON: {e}")
|
|
249
|
+
|
|
250
|
+
# Try to get image data if available
|
|
251
|
+
if mime_data.hasImage():
|
|
252
|
+
try:
|
|
253
|
+
image = QPixmap(mime_data.imageData())
|
|
254
|
+
print(f"[DEBUG] Image data received: {image.size()}")
|
|
255
|
+
except Exception as e:
|
|
256
|
+
print(f"[DEBUG] Failed to get image data: {e}")
|
|
257
|
+
|
|
258
|
+
if json_data:
|
|
259
|
+
# Handle both single object and array of objects
|
|
260
|
+
if isinstance(json_data, list):
|
|
261
|
+
# Array format: [{x, y, type, method, id, name}, ...]
|
|
262
|
+
print(f"[DEBUG] Received array of {len(json_data)} objects")
|
|
263
|
+
for obj in json_data:
|
|
264
|
+
self.add_object(obj, image)
|
|
265
|
+
event.acceptProposedAction()
|
|
266
|
+
print(f"[DEBUG] Array of objects added successfully")
|
|
267
|
+
elif isinstance(json_data, dict):
|
|
268
|
+
# Single object format: {x, y, type, method, id, name}
|
|
269
|
+
self.add_object(json_data, image)
|
|
270
|
+
event.acceptProposedAction()
|
|
271
|
+
print(f"[DEBUG] Single object added successfully: {json_data}")
|
|
272
|
+
else:
|
|
273
|
+
print(f"[DEBUG] Invalid JSON format: expected dict or list, got {type(json_data)}")
|
|
274
|
+
else:
|
|
275
|
+
print(f"[DEBUG] No valid JSON data found")
|
|
276
|
+
|
|
277
|
+
def add_object(self, obj_data: Dict[str, Any], image: Optional[QPixmap] = None):
|
|
278
|
+
"""Add an object to the list, rejecting duplicates with same x, y, type."""
|
|
279
|
+
# Extract required fields
|
|
280
|
+
obj_id = obj_data.get('id', 'unknown')
|
|
281
|
+
obj_type = obj_data.get('type', 'Unknown')
|
|
282
|
+
obj_name = obj_data.get('name', obj_id)
|
|
283
|
+
x = obj_data.get('x', 0)
|
|
284
|
+
y = obj_data.get('y', 0)
|
|
285
|
+
method = obj_data.get('method', '')
|
|
286
|
+
|
|
287
|
+
# Check for duplicates with same x, y, type
|
|
288
|
+
for existing_obj in self._objects:
|
|
289
|
+
existing_x = existing_obj.get('x', 0)
|
|
290
|
+
existing_y = existing_obj.get('y', 0)
|
|
291
|
+
existing_type = existing_obj.get('type', 'Unknown')
|
|
292
|
+
if existing_x == x and existing_y == y and existing_type == obj_type:
|
|
293
|
+
print(f"[DEBUG] Rejected duplicate object: {obj_type} at ({x}, {y})")
|
|
294
|
+
return # Reject duplicate
|
|
295
|
+
|
|
296
|
+
# Create custom widget for the object
|
|
297
|
+
item_widget = QPSACellObjectItem(
|
|
298
|
+
obj_id, obj_type, obj_name, x, y, method, image
|
|
299
|
+
)
|
|
300
|
+
item_widget.removeClicked.connect(self._on_remove_clicked)
|
|
301
|
+
|
|
302
|
+
# Create list widget item and set our custom widget
|
|
303
|
+
list_item = QListWidgetItem()
|
|
304
|
+
list_item.setSizeHint(item_widget.sizeHint())
|
|
305
|
+
|
|
306
|
+
self._objects_list.addItem(list_item)
|
|
307
|
+
self._objects_list.setItemWidget(list_item, item_widget)
|
|
308
|
+
|
|
309
|
+
# Add to internal list
|
|
310
|
+
self._objects.append(obj_data)
|
|
311
|
+
|
|
312
|
+
# Emit signals
|
|
313
|
+
self.objectAdded.emit(obj_data)
|
|
314
|
+
self.itemChanged.emit()
|
|
315
|
+
print(f"[DEBUG] Added object: {obj_type} at ({x}, {y})")
|
|
316
|
+
|
|
317
|
+
@pyqtSlot(str)
|
|
318
|
+
def _on_remove_clicked(self, obj_id: str):
|
|
319
|
+
"""Handle remove button click."""
|
|
320
|
+
# Find and remove the item from the list
|
|
321
|
+
for i in range(self._objects_list.count()):
|
|
322
|
+
list_item = self._objects_list.item(i)
|
|
323
|
+
widget = self._objects_list.itemWidget(list_item)
|
|
324
|
+
if isinstance(widget, QPSACellObjectItem) and widget.id == obj_id:
|
|
325
|
+
# Remove from internal list
|
|
326
|
+
for obj in self._objects:
|
|
327
|
+
if obj.get('id') == obj_id:
|
|
328
|
+
self._objects.remove(obj)
|
|
329
|
+
break
|
|
330
|
+
|
|
331
|
+
# Remove from UI
|
|
332
|
+
self._objects_list.takeItem(i)
|
|
333
|
+
|
|
334
|
+
# Emit signals
|
|
335
|
+
self.objectRemoved.emit(obj_id)
|
|
336
|
+
self.itemChanged.emit()
|
|
337
|
+
break
|
|
338
|
+
|
|
339
|
+
# Public properties
|
|
340
|
+
@property
|
|
341
|
+
def objects(self) -> List[Dict[str, Any]]:
|
|
342
|
+
"""Get all objects."""
|
|
343
|
+
return self._objects.copy()
|
|
344
|
+
|
|
345
|
+
def clear_objects(self):
|
|
346
|
+
"""Clear all objects."""
|
|
347
|
+
self._objects_list.clear()
|
|
348
|
+
self._objects.clear()
|
|
349
|
+
|
|
350
|
+
def remove_object(self, obj_id: str):
|
|
351
|
+
"""Remove an object by ID."""
|
|
352
|
+
self._on_remove_clicked(obj_id)
|
|
353
|
+
|
|
354
|
+
def set_list_background_color(self, color: str):
|
|
355
|
+
"""Set the background color of the internal list widget."""
|
|
356
|
+
# Use triple quotes for multi-line f-string
|
|
357
|
+
style_sheet = f"""
|
|
358
|
+
QListWidget {{
|
|
359
|
+
border: 1px solid #ccc;
|
|
360
|
+
border-radius: 4px;
|
|
361
|
+
background-color: {color};
|
|
362
|
+
}}
|
|
363
|
+
QListWidget::item {{
|
|
364
|
+
border-bottom: 1px solid #eee;
|
|
365
|
+
}}
|
|
366
|
+
QListWidget::item:selected {{
|
|
367
|
+
background-color: #e3f2fd;
|
|
368
|
+
}}
|
|
369
|
+
"""
|
|
370
|
+
self._objects_list.setStyleSheet(style_sheet)
|
|
371
|
+
|
|
372
|
+
def eventFilter(self, obj, event):
|
|
373
|
+
"""Handle mouse events for the list widget viewport."""
|
|
374
|
+
if obj == self._objects_list.viewport():
|
|
375
|
+
if event.type() == event.Type.MouseButtonPress:
|
|
376
|
+
if event.button() == Qt.MouseButton.LeftButton:
|
|
377
|
+
# Store drag start position and item
|
|
378
|
+
self._drag_start_pos = event.pos()
|
|
379
|
+
# Get the item under the mouse
|
|
380
|
+
item = self._objects_list.itemAt(event.pos())
|
|
381
|
+
self._dragging_item = item
|
|
382
|
+
elif event.type() == event.Type.MouseMove:
|
|
383
|
+
if event.buttons() & Qt.MouseButton.LeftButton:
|
|
384
|
+
if self._drag_start_pos is not None and self._dragging_item is not None:
|
|
385
|
+
# Calculate drag distance
|
|
386
|
+
drag_distance = (event.pos() - self._drag_start_pos).manhattanLength()
|
|
387
|
+
if drag_distance > 20: # Minimum drag distance
|
|
388
|
+
widget = self._objects_list.itemWidget(self._dragging_item)
|
|
389
|
+
if isinstance(widget, QPSACellObjectItem):
|
|
390
|
+
# Find the corresponding object data
|
|
391
|
+
for obj_data in self._objects:
|
|
392
|
+
if obj_data.get('id') == widget.id:
|
|
393
|
+
self._initiate_drag(obj_data, widget.image)
|
|
394
|
+
# Reset drag state
|
|
395
|
+
self._drag_start_pos = None
|
|
396
|
+
self._dragging_item = None
|
|
397
|
+
break
|
|
398
|
+
elif event.type() == event.Type.MouseButtonRelease:
|
|
399
|
+
# Reset drag state
|
|
400
|
+
self._drag_start_pos = None
|
|
401
|
+
self._dragging_item = None
|
|
402
|
+
return False # Let the event propagate
|
|
403
|
+
|
|
404
|
+
def _initiate_drag(self, obj_data: Dict[str, Any], image: Optional[QPixmap]):
|
|
405
|
+
"""Initiate drag operation with object data and image."""
|
|
406
|
+
print(f"[DEBUG] Initiating drag for object: {obj_data.get('type')} at ({obj_data.get('x')}, {obj_data.get('y')})")
|
|
407
|
+
|
|
408
|
+
# Create JSON data with required fields
|
|
409
|
+
drag_json = {
|
|
410
|
+
'x': obj_data.get('x', 0),
|
|
411
|
+
'y': obj_data.get('y', 0),
|
|
412
|
+
'id': obj_data.get('id', 'unknown'),
|
|
413
|
+
'name': obj_data.get('name', obj_data.get('id', 'unknown')),
|
|
414
|
+
'method': obj_data.get('method', ''),
|
|
415
|
+
'type': obj_data.get('type', 'Unknown')
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
# Convert to JSON string
|
|
419
|
+
json_str = json.dumps(drag_json, ensure_ascii=False)
|
|
420
|
+
print(f"[DEBUG] Drag JSON: {json_str}")
|
|
421
|
+
|
|
422
|
+
# Create MIME data
|
|
423
|
+
mime_data = QMimeData()
|
|
424
|
+
mime_data.setText(json_str)
|
|
425
|
+
mime_data.setData('application/json', json_str.encode('utf-8'))
|
|
426
|
+
|
|
427
|
+
# Add image if available
|
|
428
|
+
if image and not image.isNull():
|
|
429
|
+
mime_data.setImageData(image)
|
|
430
|
+
print(f"[DEBUG] Adding image to drag: {image.size()}")
|
|
431
|
+
|
|
432
|
+
# Create drag object
|
|
433
|
+
drag = QDrag(self)
|
|
434
|
+
drag.setMimeData(mime_data)
|
|
435
|
+
|
|
436
|
+
# Set a simple drag cursor
|
|
437
|
+
# You could set a custom pixmap here if needed
|
|
438
|
+
|
|
439
|
+
# Start drag operation
|
|
440
|
+
drag.exec(Qt.DropAction.CopyAction)
|
|
441
|
+
print(f"[DEBUG] Drag completed")
|