Repository navigation
Expand file tree
/
Copy pathServiceMonitor.py
More file actions
259 lines (213 loc) · 9.84 KB
/
Copy pathServiceMonitor.py
File metadata and controls
259 lines (213 loc) · 9.84 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
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
import tkinter as tk
from tkinter import messagebox
import psutil
import win32serviceutil
import tkinter.font as tkFont
import os
import gc
import subprocess
# v11.1.1.1
# more fixes for memory leak. GC on UI cycles now
SERVICE_FILE = "ServiceList.txt"
# Tooltip class for displaying contextual tooltips
class Tooltip:
def __init__(self, widget):
self.widget = widget
self.tip_window = None
self.text = ""
self.widget.bind("<Enter>", self.on_enter)
self.widget.bind("<Leave>", self.hide_tip)
self.widget.bind("<Motion>", self.on_motion)
def on_enter(self, event):
self.show_tip(self.text, event)
def on_motion(self, event):
if self.tip_window:
x, y = event.x_root + 10, event.y_root + 10
self.tip_window.wm_geometry(f"+{x}+{y}")
def show_tip(self, text, event):
self.text = text
if self.tip_window:
self.hide_tip()
x, y = event.x_root + 10, event.y_root + 10
self.tip_window = tw = tk.Toplevel(self.widget)
tw.wm_overrideredirect(True)
tw.wm_geometry(f"+{x}+{y}")
label = tk.Label(tw, text=text, background="lightyellow", relief="solid", borderwidth=1, font=("Arial", 10))
label.pack()
def hide_tip(self, event=None):
if self.tip_window:
self.tip_window.unbind("<Enter>")
self.tip_window.unbind("<Leave>")
self.tip_window.unbind("<Motion>")
self.tip_window.destroy()
self.tip_window = None
# Main application class
class ServiceMonitorApp:
def __init__(self, root):
# Initialize the application window and core variables
self.root = root
self.root.title("McD's Service Monitor v11.1.1.1")
self.root.geometry("400x600")
self.service_map = self.get_service_mapping()
self.services = self.load_services()
self.service_widgets = {}
# Create the UI components
self.create_main_ui()
self.create_ui()
self.update_service_status()
self.canvas.bind_all("<MouseWheel>", self.on_mouse_wheel)
# Get a mapping of service display names to actual service names
def get_service_mapping(self):
return {service.display_name(): service.name() for service in psutil.win_service_iter()}
# Load and alphabetically sort services from the service list file
def load_services(self):
try:
with open(SERVICE_FILE, "r") as file:
service_list = sorted([line.strip() for line in file if line.strip()])
return {name: self.service_map.get(name, name) for name in service_list}
except FileNotFoundError:
messagebox.showerror("Error", f"File '{SERVICE_FILE}' not found!")
return {}
# Create the main UI layout
def create_main_ui(self):
self.main_frame = tk.Frame(self.root)
self.main_frame.pack(fill="both", expand=True)
self.header_frame = tk.Frame(self.main_frame)
self.header_frame.pack(fill="x", padx=10, pady=5)
self.label = tk.Label(self.header_frame, text="Services", font=("Arial", 12, "bold"))
self.label.pack(side="left", padx=(0, 10))
self.stop_all_button = tk.Button(self.header_frame, text="Stop All", command=self.stop_all)
self.stop_all_button.pack(side="right", padx=5)
self.apply_hover_effect(self.stop_all_button)
self.start_all_button = tk.Button(self.header_frame, text="Start All", command=self.start_all)
self.start_all_button.pack(side="right", padx=5)
self.apply_hover_effect(self.start_all_button)
self.manage_button = tk.Button(self.header_frame, text="Manage", command=self.open_service_file)
self.manage_button.pack(side="right", padx=5)
self.apply_hover_effect(self.manage_button)
self.help_button = tk.Button(self.header_frame, text="Help", command=self.show_help)
self.help_button.pack(side="right", padx=5)
self.apply_hover_effect(self.help_button)
self.canvas = tk.Canvas(self.main_frame)
self.canvas.pack(side="left", fill="both", expand=True)
self.scrollbar = tk.Scrollbar(self.main_frame, orient="vertical", command=self.canvas.yview)
self.scrollbar.pack(side="right", fill="y")
self.canvas.configure(yscrollcommand=self.scrollbar.set)
self.button_frame = tk.Frame(self.canvas)
self.canvas.create_window((0, 0), window=self.button_frame, anchor="nw")
# Create the UI elements for services
def create_ui(self):
for widget in self.button_frame.winfo_children():
widget.unbind("<Enter>")
widget.unbind("<Leave>")
widget.unbind("<Motion>")
widget.destroy()
self.service_widgets.clear()
test_font = tkFont.nametofont("TkDefaultFont")
# Pad the button width by 20 pixels
button_width = max(20, max(test_font.measure(name) for name in self.services.keys()) // 8) + 20
for friendly_name, actual_name in self.services.items():
frame = tk.Frame(self.button_frame)
frame.pack(pady=5, padx=10, anchor="w")
canvas = tk.Canvas(frame, width=20, height=20, highlightthickness=0)
indicator = canvas.create_oval(2, 2, 18, 18, fill="gray")
canvas.pack(side="left")
btn = tk.Button(frame, text=friendly_name, width=button_width, command=lambda svc=actual_name: self.toggle_service(svc))
btn.pack(side="left", padx=10)
self.update_tooltip(btn, actual_name)
self.apply_hover_effect(btn)
self.service_widgets[actual_name] = (canvas, indicator, btn)
self.button_frame.update_idletasks()
self.canvas.config(scrollregion=self.canvas.bbox("all"))
# Update the status of services and refresh the UI
def update_service_status(self):
for service_name, (canvas, indicator, btn) in self.service_widgets.items():
status = self.get_service_status(service_name)
color = "lightgreen" if status else "red" if status is not None else "gray"
canvas.itemconfig(indicator, fill=color)
tooltip_text = "Click to STOP" if status else "Click to START"
btn.tooltip.text = tooltip_text
gc.collect()
self.button_frame.update_idletasks()
self.canvas.config(scrollregion=self.canvas.bbox("all"))
self.root.after(10000, self.update_service_status)
# Get the status of a service
def get_service_status(self, service_name):
try:
service = psutil.win_service_get(service_name)
return service.status() == "running"
except Exception:
return None
# Toggle the state of a service (start/stop)
def toggle_service(self, service_name):
try:
service = psutil.win_service_get(service_name)
if service.status() == "running":
win32serviceutil.StopService(service_name)
else:
win32serviceutil.StartService(service_name)
except Exception as e:
messagebox.showerror("Error", f"Failed to control service '{service_name}': {str(e)}")
finally:
self.update_service_status()
# Start all services in the list
def start_all(self):
for service_name in self.services.values():
try:
win32serviceutil.StartService(service_name)
except Exception:
continue
self.update_service_status()
# Stop all services in the list
def stop_all(self):
for service_name in self.services.values():
try:
win32serviceutil.StopService(service_name)
except Exception:
continue
self.update_service_status()
# Open the service list file in Notepad
def open_service_file(self):
if not os.path.exists(SERVICE_FILE):
with open(SERVICE_FILE, "w") as f:
f.write("")
subprocess.Popen(["notepad.exe", SERVICE_FILE])
# Update the tooltip text for a button
def update_tooltip(self, button, service_name):
status = self.get_service_status(service_name)
tooltip_text = "Click to STOP" if status else "Click to START"
if hasattr(button, "tooltip") and button.tooltip:
button.tooltip.hide_tip()
del button.tooltip
button.tooltip = Tooltip(button)
button.tooltip.text = tooltip_text
# Apply hover effects to buttons
def apply_hover_effect(self, button):
def on_enter(event):
button.config(bg="lightblue")
if hasattr(button, "tooltip"):
button.tooltip.show_tip(button.tooltip.text, event)
def on_leave(event):
button.config(bg="SystemButtonFace")
if hasattr(button, "tooltip"):
button.tooltip.hide_tip()
button.bind("<Enter>", on_enter)
button.bind("<Leave>", on_leave)
# Enable the mouse wheel
def on_mouse_wheel(self, event):
self.canvas.yview_scroll(-1 if event.delta > 0 else 1, "units")
# Help Dialog
def show_help(self):
help_msg = """Service State GREEN - running
Service State RED - stopped
Service State GRAY - unknown
Click a service button to toggle state
Click STOP ALL to Stop all Services
Click START ALL to Start all Services
Click MANAGE to edit the Service List
Restart Service Monitor after list edit!"""
messagebox.showinfo("Service Manager Help",help_msg)
if __name__ == "__main__":
root = tk.Tk()
app = ServiceMonitorApp(root)
root.mainloop()