boris-behav-obs 9.2.3__py2.py3-none-any.whl → 9.3__py2.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.
@@ -20,9 +20,14 @@ This file is part of BORIS.
20
20
 
21
21
  """
22
22
 
23
+ # to run this file, use
24
+ # python -m boris.modifier_coding_map_creator
25
+
23
26
  import binascii
27
+ import io
24
28
  import json
25
29
  import os
30
+ import re
26
31
 
27
32
  from PySide6.QtCore import (
28
33
  Qt,
@@ -34,7 +39,6 @@ from PySide6.QtCore import (
34
39
  QLineF,
35
40
  )
36
41
  from PySide6.QtGui import QColor, QBrush, QMouseEvent, QPixmap, QIcon, QPen, QPolygon, QPolygonF, QAction
37
-
38
42
  from PySide6.QtWidgets import (
39
43
  QGraphicsPolygonItem,
40
44
  QGraphicsEllipseItem,
@@ -55,6 +59,11 @@ from PySide6.QtWidgets import (
55
59
  QInputDialog,
56
60
  QFileDialog,
57
61
  QApplication,
62
+ QListWidget,
63
+ QSplitter,
64
+ QSpacerItem,
65
+ QSizePolicy,
66
+ QFrame,
58
67
  )
59
68
 
60
69
  from . import config as cfg
@@ -62,7 +71,7 @@ from . import dialog
62
71
  from . import utilities as util
63
72
 
64
73
  designColor = QColor(255, 0, 0, 128) # red opacity: 50%
65
- penWidth = 0
74
+ penWidth: int = 0
66
75
  penStyle = Qt.NoPen
67
76
  selectedBrush = QBrush()
68
77
  selectedBrush.setStyle(Qt.SolidPattern)
@@ -82,8 +91,9 @@ class ModifiersMapCreatorWindow(QMainWindow):
82
91
  def mousePressEvent(self, event):
83
92
  self.mousePress.emit(event)
84
93
 
85
- _start = 0
86
- elList, points = [], []
94
+ _start: int = 0
95
+ elList: list = []
96
+ points: list = []
87
97
 
88
98
  def __init__(self, parent):
89
99
  QGraphicsView.__init__(self, parent)
@@ -91,9 +101,13 @@ class ModifiersMapCreatorWindow(QMainWindow):
91
101
  self.setScene(QGraphicsScene(self))
92
102
  self.scene().update()
93
103
 
94
- bitmapFileName, mapName, fileName = "", "", ""
95
- flagNewArea, flagMapChanged = False, False
96
- areasList, polygonsList2 = {}, {}
104
+ bitmapFileName: str = ""
105
+ mapName: str = ""
106
+ fileName: str = ""
107
+ flagNewArea: bool = False
108
+ flag_map_changed: bool = False
109
+ areasList: dict = {}
110
+ polygonsList2: dict = {}
97
111
  areaColor = QColor("lime")
98
112
 
99
113
  def __init__(self):
@@ -147,91 +161,153 @@ class ModifiersMapCreatorWindow(QMainWindow):
147
161
  fileMenu.addSeparator()
148
162
  fileMenu.addAction(self.exitAction)
149
163
 
164
+ splitter1 = QSplitter(Qt.Vertical)
165
+
150
166
  self.view = self.View(self)
151
167
  self.view.mousePress.connect(self.viewMousePressEvent)
168
+ splitter1.addWidget(self.view)
152
169
 
153
- self.btLoad = QPushButton("Load bitmap", self)
154
- self.btLoad.clicked.connect(self.loadBitmap)
155
- self.btLoad.setVisible(False)
170
+ vlayout_list = QVBoxLayout()
171
+ vlayout_list.addWidget(QLabel("List of modifiers"))
172
+
173
+ self.area_list = QListWidget(self)
174
+ self.area_list.itemClicked.connect(self.area_list_item_click)
175
+ vlayout_list.addWidget(self.area_list)
176
+ w = QWidget()
177
+ w.setLayout(vlayout_list)
178
+ splitter1.addWidget(w)
179
+ splitter1.setSizes([300, 100])
180
+ splitter1.setStretchFactor(2, 8)
181
+
182
+ hlayout_cmd = QHBoxLayout()
156
183
 
157
184
  self.btNewArea = QPushButton("New modifier", self)
158
185
  self.btNewArea.clicked.connect(self.newArea)
159
186
  self.btNewArea.setVisible(False)
187
+ hlayout_cmd.addWidget(self.btNewArea)
188
+
189
+ self.btSaveArea = QPushButton("Save modifier", self)
190
+ self.btSaveArea.clicked.connect(self.saveArea)
191
+ self.btSaveArea.setVisible(False)
192
+ hlayout_cmd.addWidget(self.btSaveArea)
160
193
 
161
- self.hlayout = QHBoxLayout()
194
+ self.btCancelAreaCreation = QPushButton("Cancel new modifier", self)
195
+ self.btCancelAreaCreation.clicked.connect(self.cancelAreaCreation)
196
+ self.btCancelAreaCreation.setVisible(False)
197
+ hlayout_cmd.addWidget(self.btCancelAreaCreation)
198
+
199
+ hlayout_cmd.addItem(QSpacerItem(40, 20, QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Minimum))
200
+
201
+ self.btDeleteArea = QPushButton("Delete selected modifier", self)
202
+ self.btDeleteArea.clicked.connect(self.deleteArea)
203
+ self.btDeleteArea.setVisible(False)
204
+ self.btDeleteArea.setEnabled(False)
205
+ hlayout_cmd.addWidget(self.btDeleteArea)
206
+
207
+ hlayout_area = QHBoxLayout()
162
208
 
163
209
  self.lb = QLabel("Modifier")
164
210
  self.lb.setVisible(False)
165
- self.hlayout.addWidget(self.lb)
211
+ hlayout_area.addWidget(self.lb)
166
212
 
167
213
  self.leAreaCode = QLineEdit(self)
168
214
  self.leAreaCode.setVisible(False)
169
- self.hlayout.addWidget(self.leAreaCode)
215
+ hlayout_area.addWidget(self.leAreaCode)
170
216
 
171
217
  self.btColor = QPushButton()
172
218
  self.btColor.clicked.connect(self.chooseColor)
173
219
  self.btColor.setVisible(False)
174
- self.btColor.setStyleSheet("QWidget {{background-color:{}}}".format(self.areaColor.name()))
175
- self.hlayout.addWidget(self.btColor)
220
+ self.btColor.setStyleSheet(f"QWidget {{ background-color:{self.areaColor.name()} }}")
221
+ hlayout_area.addWidget(self.btColor)
176
222
 
177
223
  self.slAlpha = QSlider(Qt.Horizontal)
178
224
  self.slAlpha.setRange(20, 100)
179
225
  self.slAlpha.setValue(50)
180
226
  self.slAlpha.valueChanged.connect(self.slAlpha_changed)
181
227
  self.slAlpha.setVisible(False)
182
- self.hlayout.addWidget(self.slAlpha)
228
+ hlayout_area.addWidget(self.slAlpha)
183
229
 
184
230
  self.slAlpha_changed(50)
185
- """
186
- self.btCancelMap = QPushButton("Cancel modifiers map", self)
187
- self.btCancelMap.clicked.connect(self.cancelMap)
188
- self.btCancelMap.setVisible(False)
189
- """
190
231
 
191
- layout = QVBoxLayout()
192
- layout.addWidget(self.view)
193
- layout.addWidget(self.btLoad)
232
+ vlayout_frame = QVBoxLayout()
233
+ vlayout_frame.addLayout(hlayout_cmd)
234
+ vlayout_frame.addLayout(hlayout_area)
235
+ vlayout_frame.addItem(QSpacerItem(20, 40, QSizePolicy.Minimum, QSizePolicy.Expanding))
194
236
 
195
- hlayout2 = QHBoxLayout()
196
- hlayout2.addWidget(self.btNewArea)
197
-
198
- self.btSaveArea = QPushButton("Save modifier", self)
199
- self.btSaveArea.clicked.connect(self.saveArea)
200
- self.btSaveArea.setVisible(False)
201
- hlayout2.addWidget(self.btSaveArea)
237
+ frame = QFrame()
238
+ frame.setFrameStyle(QFrame.Panel | QFrame.Plain)
239
+ frame.setMinimumHeight(120)
240
+ frame.setMaximumHeight(120)
202
241
 
203
- self.btCancelAreaCreation = QPushButton("Cancel new modifier", self)
204
- self.btCancelAreaCreation.clicked.connect(self.cancelAreaCreation)
205
- self.btCancelAreaCreation.setVisible(False)
206
- hlayout2.addWidget(self.btCancelAreaCreation)
242
+ frame.setLayout(vlayout_frame)
207
243
 
208
- self.btDeleteArea = QPushButton("Delete selected modifier", self)
209
- self.btDeleteArea.clicked.connect(self.deleteArea)
210
- self.btDeleteArea.setVisible(True)
211
- self.btDeleteArea.setEnabled(False)
212
- hlayout2.addWidget(self.btDeleteArea)
244
+ vlayout = QVBoxLayout()
213
245
 
214
- layout.addLayout(hlayout2)
215
- layout.addLayout(self.hlayout)
216
- """layout.addWidget(self.btCancelMap)"""
246
+ vlayout.addWidget(splitter1)
247
+ vlayout.addWidget(frame)
217
248
 
218
249
  main_widget = QWidget(self)
219
- main_widget.setLayout(layout)
250
+ main_widget.setLayout(vlayout)
220
251
  self.setCentralWidget(main_widget)
221
252
 
222
253
  self.statusBar().showMessage("")
223
254
 
255
+ def area_list_item_click(self, item):
256
+ """
257
+ select the polygon corresponding to the clicked area
258
+ """
259
+
260
+ if self.selectedPolygon:
261
+ self.selectedPolygon.setPen(QPen(designColor, penWidth, penStyle, Qt.RoundCap, Qt.RoundJoin))
262
+ self.selectedPolygon = None
263
+ self.selectedPolygonMemBrush = None
264
+
265
+ modifier_name = item.text()
266
+
267
+ self.selectedPolygon = self.polygonsList2[item.text()]
268
+
269
+ self.selectedPolygonMemBrush = self.selectedPolygon.brush()
270
+
271
+ self.selectedPolygonAreaCode = modifier_name
272
+
273
+ self.selectedPolygon.setPen(QPen(QColor(255, 0, 0, 255), 2, Qt.SolidLine, Qt.RoundCap, Qt.RoundJoin))
274
+
275
+ self.leAreaCode.setText(modifier_name)
276
+ for widget in (self.lb, self.leAreaCode, self.btSaveArea, self.btDeleteArea, self.btColor, self.slAlpha):
277
+ widget.setVisible(True)
278
+
279
+ self.btSaveArea.setText("Update modifier")
280
+ self.btDeleteArea.setEnabled(True)
281
+
282
+ self.areaColor = self.selectedPolygon.brush().color()
283
+ self.btColor.setStyleSheet(f"QWidget {{background-color:{self.selectedPolygon.brush().color().name()}}}")
284
+
285
+ self.slAlpha.setValue(int(self.areaColor.alpha() / 255 * 100))
286
+
287
+ def get_current_alpha_value(self) -> str | None:
288
+ """
289
+ returns current alpha value (from button text)
290
+ """
291
+ match = re.search(r"(\d+)", self.btColor.text())
292
+
293
+ if match:
294
+ opacity_value = int(match.group(1)) # Convert the extracted string to an integer
295
+ return opacity_value
296
+ else:
297
+ return None
298
+
224
299
  def slAlpha_changed(self, val):
225
300
  """
