LabelCraft 2.1.1__tar.gz → 2.1.2__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 (105) hide show
  1. {labelcraft-2.1.1 → labelcraft-2.1.2}/LabelCraft.egg-info/PKG-INFO +6 -1
  2. {labelcraft-2.1.1 → labelcraft-2.1.2}/LabelCraft.egg-info/SOURCES.txt +2 -0
  3. labelcraft-2.1.2/LabelCraft.egg-info/entry_points.txt +2 -0
  4. {labelcraft-2.1.1 → labelcraft-2.1.2}/PKG-INFO +6 -1
  5. {labelcraft-2.1.1 → labelcraft-2.1.2}/README.md +5 -0
  6. {labelcraft-2.1.1 → labelcraft-2.1.2}/labelcraft_ui.py +22 -1
  7. {labelcraft-2.1.1 → labelcraft-2.1.2}/libs/__init__.py +1 -1
  8. {labelcraft-2.1.1 → labelcraft-2.1.2}/libs/annotation_importer.py +13 -3
  9. labelcraft-2.1.2/libs/cli.py +49 -0
  10. {labelcraft-2.1.1 → labelcraft-2.1.2}/libs/locales/en.json +1 -0
  11. {labelcraft-2.1.1 → labelcraft-2.1.2}/libs/locales/zh-CN.json +1 -0
  12. labelcraft-2.1.2/libs/theme_manager.py +267 -0
  13. {labelcraft-2.1.1 → labelcraft-2.1.2}/setup.py +1 -1
  14. labelcraft-2.1.1/LabelCraft.egg-info/entry_points.txt +0 -2
  15. {labelcraft-2.1.1 → labelcraft-2.1.2}/LabelCraft.egg-info/dependency_links.txt +0 -0
  16. {labelcraft-2.1.1 → labelcraft-2.1.2}/LabelCraft.egg-info/not-zip-safe +0 -0
  17. {labelcraft-2.1.1 → labelcraft-2.1.2}/LabelCraft.egg-info/requires.txt +0 -0
  18. {labelcraft-2.1.1 → labelcraft-2.1.2}/LabelCraft.egg-info/top_level.txt +0 -0
  19. {labelcraft-2.1.1 → labelcraft-2.1.2}/MANIFEST.in +0 -0
  20. {labelcraft-2.1.1 → labelcraft-2.1.2}/data/coco_classes.txt +0 -0
  21. {labelcraft-2.1.1 → labelcraft-2.1.2}/data/predefined_classes.txt +0 -0
  22. {labelcraft-2.1.1 → labelcraft-2.1.2}/libs/annotation_converter.py +0 -0
  23. {labelcraft-2.1.1 → labelcraft-2.1.2}/libs/canvas.py +0 -0
  24. {labelcraft-2.1.1 → labelcraft-2.1.2}/libs/coco_io.py +0 -0
  25. {labelcraft-2.1.1 → labelcraft-2.1.2}/libs/colorDialog.py +0 -0
  26. {labelcraft-2.1.1 → labelcraft-2.1.2}/libs/combobox.py +0 -0
  27. {labelcraft-2.1.1 → labelcraft-2.1.2}/libs/constants.py +0 -0
  28. {labelcraft-2.1.1 → labelcraft-2.1.2}/libs/create_ml_io.py +0 -0
  29. {labelcraft-2.1.1 → labelcraft-2.1.2}/libs/csv_io.py +0 -0
  30. {labelcraft-2.1.1 → labelcraft-2.1.2}/libs/default_label_combobox.py +0 -0
  31. {labelcraft-2.1.1 → labelcraft-2.1.2}/libs/hashableQListWidgetItem.py +0 -0
  32. {labelcraft-2.1.1 → labelcraft-2.1.2}/libs/i18n_engine.py +0 -0
  33. {labelcraft-2.1.1 → labelcraft-2.1.2}/libs/json_io.py +0 -0
  34. {labelcraft-2.1.1 → labelcraft-2.1.2}/libs/labelDialog.py +0 -0
  35. {labelcraft-2.1.1 → labelcraft-2.1.2}/libs/labelFile.py +0 -0
  36. {labelcraft-2.1.1 → labelcraft-2.1.2}/libs/lightWidget.py +0 -0
  37. {labelcraft-2.1.1 → labelcraft-2.1.2}/libs/locales/de-DE.json +0 -0
  38. {labelcraft-2.1.1 → labelcraft-2.1.2}/libs/locales/fr-FR.json +0 -0
  39. {labelcraft-2.1.1 → labelcraft-2.1.2}/libs/locales/ja-JP.json +0 -0
  40. {labelcraft-2.1.1 → labelcraft-2.1.2}/libs/locales/zh-TW.json +0 -0
  41. {labelcraft-2.1.1 → labelcraft-2.1.2}/libs/newProjectDialog.py +0 -0
  42. {labelcraft-2.1.1 → labelcraft-2.1.2}/libs/pascal_voc_io.py +0 -0
  43. {labelcraft-2.1.1 → labelcraft-2.1.2}/libs/project.py +0 -0
  44. {labelcraft-2.1.1 → labelcraft-2.1.2}/libs/resources.py +0 -0
  45. {labelcraft-2.1.1 → labelcraft-2.1.2}/libs/settings.py +0 -0
  46. {labelcraft-2.1.1 → labelcraft-2.1.2}/libs/shape.py +0 -0
  47. {labelcraft-2.1.1 → labelcraft-2.1.2}/libs/toolBar.py +0 -0
  48. {labelcraft-2.1.1 → labelcraft-2.1.2}/libs/ustr.py +0 -0
  49. {labelcraft-2.1.1 → labelcraft-2.1.2}/libs/utils.py +0 -0
  50. {labelcraft-2.1.1 → labelcraft-2.1.2}/libs/yolo_io.py +0 -0
  51. {labelcraft-2.1.1 → labelcraft-2.1.2}/libs/zoomWidget.py +0 -0
  52. {labelcraft-2.1.1 → labelcraft-2.1.2}/main.py +0 -0
  53. {labelcraft-2.1.1 → labelcraft-2.1.2}/resources/icons/app.icns +0 -0
  54. {labelcraft-2.1.1 → labelcraft-2.1.2}/resources/icons/app.png +0 -0
  55. {labelcraft-2.1.1 → labelcraft-2.1.2}/resources/icons/app.svg +0 -0
  56. {labelcraft-2.1.1 → labelcraft-2.1.2}/resources/icons/app_screen.png +0 -0
  57. {labelcraft-2.1.1 → labelcraft-2.1.2}/resources/icons/cancel.png +0 -0
  58. {labelcraft-2.1.1 → labelcraft-2.1.2}/resources/icons/close.png +0 -0
  59. {labelcraft-2.1.1 → labelcraft-2.1.2}/resources/icons/color.png +0 -0
  60. {labelcraft-2.1.1 → labelcraft-2.1.2}/resources/icons/color_line.png +0 -0
  61. {labelcraft-2.1.1 → labelcraft-2.1.2}/resources/icons/copy.png +0 -0
  62. {labelcraft-2.1.1 → labelcraft-2.1.2}/resources/icons/delete.png +0 -0
  63. {labelcraft-2.1.1 → labelcraft-2.1.2}/resources/icons/done.png +0 -0
  64. {labelcraft-2.1.1 → labelcraft-2.1.2}/resources/icons/done.svg +0 -0
  65. {labelcraft-2.1.1 → labelcraft-2.1.2}/resources/icons/edit.png +0 -0
  66. {labelcraft-2.1.1 → labelcraft-2.1.2}/resources/icons/expert1.png +0 -0
  67. {labelcraft-2.1.1 → labelcraft-2.1.2}/resources/icons/expert2.png +0 -0
  68. {labelcraft-2.1.1 → labelcraft-2.1.2}/resources/icons/eye.png +0 -0
  69. {labelcraft-2.1.1 → labelcraft-2.1.2}/resources/icons/feBlend-icon.png +0 -0
  70. {labelcraft-2.1.1 → labelcraft-2.1.2}/resources/icons/file.png +0 -0
  71. {labelcraft-2.1.1 → labelcraft-2.1.2}/resources/icons/fit-width.png +0 -0
  72. {labelcraft-2.1.1 → labelcraft-2.1.2}/resources/icons/fit-window.png +0 -0
  73. {labelcraft-2.1.1 → labelcraft-2.1.2}/resources/icons/fit.png +0 -0
  74. {labelcraft-2.1.1 → labelcraft-2.1.2}/resources/icons/format_createml.png +0 -0
  75. {labelcraft-2.1.1 → labelcraft-2.1.2}/resources/icons/format_voc.png +0 -0
  76. {labelcraft-2.1.1 → labelcraft-2.1.2}/resources/icons/format_yolo.png +0 -0
  77. {labelcraft-2.1.1 → labelcraft-2.1.2}/resources/icons/help.png +0 -0
  78. {labelcraft-2.1.1 → labelcraft-2.1.2}/resources/icons/labels.png +0 -0
  79. {labelcraft-2.1.1 → labelcraft-2.1.2}/resources/icons/labels.svg +0 -0
  80. {labelcraft-2.1.1 → labelcraft-2.1.2}/resources/icons/light_brighten.svg +0 -0
  81. {labelcraft-2.1.1 → labelcraft-2.1.2}/resources/icons/light_darken.png +0 -0
  82. {labelcraft-2.1.1 → labelcraft-2.1.2}/resources/icons/light_darken.svg +0 -0
  83. {labelcraft-2.1.1 → labelcraft-2.1.2}/resources/icons/light_lighten.png +0 -0
  84. {labelcraft-2.1.1 → labelcraft-2.1.2}/resources/icons/light_reset.png +0 -0
  85. {labelcraft-2.1.1 → labelcraft-2.1.2}/resources/icons/light_reset.svg +0 -0
  86. {labelcraft-2.1.1 → labelcraft-2.1.2}/resources/icons/new.png +0 -0
  87. {labelcraft-2.1.1 → labelcraft-2.1.2}/resources/icons/next.png +0 -0
  88. {labelcraft-2.1.1 → labelcraft-2.1.2}/resources/icons/objects.png +0 -0
  89. {labelcraft-2.1.1 → labelcraft-2.1.2}/resources/icons/open.png +0 -0
  90. {labelcraft-2.1.1 → labelcraft-2.1.2}/resources/icons/open.svg +0 -0
  91. {labelcraft-2.1.1 → labelcraft-2.1.2}/resources/icons/prev.png +0 -0
  92. {labelcraft-2.1.1 → labelcraft-2.1.2}/resources/icons/quit.png +0 -0
  93. {labelcraft-2.1.1 → labelcraft-2.1.2}/resources/icons/resetall.png +0 -0
  94. {labelcraft-2.1.1 → labelcraft-2.1.2}/resources/icons/save-as.png +0 -0
  95. {labelcraft-2.1.1 → labelcraft-2.1.2}/resources/icons/save-as.svg +0 -0
  96. {labelcraft-2.1.1 → labelcraft-2.1.2}/resources/icons/save.png +0 -0
  97. {labelcraft-2.1.1 → labelcraft-2.1.2}/resources/icons/save.svg +0 -0
  98. {labelcraft-2.1.1 → labelcraft-2.1.2}/resources/icons/undo-cross.png +0 -0
  99. {labelcraft-2.1.1 → labelcraft-2.1.2}/resources/icons/undo.png +0 -0
  100. {labelcraft-2.1.1 → labelcraft-2.1.2}/resources/icons/verify.png +0 -0
  101. {labelcraft-2.1.1 → labelcraft-2.1.2}/resources/icons/zoom-in.png +0 -0
  102. {labelcraft-2.1.1 → labelcraft-2.1.2}/resources/icons/zoom-out.png +0 -0
  103. {labelcraft-2.1.1 → labelcraft-2.1.2}/resources/icons/zoom.png +0 -0
  104. {labelcraft-2.1.1 → labelcraft-2.1.2}/resources.qrc +0 -0
  105. {labelcraft-2.1.1 → labelcraft-2.1.2}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: LabelCraft
