jbqt 0.1.12__tar.gz → 0.1.13__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.

Potentially problematic release.


This version of jbqt might be problematic. Click here for more details.

@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: jbqt
3
- Version: 0.1.12
3
+ Version: 0.1.13
4
4
  Summary:
5
5
  Author: Joseph Bochinski
6
6
  Author-email: stirgejr@gmail.com
@@ -8,7 +8,8 @@ Requires-Python: >=3.12,<4.0
8
8
  Classifier: Programming Language :: Python :: 3
9
9
  Classifier: Programming Language :: Python :: 3.12
10
10
  Requires-Dist: fuzzywuzzy (>=0.18.0,<0.19.0)
11
- Requires-Dist: jbutils (>=0.1.21,<0.2.0)
11
+ Requires-Dist: jbconsts (>=0.1.1,<0.2.0)
12
+ Requires-Dist: jbutils (>=0.1.23,<0.2.0)
12
13
  Requires-Dist: pyqt6 (>=6.9.0,<7.0.0)
13
14
  Requires-Dist: python-levenshtein (>=0.27.1,<0.28.0)
14
15
  Description-Content-Type: text/markdown
@@ -1,12 +1,9 @@
1
1
  """Common exports for jbqt"""
2
2
 
3
- from jbqt.common import consts, qt_utils
4
- from jbqt.common.consts import GlobalRefs
3
+ from jbqt.common import qt_utils
5
4
  from jbqt.common.qt_utils import register_app
6
5
 
7
6
  __all__ = [
8
- "GlobalRefs",
9
- "consts",
10
7
  "qt_utils",
11
8
  "register_app",
12
9
  ]
@@ -0,0 +1,9 @@
1
+ """Const exports"""
2
+
3
+ from jbqt.common import qt_utils
4
+ from jbqt.common.qt_utils import register_app
5
+
6
+ __all__ = [
7
+ "qt_utils",
8
+ "register_app",
9
+ ]
@@ -14,7 +14,7 @@ from PyQt6.QtWidgets import (
14
14
  QDoubleSpinBox,
15
15
  )
16
16
 
17
- from jbqt.common import consts
17
+ import jbqt.consts as consts
18
18
  from jbqt.models import IChipsWidget
19
19
 
20
20
 
@@ -24,7 +24,7 @@ def get_item_value(item: QListWidgetItem) -> str:
24
24
 
25
25
 
26
26
  def register_app(app: QApplication, icon_dir: str = "") -> None:
27
- consts.GlobalRefs.app = app
27
+ consts.QtGlobalRefs.app = app
28
28
  consts.set_icon_dir(icon_dir)
29
29
 
30
30
 
@@ -78,4 +78,4 @@ def set_widget_value(widget: QWidget, value: Any) -> None:
78
78
 
79
79
  elif isinstance(widget, QCalendarWidget):
80
80
  if isinstance(value, (date, QDate)):
81
- widget.selectedDate(value)
81
+ widget.setSelectedDate(value)
@@ -1,9 +1,10 @@
1
+ """Collection of constants and global references for the jbqt package."""
2
+
1
3
  import os
2
4
  import re
3
5
 
4
6
  from typing import Optional
5
7
 
6
- import jbutils
7
8
 
8
9
  from PyQt6.QtWidgets import QApplication, QMainWindow, QScrollArea, QWidget
9
10
  from PyQt6.QtCore import Qt, QSize
@@ -14,11 +15,8 @@ from jbqt.types import IconGetter
14
15
  TAG_RE = re.compile(r"^\((.+)\)(\d+\.\d+)$")
15
16
  LIST_ITEM_ROLE = Qt.ItemDataRole.UserRole - 1
16
17
 
17
- STYLES = jbutils.STYLES
18
- COLORS = jbutils.COLORS
19
-
20
18
 
21
- class GlobalRefs:
19
+ class QtGlobalRefs:
22
20
  app: QApplication | None = None
23
21
  main_window: QMainWindow | None = None
24
22
  scroll_area: QScrollArea | None = None
@@ -79,7 +77,7 @@ def set_icon_dir(path: str) -> None:
79
77
  QtPaths.icon_dir = path
80
78
 
81
79
 
82
- class SIZES:
80
+ class QtIconSizes:
83
81
  ICON_XS = QSize(16, 16)