226
301
  opacity slider value changed
227
302
  """
228
303
 
229
- self.btColor.setText("Opacity: {} %".format(val))
304
+ self.btColor.setText(f"Opacity: {val}")
230
305
  self.areaColor.setAlpha(int(val / 100 * 255))
231
306
 
232
307
  if self.selectedPolygon:
233
308
  self.selectedPolygon.setBrush(self.areaColor)
234
- self.areasList[self.leAreaCode.text()]["color"] = self.areaColor.rgba()
309
+
310
+ # self.areasList[self.leAreaCode.text()]["color"] = self.areaColor.rgba()
235
311
 
236
312
  if self.closedPolygon:
237
313
  self.closedPolygon.setBrush(self.areaColor)
@@ -244,30 +320,32 @@ class ModifiersMapCreatorWindow(QMainWindow):
244
320
  cd.setWindowFlags(Qt.WindowStaysOnTopHint)
245
321
  cd.setOptions(QColorDialog.ShowAlphaChannel | QColorDialog.DontUseNativeDialog)
246
322
 
247
- if cd.exec_():
323
+ if cd.exec():
248
324
  self.areaColor = cd.currentColor()
325
+ self.areaColor.setAlpha(int(self.get_current_alpha_value() / 100 * 255))
326
+
249
327
  self.btColor.setStyleSheet(f"QWidget {{background-color:{self.areaColor.name()}}}")
250
328
 
251
- if self.selectedPolygon:
252
- self.selectedPolygon.setBrush(self.areaColor)
253
- self.areasList[self.leAreaCode.text()]["color"] = self.areaColor.rgba()
329
+ if self.selectedPolygon:
330
+ self.selectedPolygon.setBrush(self.areaColor)
331
+ self.areasList[self.leAreaCode.text()]["color"] = self.areaColor.rgba()
254
332
 
255
- if self.closedPolygon:
256
- self.closedPolygon.setBrush(self.areaColor)
333
+ if self.closedPolygon:
334
+ self.closedPolygon.setBrush(self.areaColor)
257
335
 
258
336
  def closeEvent(self, event):
259
- if self.flagMapChanged:
337
+ if self.flag_map_changed:
260
338
  response = dialog.MessageDialog(
261
339
  "BORIS - Modifiers map creator",
262
340
  "What to do about the current unsaved modifiers coding map?",
263
- ["Save", "Discard", "Cancel"],
341
+ [cfg.SAVE, cfg.DISCARD, cfg.CANCEL],
264
342
  )
265
343
 
266
- if response == "Save":
344
+ if response == cfg.SAVE:
267
345
  if not self.saveMap_clicked():
268
346
  event.ignore()
269
347
 
270
- if response == "Cancel":
348
+ if response == cfg.CANCEL:
271
349
  event.ignore()
272
350
  return
273
351
 
@@ -279,12 +357,52 @@ class ModifiersMapCreatorWindow(QMainWindow):
279
357
  check if area selected with mouse
280
358
  """
