labelimgplusplus 2.3.3__tar.gz → 2.4.0__tar.gz

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.
Files changed (66) hide show
  1. {labelimgplusplus-2.3.3 → labelimgplusplus-2.4.0}/HISTORY.rst +31 -0
  2. {labelimgplusplus-2.3.3 → labelimgplusplus-2.4.0}/PKG-INFO +1 -1
  3. {labelimgplusplus-2.3.3 → labelimgplusplus-2.4.0}/labelImgPlusPlus.py +11 -10
  4. {labelimgplusplus-2.3.3 → labelimgplusplus-2.4.0}/labelimgplusplus.egg-info/PKG-INFO +1 -1
  5. {labelimgplusplus-2.3.3 → labelimgplusplus-2.4.0}/labelimgplusplus.egg-info/SOURCES.txt +1 -0
  6. {labelimgplusplus-2.3.3 → labelimgplusplus-2.4.0}/libs/resources.py +43 -43
  7. labelimgplusplus-2.4.0/libs/utils/dpi.py +50 -0
  8. {labelimgplusplus-2.3.3 → labelimgplusplus-2.4.0}/libs/utils/styles.py +85 -83
  9. {labelimgplusplus-2.3.3 → labelimgplusplus-2.4.0}/libs/widgets/batchVerifyDialog.py +3 -1
  10. {labelimgplusplus-2.3.3 → labelimgplusplus-2.4.0}/libs/widgets/canvas.py +6 -2
  11. {labelimgplusplus-2.3.3 → labelimgplusplus-2.4.0}/libs/widgets/galleryWidget.py +3 -2
  12. {labelimgplusplus-2.3.3 → labelimgplusplus-2.4.0}/libs/widgets/keypointPanel.py +4 -3
  13. {labelimgplusplus-2.3.3 → labelimgplusplus-2.4.0}/libs/widgets/shortcutsDialog.py +2 -1
  14. {labelimgplusplus-2.3.3 → labelimgplusplus-2.4.0}/libs/widgets/splitDialog.py +3 -1
  15. {labelimgplusplus-2.3.3 → labelimgplusplus-2.4.0}/libs/widgets/toolBar.py +6 -29
  16. {labelimgplusplus-2.3.3 → labelimgplusplus-2.4.0}/pyproject.toml +1 -1
  17. {labelimgplusplus-2.3.3 → labelimgplusplus-2.4.0}/CONTRIBUTING.rst +0 -0
  18. {labelimgplusplus-2.3.3 → labelimgplusplus-2.4.0}/LICENSE +0 -0
  19. {labelimgplusplus-2.3.3 → labelimgplusplus-2.4.0}/MANIFEST.in +0 -0
  20. {labelimgplusplus-2.3.3 → labelimgplusplus-2.4.0}/README.rst +0 -0
  21. {labelimgplusplus-2.3.3 → labelimgplusplus-2.4.0}/data/predefined_classes.txt +0 -0
  22. {labelimgplusplus-2.3.3 → labelimgplusplus-2.4.0}/labelimgplusplus.egg-info/dependency_links.txt +0 -0
  23. {labelimgplusplus-2.3.3 → labelimgplusplus-2.4.0}/labelimgplusplus.egg-info/entry_points.txt +0 -0
  24. {labelimgplusplus-2.3.3 → labelimgplusplus-2.4.0}/labelimgplusplus.egg-info/requires.txt +0 -0
  25. {labelimgplusplus-2.3.3 → labelimgplusplus-2.4.0}/labelimgplusplus.egg-info/top_level.txt +0 -0
  26. {labelimgplusplus-2.3.3 → labelimgplusplus-2.4.0}/libs/__init__.py +0 -0
  27. {labelimgplusplus-2.3.3 → labelimgplusplus-2.4.0}/libs/core/__init__.py +0 -0
  28. {labelimgplusplus-2.3.3 → labelimgplusplus-2.4.0}/libs/core/commands.py +0 -0
  29. {labelimgplusplus-2.3.3 → labelimgplusplus-2.4.0}/libs/core/keypoint_config.py +0 -0
  30. {labelimgplusplus-2.3.3 → labelimgplusplus-2.4.0}/libs/core/settings.py +0 -0
  31. {labelimgplusplus-2.3.3 → labelimgplusplus-2.4.0}/libs/core/shape.py +0 -0
  32. {labelimgplusplus-2.3.3 → labelimgplusplus-2.4.0}/libs/core/shortcut_config.py +0 -0
  33. {labelimgplusplus-2.3.3 → labelimgplusplus-2.4.0}/libs/data/predefined_classes.txt +0 -0
  34. {labelimgplusplus-2.3.3 → labelimgplusplus-2.4.0}/libs/formats/__init__.py +0 -0
  35. {labelimgplusplus-2.3.3 → labelimgplusplus-2.4.0}/libs/formats/annotation_loader.py +0 -0
  36. {labelimgplusplus-2.3.3 → labelimgplusplus-2.4.0}/libs/formats/annotation_probe.py +0 -0
  37. {labelimgplusplus-2.3.3 → labelimgplusplus-2.4.0}/libs/formats/coco_io.py +0 -0
  38. {labelimgplusplus-2.3.3 → labelimgplusplus-2.4.0}/libs/formats/create_ml_io.py +0 -0
  39. {labelimgplusplus-2.3.3 → labelimgplusplus-2.4.0}/libs/formats/format_metadata.py +0 -0
  40. {labelimgplusplus-2.3.3 → labelimgplusplus-2.4.0}/libs/formats/labelFile.py +0 -0
  41. {labelimgplusplus-2.3.3 → labelimgplusplus-2.4.0}/libs/formats/pascal_voc_io.py +0 -0
  42. {labelimgplusplus-2.3.3 → labelimgplusplus-2.4.0}/libs/formats/yolo_io.py +0 -0
  43. {labelimgplusplus-2.3.3 → labelimgplusplus-2.4.0}/libs/formats/yolo_seg_io.py +0 -0
  44. {labelimgplusplus-2.3.3 → labelimgplusplus-2.4.0}/libs/tools/__init__.py +0 -0
  45. {labelimgplusplus-2.3.3 → labelimgplusplus-2.4.0}/libs/tools/dataset_splitter.py +0 -0
  46. {labelimgplusplus-2.3.3 → labelimgplusplus-2.4.0}/libs/tools/label_checker.py +0 -0
  47. {labelimgplusplus-2.3.3 → labelimgplusplus-2.4.0}/libs/utils/__init__.py +0 -0
  48. {labelimgplusplus-2.3.3 → labelimgplusplus-2.4.0}/libs/utils/constants.py +0 -0
  49. {labelimgplusplus-2.3.3 → labelimgplusplus-2.4.0}/libs/utils/hashableQListWidgetItem.py +0 -0
  50. {labelimgplusplus-2.3.3 → labelimgplusplus-2.4.0}/libs/utils/stringBundle.py +0 -0
  51. {labelimgplusplus-2.3.3 → labelimgplusplus-2.4.0}/libs/utils/ustr.py +0 -0
  52. {labelimgplusplus-2.3.3 → labelimgplusplus-2.4.0}/libs/utils/utils.py +0 -0
  53. {labelimgplusplus-2.3.3 → labelimgplusplus-2.4.0}/libs/widgets/__init__.py +0 -0
  54. {labelimgplusplus-2.3.3 → labelimgplusplus-2.4.0}/libs/widgets/colorDialog.py +0 -0
  55. {labelimgplusplus-2.3.3 → labelimgplusplus-2.4.0}/libs/widgets/combobox.py +0 -0
  56. {labelimgplusplus-2.3.3 → labelimgplusplus-2.4.0}/libs/widgets/default_label_combobox.py +0 -0
  57. {labelimgplusplus-2.3.3 → labelimgplusplus-2.4.0}/libs/widgets/gallery_status_controller.py +0 -0
  58. {labelimgplusplus-2.3.3 → labelimgplusplus-2.4.0}/libs/widgets/labelCheckerDialog.py +0 -0
  59. {labelimgplusplus-2.3.3 → labelimgplusplus-2.4.0}/libs/widgets/labelDialog.py +0 -0
  60. {labelimgplusplus-2.3.3 → labelimgplusplus-2.4.0}/libs/widgets/lightWidget.py +0 -0
  61. {labelimgplusplus-2.3.3 → labelimgplusplus-2.4.0}/libs/widgets/statsWidget.py +0 -0
  62. {labelimgplusplus-2.3.3 → labelimgplusplus-2.4.0}/libs/widgets/stats_controller.py +0 -0
  63. {labelimgplusplus-2.3.3 → labelimgplusplus-2.4.0}/libs/widgets/view_scaling.py +0 -0
  64. {labelimgplusplus-2.3.3 → labelimgplusplus-2.4.0}/libs/widgets/zoomWidget.py +0 -0
  65. {labelimgplusplus-2.3.3 → labelimgplusplus-2.4.0}/resources.qrc +0 -0
  66. {labelimgplusplus-2.3.3 → labelimgplusplus-2.4.0}/setup.cfg +0 -0
@@ -1,6 +1,37 @@
1
1
  History
2
2
  =======
3
3
 
4
+ 2.4.0 (2026-06-09)
5
+ ------------------
6
+
7
+ HiDPI scaling release. The interface now scales consistently on high-density
8
+ and fractionally-scaled displays: chrome, dialogs, stylesheets, and the canvas
9
+ grab tolerance all grow by the same factor that already drove icon sizing.
10
+ Backward compatible — every change is a no-op at the standard 96 DPI baseline,
11
+ so non-HiDPI displays are unchanged.
12
+
13
+ New features
14
+ ~~~~~~~~~~~~
15
+
16
+ * Scale the full UI for HiDPI displays from a single DPI factor (#66) —
17
+ dialog and dock minimum sizes, status-bar labels, keypoint-panel
18
+ indicators, gallery preset controls, the collapsible toolbar, every
19
+ stylesheet padding/margin/border/font-size value, and the canvas
20
+ hit-test radius
21
+
22
+ Bug fixes
23
+ ~~~~~~~~~
24
+
25
+ * Fix a ``ModuleNotFoundError`` crash when selecting the "Auto" toolbar icon
26
+ size — the handler imported from a stale ``libs.toolBar`` path (#93)
27
+
28
+ Internal
29
+ ~~~~~~~~
30
+
31
+ * Add ``libs/utils/dpi.py`` as the single source of truth for screen scaling
32
+ (``get_dpi_scale_factor`` + ``scale_px``); chrome and stylesheets now scale
33
+ through it instead of recomputing DPI per site
34
+
4
35
  2.3.3 (2026-06-09)
5
36
  ------------------
6
37
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: labelimgplusplus
3
- Version: 2.3.3
3
+ Version: 2.4.0
4
4
  Summary: labelImg++ is an enhanced graphical image annotation tool with Gallery Mode for browsing and labeling images
5
5
  Author-email: Abhik Sarkar <abhiksark@gmail.com>
6
6
  License: Copyright (c) <2015-Present> Tzutalin
@@ -95,6 +95,7 @@ from libs.utils.utils import (
95
95
  new_icon, themed_icon, new_action, add_actions, format_shortcut, Struct,
96
96
  generate_color_by_text, have_qstring, natural_sort
97
97
  )
98
+ from libs.utils.dpi import scale_px
98
99
  from libs.utils.stringBundle import StringBundle
99
100
  from libs.utils.styles import get_combined_style, Theme, get_stylesheet, get_canvas_background
100
101
  from libs.utils.ustr import ustr
@@ -507,10 +508,10 @@ class MainWindow(QMainWindow, WindowMixin):
507
508
  self.file_dock.setFeatures(QDockWidget.DockWidgetMovable | QDockWidget.DockWidgetFloatable)
508
509
 
509
510
  # Set minimum sizes for better resize UX
510
- self.dock.setMinimumHeight(100)
511
- self.file_dock.setMinimumHeight(100)
512
- self.dock.setMinimumWidth(200)
513
- self.file_dock.setMinimumWidth(200)
511
+ self.dock.setMinimumHeight(scale_px(100))
512
+ self.file_dock.setMinimumHeight(scale_px(100))
513
+ self.dock.setMinimumWidth(scale_px(200))
514
+ self.file_dock.setMinimumWidth(scale_px(200))
514
515
 
515
516
  # Features toggled by advanced mode (closable/floatable for labels dock)
516
517
  self.dock_features = QDockWidget.DockWidgetClosable | QDockWidget.DockWidgetFloatable
@@ -1069,17 +1070,17 @@ class MainWindow(QMainWindow, WindowMixin):
1069
1070
  # Status bar permanent widgets (left to right)
1070
1071
  # Image counter
1071
1072
  self.label_image_count = QLabel('Image: 0 / 0')
1072
- self.label_image_count.setMinimumWidth(100)
1073
+ self.label_image_count.setMinimumWidth(scale_px(100))
1073
1074
  self.statusBar().addPermanentWidget(self.label_image_count)
1074
1075
 
1075
1076
  # Annotation count
1076
1077
  self.label_box_count = QLabel('Boxes: 0')
1077
- self.label_box_count.setMinimumWidth(70)
1078
+ self.label_box_count.setMinimumWidth(scale_px(70))
1078
1079
  self.statusBar().addPermanentWidget(self.label_box_count)
1079
1080
 
1080
1081
  # Zoom level
1081
1082
  self.label_zoom = QLabel('Zoom: 100%')
1082
- self.label_zoom.setMinimumWidth(80)
1083
+ self.label_zoom.setMinimumWidth(scale_px(80))
1083
1084
  self.statusBar().addPermanentWidget(self.label_zoom)
1084
1085
 
1085
1086
  # Save status indicator
@@ -1220,8 +1221,8 @@ class MainWindow(QMainWindow, WindowMixin):
1220
1221
  # Stats panel (side)
1221
1222
  self.gallery_stats = StatsWidget()
1222
1223
  self.gallery_stats.refresh_btn.clicked.connect(self._refresh_all_statistics)
1223
- self.gallery_stats.setMaximumWidth(300)
1224
- self.gallery_stats.setMinimumWidth(250)
1224
+ self.gallery_stats.setMaximumWidth(scale_px(300))
1225
+ self.gallery_stats.setMinimumWidth(scale_px(250))
1225
1226
  layout.addWidget(self.gallery_stats, stretch=1)
1226
1227
 
1227
1228
  self.gallery_window.setCentralWidget(central_widget)
@@ -3256,7 +3257,7 @@ class MainWindow(QMainWindow, WindowMixin):
3256
3257
 
3257
3258
  if size == 0:
3258
3259
  # Auto mode - recalculate from DPI
3259
- from libs.toolBar import calculate_icon_size
3260
+ from libs.widgets.toolBar import calculate_icon_size
3260
3261
  size = calculate_icon_size()
3261
3262
 
3262
3263
  # Update toolbar icon size
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: labelimgplusplus
3
- Version: 2.3.3
3
+ Version: 2.4.0
4
4
  Summary: labelImg++ is an enhanced graphical image annotation tool with Gallery Mode for browsing and labeling images
5
5
  Author-email: Abhik Sarkar <abhiksark@gmail.com>
6
6
  License: Copyright (c) <2015-Present> Tzutalin
@@ -38,6 +38,7 @@ libs/tools/dataset_splitter.py
38
38
  libs/tools/label_checker.py
39
39
  libs/utils/__init__.py
40
40
  libs/utils/constants.py
41
+ libs/utils/dpi.py
41
42
  libs/utils/hashableQListWidgetItem.py
42
43
  libs/utils/stringBundle.py
43
44
  libs/utils/styles.py
@@ -4195,91 +4195,91 @@ qt_resource_struct_v2 = b"\
4195
4195
  \x00\x00\x00\x00\x00\x02\x00\x00\x00\x2b\x00\x00\x00\x01\
4196
4196
  \x00\x00\x00\x00\x00\x00\x00\x00\
4197
4197
  \x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\
4198
- \x00\x00\x01\x9e\xaa\xee\xdb\x6f\
4198
+ \x00\x00\x01\x9e\xab\x52\xd7\xf6\
4199
4199
  \x00\x00\x00\x0c\x00\x00\x00\x00\x00\x01\x00\x00\x7a\x97\
4200
- \x00\x00\x01\x9e\xaa\xee\xdb\x70\
4200
+ \x00\x00\x01\x9e\xab\x52\xd7\xf7\
4201
4201
  \x00\x00\x00\x18\x00\x00\x00\x00\x00\x01\x00\x00\x7b\xd5\
4202
- \x00\x00\x01\x9e\xaa\xee\xdb\x70\
4202
+ \x00\x00\x01\x9e\xab\x52\xd7\xf7\
4203
4203
  \x00\x00\x00\x24\x00\x00\x00\x00\x00\x01\x00\x00\x7e\x0a\
4204
- \x00\x00\x01\x9e\xaa\xee\xdb\x70\
4204
+ \x00\x00\x01\x9e\xab\x52\xd7\xf7\
4205
4205
  \x00\x00\x00\x32\x00\x00\x00\x00\x00\x01\x00\x00\x7f\x3f\
4206
- \x00\x00\x01\x9e\xaa\xee\xdb\x70\
4206
+ \x00\x00\x01\x9e\xab\x52\xd7\xf7\
4207
4207
  \x00\x00\x00\x40\x00\x00\x00\x00\x00\x01\x00\x00\x80\x1c\
4208
- \x00\x00\x01\x9e\xaa\xee\xdb\x70\
4208
+ \x00\x00\x01\x9e\xab\x52\xd7\xf7\
4209
4209
  \x00\x00\x00\x4e\x00\x00\x00\x00\x00\x01\x00\x00\x81\x2d\
4210
- \x00\x00\x01\x9e\xaa\xee\xdb\x70\
4210
+ \x00\x00\x01\x9e\xab\x52\xd7\xf7\
4211
4211
  \x00\x00\x00\x5c\x00\x00\x00\x00\x00\x01\x00\x00\x82\x50\
4212
- \x00\x00\x01\x9e\xaa\xee\xdb\x70\
4212
+ \x00\x00\x01\x9e\xab\x52\xd7\xf7\
4213
4213
  \x00\x00\x00\x6a\x00\x00\x00\x00\x00\x01\x00\x00\x83\x84\
4214
- \x00\x00\x01\x9e\xaa\xee\xdb\x70\
4214
+ \x00\x00\x01\x9e\xab\x52\xd7\xf7\
4215
4215
  \x00\x00\x00\x78\x00\x00\x00\x00\x00\x01\x00\x00\x85\x23\
4216
- \x00\x00\x01\x9e\xaa\xee\xdb\x70\
4216
+ \x00\x00\x01\x9e\xab\x52\xd7\xf7\
4217
4217
  \x00\x00\x00\x86\x00\x00\x00\x00\x00\x01\x00\x00\x86\x00\
4218
- \x00\x00\x01\x9e\xaa\xee\xdb\x70\
4218
+ \x00\x00\x01\x9e\xab\x52\xd7\xf7\
4219
4219
  \x00\x00\x00\x94\x00\x00\x00\x00\x00\x01\x00\x00\x87\x11\
4220
- \x00\x00\x01\x9e\xaa\xee\xdb\x70\
4220
+ \x00\x00\x01\x9e\xab\x52\xd7\xf7\
4221
4221
  \x00\x00\x00\xa2\x00\x00\x00\x00\x00\x01\x00\x00\x87\xef\
4222
- \x00\x00\x01\x9e\xaa\xee\xdb\x70\
4222
+ \x00\x00\x01\x9e\xab\x52\xd7\xf7\
4223
4223
  \x00\x00\x00\xb0\x00\x00\x00\x00\x00\x01\x00\x00\x88\xfe\
4224
- \x00\x00\x01\x9e\xaa\xee\xdb\x70\
4224
+ \x00\x00\x01\x9e\xab\x52\xd7\xf7\
4225
4225
  \x00\x00\x00\xbe\x00\x00\x00\x00\x00\x01\x00\x00\x8a\x36\
4226
- \x00\x00\x01\x9e\xaa\xee\xdb\x70\
4226
+ \x00\x00\x01\x9e\xab\x52\xd7\xf7\
4227
4227
  \x00\x00\x00\xcc\x00\x00\x00\x00\x00\x01\x00\x00\x8b\x86\
4228
- \x00\x00\x01\x9e\xaa\xee\xdb\x70\
4228
+ \x00\x00\x01\x9e\xab\x52\xd7\xf7\
4229
4229
  \x00\x00\x00\xda\x00\x00\x00\x00\x00\x01\x00\x00\x8c\x8f\
4230
- \x00\x00\x01\x9e\xaa\xee\xdb\x70\
4230
+ \x00\x00\x01\x9e\xab\x52\xd7\xf7\
4231
4231
  \x00\x00\x00\xe8\x00\x00\x00\x00\x00\x01\x00\x00\x8d\x95\
4232
- \x00\x00\x01\x9e\xaa\xee\xdb\x70\
4232
+ \x00\x00\x01\x9e\xab\x52\xd7\xf7\
4233
4233
  \x00\x00\x00\xf8\x00\x00\x00\x00\x00\x01\x00\x00\x8e\x99\
4234
- \x00\x00\x01\x9e\xaa\xee\xdb\x70\
4234
+ \x00\x00\x01\x9e\xab\x52\xd7\xf7\
4235
4235
  \x00\x00\x01\x08\x00\x00\x00\x00\x00\x01\x00\x00\x8f\x84\
4236
- \x00\x00\x01\x9e\xaa\xee\xdb\x70\
4236
+ \x00\x00\x01\x9e\xab\x52\xd7\xf7\
4237
4237
  \x00\x00\x01\x1c\x00\x00\x00\x00\x00\x01\x00\x00\x90\xd6\
4238
- \x00\x00\x01\x9e\xaa\xee\xdb\x74\
4238
+ \x00\x00\x01\x9e\xab\x52\xd7\xfb\
4239
4239
  \x00\x00\x01\x3c\x00\x00\x00\x00\x00\x01\x00\x00\x9b\x2a\
4240
- \x00\x00\x01\x9e\xaa\xee\xdb\x74\
4240
+ \x00\x00\x01\x9e\xab\x52\xd7\xfb\
4241
4241
  \x00\x00\x01\x5c\x00\x00\x00\x00\x00\x01\x00\x00\xa4\x77\
4242
- \x00\x00\x01\x9e\xaa\xee\xdb\x73\
4242
+ \x00\x00\x01\x9e\xab\x52\xd7\xfb\
4243
4243
  \x00\x00\x01\x7c\x00\x00\x00\x00\x00\x01\x00\x00\xb0\xcb\
4244
- \x00\x00\x01\x9e\xaa\xee\xdb\x70\
4244
+ \x00\x00\x01\x9e\xab\x52\xd7\xf7\
4245
4245
  \x00\x00\x01\x96\x00\x00\x00\x00\x00\x01\x00\x00\xb3\xe1\
4246
- \x00\x00\x01\x9e\xaa\xee\xdb\x70\
4246
+ \x00\x00\x01\x9e\xab\x52\xd7\xf7\
4247
4247
  \x00\x00\x01\xb0\x00\x00\x00\x00\x00\x01\x00\x00\xb5\x2d\
4248
- \x00\x00\x01\x9e\xaa\xee\xdb\x70\
4248
+ \x00\x00\x01\x9e\xab\x52\xd7\xf7\
4249
4249
  \x00\x00\x01\xc6\x00\x00\x00\x00\x00\x01\x00\x00\xb6\x59\
4250
- \x00\x00\x01\x9e\xaa\xee\xdb\x70\
4250
+ \x00\x00\x01\x9e\xab\x52\xd7\xf7\
4251
4251
  \x00\x00\x01\xe0\x00\x00\x00\x00\x00\x01\x00\x00\xb7\x56\
4252
- \x00\x00\x01\x9e\xaa\xee\xdb\x70\
4252
+ \x00\x00\x01\x9e\xab\x52\xd7\xf7\
4253
4253
  \x00\x00\x01\xf2\x00\x00\x00\x00\x00\x01\x00\x00\xb8\xd9\
4254
- \x00\x00\x01\x9e\xaa\xee\xdb\x70\
4254
+ \x00\x00\x01\x9e\xab\x52\xd7\xf7\
4255
4255
  \x00\x00\x02\x04\x00\x00\x00\x00\x00\x01\x00\x00\xba\xe5\
4256
- \x00\x00\x01\x9e\xaa\xee\xdb\x70\
4256
+ \x00\x00\x01\x9e\xab\x52\xd7\xf7\
4257
4257
  \x00\x00\x02\x24\x00\x00\x00\x00\x00\x01\x00\x00\xbd\x1a\
4258
- \x00\x00\x01\x9e\xaa\xee\xdb\x70\
4258
+ \x00\x00\x01\x9e\xab\x52\xd7\xf7\
4259
4259
  \x00\x00\x02\x36\x00\x00\x00\x00\x00\x01\x00\x00\xbe\x78\
4260
- \x00\x00\x01\x9e\xaa\xee\xdb\x70\
4260
+ \x00\x00\x01\x9e\xab\x52\xd7\xf7\
4261
4261
  \x00\x00\x02\x4e\x00\x00\x00\x00\x00\x01\x00\x00\xbf\x9b\
4262
- \x00\x00\x01\x9e\xaa\xee\xdb\x6f\
4262
+ \x00\x00\x01\x9e\xab\x52\xd7\xf6\
4263
4263
  \x00\x00\x02\x60\x00\x00\x00\x00\x00\x01\x00\x00\xc0\xad\
4264
- \x00\x00\x01\x9e\xaa\xee\xdb\x70\
4264
+ \x00\x00\x01\x9e\xab\x52\xd7\xf7\
4265
4265
  \x00\x00\x02\x7c\x00\x00\x00\x00\x00\x01\x00\x00\xc3\x54\
4266
- \x00\x00\x01\x9e\xaa\xee\xdb\x70\
4266
+ \x00\x00\x01\x9e\xab\x52\xd7\xf7\
4267
4267
  \x00\x00\x02\x90\x00\x00\x00\x00\x00\x01\x00\x00\xc4\x8e\
4268
- \x00\x00\x01\x9e\xaa\xee\xdb\x70\
4268
+ \x00\x00\x01\x9e\xab\x52\xd7\xf7\
4269
4269
  \x00\x00\x02\xac\x00\x00\x00\x00\x00\x01\x00\x00\xc6\xc3\
4270
- \x00\x00\x01\x9e\xaa\xee\xdb\x74\
4270
+ \x00\x00\x01\x9e\xab\x52\xd7\xfb\
4271
4271
  \x00\x00\x02\xc0\x00\x00\x00\x00\x00\x01\x00\x00\xd9\x78\
4272
- \x00\x00\x01\x9e\xaa\xee\xdb\x70\
4272
+ \x00\x00\x01\x9e\xab\x52\xd7\xf7\
4273
4273
  \x00\x00\x02\xe4\x00\x00\x00\x00\x00\x01\x00\x00\xe9\xb8\
4274
- \x00\x00\x01\x9e\xaa\xee\xdb\x70\
4274
+ \x00\x00\x01\x9e\xab\x52\xd7\xf7\
4275
4275
  \x00\x00\x03\x02\x00\x00\x00\x00\x00\x01\x00\x00\xea\x96\
4276
- \x00\x00\x01\x9e\xaa\xee\xdb\x70\
4276
+ \x00\x00\x01\x9e\xab\x52\xd7\xf7\
4277
4277
  \x00\x00\x03\x20\x00\x00\x00\x00\x00\x01\x00\x00\xeb\x8b\
4278
- \x00\x00\x01\x9e\xaa\xee\xdb\x70\
4278
+ \x00\x00\x01\x9e\xab\x52\xd7\xf7\
4279
4279
  \x00\x00\x03\x36\x00\x00\x00\x00\x00\x01\x00\x00\xec\xdd\
4280
- \x00\x00\x01\x9e\xaa\xee\xdb\x70\
4280
+ \x00\x00\x01\x9e\xab\x52\xd7\xf7\
4281
4281
  \x00\x00\x03\x4c\x00\x00\x00\x00\x00\x01\x00\x00\xf0\xa0\
4282
- \x00\x00\x01\x9e\xaa\xee\xdb\x70\
4282
+ \x00\x00\x01\x9e\xab\x52\xd7\xf7\
4283
4283
  "
4284
4284
 
4285
4285
  qt_version = [int(v) for v in QtCore.qVersion().split('.')]
@@ -0,0 +1,50 @@
1
+ # libs/utils/dpi.py
2
+ """Central DPI scaling utilities for HiDPI displays.
3
+
4
+ A single source of truth for the screen scale factor so chrome, dialogs,
5
+ stylesheets, and icons all scale by the same amount. Widget modules and the
6
+ theme system import from here rather than computing DPI themselves.
7
+ """
8
+
9
+ try:
10
+ from PyQt5.QtWidgets import QApplication
11
+ except ImportError:
12
+ from PyQt4.QtGui import QApplication
13
+
14
+
15
+ def get_dpi_scale_factor():
16
+ """Return the DPI scale factor for the primary screen.
17
+
18
+ Returns:
19
+ float: Scale factor (1.0 for standard 96 DPI, higher for HiDPI
20
+ displays). Falls back to 1.0 when no application or screen is
21
+ available (e.g. headless tests, Qt4).
22
+ """
23
+ app = QApplication.instance()
24
+ if app is None:
25
+ return 1.0
26
+
27
+ try:
28
+ screen = app.primaryScreen()
29
+ if screen:
30
+ # Logical DPI accounts for the user's display scaling setting.
31
+ logical_dpi = screen.logicalDotsPerInch()
32
+ # 96 DPI is the standard baseline on most systems.
33
+ return logical_dpi / 96.0
34
+ except AttributeError:
35
+ # Qt4 has no primaryScreen(); treat as unscaled.
36
+ pass
37
+
38
+ return 1.0
39
+
40
+
41
+ def scale_px(n):
42
+ """Scale a base pixel value by the current DPI factor.
43
+
44
+ Args:
45
+ n: Pixel value defined at the 96 DPI baseline.
46
+
47
+ Returns:
48
+ int: ``n`` scaled by the DPI factor, rounded to the nearest integer.
49
+ """
50
+ return int(round(n * get_dpi_scale_factor()))
@@ -3,6 +3,8 @@
3
3
 
4
4
  from enum import Enum
5
5
 
6
+ from libs.utils.dpi import scale_px
7
+
6
8
 
7
9
  class Theme(Enum):
8
10
  """Available application themes."""
@@ -108,24 +110,24 @@ def get_toolbar_style(theme: Theme) -> str:
108
110
  QToolBar {{
109
111
  background: {c['surface']};
110
112
  border: none;
111
- border-right: 1px solid {c['border']};
112
- spacing: 2px;
113
- padding: 4px;
113
+ border-right: {scale_px(1)}px solid {c['border']};
114
+ spacing: {scale_px(2)}px;
115
+ padding: {scale_px(4)}px;
114
116
  }}
115
117
 
116
118
  QToolBar::separator {{
117
119
  background: {c['border']};
118
- width: 1px;
119
- height: 20px;
120
- margin: 6px 4px;
120
+ width: {scale_px(1)}px;
121
+ height: {scale_px(20)}px;
122
+ margin: {scale_px(6)}px {scale_px(4)}px;
121
123
  }}
122
124
 
123
125
  QToolButton {{
124
126
  background: transparent;
125
127
  border: none;
126
- border-radius: 4px;
127
- padding: 4px;
128
- margin: 1px;
128
+ border-radius: {scale_px(4)}px;
129
+ padding: {scale_px(4)}px;
130
+ margin: {scale_px(1)}px;
129
131
  color: {c['text']};
130
132
  }}
131
133
 
@@ -167,18 +169,18 @@ QDockWidget {{
167
169
 
168
170
  QDockWidget::title {{
169
171
  background: {c['surface']};
170
- padding: 6px;
171
- border-bottom: 1px solid {c['border']};
172
+ padding: {scale_px(6)}px;
173
+ border-bottom: {scale_px(1)}px solid {c['border']};
172
174
  }}
173
175
 
174
176
  QListWidget {{
175
177
  background: {c['background']};
176
- border: 1px solid {c['border']};
178
+ border: {scale_px(1)}px solid {c['border']};
177
179
  color: {c['text']};
178
180
  }}
179
181
 
180
182
  QListWidget::item {{
181
- padding: 4px;
183
+ padding: {scale_px(4)}px;
182
184
  }}
183
185
 
184
186
  QListWidget::item:selected {{
@@ -192,15 +194,15 @@ QListWidget::item:hover {{
192
194
 
193
195
  QScrollBar:vertical {{
194
196
  background: {c['surface']};
195
- width: 12px;
197
+ width: {scale_px(12)}px;
196
198
  border: none;
197
199
  }}
198
200
 
199
201
  QScrollBar::handle:vertical {{
200
202
  background: {c['border']};
201
- border-radius: 4px;
202
- min-height: 20px;
203
- margin: 2px;
203
+ border-radius: {scale_px(4)}px;
204
+ min-height: {scale_px(20)}px;
205
+ margin: {scale_px(2)}px;
204
206
  }}
205
207
 
206
208
  QScrollBar::handle:vertical:hover {{
@@ -208,20 +210,20 @@ QScrollBar::handle:vertical:hover {{
208
210
  }}
209
211
 
210
212
  QScrollBar::add-line:vertical, QScrollBar::sub-line:vertical {{
211
- height: 0px;
213
+ height: {scale_px(0)}px;
212
214
  }}
213
215
 
214
216
  QScrollBar:horizontal {{
215
217
  background: {c['surface']};
216
- height: 12px;
218
+ height: {scale_px(12)}px;
217
219
  border: none;
218
220
  }}
219
221
 
220
222
  QScrollBar::handle:horizontal {{
221
223
  background: {c['border']};
222
- border-radius: 4px;
223
- min-width: 20px;
224
- margin: 2px;
224
+ border-radius: {scale_px(4)}px;
225
+ min-width: {scale_px(20)}px;
226
+ margin: {scale_px(2)}px;
225
227
  }}
226
228
 
227
229
  QScrollBar::handle:horizontal:hover {{
@@ -229,19 +231,19 @@ QScrollBar::handle:horizontal:hover {{
229
231
  }}
230
232
 
231
233
  QScrollBar::add-line:horizontal, QScrollBar::sub-line:horizontal {{
232
- width: 0px;
234
+ width: {scale_px(0)}px;
233
235
  }}
234
236
 
235
237
  QMenu {{
236
238
  background: {c['surface']};
237
- border: 1px solid {c['border']};
239
+ border: {scale_px(1)}px solid {c['border']};
238
240
  color: {c['text']};
239
- padding: 4px;
241
+ padding: {scale_px(4)}px;
240
242
  }}
241
243
 
242
244
  QMenu::item {{
243
- padding: 6px 20px;
244
- border-radius: 4px;
245
+ padding: {scale_px(6)}px {scale_px(20)}px;
246
+ border-radius: {scale_px(4)}px;
245
247
  }}
246
248
 
247
249
  QMenu::item:selected {{
@@ -250,19 +252,19 @@ QMenu::item:selected {{
250
252
  }}
251
253
 
252
254
  QMenu::separator {{
253
- height: 1px;
255
+ height: {scale_px(1)}px;
254
256
  background: {c['border']};
255
- margin: 4px 8px;
257
+ margin: {scale_px(4)}px {scale_px(8)}px;
256
258
  }}
257
259
 
258
260
  QMenuBar {{
259
261
  background: {c['surface']};
260
262
  color: {c['text']};
261
- border-bottom: 1px solid {c['border']};
263
+ border-bottom: {scale_px(1)}px solid {c['border']};
262
264
  }}
263
265
 
264
266
  QMenuBar::item {{
265
- padding: 6px 10px;
267
+ padding: {scale_px(6)}px {scale_px(10)}px;
266
268
  }}
267
269
 
268
270
  QMenuBar::item:selected {{
@@ -271,9 +273,9 @@ QMenuBar::item:selected {{
271
273
 
272
274
  QComboBox {{
273
275
  background: {c['surface']};
274
- border: 1px solid {c['border']};
275
- border-radius: 4px;
276
- padding: 4px 8px;
276
+ border: {scale_px(1)}px solid {c['border']};
277
+ border-radius: {scale_px(4)}px;
278
+ padding: {scale_px(4)}px {scale_px(8)}px;
277
279
  color: {c['text']};
278
280
  }}
279
281
 
@@ -283,12 +285,12 @@ QComboBox:hover {{
283
285
 
284
286
  QComboBox::drop-down {{
285
287
  border: none;
286
- padding-right: 8px;
288
+ padding-right: {scale_px(8)}px;
287
289
  }}
288
290
 
289
291
  QComboBox QAbstractItemView {{
290
292
  background: {c['surface']};
291
- border: 1px solid {c['border']};
293
+ border: {scale_px(1)}px solid {c['border']};
292
294
  color: {c['text']};
293
295
  selection-background-color: {c['accent_light']};
294
296
  selection-color: {c['accent_text']};
@@ -296,9 +298,9 @@ QComboBox QAbstractItemView {{
296
298
 
297
299
  QLineEdit {{
298
300
  background: {c['background']};
299
- border: 1px solid {c['border']};
300
- border-radius: 4px;
301
- padding: 4px 8px;
301
+ border: {scale_px(1)}px solid {c['border']};
302
+ border-radius: {scale_px(4)}px;
303
+ padding: {scale_px(4)}px {scale_px(8)}px;
302
304
  color: {c['text']};
303
305
  }}
304
306
 
@@ -308,9 +310,9 @@ QLineEdit:focus {{
308
310
 
309
311
  QPushButton {{
310
312
  background: {c['surface']};
311
- border: 1px solid {c['border']};
312
- border-radius: 4px;
313
- padding: 6px 16px;
313
+ border: {scale_px(1)}px solid {c['border']};
314
+ border-radius: {scale_px(4)}px;
315
+ padding: {scale_px(6)}px {scale_px(16)}px;
314
316
  color: {c['text']};
315
317
  }}
316
318
 
@@ -331,10 +333,10 @@ QCheckBox {{
331
333
  }}
332
334
 
333
335
  QCheckBox::indicator {{
334
- width: 16px;
335
- height: 16px;
336
- border: 1px solid {c['border']};
337
- border-radius: 3px;
336
+ width: {scale_px(16)}px;
337
+ height: {scale_px(16)}px;
338
+ border: {scale_px(1)}px solid {c['border']};
339
+ border-radius: {scale_px(3)}px;
338
340
  background: {c['background']};
339
341
  }}
340
342
 
@@ -349,27 +351,27 @@ QLabel {{
349
351
 
350
352
  QGroupBox {{
351
353
  color: {c['text']};
352
- border: 1px solid {c['border']};
353
- border-radius: 4px;
354
- margin-top: 8px;
355
- padding-top: 8px;
354
+ border: {scale_px(1)}px solid {c['border']};
355
+ border-radius: {scale_px(4)}px;
356
+ margin-top: {scale_px(8)}px;
357
+ padding-top: {scale_px(8)}px;
356
358
  }}
357
359
 
358
360
  QGroupBox::title {{
359
361
  subcontrol-origin: margin;
360
- left: 10px;
361
- padding: 0 5px;
362
+ left: {scale_px(10)}px;
363
+ padding: 0 {scale_px(5)}px;
362
364
  }}
363
365
 
364
366
  QTabWidget::pane {{
365
- border: 1px solid {c['border']};
367
+ border: {scale_px(1)}px solid {c['border']};
366
368
  background: {c['background']};
367
369
  }}
368
370
 
369
371
  QTabBar::tab {{
370
372
  background: {c['surface']};
371
- border: 1px solid {c['border']};
372
- padding: 8px 16px;
373
+ border: {scale_px(1)}px solid {c['border']};
374
+ padding: {scale_px(8)}px {scale_px(16)}px;
373
375
  color: {c['text']};
374
376
  }}
375
377
 
@@ -383,8 +385,8 @@ QTabBar::tab:hover:!selected {{
383
385
  }}
384
386
 
385
387
  QProgressBar {{
386
- border: 1px solid {c['border']};
387
- border-radius: 4px;
388
+ border: {scale_px(1)}px solid {c['border']};
389
+ border-radius: {scale_px(4)}px;
388
390
  background: {c['surface']};
389
391
  text-align: center;
390
392
  color: {c['text']};
@@ -392,12 +394,12 @@ QProgressBar {{
392
394
 
393
395
  QProgressBar::chunk {{
394
396
  background: {c['accent']};
395
- border-radius: 3px;
397
+ border-radius: {scale_px(3)}px;
396
398
  }}
397
399
 
398
400
  QTableWidget {{
399
401
  background: {c['background']};
400
- border: 1px solid {c['border']};
402
+ border: {scale_px(1)}px solid {c['border']};
401
403
  color: {c['text']};
402
404
  gridline-color: {c['border']};
403
405
  }}
@@ -410,9 +412,9 @@ QTableWidget::item:selected {{
410
412
  QHeaderView::section {{
411
413
  background: {c['surface']};
412
414
  border: none;
413
- border-right: 1px solid {c['border']};
414
- border-bottom: 1px solid {c['border']};
415
- padding: 6px;
415
+ border-right: {scale_px(1)}px solid {c['border']};
416
+ border-bottom: {scale_px(1)}px solid {c['border']};
417
+ padding: {scale_px(6)}px;
416
418
  color: {c['text']};
417
419
  }}
418
420
 
@@ -428,17 +430,17 @@ QMessageBox {{
428
430
 
429
431
  QSpinBox, QDoubleSpinBox {{
430
432
  background: {c['background']};
431
- border: 1px solid {c['border']};
432
- border-radius: 4px;
433
- padding: 4px;
433
+ border: {scale_px(1)}px solid {c['border']};
434
+ border-radius: {scale_px(4)}px;
435
+ padding: {scale_px(4)}px;
434
436
  color: {c['text']};
435
437
  }}
436
438
 
437
439
  QToolTip {{
438
440
  background: {c['surface']};
439
- border: 1px solid {c['border']};
441
+ border: {scale_px(1)}px solid {c['border']};
440
442
  color: {c['text']};
441
- padding: 4px;
443
+ padding: {scale_px(4)}px;
442
444
  }}
443
445
  """
444
446
 
@@ -449,7 +451,7 @@ def get_status_bar_style(theme: Theme) -> str:
449
451
  return f"""
450
452
  QStatusBar {{
451
453
  background: {c['surface']};
452
- border-top: 1px solid {c['border']};
454
+ border-top: {scale_px(1)}px solid {c['border']};
453
455
  color: {c['text']};
454
456
  }}
455
457
 
@@ -469,23 +471,23 @@ def get_slider_style(theme: Theme) -> str:
469
471
  c = _get_colors(theme)
470
472
  return f"""
471
473
  QSlider::groove:horizontal {{
472
- height: 6px;
474
+ height: {scale_px(6)}px;
473
475
  background: {c['border']};
474
- border-radius: 3px;
476
+ border-radius: {scale_px(3)}px;
475
477
  }}
476
478
  QSlider::handle:horizontal {{
477
479
  background: {c['accent']};
478
- width: 16px;
479
- height: 16px;
480
- margin: -5px 0;
481
- border-radius: 8px;
480
+ width: {scale_px(16)}px;
481
+ height: {scale_px(16)}px;
482
+ margin: -{scale_px(5)}px 0;
483
+ border-radius: {scale_px(8)}px;
482
484
  }}
483
485
  QSlider::handle:horizontal:hover {{
484
486
  background: {c['accent_text']};
485
487
  }}
486
488
  QSlider::sub-page:horizontal {{
487
489
  background: {c['accent']};
488
- border-radius: 3px;
490
+ border-radius: {scale_px(3)}px;
489
491
  }}
490
492
  """
491
493
 
@@ -494,13 +496,13 @@ def get_gallery_controls_style(theme: Theme) -> str:
494
496
  """Generate gallery slider frame and button styles."""
495
497
  c = _get_colors(theme)
496
498
  return {
497
- 'frame': f"QFrame {{ background-color: {c['surface']}; border-bottom: 1px solid {c['border']}; }}",
499
+ 'frame': f"QFrame {{ background-color: {c['surface']}; border-bottom: {scale_px(1)}px solid {c['border']}; }}",
498
500
  'button': f"""QPushButton {{
499
501
  background-color: {c['background']};
500
- border: 1px solid {c['border']};
501
- border-radius: 4px;
502
+ border: {scale_px(1)}px solid {c['border']};
503
+ border-radius: {scale_px(4)}px;
502
504
  font-weight: bold;
503
- font-size: 11px;
505
+ font-size: {scale_px(11)}px;
504
506
  color: {c['text']};
505
507
  }}
506
508
  QPushButton:hover {{
@@ -520,11 +522,11 @@ def get_expand_button_style(theme: Theme) -> str:
520
522
  QToolButton {{
521
523
  border: none;
522
524
  background: transparent;
523
- padding: 4px;
525
+ padding: {scale_px(4)}px;
524
526
  }}
525
527
  QToolButton:hover {{
526
528
  background: {c['hover']};
527
- border-radius: 4px;
529
+ border-radius: {scale_px(4)}px;
528
530
  }}
529
531
  """
530
532
 
@@ -534,7 +536,7 @@ def get_label_dialog_style(theme: Theme) -> str:
534
536
  c = _get_colors(theme)
535
537
  return {
536
538
  'filter_label': f"color: {c['text_secondary']};",
537
- 'count_label': f"color: {c['text_secondary']}; font-size: 11px;",
539
+ 'count_label': f"color: {c['text_secondary']}; font-size: {scale_px(11)}px;",
538
540
  }
539
541
 
540
542
 
@@ -549,7 +551,7 @@ QListWidget {{
549
551
  }}
550
552
  QListWidget::item {{
551
553
  color: {c['text']};
552
- padding: 4px;
554
+ padding: {scale_px(4)}px;
553
555
  }}
554
556
  QListWidget::item:selected {{
555
557
  background: {c['accent_light']};
@@ -7,6 +7,8 @@ from PyQt5.QtWidgets import (
7
7
  )
8
8
  from PyQt5.QtCore import Qt
9
9
 
10
+ from libs.utils.dpi import scale_px
11
+
10
12
 
11
13
  class BatchVerifyDialog(QDialog):
12
14
  """Dialog for batch verifying or unverifying annotated images.
@@ -28,7 +30,7 @@ class BatchVerifyDialog(QDialog):
28
30
  """
29
31
  super().__init__(parent)
30
32
  self.setWindowTitle('Batch Verify')
31
- self.setMinimumWidth(350)
33
+ self.setMinimumWidth(scale_px(350))
32
34
 
33
35
  layout = QVBoxLayout()
34
36
  layout.addWidget(QLabel(f'Images: {image_count}'))
@@ -14,6 +14,7 @@ except ImportError:
14
14
  import math
15
15
 
16
16
  from libs.core.shape import Shape, ShapeType
17
+ from libs.utils.dpi import scale_px
17
18
  from libs.utils.utils import distance
18
19
  from libs.utils.styles import Theme
19
20
 
@@ -77,11 +78,14 @@ class Canvas(QWidget):
77
78
 
78
79
  CREATE, EDIT, CREATE_POLYGON, KEYPOINT_MODE = list(range(4))
79
80
 
80
- epsilon = 24.0
81
-
82
81
  def __init__(self, *args, **kwargs):
83
82
  super(Canvas, self).__init__(*args, **kwargs)
84
83
  # Initialise local state.
84
+ # Hit-test tolerance as a screen-pixel radius; scaled for HiDPI so
85
+ # grabbing vertices feels the same on high-density displays. Set per
86
+ # instance (not a class attribute) so the DPI factor is read after the
87
+ # QApplication exists.
88
+ self.epsilon = float(scale_px(24))
85
89
  self.mode = self.EDIT
86
90
  self.shapes = []
87
91
  self.current = None
@@ -21,6 +21,7 @@ try:
21
21
  except ImportError:
22
22
  ElementTree = None
23
23
 
24
+ from libs.utils.dpi import scale_px
24
25
  from libs.utils.styles import Theme, get_slider_style, get_gallery_controls_style, get_gallery_list_style
25
26
 
26
27
 
@@ -374,7 +375,7 @@ class GalleryWidget(QWidget):
374
375
  }
375
376
  for label, size in self.size_presets.items():
376
377
  btn = QPushButton(label)
377
- btn.setFixedSize(32, 26)
378
+ btn.setFixedSize(scale_px(32), scale_px(26))
378
379
  btn.setAutoFillBackground(True) # Required for stylesheet bg
379
380
  btn.clicked.connect(lambda checked, s=size: self._set_preset_size(s))
380
381
  slider_layout.addWidget(btn)
@@ -392,7 +393,7 @@ class GalleryWidget(QWidget):
392
393
 
393
394
  # Size value display
394
395
  self.size_value_label = QLabel(f"{self._icon_size}px")
395
- self.size_value_label.setMinimumWidth(50)
396
+ self.size_value_label.setMinimumWidth(scale_px(50))
396
397
  slider_layout.addWidget(self.size_value_label)
397
398
 
398
399
  layout.addWidget(self._slider_frame)
@@ -6,6 +6,7 @@ from PyQt5.QtWidgets import (QWidget, QVBoxLayout, QHBoxLayout, QLabel,
6
6
  from PyQt5.QtCore import pyqtSignal, Qt
7
7
 
8
8
  from libs.core.keypoint_config import get_keypoint_color, get_template
9
+ from libs.utils.dpi import scale_px
9
10
  from libs.utils.styles import Theme, get_theme_colors
10
11
 
11
12
 
@@ -54,7 +55,7 @@ class KeypointPanel(QWidget):
54
55
  row.setSpacing(4)
55
56
 
56
57
  status_label = QLabel('\u25cb')
57
- status_label.setFixedWidth(16)
58
+ status_label.setFixedWidth(scale_px(16))
58
59
  status_label.setAlignment(Qt.AlignCenter)
59
60
 
60
61
  name_btn = QPushButton(name.replace('_', ' ').title())
@@ -64,10 +65,10 @@ class KeypointPanel(QWidget):
64
65
  lambda checked, idx=i: self.keypointClicked.emit(idx))
65
66
 
66
67
  color_indicator = QLabel()
67
- color_indicator.setFixedSize(8, 8)
68
+ color_indicator.setFixedSize(scale_px(8), scale_px(8))
68
69
  color_hex = get_keypoint_color(i, template_name)
69
70
  color_indicator.setStyleSheet(
70
- 'background: %s; border-radius: 4px;' % color_hex)
71
+ 'background: %s; border-radius: %dpx;' % (color_hex, scale_px(4)))
71
72
 
72
73
  row.addWidget(color_indicator)
73
74
  row.addWidget(status_label)
@@ -7,6 +7,7 @@ from PyQt5.QtWidgets import (
7
7
  )
8
8
  from PyQt5.QtCore import Qt
9
9
 
10
+ from libs.utils.dpi import scale_px
10
11
  from libs.utils.styles import Theme, get_theme_colors, hex_to_qcolor
11
12
 
12
13
 
@@ -14,7 +15,7 @@ class ShortcutsDialog(QDialog):
14
15
  def __init__(self, shortcut_config, action_map, parent=None):
15
16
  super().__init__(parent)
16
17
  self.setWindowTitle('Keyboard Shortcuts')
17
- self.setMinimumSize(550, 500)
18
+ self.setMinimumSize(scale_px(550), scale_px(500))
18
19
  self.config = shortcut_config
19
20
  self.action_map = action_map # {action_name: QAction}
20
21
  self._pending = dict(shortcut_config.get_all())
@@ -9,6 +9,8 @@ from PyQt5.QtWidgets import (
9
9
  QProgressBar, QPushButton, QRadioButton, QSpinBox, QVBoxLayout,
10
10
  )
11
11
 
12
+ from libs.utils.dpi import scale_px
13
+
12
14
 
13
15
  class SplitDialog(QDialog):
14
16
  """Dialog for configuring dataset train/val/test split parameters."""
@@ -16,7 +18,7 @@ class SplitDialog(QDialog):
16
18
  def __init__(self, parent=None, image_count=0, default_dir=''):
17
19
  super().__init__(parent)
18
20
  self.setWindowTitle('Split Dataset')
19
- self.setMinimumWidth(450)
21
+ self.setMinimumWidth(scale_px(450))
20
22
  self._image_count = image_count
21
23
 
22
24
  layout = QVBoxLayout()
@@ -5,16 +5,18 @@ try:
5
5
  from PyQt5.QtCore import Qt, pyqtSignal, QSize
6
6
  from PyQt5.QtWidgets import (
7
7
  QToolBar, QToolButton, QWidgetAction, QWidget,
8
- QMenu, QSizePolicy, QApplication
8
+ QMenu, QSizePolicy
9
9
  )
10
10
  except ImportError:
11
11
  from PyQt4.QtGui import (
12
12
  QToolBar, QToolButton, QWidgetAction, QWidget,
13
- QMenu, QSizePolicy, QApplication
13
+ QMenu, QSizePolicy
14
14
  )
15
15
  from PyQt4.QtCore import Qt, pyqtSignal, QSize
16
16
 
17
17
  from libs.utils.styles import Theme, get_expand_button_style
18
+ # get_dpi_scale_factor re-exported for back-compat; it now lives in libs.utils.dpi.
19
+ from libs.utils.dpi import get_dpi_scale_factor, scale_px
18
20
 
19
21
 
20
22
  # Base icon size for toolbar buttons (Feather icons are 24x24)
@@ -24,31 +26,6 @@ MIN_ICON_SIZE = 16
24
26
  MAX_ICON_SIZE = 48
25
27
 
26
28
 
27
- def get_dpi_scale_factor():
28
- """Get the DPI scale factor for the primary screen.
29
-
30
- Returns:
31
- float: Scale factor (1.0 for standard 96 DPI, higher for HiDPI displays)
32
- """
33
- app = QApplication.instance()
34
- if app is None:
35
- return 1.0
36
-
37
- # Try to get the primary screen
38
- try:
39
- screen = app.primaryScreen()
40
- if screen:
41
- # Get logical DPI (accounts for user scaling settings)
42
- logical_dpi = screen.logicalDotsPerInch()
43
- # Standard DPI is 96 on most systems
44
- return logical_dpi / 96.0
45
- except AttributeError:
46
- # Qt4 fallback
47
- pass
48
-
49
- return 1.0
50
-
51
-
52
29
  def calculate_icon_size(base_size=BASE_ICON_SIZE):
53
30
  """Calculate appropriate icon size based on DPI.
54
31
 
@@ -84,8 +61,8 @@ class ToolBar(QToolBar):
84
61
 
85
62
  # Expand/collapse state
86
63
  self._expanded = False
87
- self._collapsed_width = 85
88
- self._expanded_width = 140
64
+ self._collapsed_width = scale_px(85)
65
+ self._expanded_width = scale_px(140)
89
66
  self._expand_btn = None
90
67
 
91
68
  def addAction(self, action):
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "labelimgplusplus"
7
- version = "2.3.3"
7
+ version = "2.4.0"
8
8
  description = "labelImg++ is an enhanced graphical image annotation tool with Gallery Mode for browsing and labeling images"
9
9
  readme = "README.rst"
10
10
  license = {file = "LICENSE"}