doorstop.gui.main   A
last analyzed

Complexity

Total Complexity 13

Size/Duplication

Total Lines 153
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
wmc 13
eloc 90
dl 0
loc 153
rs 10
c 0
b 0
f 0

3 Functions

Rating   Name   Duplication   Size   Complexity  
A main() 0 36 3
A _configure_logging() 0 20 3
B run() 0 59 7
1
#!/usr/bin/env python
2
# SPDX-License-Identifier: LGPL-3.0-only
3
# type: ignore
4
# pylint: disable=import-outside-toplevel
5
6
"""Graphical interface for Doorstop."""
7
8
import argparse
9
import logging
10
import os
11
import sys
12
from unittest.mock import Mock
13
14
from doorstop import common, settings
15
from doorstop.common import HelpFormatter, WarningFormatter
16
from doorstop.gui import application, widget
17
18
try:
19
    import tkinter as tk
20
    from tkinter import ttk
21
except ImportError as _exc:
22
    sys.stderr.write("WARNING: {}\n".format(_exc))
23
    tk = Mock()
24
    ttk = Mock()
25
26
27
log = common.logger(__name__)
28
29
30
def main(args=None):
31
    """Process command-line arguments and run the program."""
32
    from doorstop import GUI, VERSION
33
34
    # Shared options
35
    debug = argparse.ArgumentParser(add_help=False)
36
    debug.add_argument("-V", "--version", action="version", version=VERSION)
37
    debug.add_argument(
38
        "-v", "--verbose", action="count", default=0, help="enable verbose logging"
39
    )
40
    shared = {"formatter_class": HelpFormatter, "parents": [debug]}
41
    parser = argparse.ArgumentParser(prog=GUI, description=__doc__, **shared)
42
43
    # Build main parser
44
    parser.add_argument(
45
        "-j", "--project", metavar="PATH", help="path to the root of the project"
46
    )
47
48
    # Parse arguments
49
    args = parser.parse_args(args=args)
50
51
    # Configure logging
52
    _configure_logging(args.verbose)
53
54
    # Run the program
55
    try:
56
        success = run(args, os.getcwd(), parser.error)
57
    except KeyboardInterrupt:
58
        log.debug("program interrupted")
59
        success = False
60
    if success:
61
        log.debug("program exited")
62
        return 0
63
    else:
64
        log.debug("program exited with error")
65
        return 1
66
67
68
def _configure_logging(verbosity=0):
69
    """Configure logging using the provided verbosity level (0+)."""
70
    # Configure the logging level and format
71
    if verbosity == 0:
72
        level = settings.VERBOSE_LOGGING_LEVEL
73
        default_format = settings.VERBOSE_LOGGING_FORMAT
74
        verbose_format = settings.VERBOSE_LOGGING_FORMAT
75
    elif verbosity == 1:
76
        level = settings.VERBOSE2_LOGGING_LEVEL
77
        default_format = settings.VERBOSE_LOGGING_FORMAT
78
        verbose_format = settings.VERBOSE_LOGGING_FORMAT
79
    else:
80
        level = settings.VERBOSE2_LOGGING_LEVEL
81
        default_format = settings.TIMED_LOGGING_FORMAT
82
        verbose_format = settings.TIMED_LOGGING_FORMAT
83
84
    # Set a custom formatter
85
    logging.basicConfig(level=level)
86
    formatter = WarningFormatter(default_format, verbose_format)
87
    logging.root.handlers[0].setFormatter(formatter)
88
89
90
def run(args, cwd, error):
91
    """Start the GUI.
92
93
    :param args: Namespace of CLI arguments (from this module or the CLI)
94
    :param cwd: current working directory
95
    :param error: function to call for CLI errors
96
97
    """
98
    from doorstop import __project__, __version__
99
100
    # Exit if tkinter is not available
101
    if isinstance(tk, Mock) or isinstance(ttk, Mock):
102
        return error("tkinter is not available")
103
104
    else:
105
        root = widget.Tk()
106
        root.title("{} ({})".format(__project__, __version__))
107
108
        from sys import platform as _platform
109
110
        from doorstop.gui import resources
111
112
        # Load the icon
113
        if _platform in ("linux", "linux2", "darwin"):
114
            # Linux
115
            root.tk.call(
116
                # pylint: disable=protected-access
117
                "wm",
118
                "iconphoto",
119
                root._w,
120
                tk.PhotoImage(data=resources.b64_doorstopicon_png),
121
            )
122
        elif _platform in ("win32", "win64"):
123
            # Windows
124
            import base64
125
            import tempfile
126
127
            try:
128
                with tempfile.TemporaryFile(
129
                    mode="w+b", suffix=".ico", delete=False
130
                ) as theTempIconFile:
131
                    theTempIconFile.write(
132
                        base64.b64decode(resources.b64_doorstopicon_ico)
133
                    )
134
                    theTempIconFile.flush()
135
                root.iconbitmap(theTempIconFile.name)
136
            finally:
137
                try:
138
                    os.unlink(theTempIconFile.name)
139
                except Exception:  # pylint: disable=W0703
140
                    pass
141
142
        app = application.Application(root, cwd, args.project)
143
144
        root.update()
145
        root.minsize(root.winfo_width(), root.winfo_height())
146
        app.mainloop()
147
148
        return True
149
150
151
if __name__ == "__main__":
152
    sys.exit(main())
153