281
359
 
360
+ def new_polygon():
361
+ """
362
+ create a newpolygon
363
+ """
364
+ newPolygon = QPolygonF()
365
+ for p in self.view.points:
366
+ newPolygon.append(QPoint(p[0], p[1]))
367
+
368
+ # draw polygon a red polygon
369
+ self.closedPolygon = QGraphicsPolygonItem(newPolygon)
370
+ self.closedPolygon.setPen(QPen(designColor, penWidth, penStyle, Qt.RoundCap, Qt.RoundJoin))
371
+ self.closedPolygon.setBrush(self.areaColor)
372
+ self.view.scene().addItem(self.closedPolygon)
373
+
374
+ def add_last_line():
375
+ """
376
+ add last line to start point
377
+ """
378
+ line = QGraphicsLineItem(
379
+ QLineF(
380
+ self.view._start,
381
+ QPoint(self.view.points[0][0], self.view.points[0][1]),
382
+ )
383
+ )
384
+ line.setPen(
385
+ QPen(
386
+ designColor,
387
+ penWidth,
388
+ Qt.SolidLine,
389
+ Qt.RoundCap,
390
+ Qt.RoundJoin,
391
+ )
392
+ )
393
+
394
+ self.view.scene().addItem(line)
395
+ self.view.elList.append(line)
396
+
397
+ self.statusBar().showMessage("Area completed")
398
+
282
399
  if not self.bitmapFileName:
