batoolset-gui 0.2.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 (79) hide show
  1. batoolset_gui-0.2.0/LICENSE +24 -0
  2. batoolset_gui-0.2.0/PKG-INFO +54 -0
  3. batoolset_gui-0.2.0/README.md +22 -0
  4. batoolset_gui-0.2.0/batoolset/GUI/__init__.py +0 -0
  5. batoolset_gui-0.2.0/batoolset/GUI/customdialog.py +136 -0
  6. batoolset_gui-0.2.0/batoolset/GUI/exception_handler.py +74 -0
  7. batoolset_gui-0.2.0/batoolset/GUI/list_gui.py +514 -0
  8. batoolset_gui-0.2.0/batoolset/GUI/multi_image_display.py +1038 -0
  9. batoolset_gui-0.2.0/batoolset/GUI/open.py +325 -0
  10. batoolset_gui-0.2.0/batoolset/GUI/overlay_hints.py +45 -0
  11. batoolset_gui-0.2.0/batoolset/GUI/paint2.py +1369 -0
  12. batoolset_gui-0.2.0/batoolset/GUI/scrollablepaint.py +1165 -0
  13. batoolset_gui-0.2.0/batoolset/dialogs/__init__.py +0 -0
  14. batoolset_gui-0.2.0/batoolset/dialogs/opensave.py +228 -0
  15. batoolset_gui-0.2.0/batoolset/drawings/__init__.py +0 -0
  16. batoolset_gui-0.2.0/batoolset/drawings/math_text.py +294 -0
  17. batoolset_gui-0.2.0/batoolset/drawings/pdf_svg_renderer.py +376 -0
  18. batoolset_gui-0.2.0/batoolset/drawings/shapes/Position.py +183 -0
  19. batoolset_gui-0.2.0/batoolset/drawings/shapes/__init__.py +0 -0
  20. batoolset_gui-0.2.0/batoolset/drawings/shapes/automated_alignment_drawing_refactored.py +346 -0
  21. batoolset_gui-0.2.0/batoolset/drawings/shapes/circle2d.py +99 -0
  22. batoolset_gui-0.2.0/batoolset/drawings/shapes/compute_optimal_size_all_merged.py +328 -0
  23. batoolset_gui-0.2.0/batoolset/drawings/shapes/ellipse2d.py +336 -0
  24. batoolset_gui-0.2.0/batoolset/drawings/shapes/freehand2d.py +235 -0
  25. batoolset_gui-0.2.0/batoolset/drawings/shapes/group.py +1985 -0
  26. batoolset_gui-0.2.0/batoolset/drawings/shapes/image2d.py +3157 -0
  27. batoolset_gui-0.2.0/batoolset/drawings/shapes/line2d.py +579 -0
  28. batoolset_gui-0.2.0/batoolset/drawings/shapes/point2d.py +257 -0
  29. batoolset_gui-0.2.0/batoolset/drawings/shapes/polygon2d.py +357 -0
  30. batoolset_gui-0.2.0/batoolset/drawings/shapes/polyline2d.py +235 -0
  31. batoolset_gui-0.2.0/batoolset/drawings/shapes/rect2d.py +388 -0
  32. batoolset_gui-0.2.0/batoolset/drawings/shapes/rectangle2d.py +601 -0
  33. batoolset_gui-0.2.0/batoolset/drawings/shapes/scalebar.py +310 -0
  34. batoolset_gui-0.2.0/batoolset/drawings/shapes/serializablefont.py +133 -0
  35. batoolset_gui-0.2.0/batoolset/drawings/shapes/serializableguiparams.py +14 -0
  36. batoolset_gui-0.2.0/batoolset/drawings/shapes/square2d.py +102 -0
  37. batoolset_gui-0.2.0/batoolset/drawings/shapes/test_publication_style.py +220 -0
  38. batoolset_gui-0.2.0/batoolset/drawings/shapes/textorientation.py +45 -0
  39. batoolset_gui-0.2.0/batoolset/drawings/shapes/tools.py +467 -0
  40. batoolset_gui-0.2.0/batoolset/drawings/shapes/transform2D.py +350 -0
  41. batoolset_gui-0.2.0/batoolset/drawings/shapes/txt2d.py +615 -0
  42. batoolset_gui-0.2.0/batoolset/drawings/svg_clip_renderer.py +247 -0
  43. batoolset_gui-0.2.0/batoolset/drawings/svg_embed.py +165 -0
  44. batoolset_gui-0.2.0/batoolset/drawings/svg_text_renderer.py +183 -0
  45. batoolset_gui-0.2.0/batoolset/drawings/widgets/__init__.py +0 -0
  46. batoolset_gui-0.2.0/batoolset/drawings/widgets/col.py +343 -0
  47. batoolset_gui-0.2.0/batoolset/drawings/widgets/image_group.py +248 -0
  48. batoolset_gui-0.2.0/batoolset/drawings/widgets/image_organizer.py +96 -0
  49. batoolset_gui-0.2.0/batoolset/drawings/widgets/paint.py +184 -0
  50. batoolset_gui-0.2.0/batoolset/drawings/widgets/vectorial.py +256 -0
  51. batoolset_gui-0.2.0/batoolset/figure/__init__.py +0 -0
  52. batoolset_gui-0.2.0/batoolset/figure/alignment.py +822 -0
  53. batoolset_gui-0.2.0/batoolset/figure/column.py +1117 -0
  54. batoolset_gui-0.2.0/batoolset/figure/fig_tools.py +285 -0
  55. batoolset_gui-0.2.0/batoolset/figure/minimal_debugger_scale_etc.py +139 -0
  56. batoolset_gui-0.2.0/batoolset/figure/panel.py +697 -0
  57. batoolset_gui-0.2.0/batoolset/figure/row.py +852 -0
  58. batoolset_gui-0.2.0/batoolset/figure/user_selection.py +287 -0
  59. batoolset_gui-0.2.0/batoolset/icons/__init__.py +0 -0
  60. batoolset_gui-0.2.0/batoolset/icons/tools.py +248 -0
  61. batoolset_gui-0.2.0/batoolset/luts/__init__.py +0 -0
  62. batoolset_gui-0.2.0/batoolset/luts/lut_minimal_test.py +1308 -0
  63. batoolset_gui-0.2.0/batoolset/luts/lutcreatorfromformula.py +209 -0
  64. batoolset_gui-0.2.0/batoolset/luts/luteditor.py +243 -0
  65. batoolset_gui-0.2.0/batoolset/pyqts/__init__.py +0 -0
  66. batoolset_gui-0.2.0/batoolset/pyqts/pleasewaitdialog.py +14 -0
  67. batoolset_gui-0.2.0/batoolset/pyqts/tools.py +760 -0
  68. batoolset_gui-0.2.0/batoolset/serializations/__init__.py +0 -0
  69. batoolset_gui-0.2.0/batoolset/serializations/tools.py +1338 -0
  70. batoolset_gui-0.2.0/batoolset/theme.py +647 -0
  71. batoolset_gui-0.2.0/batoolset/uis/__init__.py +0 -0
  72. batoolset_gui-0.2.0/batoolset/uis/blinker.py +56 -0
  73. batoolset_gui-0.2.0/batoolset_gui.egg-info/PKG-INFO +54 -0
  74. batoolset_gui-0.2.0/batoolset_gui.egg-info/SOURCES.txt +77 -0
  75. batoolset_gui-0.2.0/batoolset_gui.egg-info/dependency_links.txt +1 -0
  76. batoolset_gui-0.2.0/batoolset_gui.egg-info/requires.txt +6 -0
  77. batoolset_gui-0.2.0/batoolset_gui.egg-info/top_level.txt +1 -0
  78. batoolset_gui-0.2.0/setup.cfg +4 -0
  79. batoolset_gui-0.2.0/setup.py +33 -0
@@ -0,0 +1,24 @@
1
+ BSD 2-Clause License
2
+
3
+ Copyright (c) 2024, baigouy
4
+
5
+ Redistribution and use in source and binary forms, with or without
6
+ modification, are permitted provided that the following conditions are met:
7
+
8
+ 1. Redistributions of source code must retain the above copyright notice, this
9
+ list of conditions and the following disclaimer.
10
+
11
+ 2. Redistributions in binary form must reproduce the above copyright notice,
12
+ this list of conditions and the following disclaimer in the documentation
13
+ and/or other materials provided with the distribution.
14
+
15
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
16
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
18
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
19
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
21
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
22
+ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
23
+ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
24
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
@@ -0,0 +1,54 @@
1
+ Metadata-Version: 2.4
2
+ Name: batoolset-gui
3
+ Version: 0.2.0
4
+ Summary: Qt/GUI layer for batoolset (drawings, figure, luts, widgets)
5
+ Home-page: https://github.com/baigouy/BAtoolset-gui
6
+ Author: Benoit Aigouy
7
+ Author-email: benoit.aigouy@gmail.com
8
+ License: BSD
9
+ Classifier: Programming Language :: Python :: 3
10
+ Classifier: License :: OSI Approved :: BSD License
11
+ Classifier: Operating System :: OS Independent
12
+ Requires-Python: >=3.7, <3.12
13
+ Description-Content-Type: text/markdown
14
+ License-File: LICENSE
15
+ Requires-Dist: batoolset>=0.2.0
16
+ Requires-Dist: PyQt6
17
+ Requires-Dist: QtPy>=2.1.0
18
+ Requires-Dist: qtawesome
19
+ Requires-Dist: pyqt6-plugins
20
+ Requires-Dist: matplotlib>=3.5.2
21
+ Dynamic: author
22
+ Dynamic: author-email
23
+ Dynamic: classifier
24
+ Dynamic: description
25
+ Dynamic: description-content-type
26
+ Dynamic: home-page
27
+ Dynamic: license
28
+ Dynamic: license-file
29
+ Dynamic: requires-dist
30
+ Dynamic: requires-python
31
+ Dynamic: summary
32
+
33
+ # batoolset-gui
34
+
35
+ The Qt/GUI layer of [batoolset](https://github.com/baigouy/BAtoolset): vector
36
+ shapes and images for figures (`batoolset.drawings`), figure layout
37
+ (`batoolset.figure`), LUTs with an editor and inverted LUTs (`batoolset.luts`),
38
+ painting widgets and dialogs (`batoolset.GUI`, `batoolset.dialogs`,
39
+ `batoolset.pyqts`, `batoolset.uis`) and object serialization
40
+ (`batoolset.serializations`).
41
+
42
+ It installs into the same `batoolset` package as the core library, which needs
43
+ no Qt. Since 0.2.0 these modules are no longer part of `batoolset` itself.
44
+
45
+ ```bash
46
+ pip install batoolset-gui
47
+ ```
48
+
49
+ This installs `batoolset` (0.2.0 or later), PyQt6 and QtPy. It is used by
50
+ [PyFigures](https://github.com/baigouy/PyFigures).
51
+
52
+ ## Licence
53
+
54
+ BSD 2-Clause, see [LICENSE](LICENSE).
@@ -0,0 +1,22 @@
1
+ # batoolset-gui
2
+
3
+ The Qt/GUI layer of [batoolset](https://github.com/baigouy/BAtoolset): vector
4
+ shapes and images for figures (`batoolset.drawings`), figure layout
5
+ (`batoolset.figure`), LUTs with an editor and inverted LUTs (`batoolset.luts`),
6
+ painting widgets and dialogs (`batoolset.GUI`, `batoolset.dialogs`,
7
+ `batoolset.pyqts`, `batoolset.uis`) and object serialization
8
+ (`batoolset.serializations`).
9
+
10
+ It installs into the same `batoolset` package as the core library, which needs
11
+ no Qt. Since 0.2.0 these modules are no longer part of `batoolset` itself.
12
+
13
+ ```bash
14
+ pip install batoolset-gui
15
+ ```
16
+
17
+ This installs `batoolset` (0.2.0 or later), PyQt6 and QtPy. It is used by
18
+ [PyFigures](https://github.com/baigouy/PyFigures).
19
+
20
+ ## Licence
21
+
22
+ BSD 2-Clause, see [LICENSE](LICENSE).
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