3
- Version: 2.1.1
3
+ Version: 2.1.2
4
4
  Summary: LabelCraft - A modern graphical image annotation tool based on labelImg
5
5
  Home-page: https://github.com/syd168/LabelCraft
6
6
  Author: LabelCraft Contributors
@@ -99,6 +99,11 @@ Dynamic language switching without restart:
99
99
  - Python API for integration
100
100
  - Customizable shortcuts
101
101
  - Brightness adjustment
102
+ - **Cross-Platform Dark Mode** (v2.1.1+)
103
+ - Windows 11/10: Automatic registry detection
104
+ - Linux (GNOME/KDE/Ubuntu): dconf/gsettings support
105
+ - macOS: System appearance detection
106
+ - See [Windows 11 Dark Mode Guide](doc/WINDOWS11_DARK_MODE.md) for setup
102
107
 
103
108
  ## 📸 Screenshot
104
109
 
@@ -18,6 +18,7 @@ libs/__init__.py
18
18
  libs/annotation_converter.py
19
19
  libs/annotation_importer.py
20
20
  libs/canvas.py
21
+ libs/cli.py
21
22
  libs/coco_io.py
22
23
  libs/colorDialog.py
23
24
  libs/combobox.py
@@ -37,6 +38,7 @@ libs/project.py
37
38
  libs/resources.py