283
400
  return
284
401
 
285
402
  self.btDeleteArea.setEnabled(False)
286
403
 
287
- test = self.view.mapToScene(event.pos()).toPoint()
404
+ # test = self.view.mapToScene(event.pos()).toPoint()
405
+ test = self.view.mapToScene(event.position().toPoint()).toPoint()
288
406
 
289
407
  if test.x() < 0 or test.y() < 0 or test.x() > self.pixmap.size().width() or test.y() > self.pixmap.size().height():
290
408
  return
@@ -298,10 +416,12 @@ class ModifiersMapCreatorWindow(QMainWindow):
298
416
  self.selectedPolygon = None
299
417
  self.selectedPolygonMemBrush = None
300
418
 
301
- for areaCode in self.polygonsList2:
419
+ for idx, areaCode in enumerate(sorted(self.polygonsList2.keys())):
420
+ for widget in (self.lb, self.leAreaCode, self.btSaveArea, self.btDeleteArea, self.btColor, self.slAlpha):
421
+ widget.setVisible(False)
422
+
302
423
  if self.polygonsList2[areaCode].contains(test):
303
- if txt:
304
- txt += ","
424
+ txt += "," if txt else ""
305
425
 
306
426
  txt += areaCode
307
427
  self.selectedPolygon = self.polygonsList2[areaCode]
@@ -320,20 +440,24 @@ class ModifiersMapCreatorWindow(QMainWindow):
320
440
  )
321
441
 
322
442
  self.leAreaCode.setText(areaCode)
323
- self.leAreaCode.setVisible(True)
324
443
  self.btDeleteArea.setEnabled(True)
325
444
 
445
+ # select area in list widget
446
+ item = self.area_list.item(idx)
447
+ self.area_list.setCurrentItem(item)
448
+
449
+ for widget in (self.lb, self.leAreaCode, self.btSaveArea, self.btDeleteArea, self.btColor, self.slAlpha):
450
+ widget.setVisible(True)
451
+
326
452
  self.areaColor = self.selectedPolygon.brush().color()
327
- self.btColor.setStyleSheet("QWidget {{background-color:{}}}".format(self.selectedPolygon.brush().color().name()))
328
- self.btColor.setVisible(True)
453
+ self.btColor.setStyleSheet(f"QWidget {{background-color:{self.selectedPolygon.brush().color().name()} }}")
329
454
 
330
- self.slAlpha.setValue(int(self.selectedPolygon.brush().color().alpha() / 255 * 100))
331
- self.slAlpha.setVisible(True)
455
+ self.slAlpha.setValue(int(self.areaColor.alpha() / 255 * 100))
332
456
 
333
457
  break
334
458
 
335
459
  if txt:
336
- self.statusBar().showMessage("Modifier{}: {}".format("s" if "," in txt else "", txt))
460
+ self.statusBar().showMessage(f"Modifier{'s' if ',' in txt else ''}: {txt}")
337
461
  else:
338
462
  self.statusBar().showMessage("")
339
463
 
@@ -343,7 +467,7 @@ class ModifiersMapCreatorWindow(QMainWindow):
343
467
  self.slAlpha.setVisible(False)
344
468
  return
345
469
 
346
- # delete last line item
470
+ # right button: delete last line item
347
471
  if (event.buttons() & Qt.RightButton) and not self.closedPolygon:
348
472
  if self.view.points:
349
473
  self.view.points = self.view.points[0:-1]
@@ -358,6 +482,11 @@ class ModifiersMapCreatorWindow(QMainWindow):
358
482
  self.view.scene().removeItem(self.view.elList[-1])
359
483
  self.view.elList = self.view.elList[0:-1]
360
484
 
485
+ # middle button automatically close the polygon
486
+ if (event.buttons() & Qt.MiddleButton) and not self.closedPolygon:
487
+ add_last_line()
488
+ new_polygon()
489
+
361
490
  # add line item
362
491
  if event.buttons() == Qt.LeftButton and not self.closedPolygon:
363
492
  if self.view._start:
@@ -377,40 +506,8 @@ class ModifiersMapCreatorWindow(QMainWindow):
377
506
 
378
507
  # test if polygon closed (dist min 10 px)
379
508
  if abs(end.x() - self.view.points[0][0]) < 10 and abs(end.y() - self.view.points[0][1]) < 10:
380
- line = QGraphicsLineItem(
381
- QLineF(
382
- self.view._start,
383
- QPoint(self.view.points[0][0], self.view.points[0][1]),
384
- )
385
- )
386
- line.setPen(
387
- QPen(
388
- designColor,
389
- penWidth,
390
- Qt.SolidLine,
391
- Qt.RoundCap,
392
- Qt.RoundJoin,
393
- )
394
- )
395
-
396
- self.view.scene().addItem(line)
397
- self.view.elList.append(line)
398
-
399
- self.statusBar().showMessage("Area completed")
400
-
401
- # create polygon
402
- newPolygon = QPolygonF()
403
- for p in self.view.points:
404
- newPolygon.append(QPoint(p[0], p[1]))
405
-
406
- # draw polygon a red polygon
407
- self.closedPolygon = QGraphicsPolygonItem(newPolygon)
408
-
409
- self.closedPolygon.setPen(QPen(designColor, penWidth, penStyle, Qt.RoundCap, Qt.RoundJoin))
410
-
411
- self.closedPolygon.setBrush(self.areaColor)
412
-
413
- self.view.scene().addItem(self.closedPolygon)
509
+ add_last_line()
510
+ new_polygon()
414
511
 
415
512
  return
416
513
 
@@ -454,14 +551,14 @@ class ModifiersMapCreatorWindow(QMainWindow):
454
551
  )
455
552
  if ok:
456
553
  self.mapName = text
457
- self.setWindowTitle("{} - Modifiers map creator tool - {}".format(cfg.programName, self.mapName))
554
+ self.setWindowTitle(f"{cfg.programName} - Modifiers map creator tool - {self.mapName}")
458
555
 
459
556
  def newMap(self):
460
557
  """
461
558
  create a new map
462
559
  """
463
560
 
464
- if self.flagMapChanged:
561
+ if self.flag_map_changed:
465
562
  response = dialog.MessageDialog(
466
563
  cfg.programName + " - Modifiers map creator",
467
564
  "What to do about the current unsaved coding map?",
@@ -493,28 +590,25 @@ class ModifiersMapCreatorWindow(QMainWindow):
493
590
 
494
591
  self.setWindowTitle(cfg.programName + " - Map creator tool - " + self.mapName)
495
592
 
496
- self.btLoad.setVisible(True)
497
- """self.btCancelMap.setVisible(True)"""
498
-
499
- self.statusBar().showMessage('Click "Load bitmap" button to select and load a bitmap into the viewer')
593
+ self.loadBitmap()
500
594
 
501
595
  def openMap(self):
502
596
  """
503
597
  load bitmap from data
504
598
  show it in view scene
505
599
  """
506
- if self.flagMapChanged:
600
+ if self.flag_map_changed:
507
601
  response = dialog.MessageDialog(
508
602
  cfg.programName + " - Map creator",
509
603
  "What to do about the current unsaved coding map?",
510
- ["Save", "Discard", "Cancel"],
604
+ (cfg.SAVE, cfg.DISCARD, cfg.CANCEL),
511
605
  )
512
606
 
513
- if response == "Save":
607
+ if response == cfg.SAVE:
514
608
  if not self.saveMap_clicked():
515
609
  return
516
610
 
517
- if response == "Cancel":
611
+ if response == cfg.CANCEL:
518
612
  return
519
613
 
520
614
  fileName, _ = QFileDialog().getOpenFileName(
@@ -525,14 +619,14 @@ class ModifiersMapCreatorWindow(QMainWindow):
525
619
  )
526
620
 
527
621
  if not fileName:
528
- self.statusBar().showMessage("No file", 5000)
622
+ return
529
623
  try:
530
624
  self.codingMap = json.loads(open(fileName, "r").read())
531
625
  except Exception:
532
626
  QMessageBox.critical(
533
627
  self,
534
628
  cfg.programName,
535
- "The file {} seems not a behaviors coding map...".format(fileName),
629
+ f"The file {fileName} seems not a behaviors coding map...",
536
630
  )
537
631
  return
538
632
 
@@ -547,6 +641,7 @@ class ModifiersMapCreatorWindow(QMainWindow):
547
641
  self.fileName = fileName
548
642
 
549
643
  self.areasList = self.codingMap["areas"] # dictionary of dictionaries
644
+
550
645
  bitmapContent = binascii.a2b_base64(self.codingMap["bitmap"])
551
646
 
552
647
  self.pixmap.loadFromData(bitmapContent)
@@ -582,44 +677,45 @@ class ModifiersMapCreatorWindow(QMainWindow):
582
677
 
583
678
  self.btNewArea.setVisible(True)
584
679
 
585
- self.btLoad.setVisible(False)
586
-
587
680
  self.saveMapAction.setEnabled(True)
588
681
  self.saveAsMapAction.setEnabled(True)
589
682
  self.mapNameAction.setEnabled(True)
590
- self.statusBar().showMessage('Click "New area" to create a new area')
591
683
 
592
- def saveMap(self):
593
- if self.fileName:
594
- # create dict with map name key
595
- mapDict = {"name": self.mapName}
684
+ self.update_area_list()
596
685
 
597
- # add areas
598
- mapDict["areas"] = self.areasList
686
+ self.statusBar().showMessage('Click the "New modifier" button to create a new modifier')
599
687
 
600
- import io
688
+ def saveMap(self) -> bool:
689
+ """
690
+ save the modifier coding map on file
691
+ """
692
+ if not self.fileName:
693
+ return False
694
+ # create dict with map name key
695
+ mapDict = {"name": self.mapName}
601
696
 
602
- # Save QPixmap to QByteArray via QBuffer.
603
- byte_array = QByteArray()
604
- buffer = QBuffer(byte_array)
605
- buffer.open(QIODevice.WriteOnly)
606
- self.pixmap.save(buffer, "PNG")
697
+ # add areas
698
+ mapDict["areas"] = self.areasList
607
699
 
608
- string_io = io.BytesIO(byte_array)
700
+ # Save QPixmap to QByteArray via QBuffer.
701
+ byte_array = QByteArray()
702
+ buffer = QBuffer(byte_array)
703
+ buffer.open(QIODevice.WriteOnly)
704
+ self.pixmap.save(buffer, "PNG")
609
705
 
610
- string_io.seek(0)
706
+ string_io = io.BytesIO(byte_array)
611
707
 
612
- # add bitmap
613
- mapDict["bitmap"] = binascii.b2a_base64(string_io.read()).decode("utf-8")
708
+ string_io.seek(0)
614
709
 
615
- with open(self.fileName, "w") as outfile:
616
- outfile.write(json.dumps(mapDict))
710
+ # add bitmap
711
+ mapDict["bitmap"] = binascii.b2a_base64(string_io.read()).decode("utf-8")
617
712
 
618
- self.flagMapChanged = False
713
+ with open(self.fileName, "w") as outfile:
714
+ outfile.write(json.dumps(mapDict))
619
715
 
620
- return True
621
- else:
622
- return False
716
+ self.flag_map_changed = False
717
+
718
+ return True
623
719
 
624
720
  def saveAsMap_clicked(self):
625
721
  filters = "Modifiers map (*.boris_map);;All files (*)"
@@ -657,8 +753,11 @@ class ModifiersMapCreatorWindow(QMainWindow):
657
753
  return False
658
754
 
659
755
  def newArea(self):
756
+ """
757
+ create a new area
758
+ """
660
759
  if not self.bitmapFileName:
661
- QMessageBox.critical(self, cfg.programName, "A bitmap must be loaded before to define areas")
760
+ QMessageBox.critical(self, cfg.programName, "An image must be loaded before to define areas")
662
761
  return
663
762
 
664
763
  if self.selectedPolygon:
@@ -666,90 +765,113 @@ class ModifiersMapCreatorWindow(QMainWindow):
666
765
  self.selectedPolygon = None
667
766
 
668
767
  self.flagNewArea = True
669
- self.btSaveArea.setVisible(True)
670
- self.btCancelAreaCreation.setVisible(True)
671
- self.btNewArea.setVisible(False)
672
- self.lb.setVisible(True)
673
768
  self.leAreaCode.clear()
674
- self.leAreaCode.setVisible(True)
675
- self.btColor.setVisible(True)
676
- self.slAlpha.setVisible(True)
769
+
770
+ for widget in (
771
+ self.btSaveArea,
772
+ self.btCancelAreaCreation,
773
+ self.lb,
774
+ self.leAreaCode,
775
+ self.btColor,
776
+ self.slAlpha,
777
+ ):
778
+ widget.setVisible(True)
779
+
780
+ self.btSaveArea.setText("Save modifier")
781
+
782
+ self.btNewArea.setVisible(False)
677
783
  self.btDeleteArea.setVisible(False)
678
784
 
679
785
  self.statusBar().showMessage(
680
- "Select the vertices of the area for this modifier with the mouse (right click will cancel the last point)"
786
+ "Draw a polygon by clicking the vertices (right-click will delete the last point, middle-click will close the polygon)"
681
787
  )
682
788
 
683
789
  def saveArea(self):
684
- if not self.closedPolygon:
685
- QMessageBox.critical(
686
- self,
687
- cfg.programName,
688
- "You must close your area before saving it.\nThe last vertex must correspond to the first one.",
689
- )
690
-
691
- if len(self.view.points) < 3:
692
- QMessageBox.critical(self, cfg.programName, "You must define a closed area")
693
- return
694
-
695
- # check if no area code
696
- if not self.leAreaCode.text():
697
- QMessageBox.critical(self, cfg.programName, "You must define a code for the new modifier")
698
- return
790
+ """
791
+ save the new modifier in the list
792
+ """
699
793
 
700
794
  # check if not allowed character
701
- for c in "|,()":
795
+ for c in cfg.CHAR_FORBIDDEN_IN_MODIFIERS:
702
796
  if c in self.leAreaCode.text():
703
797
  QMessageBox.critical(
704
798
  self,
705
799
  cfg.programName,
706
- "The modifier contains a character that is not allowed <b>()|,</b>.",
800
+ f"The modifier contains a character that is not allowed <b>{cfg.CHAR_FORBIDDEN_IN_MODIFIERS}</b>.",
707
801
  )
708
802
  return
709
803
 
710
- # check if area code already used
804
+ # check if modification
805
+ if "save" in self.btSaveArea.text().lower():
806
+ if not self.closedPolygon:
807
+ QMessageBox.critical(
808
+ self,
809
+ cfg.programName,
810
+ "You must close your area before saving it.\nThe last vertex must correspond to the first one.",
811
+ )
711
812
 
712
- if self.leAreaCode.text() in self.areasList:
713
- QMessageBox.critical(self, cfg.programName, "The modifier is already in use")
714
- return
813
+ if len(self.view.points) < 3:
814
+ QMessageBox.critical(self, cfg.programName, "You must define a closed area")
815
+ return
715
816
 
716
- # create polygon
717
- newPolygon = QPolygon()
718
- for p in self.view.points:
719
- newPolygon.append(QPoint(p[0], p[1]))
817
+ # check if no modifier name
818
+ if not self.leAreaCode.text():
819
+ QMessageBox.critical(self, cfg.programName, "You must define a code for the new modifier")
820
+ return
720
821
 
721
- self.areasList[self.leAreaCode.text()] = {
722
- "geometry": self.view.points,
723
- "color": self.areaColor.rgba(),
724
- }
822
+ # check if modifier name already used
823
+ if self.leAreaCode.text() in self.areasList:
824
+ QMessageBox.critical(self, cfg.programName, "The modifier name is already in use")
825
+ return
725
826
 
726
- # remove all lines
727
- for line in self.view.elList:
728
- self.view.scene().removeItem(line)
827
+ # create polygon
828
+ newPolygon = QPolygon()
829
+ for p in self.view.points:
830
+ newPolygon.append(QPoint(p[0], p[1]))
729
831
 
730
- # draw polygon
731
- self.closedPolygon.setBrush(QBrush(self.areaColor, Qt.SolidPattern))
732
- self.polygonsList2[self.leAreaCode.text()] = self.closedPolygon
733
- self.closedPolygon = None
734
- self.view._start = 0
735
- self.view.points = []
736
- self.view.elList = []
737
- self.flagNewArea = False
738
- self.closedPolygon = None
832
+ self.areasList[self.leAreaCode.text()] = {
833
+ "geometry": self.view.points,
834
+ "color": self.areaColor.rgba(),
835
+ }
739
836
 
740
- self.btSaveArea.setVisible(False)
741
- self.btCancelAreaCreation.setVisible(False)
742
- self.lb.setVisible(False)
743
- self.leAreaCode.setVisible(False)
744
- self.btColor.setVisible(False)
745
- self.slAlpha.setVisible(False)
746
- self.btDeleteArea.setVisible(True)
747
- self.btNewArea.setVisible(True)
837
+ # remove all lines
838
+ for line in self.view.elList:
839
+ self.view.scene().removeItem(line)
748
840
 
749
- self.leAreaCode.setText("")
841
+ # draw polygon
842
+ self.closedPolygon.setBrush(QBrush(self.areaColor, Qt.SolidPattern))
843
+ self.polygonsList2[self.leAreaCode.text()] = self.closedPolygon
844
+ self.closedPolygon = None
845
+ self.view._start = 0
846
+ self.view.points = []
847
+ self.view.elList = []
848
+ self.flagNewArea = False
849
+
850
+ self.statusBar().showMessage("New modifier saved", 5000)
851
+
852
+ else: # modification
853
+ if self.leAreaCode.text() not in self.polygonsList2:
854
+ self.polygonsList2[self.leAreaCode.text()] = self.polygonsList2.pop(self.area_list.currentItem().text())
855
+ self.polygonsList2[self.leAreaCode.text()].setBrush(self.areaColor)
856
+
857
+ self.statusBar().showMessage("Modifier modified", 5000)
858
+
859
+ for widget in (
860
+ self.btSaveArea,
861
+ self.btCancelAreaCreation,
862
+ self.lb,
863
+ self.leAreaCode,
864
+ self.btColor,
865
+ self.slAlpha,
866
+ self.btDeleteArea,
867
+ self.btNewArea,
868
+ ):
869
+ widget.setVisible(False)
750
870
 
751
- self.flagMapChanged = True
752
- self.statusBar().showMessage("New modifier saved", 5000)
871
+ self.btNewArea.setVisible(True)
872
+ self.leAreaCode.setText("")
873
+ self.update_area_list()
874
+ self.flag_map_changed = True
753
875
 
754
876
  def cancelAreaCreation(self):
755
877
  if self.closedPolygon:
@@ -777,6 +899,17 @@ class ModifiersMapCreatorWindow(QMainWindow):
777
899
  self.leAreaCode.setVisible(False)
778
900
  self.leAreaCode.setText("")
779
901
 
902
+ def update_area_list(self):
903
+ """
904
+ update the area list widget
905
+ """
906
+ self.area_list.clear()
907
+
908
+ print(f"{self.polygonsList2=}")
909
+
910
+ for modifier_name in sorted(self.polygonsList2.keys()):
911
+ self.area_list.addItem(modifier_name)
912
+
780
913
  def deleteArea(self):
781
914
  """
782
915
  remove selected area from map
@@ -784,27 +917,32 @@ class ModifiersMapCreatorWindow(QMainWindow):
784
917
 
785
918
  if self.selectedPolygon:
786
919
  self.view.scene().removeItem(self.selectedPolygon)
787
- self.view.scene().removeItem(self.polygonsList2[self.selectedPolygonAreaCode])
788
920
 
789
- del self.polygonsList2[self.selectedPolygonAreaCode]
790
- del self.areasList[self.selectedPolygonAreaCode]
921
+ if self.selectedPolygonAreaCode:
922
+ self.view.scene().removeItem(self.polygonsList2[self.selectedPolygonAreaCode])
923
+ del self.polygonsList2[self.selectedPolygonAreaCode]
924
+ del self.areasList[self.selectedPolygonAreaCode]
791
925
 
792
- self.flagMapChanged = True
926
+ self.selectedPolygonAreaCode = None
927
+ self.selectedPolygon = None
928
+
929
+ self.flag_map_changed = True
793
930
 
794
931
  self.view.elList = []
795
932
 
796
933
  self.view._start = 0
797
934
  self.view.points = []
798
935
  self.flagNewArea = False
799
- self.btSaveArea.setVisible(False)
800
- self.lb.setVisible(False)
801
936
 
802
- self.btColor.setVisible(False)
803
- self.slAlpha.setVisible(False)
937
+ for widget in (self.btSaveArea, self.lb, self.btColor, self.slAlpha, self.leAreaCode):
938
+ widget.setVisible(False)
939
+
804
940
  self.btNewArea.setVisible(True)
805
941
 
806
- self.leAreaCode.setVisible(False)
807
942
  self.leAreaCode.setText("")
943
+ self.statusBar().showMessage("")
944
+
945
+ self.update_area_list()
808
946
 
809
947
  def cancelMap(self):
810
948
  """
@@ -814,23 +952,19 @@ class ModifiersMapCreatorWindow(QMainWindow):
814
952
  self.areasList = {}
815
953
  self.polygonsList2 = {}
816
954
  self.view.scene().clear()
817
- self.btLoad.setVisible(False)
818
955
  self.btDeleteArea.setVisible(False)
819
956
  self.btNewArea.setVisible(False)
820
957
  self.saveMapAction.setEnabled(False)
821
958
  self.saveAsMapAction.setEnabled(False)
822
959
  self.mapNameAction.setEnabled(False)
823
960
  self.statusBar().showMessage("")
824
- self.flagMapChanged = False
961
+ self.flag_map_changed = False
825
962
 
826
963
  def loadBitmap(self):
827
964
  """
828
965
  load bitmap as background for coding map
829
- resize bitmap to 512 px if bigger
830
966
  """
831
967
 
832
- maxSize = 512
833
-
834
968
  fileName, _ = QFileDialog().getOpenFileName(self, "Load bitmap", "", "bitmap files (*.png *.jpg);;All files (*)")
835
969
 
836
970
  if not fileName:
@@ -839,6 +973,8 @@ class ModifiersMapCreatorWindow(QMainWindow):
839
973
 
840
974
  self.pixmap.load(self.bitmapFileName)
841
975
 
976
+ # scale image
977
+ """
842
978
  if self.pixmap.size().width() > maxSize or self.pixmap.size().height() > maxSize:
843
979
  self.pixmap = self.pixmap.scaled(maxSize, maxSize, Qt.KeepAspectRatio)
844
980
  QMessageBox.information(
@@ -847,9 +983,7 @@ class ModifiersMapCreatorWindow(QMainWindow):
847
983
  "The bitmap was resized to %d x %d pixels\nThe original file was not modified"
848
984
  % (self.pixmap.size().width(), self.pixmap.size().height()),
849
985
  )
850
-
851
- # scale image
852
- # pixmap = pixmap.scaled (256, 256, Qt.KeepAspectRatio)
986
+ """
853
987
 
854
988
  self.view.setSceneRect(0, 0, self.pixmap.size().width(), self.pixmap.size().height())
855
989
  pixitem = QGraphicsPixmapItem(self.pixmap)
@@ -858,14 +992,13 @@ class ModifiersMapCreatorWindow(QMainWindow):
858
992
 
859
993
  self.btNewArea.setVisible(True)
860
994
 
861
- self.btLoad.setVisible(False)
862
995
  self.saveMapAction.setEnabled(True)
863
996
  self.saveAsMapAction.setEnabled(True)
864
997
  self.mapNameAction.setEnabled(True)
865
998
 
866
999
  self.statusBar().showMessage("""Click "New modifier" to create a new modifier""")
867
1000
 
868
- self.flagMapChanged = True
1001
+ self.flag_map_changed = True
869
1002
 
870
1003
 
871
1004
  if __name__ == "__main__":
@@ -873,6 +1006,25 @@ if __name__ == "__main__":
873
1006
 
874
1007
  app = QApplication(sys.argv)
875
1008
  window = ModifiersMapCreatorWindow()
876
- window.resize(640, 640)
1009
+ window.resize(800, 700)
1010
+
1011
+ print(f"{window.width()=}")
1012
+ print(f"{window.height()=}")
1013
+
1014
+ # Get the screen geometry (screen size and position)
1015
+ screen_geometry = app.primaryScreen().geometry()
1016
+
1017
+ print(f"{screen_geometry=}")
1018
+
1019
+ # Calculate the center of the screen
1020
+ center_x = (screen_geometry.width() - window.width()) // 2
1021
+ center_y = (screen_geometry.height() - window.height()) // 2
1022
+
1023
+ print(f"{center_x=}")
1024
+ print(f"{center_y=}")
1025
+
1026
+ # Move the widget to the center of the screen
1027
+ window.move(center_x, center_y)
1028
+
877
1029
  window.show()
878
- sys.exit(app.exec_())
1030
+ sys.exit(app.exec())