84
82
  ICON_SM = QSize(20, 20)
85
83
  ICON_MD = QSize(22, 22)
@@ -109,14 +107,14 @@ def recolor_icon(
109
107
  color = QColor(color)
110
108
 
111
109
  if scale is None:
112
- scale = SIZES.ICON_MD
110
+ scale = QtIconSizes.ICON_MD
113
111
  pixmap = QPixmap(image_path)
114
112
  icon_key = os.path.splitext(os.path.basename(image_path))[0]
115
113
 
116
114
  if pixmap.isNull():
117
115
  fallback = THEME_ICONS.get(icon_key, STD_ICONS.get(icon_key))
118
116
  if fallback:
119
- pixmap = fallback.pixmap()
117
+ pixmap = fallback.pixmap(scale)
120
118
 
121
119
  recolored_pixmap = QPixmap(pixmap.size())
122
120
 
@@ -177,3 +175,9 @@ class ICONS:
177
175
  return getattr(cls, name)
178
176
  else:
179
177
  return default
178
+
179
+
180
+ __all__ = [
181
+ "ICONS",
182
+ "QtIconSizes",
183
+ ]
@@ -12,7 +12,7 @@ from PyQt6.QtWidgets import (
12
12
  )
13
13
 
14
14
 
15
- INPUT_SIGNAL_TYPES: tuple[Any] = (str, int, float)
15
+ INPUT_SIGNAL_TYPES: tuple[Any, ...] = (str, int, float)
16
16
 
17
17
 
18
18
  class InputDialog(QDialog):
@@ -1,13 +1,10 @@
1
1
  """Model for chips widget"""
2
2
 
3
- from abc import abstractmethod, ABC
4
-
5
- from PyQt6.QtCore import QObject
6
3
  from PyQt6.QtWidgets import QWidget
7
4
 
8
5
 
9
6
  class IChipButton(QWidget):
10
7
  """ChipButton model"""
11
8
 
12
- def __init__(self, parent: QObject = None) -> None:
9
+ def __init__(self, parent: QWidget | None = None) -> None:
13
10
  super().__init__(parent)
@@ -1,7 +1,5 @@
1
1
  """Model for chips widget"""
2
2
 
3
- from abc import abstractmethod, ABC
4
-
5
3
  from PyQt6.QtCore import QObject
6
4
  from PyQt6.QtWidgets import QWidget
7
5
 
@@ -11,9 +9,9 @@ from jbqt.models.chip_button import IChipButton
11
9
  class IChipsWidget(QWidget):
12
10
  """ChipsWidget model"""
13
11
 
14
- def __init__(self, parent: QObject = None) -> None:
12
+ def __init__(self, parent: QWidget | None = None) -> None:
15
13
  super().__init__(parent)
16
- self.values: list = None
14
+ self.values: list[str] = []
17
15
 
18
16
  def add_chip(self):
19
17
  pass
@@ -0,0 +1,14 @@
1
+ """Common type definitions for the jbqt package"""
2
+
3
+ from typing import Protocol, Optional, runtime_checkable
4
+ from PyQt6.QtGui import QIcon, QColor
5
+ from PyQt6.QtCore import QSize
6
+
7
+
8
+ @runtime_checkable
9
+ class IconGetter(Protocol):
10
+ """Factory function that assembles a QIcon instance"""
11
+
12
+ def __call__(
13
+ self, color: Optional[str | QColor] = ..., size: QSize | int | None = ...
14
+ ) -> QIcon: ...
@@ -11,13 +11,12 @@ from PyQt6.QtWidgets import (
11
11
  QWidget,
12
12
  )
13
13
 
14
- from jbqt.common import consts
15
- from jbqt.common.consts import STYLES, COLORS
14
+ import jbqt.consts as consts
15
+ from jbconsts import STYLES, COLORS
16
16
  from jbqt.models import IChipButton
17
17
  from jbqt.widgets.simple import ClickableLabel
18
18
  from jbqt.dialogs import InputDialog
19
19
  from jbqt.widgets.widget_utils import (
20
- debug_scroll_pos,
21
20
  preserve_scroll,
22
21
  )
23
22
 
@@ -29,12 +28,11 @@ class ChipButton(IChipButton):
29
28
  on_update: Callable,
30
29
  on_remove: Callable,
31
30
  use_weight: bool = False,
32
- weight_re: Pattern = None,
33
- debug: bool = False,
31
+ weight_re: Pattern | None = None,
34
32
  ):
35
33
  super().__init__()
36
34
 
37
- self._tag = tag
35
+ self._tag: str = tag
38
36
  self.weight: float = 1.0
39
37
  self.use_weight: bool = use_weight
40
38
 
@@ -64,7 +62,7 @@ class ChipButton(IChipButton):
64
62
  self.tag_label.setFixedHeight(24)
65
63
  self.tag_label.clicked.connect(self.toggle_edit_mode)
66
64
 
67
- self.weight_button: QPushButton = None
65
+ self.weight_button: QPushButton | None = None
68
66
 
69
67
  if use_weight:
70
68
  self.weight_button = QPushButton(consts.ICONS.CODE(), "")
@@ -137,17 +135,24 @@ class ChipButton(IChipButton):
137
135
  def text(self, value: str) -> None:
138
136
  self._tag = value
139
137
 
140
- def eventFilter(self, obj: QObject, event: QKeyEvent) -> bool:
141
- focus = consts.GlobalRefs.app.focusWidget()
142
- """ if event.type() == QEvent.Type.KeyPress:
143
- print("focus", focus) """
144
- if event.type() == QEvent.Type.KeyPress and focus == self.edit_line:
145
- match event.key():
138
+ def eventFilter(
139
+ self, a0: QObject | None, a1: QKeyEvent | QEvent | None
140
+ ) -> bool:
141
+ focus: QWidget | None = None
142
+
143
+ if consts.QtGlobalRefs.app:
144
+ focus = consts.QtGlobalRefs.app.focusWidget()
145
+
146
+ if not a1 or not isinstance(a1, QKeyEvent):
147
+ return super().eventFilter(a0, a1)
148
+
149
+ if a1.type() == QEvent.Type.KeyPress and focus == self.edit_line:
150
+ match a1.key():
146
151
  case Qt.Key.Key_Return | Qt.Key.Key_Enter:
147
152
  self.edit_tag()
148
153
  case Qt.Key.Key_Escape:
149
154
  self.cancel()
150
- return super().eventFilter(obj, event)
155
+ return super().eventFilter(a0, a1)
151
156
 
152
157
  def toggle_hidden(self, hide: bool = True) -> None:
153
158
  for widget in self.widgets:
@@ -167,7 +172,9 @@ class ChipButton(IChipButton):
167
172
  self.edit_line.setMinimumWidth(self.tag_label.width() + 10)
168
173
  self.toggle_hidden(False)
169
174
 
170
- def emit_update(self, text: str = None, weight: float = None) -> None:
175
+ def emit_update(
176
+ self, text: str | None = None, weight: float | None = None
177
+ ) -> None:
171
178
  text = text or self._tag
172
179
  if weight is None:
173
180
  weight = self.weight
@@ -14,11 +14,10 @@ from PyQt6.QtWidgets import (
14
14
  )
15
15
 
16
16
  import jbutils as utils
17
- from jbqt.common import consts
17
+ import jbqt.consts as consts
18
18
  from jbqt.models import IChipsWidget
19
19
  from jbqt.widgets.chip_button import ChipButton
20
20
  from jbqt.widgets.multiselect import MultiSelectComboBox
21
- from jbqt.widgets.widget_utils import debug_scroll_pos
22
21
 
23
22
 
24
23
  class ChipsWidget(IChipsWidget):
@@ -26,16 +25,16 @@ class ChipsWidget(IChipsWidget):
26
25
 
27
26
  def __init__(
28
27
  self,
29
- chips=None,
30
- data: dict = None,
28
+ chips: list[str] | None = None,
29
+ data: dict | list | None = None,
31
30
  path: str = "",
32
31
  label: str = "",
33
- weight_re: Pattern = None,
32
+ weight_re: Pattern | None = None,
34
33
  use_weight: bool = False,
35
34
  debug: bool = False,
36
35
  ):
37
36
  super().__init__()
38
- self.values = chips or []
37
+ self.values: list[str] = chips or []
39
38
  # Main layout
40
39
  main_layout = QVBoxLayout()
41
40
  self.debug = debug
@@ -48,10 +47,10 @@ class ChipsWidget(IChipsWidget):
48
47
  weight_re = weight_re or consts.TAG_RE
49
48
 
50
49
  self.weight_re = weight_re
51
- self.options = data
50
+ self.options = data or {}
52
51
  self.data = data or {}
53
52
  self.data_path = path
54
- self.list_widget: QComboBox | MultiSelectComboBox = None
53
+ self.list_widget: QComboBox | MultiSelectComboBox | None = None
55
54
 
56
55
  if data is not None:
57
56
  if isinstance(data, dict):
@@ -112,21 +111,32 @@ class ChipsWidget(IChipsWidget):
112
111
 
113
112
  self.setLayout(main_layout)
114
113
 
115
- def eventFilter(self, obj: QObject, event: QKeyEvent) -> bool:
116
- focus = consts.GlobalRefs.app.focusWidget()
114
+ def eventFilter(
115
+ self, a0: QObject | None, a1: QKeyEvent | QEvent | None
116
+ ) -> bool:
117
+ focus: QWidget | None = None
118
+ if consts.QtGlobalRefs.app:
119
+ focus = consts.QtGlobalRefs.app.focusWidget()
117
120
 
118
- if event.type() == QEvent.Type.KeyPress and focus == self.input_field:
119
- match event.key():
121
+ if not a1 or not isinstance(a1, QKeyEvent):
122
+ return super().eventFilter(a0, a1)
123
+
124
+ if a1.type() == QEvent.Type.KeyPress and focus == self.input_field:
125
+ match a1.key():
120
126
  case Qt.Key.Key_Return | Qt.Key.Key_Enter:
121
127
  self.add_chip()
122
- return super().eventFilter(obj, event)
128
+ return super().eventFilter(a0, a1)
123
129
 
124
130
  def get_unweight_chip(self, tag: str) -> str:
125
- if self.weight_re and self.weight_re.search(tag):
126
- return self.weight_re.search(tag).groups()[0]
131
+ if self.weight_re:
132
+ search_re = self.weight_re.search(tag)
133
+ if search_re:
134
+ return search_re.groups()[0]
135
+
127
136
  return tag
128
137
 
129
- def get_chip_list(self, tags: list[str]) -> list[str]:
138
+ def get_chip_list(self, tags: list[str] | None = None) -> list[str]:
139
+ tags = tags or []
130
140
  return [self.get_unweight_chip(tag) for tag in tags]
131
141
 
132
142
  def same_values(self, values: list[str]) -> bool:
@@ -146,6 +156,9 @@ class ChipsWidget(IChipsWidget):
146
156
  self.set_items(selections, debug=True)
147
157
 
148
158
  def update_multiselect(self, values: list[str]) -> None:
159
+ if not self.list_widget:
160
+ return
161
+
149
162
  if isinstance(self.list_widget, MultiSelectComboBox):
150
163
  self.list_widget.set_selected(values, emit=False)
151
164
  else:
@@ -154,17 +167,27 @@ class ChipsWidget(IChipsWidget):
154
167
 
155
168
  def clear_widgets(self) -> None:
156
169
  while self.chip_layout.count():
157
- widget = self.chip_layout.takeAt(0).widget()
170
+ layout = self.chip_layout.takeAt(0)
171
+ if not layout:
172
+ return
173
+
174
+ widget = layout.widget()
175
+
158
176
  if widget is not None:
159
177
  widget.deleteLater()
160
178
 
161
179
  def set_items(
162
- self, values: list[str] = None, emit: bool = True, debug: bool = False
180
+ self,
181
+ values: list[str] | None = None,
182
+ emit: bool = True,
183
+ debug: bool = False,
163
184
  ) -> None:
164
185
  self.clear_widgets()
186
+ if self.values is None:
187
+ self.values = []
165
188
  # TODO: Temp fix for weighted/custom tags and incoming multiselect values
166
189
  if values:
167
- tags = self.get_tag_list(self.values)
190
+ tags = self.get_chip_list(self.values)
168
191
  for value in values:
169
192
  if value not in tags:
170
193
  self.values.append(value)
@@ -172,22 +195,18 @@ class ChipsWidget(IChipsWidget):
172
195
  self.values = utils.dedupe_list(self.values)
173
196
  self.values = [value for value in self.values if value]
174
197
  for item in self.values:
175
- if not consts.GlobalRefs.debug_set:
198
+ if not consts.QtGlobalRefs.debug_set:
176
199
  debug = True
177
- consts.GlobalRefs.debug_set = True
200
+ consts.QtGlobalRefs.debug_set = True
178
201
  else:
179
202
  debug = False
180
203
  chip_button = ChipButton(
181
204
  item,
182
205
  self.update_chip,
183
206
  self.remove_chip,
184
- debug=debug,
185
207
  weight_re=self.weight_re,
186
208
  use_weight=self.use_weight,
187
209
  )
188
- """ chip_button.setToolTip(
189
- f"Tag Count: {consts.TAG_COUNTS.get(item, "N/A")}"
190
- ) """
191
210
 
192
211
  self.chip_layout.addWidget(chip_button)
193
212
 
@@ -212,7 +231,7 @@ class ChipsWidget(IChipsWidget):
212
231
  self.emit_changes()
213
232
  self.input_field.setText("")
214
233
 
215
- def remove_chip(self, button: ChipButton):
234
+ def remove_chip(self, button: ChipButton): # type: ignore
216
235
  self.chip_layout.removeWidget(button)
217
236
  button.deleteLater() # Schedule button widget for deletion
218
237
  self.values.remove(button.text)
@@ -227,6 +246,6 @@ class ChipsWidget(IChipsWidget):
227
246
  self.set_items()
228
247
 
229
248
  def remove_all(self, *_) -> None:
230
- self.values = []
249
+ self.values.clear()
231
250
  self.clear_widgets()
232
251
  self.emit_changes()
@@ -1,11 +1,12 @@
1
1
  from re import Pattern
2
2
 
3
- from PyQt6.QtCore import QObject, pyqtSignal, Qt
3
+ from PyQt6.QtCore import QObject, pyqtSignal, Qt, QEvent
4
4
  from PyQt6.QtGui import QCursor, QMouseEvent, QWheelEvent
5
- from PyQt6.QtWidgets import QComboBox, QListWidget, QListWidgetItem
5
+ from PyQt6.QtWidgets import QComboBox, QListWidget, QListWidgetItem, QLineEdit
6
6
 
7
7
  import jbutils as utils
8
- from jbqt.common import consts, qt_utils
8
+ import jbqt.consts as consts
9
+ from jbqt.common import qt_utils
9
10
 
10
11
 
11
12
  class MultiSelectComboBox(QComboBox):
@@ -13,11 +14,11 @@ class MultiSelectComboBox(QComboBox):
13
14
 
14
15
  def __init__(
15
16
  self,
16
- data: list | dict = None,
17
- selected: list[str] = None,
17
+ data: list | dict | None = None,
18
+ selected: list[str] | None = None,
18
19
  multi_enabled: bool = True,
19
20
  use_weight: bool = False,
20
- weight_re: Pattern = None,
21
+ weight_re: Pattern | None = None,
21
22
  ):
22
23
  super().__init__()
23
24
 
@@ -30,11 +31,12 @@ class MultiSelectComboBox(QComboBox):
30
31
  self._multi_select_enabled: bool = multi_enabled
31
32
  # Make the combo box read-only and use a custom view
32
33
  self.setEditable(True)
33
- self.lineEdit().setReadOnly(True)
34
+
35
+ self.line_edit().setReadOnly(True)
34
36
  # Override the mouse press event of the QLineEdit
35
- self.lineEdit().installEventFilter(self)
37
+ self.line_edit().installEventFilter(self)
36
38
 
37
- self.view: QListWidget = QListWidget()
39
+ self.view: QListWidget = QListWidget() # type: ignore
38
40
  self._selected = selected or []
39
41
 
40
42
  # Set the custom view
@@ -52,26 +54,32 @@ class MultiSelectComboBox(QComboBox):
52
54
  self.view.itemClicked.connect(self.toggle_check_state)
53
55
  self.update_display_text()
54
56
 
55
- def eventFilter(self, obj: QObject, event: QMouseEvent) -> bool:
57
+ def line_edit(self) -> QLineEdit:
58
+ return self.lineEdit() or QLineEdit(parent=self)
59
+
60
+ def eventFilter(
61
+ self, a0: QObject | None, a1: QMouseEvent | QEvent | None
62
+ ) -> bool:
56
63
 
57
64
  if (
58
- obj == self.lineEdit()
59
- and event.type() == QMouseEvent.Type.MouseButtonRelease
65
+ a0 == self.lineEdit()
66
+ and isinstance(a1, QMouseEvent)
67
+ and a1.type() == QMouseEvent.Type.MouseButtonRelease
60
68
  ):
61
69
  self.showPopup()
62
70
 
63
71
  return True
64
- return super().eventFilter(obj, event)
72
+ return super().eventFilter(a0, a1)
65
73
 
66
- def wheelEvent(self, event: QWheelEvent):
74
+ def wheelEvent(self, e: QWheelEvent | None):
67
75
  # Check if the mouse cursor is within the bounds of the line edit
68
76
  pos = self.mapFromGlobal(QCursor.pos())
69
- if self.lineEdit().rect().contains(pos):
77
+ if self.line_edit().rect().contains(pos):
70
78
  # Consume the wheel event to prevent scrolling
71
79
  return
72
80
 
73
81
  # If not over the line edit, let the base class handle it (scrolling)
74
- super().wheelEvent(event)
82
+ super().wheelEvent(e)
75
83
 
76
84
  def toggle_check_state(self, item: QListWidgetItem):
77
85
  # Toggle check state if the item is selectable
@@ -94,7 +102,7 @@ class MultiSelectComboBox(QComboBox):
94
102
 
95
103
  for i in range(self.view.count()):
96
104
  item = self.view.item(i)
97
- if item != selected:
105
+ if item and item != selected:
98
106
  item.setCheckState(Qt.CheckState.Unchecked)
99
107
  self.update_display_text()
100
108
 
@@ -143,11 +151,11 @@ class MultiSelectComboBox(QComboBox):
143
151
  selected_items = []
144
152
  for i in range(self.view.count()):
145
153
  item = self.view.item(i)
146
- if item.checkState() == Qt.CheckState.Checked:
154
+ if item and item.checkState() == Qt.CheckState.Checked:
147
155
  selected_items.append(item.text().strip())
148
156
 
149
157
  text = ", ".join(selected_items)
150
- self.lineEdit().setText(text)
158
+ self.line_edit().setText(text)
151
159
 
152
160
  def showPopup(self):
153
161
  # Override showPopup to update text when the dropdown is shown
@@ -173,8 +181,10 @@ class MultiSelectComboBox(QComboBox):
173
181
  self.selectedChanged.emit(self.get_selected())
174
182
 
175
183
  def get_unweight_chip(self, tag: str) -> str:
176
- if self.weight_re and self.weight_re.search(tag):
177
- return self.weight_re.search(tag).groups()[0]
184
+ if self.weight_re:
185
+ search_re = self.weight_re.search(tag)
186
+ if search_re:
187
+ return search_re.groups()[0]
178
188
  return tag
179
189
 
180
190
  def get_chip_list(self, tags: list[str]) -> list[str]:
@@ -190,6 +200,8 @@ class MultiSelectComboBox(QComboBox):
190
200
  # Update the checked state of items based on the new selection
191
201
  for i in range(self.view.count()):
192
202
  item = self.view.item(i)
203
+ if not item:
204
+ continue
193
205
 
194
206
  item.setCheckState(
195
207
  Qt.CheckState.Checked
@@ -14,9 +14,9 @@ class ClickableLabel(QLabel):
14
14
  super().__init__(text, parent)
15
15
  self.setAlignment(Qt.AlignmentFlag.AlignCenter)
16
16
 
17
- def mousePressEvent(self, event):
17
+ def mousePressEvent(self, ev):
18
18
  self.clicked.emit()
19
- super().mousePressEvent(event)
19
+ super().mousePressEvent(ev)
20
20
 
21
21
 
22
22
  def is_valid_key(event: QKeyEvent) -> bool:
@@ -37,6 +37,7 @@ def is_valid_key(event: QKeyEvent) -> bool:
37
37
 
38
38
  if event.keyCombination().keyboardModifiers().name == "ControlModifier":
39
39
  return True
40
+ return False
40
41
 
41
42
 
42
43
  class LongIntSpinBox(QLineEdit):
@@ -55,13 +56,16 @@ class LongIntSpinBox(QLineEdit):
55
56
  if value != self.value():
56
57
  self.setText(str(value))
57
58
 
58
- def value(self) -> int:
59
+ def value(self) -> int | None:
59
60
  text = self.text()
60
61
  if NUM_RE.match(text):
61
62
  return int(text)
62
63
 
63
- def eventFilter(self, obj: QObject, event: QKeyEvent) -> bool:
64
- if event.type() == QEvent.Type.KeyPress:
65
- if not is_valid_key(event):
66
- return True
67
- return super().eventFilter(obj, event)
64
+ def eventFilter(
65
+ self, a0: QObject | None, a1: QKeyEvent | QEvent | None
66
+ ) -> bool:
67
+ if isinstance(a1, QKeyEvent):
68
+ if a1.type() == QEvent.Type.KeyPress:
69
+ if not is_valid_key(a1):
70
+ return True
71
+ return super().eventFilter(a0, a1)
@@ -0,0 +1,88 @@
1
+ from collections.abc import Callable
2
+
3
+ from jbqt.consts import QtGlobalRefs
4
+
5
+
6
+ def debug_scroll_pos(func: Callable):
7
+ def wrapper(self, *args, **kwargs):
8
+ if QtGlobalRefs.scroll_area is None:
9
+ return func(self, *args, **kwargs)
10
+ focus = None
11
+ if QtGlobalRefs.main_window:
12
+ focus = QtGlobalRefs.main_window.focusWidget()
13
+ print(f"scroll pos {func.__name__}")
14
+ print("before", QtGlobalRefs.scroll_area.verticalScrollBar().value(), focus)
15
+ result = func(self, *args, **kwargs)
16
+
17
+ if QtGlobalRefs.main_window:
18
+ focus = QtGlobalRefs.main_window.focusWidget()
19
+ print(
20
+ f"after {QtGlobalRefs.scroll_area.verticalScrollBar().value()} {focus}\n"
21
+ )
22
+
23
+ return result
24
+
25
+ return wrapper
26
+
27
+
28
+ def debug_scroll_pos_no_args(func: Callable):
29
+ def wrapper(self):
30
+ if QtGlobalRefs.scroll_area is None:
31
+ return func(self)
32
+ focus = None
33
+ if QtGlobalRefs.main_window:
34
+ focus = QtGlobalRefs.main_window.focusWidget()
35
+ print(f"scroll pos {func.__name__}")
36
+ print("before", QtGlobalRefs.scroll_area.verticalScrollBar().value(), focus)
37
+ result = func(self)
38
+ if QtGlobalRefs.main_window:
39
+ focus = QtGlobalRefs.main_window.focusWidget()
40
+ print(
41
+ f"after {QtGlobalRefs.scroll_area.verticalScrollBar().value()} {focus}\n"
42
+ )
43
+
44
+ return result
45
+
46
+ return wrapper
47
+
48
+
49
+ def _call_func(func: Callable, *args, **kwargs):
50
+ if args and kwargs:
51
+ return func(*args, **kwargs)
52
+ elif args:
53
+ return func(*args)
54
+ elif kwargs:
55
+ return func(**kwargs)
56
+ else:
57
+ return func()
58
+
59
+
60
+ def preserve_scroll(func: Callable):
61
+ def wrapper(self, *args, **kwargs):
62
+ if QtGlobalRefs.scroll_area is None:
63
+ return _call_func(func, self, *args, **kwargs)
64
+
65
+ scrollbar = QtGlobalRefs.scroll_area.verticalScrollBar()
66
+ if not scrollbar:
67
+ return
68
+
69
+ scroll_pos = scrollbar.value()
70
+
71
+ if not QtGlobalRefs.app:
72
+ return
73
+
74
+ focus = QtGlobalRefs.app.focusWidget()
75
+ if not focus:
76
+ return
77
+
78
+ result = _call_func(
79
+ func,
80
+ self,
81
+ *args,
82
+ **kwargs,
83
+ )
84
+ scrollbar.setValue(scroll_pos)
85
+ focus.setFocus()
86
+ return result
87
+
88
+ return wrapper
@@ -4,7 +4,7 @@ build-backend = "poetry.core.masonry.api"
4
4
 
5
5
  [tool.poetry]
6
6
  name = "jbqt"
7
- version = "0.1.12"
7
+ version = "0.1.13"
8
8
  description = ""
9
9
  authors = [ "Joseph Bochinski <stirgejr@gmail.com>",]
10
10
  readme = "README.md"
@@ -14,4 +14,5 @@ python = "^3.12"
14
14
  pyqt6 = "^6.9.0"
15
15
  fuzzywuzzy = "^0.18.0"
16
16
  python-levenshtein = "^0.27.1"
17
- jbutils = "^0.1.21"
17
+ jbconsts = "^0.1.1"
18
+ jbutils = "^0.1.23"
@@ -1,16 +0,0 @@
1
- """Const exports"""
2
-
3
- from jbqt.common import consts, qt_utils
4
- from jbqt.common.consts import COLORS, STYLES, SIZES, GlobalRefs, ICONS
5
- from jbqt.common.qt_utils import register_app
6
-
7
- __all__ = [
8
- "COLORS",
9
- "consts",
10
- "GlobalRefs",
11
- "ICONS",
12
- "qt_utils",
13
- "register_app",
14
- "SIZES",
15
- "STYLES",
16
- ]
@@ -1,8 +0,0 @@
1
- from collections.abc import Callable
2
- from typing import Optional
3
-
4
- from PyQt6.QtCore import QSize
5
- from PyQt6.QtGui import QIcon, QColor
6
-
7
-
8
- IconGetter = Callable[[Optional[str | QColor], QSize | int | None], QIcon]
@@ -1,77 +0,0 @@
1
- from collections.abc import Callable
2
-
3
- from jbqt.common.consts import GlobalRefs
4
-
5
-
6
- def debug_scroll_pos(func: Callable):
7
- def wrapper(self, *args, **kwargs):
8
- if GlobalRefs.scroll_area is None:
9
- return func(self, *args, **kwargs)
10
- focus = None
11
- if GlobalRefs.main_window:
12
- focus = GlobalRefs.main_window.focusWidget()
13
- print(f"scroll pos {func.__name__}")
14
- print("before", GlobalRefs.scroll_area.verticalScrollBar().value(), focus)
15
- result = func(self, *args, **kwargs)
16
-
17
- if GlobalRefs.main_window:
18
- focus = GlobalRefs.main_window.focusWidget()
19
- print(
20
- f"after {GlobalRefs.scroll_area.verticalScrollBar().value()} {focus}\n"
21
- )
22
-
23
- return result
24
-
25
- return wrapper
26
-
27
-
28
- def debug_scroll_pos_no_args(func: Callable):
29
- def wrapper(self):
30
- if GlobalRefs.scroll_area is None:
31
- return func(self)
32
- focus = None
33
- if GlobalRefs.main_window:
34
- focus = GlobalRefs.main_window.focusWidget()
35
- print(f"scroll pos {func.__name__}")
36
- print("before", GlobalRefs.scroll_area.verticalScrollBar().value(), focus)
37
- result = func(self)
38
- if GlobalRefs.main_window:
39
- focus = GlobalRefs.main_window.focusWidget()
40
- print(
41
- f"after {GlobalRefs.scroll_area.verticalScrollBar().value()} {focus}\n"
42
- )
43
-
44
- return result
45
-
46
- return wrapper
47
-
48
-
49
- def _call_func(func: Callable, *args, **kwargs):
50
- if args and kwargs:
51
- return func(*args, **kwargs)
52
- elif args:
53
- return func(*args)
54
- elif kwargs:
55
- return func(**kwargs)
56
- else:
57
- return func()
58
-
59
-
60
- def preserve_scroll(func: Callable):
61
- def wrapper(self, *args, **kwargs):
62
- if GlobalRefs.scroll_area is None:
63
- return _call_func(func, self, *args, **kwargs)
64
-
65
- scroll_pos = GlobalRefs.scroll_area.verticalScrollBar().value()
66
- focus = GlobalRefs.app.focusWidget()
67
- result = _call_func(
68
- func,
69
- self,
70
- *args,
71
- **kwargs,
72
- )
73
- GlobalRefs.scroll_area.verticalScrollBar().setValue(scroll_pos)
74
- focus.setFocus()
75
- return result
76
-
77
- return wrapper
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes