quickmacapp 2023.2.21__tar.gz → 2023.4.24__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.
@@ -1,7 +1,8 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) 2023 Palo Alto Networks
4
- Portions Copyright (c) 2021 Glyph
3
+ Copyright
4
+ (c) 2023 Glyph
5
+ (c) 2023 Palo Alto Networks
5
6
 
6
7
  Permission is hereby granted, free of charge, to any person obtaining a copy
7
8
  of this software and associated documentation files (the "Software"), to deal
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: quickmacapp
3
- Version: 2023.2.21
3
+ Version: 2023.4.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
@@ -15,19 +15,22 @@ QuickMacApp
15
15
  rapidly and if you want to use this to ship an app you probably will want
16
16
  to contribute to it as well.
17
17
 
18
- Make it easier to write small applications for macOS (that use Twisted).
18
+ Make it easier to write small applications for macOS in Python, using Twisted.
19
19
 
20
20
  To get a very basic status menu API:
21
21
 
22
22
  .. code::
23
23
  python
24
24
 
25
- from quickmacapp import mainpoint, Status, quit
25
+ from quickmacapp import mainpoint, Status, answer, quit
26
+ from twisted.internet.defer import Deferred
26
27
 
27
28
  @mainpoint()
28
- def app(reactor: IReactorTime):
29
- Status("☀️ 💣").menu([("Do Something", lambda: print("something")),
30
- ("Quit", quit)])
29
+ def app(reactor):
30
+ s = Status("☀️ 💣")
31
+ s.menu([("Do Something", lambda: Deferred.fromCoroutine(answer("something"))),
32
+ ("Quit", quit)])
33
+ app.runMain()
31
34
 
32
35
  Packaging this into a working app bundle is currently left as an exercise for
33
36
  the reader.
@@ -8,19 +8,22 @@ QuickMacApp
8
8
  rapidly and if you want to use this to ship an app you probably will want
9
9
  to contribute to it as well.
10
10
 
11
- Make it easier to write small applications for macOS (that use Twisted).
11
+ Make it easier to write small applications for macOS in Python, using Twisted.
12
12
 
13
13
  To get a very basic status menu API:
14
14
 
15
15
  .. code::
16
16
  python
17
17
 
18
- from quickmacapp import mainpoint, Status, quit
18
+ from quickmacapp import mainpoint, Status, answer, quit
19
+ from twisted.internet.defer import Deferred
19
20
 
20
21
  @mainpoint()
21
- def app(reactor: IReactorTime):
22
- Status("☀️ 💣").menu([("Do Something", lambda: print("something")),
23
- ("Quit", quit)])
22
+ def app(reactor):
23
+ s = Status("☀️ 💣")
24
+ s.menu([("Do Something", lambda: Deferred.fromCoroutine(answer("something"))),
25
+ ("Quit", quit)])
26
+ app.runMain()
24
27
 
25
28
  Packaging this into a working app bundle is currently left as an exercise for
26
29
  the reader.
@@ -9,7 +9,7 @@ build-backend = "setuptools.build_meta"
9
9
  name = "quickmacapp"
10
10
  description = "Make it easier to write Mac apps in Python"
11
11
  readme = "README.rst"
12
- version = "2023.02.21"
12
+ version = "2023.04.24"
13
13
  dependencies = [
14
14
  "pyobjc-framework-Cocoa",
15
15
  "pyobjc-framework-ExceptionHandling",
@@ -1,4 +1,5 @@
1
1
  from ._quickapp import Actionable, Status, mainpoint, menu, quit
2
+ from ._interactions import ask, choose, answer
2
3
 
3
4
  __all__ = [
4
5
  "Actionable",
@@ -6,4 +7,7 @@ __all__ = [
6
7
  "mainpoint",
7
8
  "menu",
8
9
  "quit",
10
+ "ask",
11
+ "choose",
12
+ "answer",
9
13
  ]
@@ -0,0 +1,107 @@
1
+ from typing import Iterable, Iterator, TypeVar
2
+
3
+ from AppKit import (
4
+ NSAlertFirstButtonReturn,
5
+ NSAlertSecondButtonReturn,
6
+ NSAlertThirdButtonReturn,
7
+ NSAlert,
8
+ NSApp,
9
+ )
10
+ from Foundation import (
11
+ NSRunLoop,
12
+ NSTextField,
13
+ NSRect,
14
+ )
15
+ from twisted.internet.defer import Deferred
16
+
17
+
18
+ NSModalResponse = int
19
+ T = TypeVar("T")
20
+
21
+
22
+ def asyncModal(alert: NSAlert) -> Deferred[NSModalResponse]:
23
+ """
24
+ Run an NSAlert asynchronously.
25
+ """
26
+ d: Deferred[NSModalResponse] = Deferred()
27
+
28
+ def runAndReport() -> None:
29
+ try:
30
+ NSApp().activateIgnoringOtherApps_(True)
31
+ result = alert.runModal()
32
+ except:
33
+ d.errback()
34
+ else:
35
+ d.callback(result)
36
+
37
+ NSRunLoop.currentRunLoop().performBlock_(runAndReport)
38
+ return d
39
+
40
+
41
+ def _alertReturns() -> Iterator[NSModalResponse]:
42
+ """
43
+ Enumerate the values used by NSAlert for return values in the order of the
44
+ buttons that occur.
45
+ """
46
+ yield NSAlertFirstButtonReturn
47
+ yield NSAlertSecondButtonReturn
48
+ yield NSAlertThirdButtonReturn
49
+ i = 1
50
+ while True:
51
+ yield NSAlertThirdButtonReturn + i
52
+ i += 1
53
+
54
+
55
+ async def choose(values: Iterable[tuple[T, str]], question: str, description: str="") -> T:
56
+ """
57
+ Prompt the user to choose between the given values, on buttons labeled in
58
+ the given way.
59
+ """
60
+ msg = NSAlert.alloc().init()
61
+ msg.setMessageText_(question)
62
+ msg.setInformativeText_(description)
63
+ potentialResults = {}
64
+ for (value, label), alertReturn in zip(values, _alertReturns()):
65
+ msg.addButtonWithTitle_(label)
66
+ potentialResults[alertReturn] = value
67
+ msg.layout()
68
+ return potentialResults[await asyncModal(msg)]
69
+
70
+
71
+ async def ask(question: str, description: str="", defaultValue: str="") -> str | None:
72
+ """
73
+ Prompt the user for a short string of text.
74
+ """
75
+ msg = NSAlert.alloc().init()
76
+ msg.addButtonWithTitle_("OK")
77
+ msg.addButtonWithTitle_("Cancel")
78
+ msg.setMessageText_(question)
79
+ msg.setInformativeText_(description)
80
+
81
+ txt = NSTextField.alloc().initWithFrame_(NSRect((0, 0), (200, 100)))
82
+ txt.setMaximumNumberOfLines_(5)
83
+ txt.setStringValue_(defaultValue)
84
+ msg.setAccessoryView_(txt)
85
+ msg.window().setInitialFirstResponder_(txt)
86
+ msg.layout()
87
+
88
+ response: NSModalResponse = await asyncModal(msg)
89
+
90
+ if response == NSAlertFirstButtonReturn:
91
+ result: str = txt.stringValue()
92
+ return result
93
+
94
+ return None
95
+
96
+
97
+ async def answer(message: str, description: str="") -> None:
98
+ """
99
+ Give the user a message.
100
+ """
101
+ msg = NSAlert.alloc().init()
102
+ msg.setMessageText_(message)
103
+ msg.setInformativeText_(description)
104
+ # msg.addButtonWithTitle("OK")
105
+ msg.layout()
106
+
107
+ await asyncModal(msg)
@@ -5,7 +5,7 @@ import sys
5
5
  import traceback
6
6
  from typing import Callable, Protocol, Any
7
7
 
8
- from objc import ivar, IBAction
8
+ from objc import ivar, IBAction, super
9
9
 
10
10
  from Foundation import (
11
11
  NSObject,
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: quickmacapp
3
- Version: 2023.2.21
3
+ Version: 2023.4.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
@@ -15,19 +15,22 @@ QuickMacApp
15
15
  rapidly and if you want to use this to ship an app you probably will want
16
16
  to contribute to it as well.
17
17
 
18
- Make it easier to write small applications for macOS (that use Twisted).
18
+ Make it easier to write small applications for macOS in Python, using Twisted.
19
19
 
20
20
  To get a very basic status menu API:
21
21
 
22
22
  .. code::
23
23
  python
24
24
 
25
- from quickmacapp import mainpoint, Status, quit
25
+ from quickmacapp import mainpoint, Status, answer, quit
26
+ from twisted.internet.defer import Deferred
26
27
 
27
28
  @mainpoint()
28
- def app(reactor: IReactorTime):
29
- Status("☀️ 💣").menu([("Do Something", lambda: print("something")),
30
- ("Quit", quit)])
29
+ def app(reactor):
30
+ s = Status("☀️ 💣")
31
+ s.menu([("Do Something", lambda: Deferred.fromCoroutine(answer("something"))),
32
+ ("Quit", quit)])
33
+ app.runMain()
31
34
 
32
35
  Packaging this into a working app bundle is currently left as an exercise for
33
36
  the reader.
@@ -2,6 +2,7 @@ LICENSE
2
2
  README.rst
3
3
  pyproject.toml
4
4
  src/quickmacapp/__init__.py
5
+ src/quickmacapp/_interactions.py
5
6
  src/quickmacapp/_quickapp.py
6
7
  src/quickmacapp/py.typed
7
8
  src/quickmacapp.egg-info/PKG-INFO