38
39
  libs/settings.py
39
40
  libs/shape.py
41
+ libs/theme_manager.py
40
42
  libs/toolBar.py
41
43
  libs/ustr.py
42
44
  libs/utils.py
@@ -0,0 +1,2 @@
1
+ [gui_scripts]
2
+ labelcraft = libs.cli:main
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: LabelCraft
3
- Version: 2.1.1
3
+ Version: 2.1.2
4
4
  Summary: LabelCraft - A modern graphical image annotation tool based on labelImg
5
5
  Home-page: https://github.com/syd168/LabelCraft
6
6
  Author: LabelCraft Contributors
@@ -99,6 +99,11 @@ Dynamic language switching without restart:
99
99
  - Python API for integration
100
100
  - Customizable shortcuts
101
101
  - Brightness adjustment
102
+ - **Cross-Platform Dark Mode** (v2.1.1+)
103
+ - Windows 11/10: Automatic registry detection
104
+ - Linux (GNOME/KDE/Ubuntu): dconf/gsettings support
105
+ - macOS: System appearance detection
106
+ - See [Windows 11 Dark Mode Guide](doc/WINDOWS11_DARK_MODE.md) for setup
102
107
 
103
108
  ## 📸 Screenshot
104
109
 
@@ -62,6 +62,11 @@ Dynamic language switching without restart:
62
62
  - Python API for integration
63
63
  - Customizable shortcuts
64
64
  - Brightness adjustment
65
+ - **Cross-Platform Dark Mode** (v2.1.1+)
66
+ - Windows 11/10: Automatic registry detection
67
+ - Linux (GNOME/KDE/Ubuntu): dconf/gsettings support
68
+ - macOS: System appearance detection
69
+ - See [Windows 11 Dark Mode Guide](doc/WINDOWS11_DARK_MODE.md) for setup
65
70
 
66
71
  ## 📸 Screenshot
67
72
 
@@ -40,6 +40,7 @@ from libs.ustr import ustr
40
40
  from libs.hashableQListWidgetItem import HashableQListWidgetItem
41
41
  from libs.project import Project, RecentProjectsManager
42
42
  from libs.newProjectDialog import NewProjectDialog
43
+ from libs.theme_manager import apply_system_theme
43
44
  from libs import __version__
44
45
 
45
46
  __appname__ = 'LabelCraft'
@@ -4896,7 +4897,23 @@ class MainWindow(QMainWindow, WindowMixin):
4896
4897
  print(f"Using {len(classes_list)} classes from label history")
4897
4898
  else:
4898
4899
  classes_list = []
4899
- print("Warning: No classes list available. YOLO/COCO export may fail.")
4900
+ print("Warning: No classes list available.")
4901
+
4902
+ # For YOLO and COCO formats, show warning and ask user to continue
4903
+ if selected_format[0] in [LabelFileFormat.YOLO, LabelFileFormat.COCO]:
4904
+ reply = QMessageBox.warning(
4905
+ self,
4906
+ self.get_str('warningTitle'),
4907
+ f'{self.get_str("exportNoLabelsWarning")}\n\n'
4908
+ f'Project has no defined labels. Export to {selected_format[0].name} format requires labels.\n\n'
4909
+ f'Options:\n'
4910
+ f'1. Add labels to project first (recommended)\n'
4911
+ f'2. Continue with empty labels (annotations will use numeric IDs)',
4912
+ QMessageBox.StandardButton.Cancel | QMessageBox.StandardButton.Ok,
4913
+ QMessageBox.StandardButton.Cancel
4914
+ )
4915
+ if reply == QMessageBox.StandardButton.Cancel:
4916
+ return
4900
4917
 
4901
4918
  try:
4902
4919
  # Iterate through all annotation files
@@ -5293,6 +5310,10 @@ def get_main_app(argv=None):
5293
5310
  app.setApplicationName(__appname__)
5294
5311
  app.setWindowIcon(new_icon("app"))
5295
5312
 
5313
+ # Apply system theme (light/dark mode)
5314
+ # This will automatically follow the system's theme preference
5315
+ apply_system_theme(app)
5316
+
5296
5317
  # Tzutalin 201705+: Accept extra arguments to change predefined class file
