batoolset-gui 0.2.0__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.
Files changed (74) hide show
  1. batoolset/GUI/__init__.py +0 -0
  2. batoolset/GUI/customdialog.py +136 -0
  3. batoolset/GUI/exception_handler.py +74 -0
  4. batoolset/GUI/list_gui.py +514 -0
  5. batoolset/GUI/multi_image_display.py +1038 -0
  6. batoolset/GUI/open.py +325 -0
  7. batoolset/GUI/overlay_hints.py +45 -0
  8. batoolset/GUI/paint2.py +1369 -0
  9. batoolset/GUI/scrollablepaint.py +1165 -0
  10. batoolset/dialogs/__init__.py +0 -0
  11. batoolset/dialogs/opensave.py +228 -0
  12. batoolset/drawings/__init__.py +0 -0
  13. batoolset/drawings/math_text.py +294 -0
  14. batoolset/drawings/pdf_svg_renderer.py +376 -0
  15. batoolset/drawings/shapes/Position.py +183 -0
  16. batoolset/drawings/shapes/__init__.py +0 -0
  17. batoolset/drawings/shapes/automated_alignment_drawing_refactored.py +346 -0
  18. batoolset/drawings/shapes/circle2d.py +99 -0
  19. batoolset/drawings/shapes/compute_optimal_size_all_merged.py +328 -0
  20. batoolset/drawings/shapes/ellipse2d.py +336 -0
  21. batoolset/drawings/shapes/freehand2d.py +235 -0
  22. batoolset/drawings/shapes/group.py +1985 -0
  23. batoolset/drawings/shapes/image2d.py +3157 -0
  24. batoolset/drawings/shapes/line2d.py +579 -0
  25. batoolset/drawings/shapes/point2d.py +257 -0
  26. batoolset/drawings/shapes/polygon2d.py +357 -0
  27. batoolset/drawings/shapes/polyline2d.py +235 -0
  28. batoolset/drawings/shapes/rect2d.py +388 -0
  29. batoolset/drawings/shapes/rectangle2d.py +601 -0
  30. batoolset/drawings/shapes/scalebar.py +310 -0
  31. batoolset/drawings/shapes/serializablefont.py +133 -0
  32. batoolset/drawings/shapes/serializableguiparams.py +14 -0
  33. batoolset/drawings/shapes/square2d.py +102 -0
  34. batoolset/drawings/shapes/test_publication_style.py +220 -0
  35. batoolset/drawings/shapes/textorientation.py +45 -0
  36. batoolset/drawings/shapes/tools.py +467 -0
  37. batoolset/drawings/shapes/transform2D.py +350 -0
  38. batoolset/drawings/shapes/txt2d.py +615 -0
  39. batoolset/drawings/svg_clip_renderer.py +247 -0
  40. batoolset/drawings/svg_embed.py +165 -0
  41. batoolset/drawings/svg_text_renderer.py +183 -0
  42. batoolset/drawings/widgets/__init__.py +0 -0
  43. batoolset/drawings/widgets/col.py +343 -0
  44. batoolset/drawings/widgets/image_group.py +248 -0
  45. batoolset/drawings/widgets/image_organizer.py +96 -0
  46. batoolset/drawings/widgets/paint.py +184 -0
  47. batoolset/drawings/widgets/vectorial.py +256 -0
  48. batoolset/figure/__init__.py +0 -0
  49. batoolset/figure/alignment.py +822 -0
  50. batoolset/figure/column.py +1117 -0
  51. batoolset/figure/fig_tools.py +285 -0
  52. batoolset/figure/minimal_debugger_scale_etc.py +139 -0
  53. batoolset/figure/panel.py +697 -0
  54. batoolset/figure/row.py +852 -0
  55. batoolset/figure/user_selection.py +287 -0
  56. batoolset/icons/__init__.py +0 -0
  57. batoolset/icons/tools.py +248 -0
  58. batoolset/luts/__init__.py +0 -0
  59. batoolset/luts/lut_minimal_test.py +1308 -0
  60. batoolset/luts/lutcreatorfromformula.py +209 -0
  61. batoolset/luts/luteditor.py +243 -0
  62. batoolset/pyqts/__init__.py +0 -0
  63. batoolset/pyqts/pleasewaitdialog.py +14 -0
  64. batoolset/pyqts/tools.py +760 -0
  65. batoolset/serializations/__init__.py +0 -0
  66. batoolset/serializations/tools.py +1338 -0
  67. batoolset/theme.py +647 -0
  68. batoolset/uis/__init__.py +0 -0
  69. batoolset/uis/blinker.py +56 -0
  70. batoolset_gui-0.2.0.dist-info/METADATA +54 -0
  71. batoolset_gui-0.2.0.dist-info/RECORD +74 -0
  72. batoolset_gui-0.2.0.dist-info/WHEEL +5 -0
  73. batoolset_gui-0.2.0.dist-info/licenses/LICENSE +24 -0
  74. batoolset_gui-0.2.0.dist-info/top_level.txt +1 -0
File without changes
@@ -0,0 +1,136 @@
1
+ from qtpy import QtWidgets, QtCore
2
+ from qtpy.QtWidgets import (QDialog, QVBoxLayout, QLabel, QPushButton, QApplication, QWidget,QFrame)
3
+ from qtpy.QtCore import Qt,Signal
4
+
5
+ from batoolset.pyqts.tools import disable_wheel_for_combo_and_spin
6
+
7
+
8
+ # from batoolset.pyqts.tools import disable_wheel_for_widget_and_children
9
+
10
+
11
+ class CustomDialog(QDialog):
12
+ option_selected = Signal(str)
13
+ accepting_keywords = ["yes", "ok", "done", "validate", "y"]
14
+ rejecting_keywords = ["no", "cancel", "quit", "abandon", "reject", "stop", "n", "q"]
15
+
16
+ def __init__(self, title="Custom Dialog", message="Do you want to proceed?", main_widget=None, parent=None, options=None, add_separator=True, auto_adjust=False, reset_style_sheet=True):
17
+ super().__init__(parent)
18
+
19
+ if reset_style_sheet:
20
+ # self.setStyleSheet("") # make sure to reset style sheet # this doesn't work
21
+ self.setStyleSheet("background-color: none;") # make sure to reset style sheet
22
+ # main_widget.setStyleSheet("")
23
+ main_widget.setStyleSheet("background-color: none;")
24
+
25
+ # self.setStyleSheet("background-color: lightblue;")
26
+
27
+ # Set the object name for the widget
28
+ # self.setObjectName("VectorialDrawPane2")
29
+
30
+ # Set the background color
31
+ # if background is not None:
32
+ # self.setStyleSheet("") # prevent it from getting the style sheet of the parent
33
+
34
+ # Set the window flags to stay on top
35
+ self.setWindowFlags(self.windowFlags() | Qt.WindowStaysOnTopHint)
36
+
37
+ self.setWindowTitle(title)
38
+
39
+ self.main_widget = main_widget
40
+
41
+ layout = QVBoxLayout()
42
+ if main_widget:
43
+ # main_widget.setStyleSheet("") # also reset style sheet of content
44
+ layout.addWidget(main_widget)
45
+
46
+ # Add a separator
47
+ separator = QFrame()
48
+ separator.setFrameShape(QFrame.HLine)
49
+ separator.setFrameShadow(QFrame.Sunken)
50
+ layout.addWidget(separator)
51
+
52
+ if message:
53
+ label = QLabel(message)
54
+ layout.addWidget(label)
55
+
56
+ if options is None:
57
+ options = ["Yes", "No"]
58
+
59
+ if isinstance(options, str):
60
+ options=[options]
61
+
62
+ for option in options:
63
+ button = QPushButton(option)
64
+ button.setFocusPolicy(Qt.NoFocus)# prevent auto selection from buttons
65
+ layout.addWidget(button)
66
+ button.clicked.connect(lambda _, opt=option: self.handle_option_clicked(opt))
67
+
68
+ self.setLayout(layout)
69
+
70
+ # disable_wheel_for_widget_and_children(self)
71
+
72
+ # disable_wheel_for_combo_and_spin(self.main_widget)
73
+ # disable_wheel_for_combo_and_spin(self.main_widget)
74
+
75
+ if not auto_adjust:
76
+ try:
77
+ try:
78
+ # Get the screen where the application is currently displayed
79
+ screen = self.screen()
80
+ if screen is not None:
81
+ screen_geometry = screen.geometry()
82
+ else:
83
+ # If the application is not displayed on any screen, use the primary screen
84
+ screen_geometry = QtWidgets.QApplication.primaryScreen().geometry()
85
+ except:
86
+ try:
87
+ from qtpy.QtGui import QGuiApplication
88
+ screen_geometry = QGuiApplication.primaryScreen().geometry()
89
+ except:
90
+ pass # no big deal if all fails...
91
+ # Set width and height to 75% of the screen size
92
+ width = int(screen_geometry.width() * 0.85)
93
+ height = int(screen_geometry.height() * 0.85)
94
+ self.resize(width, height)
95
+ except:
96
+ pass # no big deal if it fails...
97
+ else:
98
+ # force the window to assume the smallest minimum size can be very useful when min size is set
99
+ self.adjustSize()
100
+
101
+ def handle_option_clicked(self, option):
102
+ self.selected_option = option
103
+ if option.lower() in CustomDialog.accepting_keywords:
104
+ self.accept() # Close the dialog with QDialog.Accepted status
105
+ elif option.lower() in CustomDialog.rejecting_keywords:
106
+ self.reject() # Close the dialog with QDialog.Rejected status
107
+ else:
108
+ self.accept() # Close the dialog with QDialog.Accepted status
109
+
110
+ # def exec_(self):
111
+ # result = super().exec_()
112
+ # if result == QDialog.Accepted:
113
+ # return self.selected_option
114
+ # else:
115
+ # return None
116
+
117
+ def get_selected_answer(self):
118
+ return self.selected_option
119
+
120
+ if __name__ == "__main__":
121
+ import sys
122
+ app = QApplication(sys.argv)
123
+
124
+ options = ["Option 1", "Option 2", "Option 3", "No"] # None
125
+ options = "Done"
126
+ message = "Do you want to proceed?" # "Do you want to proceed?" # None
127
+ auto_adjust = True # force the window to take minimal size if used (calls adjustsize)
128
+ custom_dialog = CustomDialog(title="Custom Dialog", message=message , main_widget=QWidget(), options=options, auto_adjust=auto_adjust)
129
+ custom_dialog.option_selected.connect(lambda option: print(f"Selected option: {option}"))
130
+ selected_option = custom_dialog.exec_()
131
+ print(selected_option)
132
+ if selected_option:
133
+ print(f"Selected option: {selected_option}")
134
+ print(custom_dialog.selected_option)
135
+ else:
136
+ print("Dialog was closed without selecting an option.")
@@ -0,0 +1,74 @@
1
+ """Qt-safe global exception handler.
2
+
3
+ Under PyQt6, an unhandled Python exception that propagates out of a slot, timer
4
+ callback or event handler makes Qt call ``qFatal()``/``abort()``. On Windows that
5
+ surfaces as a hard crash with exit code ``0xC0000409``
6
+ (STATUS_STACK_BUFFER_OVERRUN) — the whole application dies with no message and no
7
+ traceback, so it looks like a native stack overflow even though it is just a
8
+ Python exception (e.g. a UnicodeEncodeError while autosaving).
9
+
10
+ The important detail: PyQt only calls ``qFatal()`` when the *default*
11
+ ``sys.excepthook`` is installed. Installing a non-default hook both suppresses the
12
+ abort (the app keeps running) and lets us log the error and warn the user.
13
+ """
14
+ import sys
15
+ import traceback
16
+
17
+ from batoolset.tools.logger import TA_logger
18
+
19
+ logger = TA_logger()
20
+
21
+ _original_excepthook = sys.excepthook
22
+ _reporting = False # guard against re-entrancy while a dialog is being shown
23
+
24
+
25
+ def _show_dialog(short, details):
26
+ """Best-effort warning dialog; never let the reporter itself raise."""
27
+ try:
28
+ from qtpy.QtWidgets import QApplication, QMessageBox
29
+ if QApplication.instance() is None:
30
+ return
31
+ box = QMessageBox()
32
+ box.setIcon(QMessageBox.Warning)
33
+ box.setWindowTitle('An error occurred')
34
+ box.setText(short)
35
+ box.setInformativeText(
36
+ 'The application will keep running, but the last action may not have '
37
+ 'completed. Details are in the log.')
38
+ box.setDetailedText(details)
39
+ box.setStandardButtons(QMessageBox.Ok)
40
+ box.exec_()
41
+ except Exception:
42
+ pass
43
+
44
+
45
+ def global_excepthook(exctype, value, tb):
46
+ """Log the exception and warn the user instead of aborting the process."""
47
+ # keep Ctrl+C / normal interpreter-exit behaviour
48
+ if issubclass(exctype, (KeyboardInterrupt, SystemExit)):
49
+ _original_excepthook(exctype, value, tb)
50
+ return
51
+
52
+ details = ''.join(traceback.format_exception(exctype, value, tb))
53
+ try:
54
+ logger.error('Unhandled exception (suppressed to keep the app alive):\n' + details)
55
+ except Exception:
56
+ sys.stderr.write(details)
57
+
58
+ global _reporting
59
+ if _reporting:
60
+ # already showing a dialog (or we faulted while reporting) — don't stack them
61
+ return
62
+ _reporting = True
63
+ try:
64
+ _show_dialog(f'{exctype.__name__}: {value}', details)
65
+ finally:
66
+ _reporting = False
67
+
68
+
69
+ def install_global_exception_handler():
70
+ """Install the Qt-safe global exception hook. Idempotent."""
71
+ if getattr(sys, '_batoolset_excepthook_installed', False):
72
+ return
73
+ sys.excepthook = global_excepthook
74
+ sys._batoolset_excepthook_installed = True