things3.things3_app.main()   A
last analyzed

Complexity

Conditions 1

Size

Total Lines 3
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 2
nop 0
dl 0
loc 3
rs 10
c 0
b 0
f 0
1
#!/usr/bin/env python3
2
# -*- coding: utf-8 -*-
3
4
"""KanbanView (app) for Things 3."""
5
6
from __future__ import print_function
7
8
import sys
9
import signal
10
from os import system
11
from threading import Thread
12
import webview  # type: ignore
13
import objc  # type: ignore # pylint: disable=unused-import,import-error # noqa F401
14
from things3 import things3_api
15
16
17
class Things3App:
18
    """App wrapper for simple read-only API for Things 3."""
19
20
    database = None
21
    FILE = "kanban.html"
22
    api = None
23
    api_thread = None
24
    window = None
25
26
    def open_api(self):
27
        """Delay opening the browser."""
28
        print(f"Using database 2: {self.database}")
29
        self.api.main()
30
31
    def __init__(self, database=None, debug_text=""):
32
        self.database = database
33
        self.api = things3_api.Things3API(database=self.database, debug_text=debug_text)
34
35
    def sigterm_handler(self, _signo, _stack_frame):
36
        """Make sure the server shuts down."""
37
        print("Sigterm...")
38
        self.api.flask_context.shutdown()
39
40
    def main(self, appstore=False):
41
        """Run the app."""
42
        # kill possible zombie processes; can't use psutil in py2app context
43
        system("lsof -nti:" + str(things3_api.Things3API.port) + " | xargs kill -9")
44
45
        # Make sure the server shuts down
46
        signal.signal(signal.SIGTERM, self.sigterm_handler)
47
48
        print(f"Using database 1: {self.database}")
49
50
        self.window = webview.create_window(
51
            title="KanbanView",
52
            url=f"http://{things3_api.Things3API.host}:"
53
            + f"{things3_api.Things3API.port}/{self.FILE}",
54
            width=1280,
55
            height=650,
56
            min_size=(1280, 650),
57
            frameless=False,
58
        )
59
60
        if not appstore:
61
            self.window.closed += advertise
62
        self.window.closing += self.closing
63
        self.api_thread = Thread(target=self.open_api)
64
65
        try:
66
            self.api_thread.start()
67
            webview.start(self.closing, self.window)  # blocking
68
            self.api.flask_context.shutdown()
69
            self.api_thread.join()
70
        except KeyboardInterrupt:
71
            print("Shutting down...")
72
            self.api.flask_context.shutdown()
73
            self.api_thread.join()
74
            sys.exit(0)
75
76
    def closing(self, _window=None):
77
        """Close the app"""
78
        if not _window:
79
            self.window.destroy()
80
            # self.api.flask_context.shutdown()
81
            # self.api_thread.join()
82
        else:
83
            print(f"Registering close function for: {_window}")
84
85
86
def advertise():
87
    """Show a hint to buy the app"""
88
    text = (
89
        "Thank you for using KanbanView! "
90
        + "If you enjoy using it, please consider buying the app."
91
    )
92
    title = "KanbanView"
93
    url = "https://kanbanview.app"
94
    system(
95
        """osascript -e ' """
96
        + f"""set dialog to (display dialog "{text}" """
97
        + """buttons {"Buy", "Later"} default button 1 """
98
        + """giving up after 10 """
99
        + f"""with title "{title}" """
100
        + """with icon POSIX file "resources/icon.icns")' """
101
        + """-e 'if the button returned of the result is "Buy" then' """
102
        + f"""-e 'do shell script "open {url}"' -e 'end if'"""
103
    )
104
105
106
def main():
107
    """Main entry point for CLI installation"""
108
    Things3App().main()
109
110
111
if __name__ == "__main__":
112
    main()
113