5297
5318
  argparser = argparse.ArgumentParser(
5298
5319
  prog='labelcraft',
@@ -1,2 +1,2 @@
1
- __version_info__ = ('2', '1', '1')
1
+ __version_info__ = ('2', '1', '2')
2
2
  __version__ = '.'.join(__version_info__)
@@ -817,6 +817,8 @@ class AnnotationImporter:
817
817
 
818
818
  # Get classes list - prioritize project labels over source labels
819
819
  # This ensures imported annotations match the project's label definitions
820
+ labels_added_to_project = False
821
+
820
822
  if self.current_project.labels:
821
823
  # Project has defined labels, use them (most important)
822
824
  classes_list = self.current_project.labels
@@ -828,10 +830,18 @@ class AnnotationImporter:
828
830
  print(f"Imported annotations will be mapped to project labels by class ID.")
829
831
  print(f"This may cause incorrect labels if the class order differs!")
830
832
  elif yolo_labels:
831
- # No project labels, use YOLO labels from data.yaml
833
+ # No project labels, use YOLO labels from data.yaml AND add them to project
832
834
  classes_list = yolo_labels
833
- print(f"Using {len(classes_list)} classes from YOLO data.yaml: {classes_list[:5]}...")
834
- print(f"Tip: Consider adding these labels to your project for consistency.")
835
+ print(f"Project has no labels. Using {len(classes_list)} classes from YOLO data.yaml: {classes_list[:5]}...")
836
+
837
+ # Automatically add these labels to the project
838
+ try:
839
+ self.current_project.labels = classes_list.copy()
840
+ self.current_project.save() # Save project to persist labels
841
+ labels_added_to_project = True
842
+ print(f"✓ Added {len(classes_list)} labels to project automatically")
843
+ except Exception as e:
844
+ print(f"Warning: Failed to add labels to project: {e}")
835
845
  else:
836
846
  # No labels available at all
837
847
  classes_list = []
@@ -0,0 +1,49 @@
1
+ #!/usr/bin/env python
2
+ # -*- coding: utf-8 -*-
3
+ """
4
+ LabelCraft CLI Entry Point
5
+ This module serves as the proper entry point for the labelcraft command,
6
+ ensuring it works correctly regardless of the installation location.
7
+ """
8
+ import sys
9
+ import os
10
+
11
+ def main():
12
+ """
13
+ Main entry point for LabelCraft command-line interface.
14
+ This ensures proper module resolution and data file loading
15
+ from any installation location.
16
+ """
17
+ # Get the directory where this module is located
18
+ module_dir = os.path.dirname(os.path.abspath(__file__))
19
+ # Get the parent directory (project root)
20
+ project_root = os.path.dirname(module_dir)
21
+
22
+ # Add project root and module directory to Python path for proper imports
23
+ if project_root not in sys.path:
24
+ sys.path.insert(0, project_root)
25
+ if module_dir not in sys.path:
26
+ sys.path.insert(0, module_dir)
27
+
28
+ try:
29
+ from PySide6.QtWidgets import QApplication
30
+ from labelcraft_ui import get_main_app
31
+
32
+ # Create and run the application
33
+ app, _win = get_main_app(sys.argv)
34
+ sys.exit(app.exec())
35
+
36
+ except ImportError as e:
37
+ print(f"Error: Failed to import required modules: {e}")
38
+ print("Please ensure PySide6 and lxml are installed:")
39
+ print(" pip install pyside6>=6.5.0 lxml>=4.9.0")
40
+ sys.exit(1)
41
+ except Exception as e:
42
+ print(f"Error: Failed to start LabelCraft: {e}")
43
+ import traceback
44
+ traceback.print_exc()
45
+ sys.exit(1)
46
+
47
+
48
+ if __name__ == '__main__':
49
+ main()
@@ -518,5 +518,6 @@
518
518
  "setVerifiedDetail": "Mark selected annotations as verified",
519
519
  "setUnverifiedDetail": "Mark selected annotations as unverified",
520
520
  "showVerificationStatusDetail": "Toggle showing verification status color in completed annotations list",
521
+ "exportNoLabelsWarning": "⚠️ No Labels Defined\n\nProject has no defined labels. Export to this format requires labels for proper conversion.",
521
522
  "tutorialContent": "<div style='font-size: 14px; line-height: 1.8;'>\n<h2 style='text-align: center; margin-bottom: 20px;'>Welcome to LabelCraft!</h2>\n\n<h3 style='margin-top: 20px; font-weight: bold;'>Basic Annotation Steps:</h3>\n<p><b>1. Create/Open Project:</b> File → New Project or Open Project<br>\n<b>2. Add Images:</b> Click \"+ Add Images\" or \"+ Add Folder\" in the bottom panel<br>\n<b>3. Annotate:</b> Double-click an image, then press 'W' to draw bounding boxes<br>\n<b>4. Save:</b> Press Ctrl+S to save annotations (auto-saves when moving to next image)<br>\n<b>5. Export:</b> Output → Export to convert annotations to desired format</p>\n\n<h3 style='margin-top: 20px; font-weight: bold;'>Helpful Tips:</h3>\n<p><b>• Shortcuts:</b> Help → Keyboard shortcuts to view all shortcuts<br>\n<b>• Labels:</b> Use the right panel to filter and manage labels<br>\n<b>• Navigation:</b> Mouse wheel to zoom, drag to pan<br>\n<b>• Brightness:</b> Adjust image brightness with slider in toolbar</p>\n\n<h3 style='margin-top: 20px; font-weight: bold;'>For more detailed documentation:</h3>\n<p>• <a href='https://github.com/syd168/LabelCraft'>Visit our GitHub repository for complete tutorials and examples</a><br>\n• <a href='https://github.com/syd168/LabelCraft/blob/master/README.md'>Documentation & Tutorials</a><br>\n• <a href='https://github.com/syd168/LabelCraft/issues'>Report Issues</a></p>\n</div>"
522
523
  }
@@ -518,5 +518,6 @@
518
518
  "setVerifiedDetail": "将选中的标注标记为已验证",
519
519
  "setUnverifiedDetail": "将选中的标注标记为未验证",
520
520
  "showVerificationStatusDetail": "切换在已完成标注列表中显示验证状态颜色",
521
+ "exportNoLabelsWarning": "⚠️ 未定义标签\n\n项目没有定义标签。导出到此格式需要标签才能正确转换。",
521
522
  "tutorialContent": "<div style='font-size: 14px; line-height: 1.8;'>\n<h2 style='text-align: center; margin-bottom: 20px;'>欢迎使用 LabelCraft!</h2>\n\n<h3 style='margin-top: 20px; font-weight: bold;'>基本标注步骤:</h3>\n<p><b>1. 创建/打开项目:</b> 文件 → 新建项目 或 打开项目<br>\n<b>2. 添加图像:</b> 点击底部面板的 \"+ 添加图像\" 或 \"+ 添加文件夹\"<br>\n<b>3. 标注:</b> 双击图像,然后按 'W' 键绘制边界框<br>\n<b>4. 保存:</b> 按 Ctrl+S 保存标注(切换到下一张图像时自动保存)<br>\n<b>5. 导出:</b> 输出 → 导出 将标注转换为所需格式</p>\n\n<h3 style='margin-top: 20px; font-weight: bold;'>实用技巧:</h3>\n<p><b>• 快捷键:</b> 帮助 → 键盘快捷键 查看所有快捷键<br>\n<b>• 标签管理:</b> 使用右侧面板过滤和管理标签<br>\n<b>• 导航:</b> 鼠标滚轮缩放,拖拽平移<br>\n<b>• 亮度:</b> 使用工具栏中的滑块调整图像亮度</p>\n\n<h3 style='margin-top: 20px; font-weight: bold;'>更多详细文档:</h3>\n<p>• <a href='https://github.com/syd168/LabelCraft'>访问我们的 GitHub 仓库获取完整教程和示例</a><br>\n• <a href='https://github.com/syd168/LabelCraft/blob/master/README.md'>文档与教程</a><br>\n• <a href='https://github.com/syd168/LabelCraft/issues'>报告问题</a></p>\n</div>"
522
523
  }
@@ -0,0 +1,267 @@
1
+ #!/usr/bin/env python
2
+ # -*- coding: utf-8 -*-
3
+ """
4
+ LabelCraft Theme Manager
5
+ Provides automatic system theme detection and application.
6
+ Supports light/dark mode following system settings on:
7
+ - Linux (GNOME, KDE, Ubuntu)
8
+ - Windows (Windows 10, Windows 11)
9
+ - macOS
10
+ """
11
+ from PySide6.QtGui import QPalette, QColor
12
+ from PySide6.QtWidgets import QApplication
13
+ from PySide6.QtCore import Qt
14
+ import platform
15
+ import os
16
+
17
+
18
+ def _create_dark_palette():
19
+ """
20
+ Create a comprehensive dark theme palette.
21
+
22
+ Returns:
23
+ QPalette: Configured dark palette
24
+ """
25
+ dark_palette = QPalette()
26
+
27
+ # Base colors for dark theme
28
+ dark_bg = QColor(53, 53, 53)
29
+ darker_bg = QColor(25, 25, 25)
30
+ light_text = QColor(255, 255, 255)
31
+ link_color = QColor(42, 130, 218)
32
+
33
+ # Window and base colors
34
+ dark_palette.setColor(QPalette.ColorRole.Window, dark_bg)
35
+ dark_palette.setColor(QPalette.ColorRole.Base, darker_bg)
36
+ dark_palette.setColor(QPalette.ColorRole.AlternateBase, dark_bg)
37
+
38
+ # Text colors
39
+ dark_palette.setColor(QPalette.ColorRole.WindowText, light_text)
40
+ dark_palette.setColor(QPalette.ColorRole.Text, light_text)
41
+ dark_palette.setColor(QPalette.ColorRole.ButtonText, light_text)
42
+
43
+ # Button and control colors
44
+ dark_palette.setColor(QPalette.ColorRole.Button, dark_bg)
45
+ dark_palette.setColor(QPalette.ColorRole.Shadow, QColor(20, 20, 20))
46
+ dark_palette.setColor(QPalette.ColorRole.Highlight, link_color)
47
+ dark_palette.setColor(QPalette.ColorRole.HighlightedText, QColor(255, 255, 255))
48
+
49
+ # Link colors
50
+ dark_palette.setColor(QPalette.ColorRole.Link, link_color)
51
+
52
+ # Tooltips
53
+ dark_palette.setColor(QPalette.ColorRole.ToolTipBase, light_text)
54
+ dark_palette.setColor(QPalette.ColorRole.ToolTipText, dark_bg)
55
+
56
+ # Disabled state
57
+ dark_palette.setColor(QPalette.ColorGroup.Disabled, QPalette.ColorRole.WindowText, QColor(128, 128, 128))
58
+ dark_palette.setColor(QPalette.ColorGroup.Disabled, QPalette.ColorRole.Text, QColor(128, 128, 128))
59
+ dark_palette.setColor(QPalette.ColorGroup.Disabled, QPalette.ColorRole.ButtonText, QColor(128, 128, 128))
60
+
61
+ return dark_palette
62
+
63
+
64
+ def _create_light_palette():
65
+ """
66
+ Create a comprehensive light theme palette.
67
+
68
+ Returns:
69
+ QPalette: Configured light palette
70
+ """
71
+ light_palette = QPalette()
72
+
73
+ # Use system default light colors
74
+ # Light palette is typically the default, so we return a new instance
75
+ return light_palette
76
+
77
+
78
+ def _detect_windows_dark_mode():
79
+ """
80
+ Detect if Windows 11/10 is using dark mode.
81
+
82
+ Checks the Windows Registry:
83
+ HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Themes\Personalize
84
+ AppsUseLightTheme = 0 means dark mode
85
+
86
+ Returns:
87
+ bool: True if dark mode detected, False otherwise
88
+ """
89
+ try:
90
+ import winreg
91
+ try:
92
+ # Try to open the registry key
93
+ key = winreg.OpenKey(
94
+ winreg.HKEY_CURRENT_USER,
95
+ r'Software\Microsoft\Windows\CurrentVersion\Themes\Personalize'
96
+ )
97
+ # Read AppsUseLightTheme value
98
+ # 0 = dark mode, 1 = light mode
99
+ value, regtype = winreg.QueryValueEx(key, 'AppsUseLightTheme')
100
+ winreg.CloseKey(key)
101
+ return value == 0
102
+ except Exception:
103
+ # Key or value not found - assume light mode
104
+ return False
105
+ except ImportError:
106
+ # winreg not available (not on Windows)
107
+ return False
108
+ except Exception:
109
+ # Any other error - assume light mode
110
+ return False
111
+
112
+
113
+ def _detect_linux_dark_mode():
114
+ """
115
+ Detect if Linux desktop environment is using dark mode.
116
+
117
+ Supports:
118
+ - GNOME/Ubuntu (via dconf)
119
+ - KDE Plasma (via kdeglobals)
120
+
121
+ Returns:
122
+ bool: True if dark mode detected, False otherwise
123
+ """
124
+ # Method 1: Check GNOME/Ubuntu GTK theme via dconf
125
+ try:
126
+ import subprocess
127
+ result = subprocess.run(
128
+ ['dconf', 'read', '/org/gnome/desktop/interface/gtk-theme'],
129
+ capture_output=True,
130
+ text=True,
131
+ timeout=2
132
+ )
133
+ if result.returncode == 0:
134
+ theme_name = result.stdout.strip().strip("'\"")
135
+ if 'dark' in theme_name.lower():
136
+ return True
137
+ except (FileNotFoundError, subprocess.TimeoutExpired, Exception):
138
+ pass
139
+
140
+ # Method 2: Check GNOME settings (alternative method)
141
+ try:
142
+ import subprocess
143
+ result = subprocess.run(
144
+ ['gsettings', 'get', 'org.gnome.desktop.interface', 'gtk-application-prefer-dark-theme'],
145
+ capture_output=True,
146
+ text=True,
147
+ timeout=2
148
+ )
149
+ if result.returncode == 0:
150
+ return 'true' in result.stdout.lower()
151
+ except (FileNotFoundError, subprocess.TimeoutExpired, Exception):
152
+ pass
153
+
154
+ # Method 3: Check environment variables
155
+ if os.environ.get('GTK_THEME', '').lower().endswith('-dark') or 'dark' in os.environ.get('GTK_THEME', '').lower():
156
+ return True
157
+
158
+ if 'dark' in os.environ.get('QT_STYLE_OVERRIDE', '').lower():
159
+ return True
160
+
161
+ # Method 4: Check Plasma/KDE settings
162
+ try:
163
+ config_path = os.path.expanduser('~/.config/kdeglobals')
164
+ if os.path.exists(config_path):
165
+ with open(config_path, 'r') as f:
166
+ content = f.read()
167
+ if '[General]' in content and 'ColorScheme=' in content:
168
+ for line in content.split('\n'):
169
+ if 'ColorScheme=' in line and 'dark' in line.lower():
170
+ return True
171
+ except Exception:
172
+ pass
173
+
174
+ return False
175
+
176
+
177
+ def _detect_macos_dark_mode():
178
+ """
179
+ Detect if macOS is using dark mode.
180
+
181
+ Uses `defaults read` to check the system appearance setting.
182
+
183
+ Returns:
184
+ bool: True if dark mode detected, False otherwise
185
+ """
186
+ try:
187
+ import subprocess
188
+ result = subprocess.run(
189
+ ['defaults', 'read', '-g', 'AppleInterfaceStyle'],
190
+ capture_output=True,
191
+ text=True,
192
+ timeout=2
193
+ )
194
+ # If the command returns 'Dark', dark mode is enabled
195
+ return 'Dark' in result.stdout or result.returncode == 0
196
+ except (FileNotFoundError, subprocess.TimeoutExpired, Exception):
197
+ # If the command fails, assume light mode (default on macOS)
198
+ return False
199
+
200
+
201
+ def _detect_system_dark_mode():
202
+ """
203
+ Detect if the system is using dark mode.
204
+
205
+ Tries multiple methods based on the operating system:
206
+ - Windows: Registry check (AppsUseLightTheme)
207
+ - Linux: dconf, gsettings, or environment variables
208
+ - macOS: defaults read
209
+
210
+ Returns:
211
+ bool: True if dark mode detected, False otherwise
212
+ """
213
+ system = platform.system()
214
+
215
+ if system == 'Windows':
216
+ return _detect_windows_dark_mode()
217
+ elif system == 'Linux':
218
+ return _detect_linux_dark_mode()
219
+ elif system == 'Darwin': # macOS
220
+ return _detect_macos_dark_mode()
221
+ else:
222
+ # Unknown system - assume light mode
223
+ return False
224
+
225
+
226
+ def apply_system_theme(app):
227
+ """
228
+ Apply system theme (light/dark mode) to the application.
229
+
230
+ This function detects the system's current theme preference
231
+ and applies it to the QApplication. The application will
232
+ automatically use the correct colors and styles based on
233
+ the system's light/dark mode setting.
234
+
235
+ Args:
236
+ app (QApplication): The Qt application instance
237
+ """
238
+ # Use Fusion style for better cross-platform theme support
239
+ app.setStyle('Fusion')
240
+
241
+ # Detect if system is in dark mode
242
+ is_dark = _detect_system_dark_mode()
243
+
244
+ # Apply the appropriate palette
245
+ if is_dark:
246
+ dark_palette = _create_dark_palette()
247
+ app.setPalette(dark_palette)
248
+ else:
249
+ # Light mode uses default palette
250
+ light_palette = _create_light_palette()
251
+ app.setPalette(light_palette)
252
+
253
+ # Return theme info
254
+ return {
255
+ 'is_dark_mode': is_dark,
256
+ 'palette': app.palette()
257
+ }
258
+
259
+
260
+ def is_system_dark_mode():
261
+ """
262
+ Detect if the system is using dark mode.
263
+
264
+ Returns:
265
+ bool: True if dark mode is detected, False for light mode
266
+ """
267
+ return _detect_system_dark_mode()
@@ -120,7 +120,7 @@ setup(
120
120
  },
121
121
  entry_points={
122
122
  'gui_scripts': [
123
- 'labelcraft=main:main',
123
+ 'labelcraft=libs.cli:main',
124
124
  ]
125
125
  },
126
126
  include_package_data=True,
@@ -1,2 +0,0 @@
1
- [gui_scripts]
2
- labelcraft = main:main
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes