quickmacapp 2025.4.15__py3-none-any.whl → 2025.6.24__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.
quickmacapp/__init__.py CHANGED
@@ -1,9 +1,10 @@
1
- from ._quickapp import Actionable, Status, mainpoint, menu, quit
1
+ from ._quickapp import Actionable, ItemState, Status, mainpoint, menu, quit
2
2
  from ._interactions import ask, choose, answer, getpass
3
3
  from ._background import dockIconWhenVisible
4
4
 
5
5
  __all__ = [
6
6
  "Actionable",
7
+ "ItemState",
7
8
  "Status",
8
9
  "mainpoint",
9
10
  "menu",
quickmacapp/_quickapp.py CHANGED
@@ -3,31 +3,48 @@ from __future__ import annotations
3
3
  import os
4
4
  import sys
5
5
  import traceback
6
- from typing import Callable, Protocol, Any
7
-
8
- from objc import ivar, IBAction, super
9
-
10
- from Foundation import (
11
- NSObject,
12
- NSException,
13
- )
6
+ from dataclasses import dataclass
7
+ from types import FunctionType
8
+ from typing import Any, Callable, Iterable, Protocol, Sequence, Literal
14
9
 
15
10
  from AppKit import (
16
11
  NSApp,
17
12
  NSApplication,
18
13
  NSEvent,
19
- NSResponder,
20
- NSMenu,
21
14
  NSImage,
15
+ NSMenu,
22
16
  NSMenuItem,
17
+ NSResponder,
23
18
  NSStatusBar,
24
19
  NSVariableStatusItemLength,
20
+ NSControlStateValueOn,
21
+ NSControlStateValueOff,
25
22
  )
26
-
23
+ from ExceptionHandling import NSStackTraceKey # type:ignore
24
+ from Foundation import NSException, NSObject
25
+ from objc import IBAction, ivar, super
27
26
  from PyObjCTools.Debugging import _run_atos, isPythonException
28
- from ExceptionHandling import ( # type:ignore
29
- NSStackTraceKey,
30
- )
27
+
28
+
29
+ def asSelectorString(f: FunctionType) -> str:
30
+ """
31
+ Convert a method on a PyObjC class into a selector string.
32
+ """
33
+ return f.__name__.replace("_", ":")
34
+
35
+
36
+ @dataclass(kw_only=True)
37
+ class ItemState:
38
+ """
39
+ The state of a menu item.
40
+ """
41
+
42
+ enabled: bool = True
43
+ "Should the menu item be disabled? True if not, False if so."
44
+ checked: bool = False
45
+ "Should the menu item display a check-mark next to itself? True if so, False if not."
46
+ key: str | None = None
47
+ "Should the menu shortcut mnemonic key be set, blank, or derived from the item's title?"
31
48
 
32
49
 
33
50
  class Actionable(NSObject):
@@ -35,15 +52,20 @@ class Actionable(NSObject):
35
52
  Wrap a Python no-argument function call in an NSObject with a C{doIt:}
36
53
  method.
37
54
  """
38
- _thunk: Callable[[], None]
39
55
 
40
- def initWithFunction_(self, thunk: Callable[[], None]) -> Actionable:
56
+ _thunk: Callable[[], object]
57
+ _state: ItemState
58
+
59
+ def initWithFunction_andState_(
60
+ self, thunk: Callable[[], None], state: ItemState
61
+ ) -> Actionable:
41
62
  """
42
63
  Remember the given callable.
43
64
 
44
65
  @param thunk: the callable to run in L{doIt_}.
45
66
  """
46
67
  self._thunk = thunk
68
+ self._state = state
47
69
  return self
48
70
 
49
71
  @IBAction
@@ -52,10 +74,41 @@ class Actionable(NSObject):
52
74
  Call the given callable; exposed as an C{IBAction} in case you want IB
53
75
  to be able to see it.
54
76
  """
55
- self._thunk()
77
+ result = self._thunk()
78
+ if isinstance(result, ItemState):
79
+ self._state = result
80
+
81
+ def validateMenuItem_(self, item: NSMenuItem) -> bool:
82
+ item.setState_(
83
+ NSControlStateValueOn if self._state.checked else NSControlStateValueOff
84
+ )
85
+ return self._state.enabled
86
+
87
+
88
+ ACTION_METHOD = asSelectorString(Actionable.doIt_)
89
+
90
+
91
+ def _adjust(
92
+ items: Iterable[
93
+ tuple[str, Callable[[], object]] | tuple[str, Callable[[], object], ItemState]
94
+ ],
95
+ ) -> Iterable[tuple[str, Callable[[], object], ItemState]]:
96
+ for item in items:
97
+ if len(item) == 3:
98
+ yield item
99
+ else:
100
+ yield (*item, ItemState())
101
+
56
102
 
103
+ ItemSeq = Sequence[
104
+ tuple[str, Callable[[], object]] | tuple[str, Callable[[], object], ItemState]
105
+ ]
57
106
 
58
- def menu(title: str, items: list[tuple[str, Callable[[], object]]]) -> NSMenu:
107
+
108
+ def menu(
109
+ title: str,
110
+ items: ItemSeq,
111
+ ) -> NSMenu:
59
112
  """
60
113
  Construct an NSMenu from a list of tuples describing it.
61
114
 
@@ -68,11 +121,16 @@ def menu(title: str, items: list[tuple[str, Callable[[], object]]]) -> NSMenu:
68
121
  @return: a new Menu tha is not attached to anything.
69
122
  """
70
123
  result = NSMenu.alloc().initWithTitle_(title)
71
- for (subtitle, thunk) in items:
124
+ for subtitle, thunk, state in _adjust(items):
125
+ initialKeyEquivalent = subtitle[0].lower()
72
126
  item = NSMenuItem.alloc().initWithTitle_action_keyEquivalent_(
73
- subtitle, "doIt:", subtitle[0].lower()
127
+ subtitle,
128
+ ACTION_METHOD,
129
+ initialKeyEquivalent if state.key is None else state.key,
130
+ )
131
+ item.setTarget_(
132
+ Actionable.alloc().initWithFunction_andState_(thunk, state).retain()
74
133
  )
75
- item.setTarget_(Actionable.alloc().initWithFunction_(thunk).retain())
76
134
  result.addItem_(item)
77
135
  result.update()
78
136
  return result
@@ -97,11 +155,12 @@ class Status:
97
155
  self.item.button().setImage_(image)
98
156
  elif text is None:
99
157
  from __main__ import __file__ as default
158
+
100
159
  text = os.path.basename(default)
101
160
  if text is not None:
102
161
  self.item.button().setTitle_(text)
103
162
 
104
- def menu(self, items: list[tuple[str, Callable[[], object]]]) -> None:
163
+ def menu(self, items: ItemSeq) -> None:
105
164
  """
106
165
  Set the status drop-down menu.
107
166
 
@@ -183,6 +242,7 @@ class QuickApplication(NSApplication):
183
242
  be more complicated in LSUIElement apps, but there might be a better
184
243
  way to do this.)
185
244
  """
245
+
186
246
  keyEquivalentHandler: NSResponder = ivar()
187
247
 
188
248
  def sendEvent_(self, event: NSEvent) -> None:
@@ -210,6 +270,7 @@ class MainRunner(Protocol):
210
270
  """
211
271
  A function which has been decorated with a runMain attribute.
212
272
  """
273
+
213
274
  def __call__(self, reactor: Any) -> None:
214
275
  """
215
276
  @param reactor: A Twisted reactor, which provides the usual suspects of
@@ -218,6 +279,7 @@ class MainRunner(Protocol):
218
279
 
219
280
  runMain: Callable[[], None]
220
281
 
282
+
221
283
  def mainpoint() -> Callable[[Callable[[Any], None]], MainRunner]:
222
284
  """
223
285
  Add a .runMain attribute to function
@@ -227,10 +289,11 @@ def mainpoint() -> Callable[[Callable[[Any], None]], MainRunner]:
227
289
  The runMain attribute starts a reactor and calls the original function
228
290
  with a running, initialized, reactor.
229
291
  """
292
+
230
293
  def wrapup(appmain: Callable[[Any], None]) -> MainRunner:
231
294
  def doIt() -> None:
232
- from twisted.internet import cfreactor
233
295
  import PyObjCTools.AppHelper
296
+ from twisted.internet import cfreactor
234
297
 
235
298
  QuickApplication.sharedApplication()
236
299
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: quickmacapp
3
- Version: 2025.4.15
3
+ Version: 2025.6.24
4
4
  Summary: Make it easier to write Mac apps in Python
5
5
  Description-Content-Type: text/x-rst
6
6
  License-File: LICENSE
@@ -0,0 +1,12 @@
1
+ quickmacapp/__init__.py,sha256=WAfaenkgALceVKrSmVBoEOJER-SX65r4WjkaBGtmO24,363
2
+ quickmacapp/_background.py,sha256=M_ob3EF8v-Z_gpKuFDl2N9XrQz0eBLFfUp2XErDB3DU,5943
3
+ quickmacapp/_interactions.py,sha256=eLv_mVf5Jr-puNizlR8tDL_DlLy1Rc82XQ80o0GP5R4,3439
4
+ quickmacapp/_notifications.py,sha256=DNm4-vOF0wQNUUTBHqp3-RKXVQK98ef53isxwsWqyj8,16965
5
+ quickmacapp/_quickapp.py,sha256=cjMEzhofV8pq-rkcdjHTvUG1Omn2kQDKD4uzmAWLAAA,9273
6
+ quickmacapp/notifications.py,sha256=UCigVIurf-zWAqVXUYjTtz_xMjcoI23dqyJ4OzZurQM,10275
7
+ quickmacapp/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
8
+ quickmacapp-2025.6.24.dist-info/licenses/LICENSE,sha256=7RIBNNvrnKHR3lw9z3KXv3Q6RRjhyxtNQoMnoUsf3_M,1091
9
+ quickmacapp-2025.6.24.dist-info/METADATA,sha256=9vY7FxxnBVFNeZJIEvJi2bN4ZEiEhiHThFjAQJlwp2Y,1344
10
+ quickmacapp-2025.6.24.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
11
+ quickmacapp-2025.6.24.dist-info/top_level.txt,sha256=_iJkekUYnuWhCZbFSQyo2d5_6B7OoPwx7k527bokzeA,12
12
+ quickmacapp-2025.6.24.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (78.1.0)
2
+ Generator: setuptools (80.9.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
@@ -1,12 +0,0 @@
1
- quickmacapp/__init__.py,sha256=NsZW_cPvJTunDShN3RjOyK1gTwTKsghMb7wb6vRgTXI,335
2
- quickmacapp/_background.py,sha256=M_ob3EF8v-Z_gpKuFDl2N9XrQz0eBLFfUp2XErDB3DU,5943
3
- quickmacapp/_interactions.py,sha256=eLv_mVf5Jr-puNizlR8tDL_DlLy1Rc82XQ80o0GP5R4,3439
4
- quickmacapp/_notifications.py,sha256=DNm4-vOF0wQNUUTBHqp3-RKXVQK98ef53isxwsWqyj8,16965
5
- quickmacapp/_quickapp.py,sha256=Ed5n9rAw12nlHxyBMzyPc1Vt4K2OVIqCszWPqQyVElk,7571
6
- quickmacapp/notifications.py,sha256=UCigVIurf-zWAqVXUYjTtz_xMjcoI23dqyJ4OzZurQM,10275
7
- quickmacapp/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
8
- quickmacapp-2025.4.15.dist-info/licenses/LICENSE,sha256=7RIBNNvrnKHR3lw9z3KXv3Q6RRjhyxtNQoMnoUsf3_M,1091
9
- quickmacapp-2025.4.15.dist-info/METADATA,sha256=KV6153Iy7rHC5Xrgs8RVtTtBauSRG9GNKzOk3btM-Rg,1344
10
- quickmacapp-2025.4.15.dist-info/WHEEL,sha256=CmyFI0kx5cdEMTLiONQRbGQwjIoR1aIYB7eCAQ4KPJ0,91
11
- quickmacapp-2025.4.15.dist-info/top_level.txt,sha256=_iJkekUYnuWhCZbFSQyo2d5_6B7OoPwx7k527bokzeA,12
12
- quickmacapp-2025.4.15.dist-info/RECORD,,