Repository navigation
Expand file tree
/
Copy pathapp.py
More file actions
158 lines (127 loc) · 5.47 KB
/
Copy pathapp.py
File metadata and controls
158 lines (127 loc) · 5.47 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
"""HopeIT Manager - single-window shell with a left sidebar.
Run with: python app.py
The sidebar swaps modules into one content area; nothing opens a new
window (add/edit use a short-lived modal dialog). On startup the app runs
a "ping" query and shows the result in the status bar; a failed
connection shows a dialog and still opens the shell so config.ini can be
fixed and retried.
"""
import tkinter as tk
from tkinter import ttk, messagebox
import config
import db
from modules import theme
from modules.customers import CustomersPanel
from modules.products import ProductsPanel
from modules.orders import OrdersPanel
from modules.reports import ReportsPanel
PANELS = [
("customers", "Customers", CustomersPanel),
("products", "Products", ProductsPanel),
("orders", "Orders", OrdersPanel),
("reports", "Reports", ReportsPanel),
]
class App(tk.Tk):
def __init__(self):
super().__init__()
self.title("HopeIT Manager")
self.minsize(960, 620)
self.geometry("1180x720")
theme.apply(self) # follows the OS light/dark setting
self._panels = {}
self._nav_buttons = {}
self._active = None
self._build_sidebar()
self._build_statusbar()
self._build_content()
theme.center(self)
self.show("customers")
self.after(50, self._settle_theme)
self.after(150, self.startup_ping)
# ------------------------------------------------------------ layout
def _build_sidebar(self):
bar = ttk.Frame(self, style="Sidebar.TFrame", padding=(theme.PAD_M, theme.PAD_L))
bar.pack(side="left", fill="y")
ttk.Label(bar, text="HopeIT", style="Sidebar.TLabel",
font=("Segoe UI", 20, "bold")).pack(anchor="w")
sub = ttk.Label(bar, text=config.COMPANY_NAME, style="Sidebar.TLabel")
sub.pack(anchor="w", pady=(0, theme.PAD_L))
for key, label, _cls in PANELS:
btn = ttk.Button(bar, text=label, style="Nav.TButton",
command=lambda k=key: self.show(k))
btn.pack(fill="x", pady=theme.PAD_XS)
self._nav_buttons[key] = btn
footer = ttk.Label(bar, text="Made by: Ronan-R-R", style="Sidebar.TLabel")
footer.pack(side="bottom", anchor="w")
ttk.Separator(self, orient="vertical").pack(side="left", fill="y")
def _build_content(self):
self.container = ttk.Frame(self, padding=theme.PAD_L)
self.container.pack(side="left", fill="both", expand=True)
def _build_statusbar(self):
bar = ttk.Frame(self, padding=(theme.PAD_M, theme.PAD_S))
bar.pack(side="bottom", fill="x")
ttk.Separator(self, orient="horizontal").pack(side="bottom", fill="x")
self.status = ttk.Label(bar, text="Connecting...", style="Muted.TLabel")
self.status.pack(side="left")
self.theme_btn = ttk.Button(bar, width=12, command=self.flip_theme,
text="Light mode" if theme.current_mode() == "dark"
else "Dark mode")
self.theme_btn.pack(side="right")
ttk.Button(bar, text="Test connection", command=self.test_connection).pack(
side="right", padx=theme.PAD_S)
# ---------------------------------------------------------- behaviour
def show(self, key):
if self._active == key:
return
for panel in self._panels.values():
panel.pack_forget()
if key not in self._panels:
cls = next(c for k, _l, c in PANELS if k == key)
self._panels[key] = cls(self.container, self)
panel = self._panels[key]
panel.pack(fill="both", expand=True)
if hasattr(panel, "on_show"):
panel.on_show()
self._active = key
for k, btn in self._nav_buttons.items():
btn.configure(style="Accent.TButton" if k == key else "Nav.TButton")
def _restyle_all(self):
for key, btn in self._nav_buttons.items():
btn.configure(style="Accent.TButton" if key == self._active else "Nav.TButton")
for panel in self._panels.values():
if hasattr(panel, "on_theme_change"):
panel.on_theme_change()
def _settle_theme(self):
# sv-ttk can under-paint on the first frame; re-assert once the
# window is up, exactly like the manual toggle does.
theme.reassert(self)
self._restyle_all()
def flip_theme(self):
mode = theme.toggle(self)
self.theme_btn.configure(text="Light mode" if mode == "dark" else "Dark mode")
self._restyle_all()
def set_status(self, text, ok=None):
colour = None
if ok is True:
colour = theme.palette()["ok"]
elif ok is False:
colour = theme.palette()["err"]
self.status.configure(text=text,
foreground=colour or theme.palette()["muted"])
def startup_ping(self):
ok, message = db.ping()
self.set_status(message, ok)
if not ok:
messagebox.showerror(
"Database connection failed",
message + "\n\nEdit config.ini and press 'Test connection'.",
parent=self)
def test_connection(self):
ok, message = db.ping()
self.set_status(message, ok)
(messagebox.showinfo if ok else messagebox.showerror)(
"Connection OK" if ok else "Connection failed", message, parent=self)
def main():
App().mainloop()
if __name__ == "__main__":
main()