-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathtmonitor.py
More file actions
291 lines (245 loc) · 11.4 KB
/
tmonitor.py
File metadata and controls
291 lines (245 loc) · 11.4 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
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
# -*- coding: utf-8 -*-
#
# Written by: Carlos Bazaga
#
# Licensed under BSD 2-Clause
#
import Tkinter as tk
import ScrolledText
import time
import tspy
class Tmonitor(tk.Tk):
"""
This class implements a Monitor App for Tspy and Tpools.
Contains:
A monitorization window wich shows the text of monitorization.
This window has two buttons on its left, one to stop/resume monitorization
and one to switch between the two monitorization methods: Tpools or Tspies.
A top collection of personalized buttons for Task launch.
A set of windows intended for use as Tasks terminals.
"""
def __init__(self, title, interval, tpools=[], buttons=[]):
"""Return a new Tmonitor."""
tk.Tk.__init__(self)
self.mon_est = True # Flags Tspy monitorization as active. Otherwise Tpool is.
self.name = title # Window title.
self.interval = interval # Interval of monitorization.
self.tpools = set(tpools) # List of Tpools to monitorize.
self.tspy = tspy.Tspy() # A Tspy for current process.
self.title(title)
# Create monitor window frame.
monitor_frame = tk.Frame(self)
monitor_frame.pack(side="bottom", fill="both", expand=True)
# Create monitor text widget.
self.text_widget = ScrolledText.ScrolledText(monitor_frame)
self.text_widget.pack(side="right",fill="both", expand=True)
# Create monitoring control column frame.
ctrl_buttons_frame = tk.Frame(monitor_frame)
ctrl_buttons_frame.pack(side="left", fill="y", expand=False)
# Create monitoring control buttons.
self.button_go = tk.Button(ctrl_buttons_frame, text='Go', command=self._command_go)
self.button_stop = tk.Button(ctrl_buttons_frame, text='Stop', command=self._command_stop)
button_switch = tk.Button(ctrl_buttons_frame, text='Switch\nMode', command=self._command_switch)
self.button_stop.pack(side="top", fill="both", expand=True)
button_switch.pack(side="bottom", fill="both", expand=True)
# Create "on demand" buttons row frame.
self.task_buttons_frame = tk.Frame(self)
self.task_buttons_frame.pack(side="top", fill="x", expand=False)
# Spawn "on demand" buttons buttons.
self.spawn_buttons(buttons)
# Set "window close" event capture.
self.protocol("WM_DELETE_WINDOW", self.kill)
# Program first update.
self.loop = True
self.progr = self.after(int(self.interval*1000), self.update)
def start(self):
"""Create the Tmonitor window and run the Tk mainloop."""
self.mainloop()
def kill(self):
"""Stop the "Monitor" in an ordered way."""
if self.loop and self.progr:
self.loop = False
self.after_cancel(self.progr)
self.progr = None
# Destroy App.
self.destroy()
def update(self):
"""Update the Monitor text widget."""
if self.mon_est:
text = '"Tspy" Monitoring:\n'
text += time.ctime()
text += self._format_tspy()
else:
text = '"Tpools" Monitoring:\n'
text += time.ctime()
text += self._format_monitor_tpools()
# Update the text widget.
self.text_widget.config(state = tk.NORMAL) # Enable writing.
self.text_widget.delete('1.0', tk.END) # Empty the widget.
self.text_widget.insert(tk.END, text) # Text insert.
self.text_widget.config(state = tk.DISABLED) # Disable writing.
# Program next update.
self.progr = None
if self.loop and not self.progr:
self.progr = self.after(int(self.interval*1000), self.update)
def add_Tpool(self, tpool):
"""Add "tpool" to the monitoring list."""
self.tpools.add(tpool)
def remove_Tpool(self, tpool):
"""Remove "tpool" from the monitoring list."""
self.tpools.discard(tpool)
def spawn_buttons(self, buttons):
"""
Create a set of buttons in different rows.
"butons" must follow this pattern:
A list of lists each one represents a new row of buttons[[button_definition]]
With "button definition" as follows: (name, task, args, kwargs)
Were "name" stands for button text and "task" is the target function.
"args" and "kwargs" stands as usual.
"""
for row in buttons:
# Create row frame.
row_frame = tk.Frame(self.task_buttons_frame)
row_frame.pack(side="top", fill="x", expand=False)
for name, task, args, kwargs in row:
# Create a lambda function for button command.
Lambda_command = lambda task=task, args=args, kwargs=kwargs : task(*args, **kwargs)
# Create button.
tk.Button(row_frame, text=name, command=Lambda_command).pack(side="left", fill="x", expand=True)
def pop_terminal(self, name, close_callback=None):
"""Create and return a new output terminal in a separate window."""
def write(text):
"""Write the given "text" on the text_widget."""
output_widget.config(state = tk.NORMAL) # Enable writing.
output_widget.insert(tk.END,text) # Text insert.
output_widget.config(state = tk.DISABLED) # Disable writing.
def clear():
"""Clear the content of the text_widget."""
output_widget.config(state = tk.NORMAL) # Enable writing.
output_widget.delete('1.0', tk.END) # Empty the widget.
output_widget.config(state = tk.DISABLED) # Disable writing.
def close():
"""Notify and Close the Window.
Any following atempt to write will raise an exception.
"""
if close_notify:
close_notify()
output_window.destroy()
def spawn_buttons(buttons):
"""
Create a set of buttons in different rows.
"butons" must follow this pattern:
A list of lists each one represents a new row of buttons[[button_definition]]
With "button definition" as follows: (name, task, args, kwargs)
Were "name" stands for button text and "task" is the target function.
"args" and "kwargs" stands as usual.
"""
for row in buttons:
# Create row frame.
row_frame = tk.Frame(buttons_frame)
row_frame.pack(side="top", fill="x", expand=False)
for name, task, args, kwargs in row:
# Create a lambda function for button command.
Lambda_command = lambda task=task, args=args, kwargs=kwargs : task(*args, **kwargs)
# Create button.
tk.Button(row_frame, text=name, command=Lambda_command).pack(side="left", fill="x", expand=True)
close_notify = close_callback
# Output window.
output_window = tk.Toplevel()
output_window.title(name)
output_window.protocol("WM_DELETE_WINDOW", close)
# Output buttons row frame.
buttons_frame = tk.Frame(output_window)
buttons_frame.pack(side="top", fill="x", expand=False)
# Output Text.
output_widget = ScrolledText.ScrolledText(output_window)
output_widget.pack(side="bottom", fill="both", expand=True)
output_widget.config(state = tk.DISABLED) # Disable writing.
output_widget.write = write
output_widget.clear = clear
output_widget.close = close
output_widget.spawn_buttons = spawn_buttons
return output_widget
def _command_stop(self):
"""Stop the update of the monitoring loop."""
if self.loop and self.progr:
self.loop = False
self.after_cancel(self.progr)
self.progr = None
# Switch buttons.
self.button_stop.pack_forget()
self.button_go.pack(side="top", fill="both", expand=True)
def _command_go(self):
"""Starts the update of the monitoring loop."""
if not self.loop and not self.progr:
self.loop = True
self.progr = self.after(int(self.interval*1000), self.update)
# Switch buttons.
self.button_go.pack_forget()
self.button_stop.pack(side="top", fill="both", expand=True)
def _command_switch(self):
"""Switch monitoring between "Tpools" and "Tspy"."""
self.mon_est = not self.mon_est
def _format_tspy(self):
"""Return the formatted text for "Tspy"."""
text = '{0}\n'.format(self._format_processes([self.tspy.get_report()], 0))
return text
def _format_processes(self, processes, lvl):
"""Return the formatted text for "Processes"."""
text = ''
for process_head, process_threads, process_sons in processes:
text += '\n\n{0}●───► {2:35}{1} {3} Pid: {4}'.format(' '*lvl, ' '*(6-lvl), *process_head)
text += self._format_threads(process_threads, lvl+1)
text += self._format_processes(process_sons, lvl+1)
return text
def _format_threads(self, threads, lvl):
"""Return the formatted text for "Threads"."""
text = ''
for thread_head in threads:
text += '\n{0}├───► {2:35}{1} {3} Tid: {4}'.format(' '*lvl, ' '*(6-lvl), *thread_head)
text = text[::-1].replace('├'[::-1], '└'[::-1], 1)[::-1]
return text
def _format_monitor_tpools(self):
"""Return the formatted text for "Tpools"."""
text = ''
# Remove problematic "Tpools" from "Tmonitor"
for tpool in set(self.tpools):
try:
tpool.get_name()
except Exception as Exc:
self.tpools.discard(tpool)
# Get "Tpools" status.
try:
for tpool in sorted(self.tpools, key=lambda x: x.get_name()):
tpool_name, tpool_workers = tpool.get_status()
text += '\n\n● {0}:'.format(tpool_name)
text += '{0}\n'.format(self._format_tpool_workers(tpool_workers))
except Exception as Exc:
text += '\nERROR: {0}'.format(Exc)
return text
def _format_tpool_workers(self, workers):
"""Return the formatted text for "Workers" in a "Tpool"."""
text = ''
for worker_name, worker_workload, worker_tasks in workers:
text += '\n\n ●───► {0}: (Workload: {1})'.format(worker_name, worker_workload)
text += self._format_worker_tasks(worker_tasks)
return text
def _format_worker_tasks(self, tasks):
"""Return the formatted text for "Tasks" in a "Worker"."""
text = ''
for task in tasks:
textt = '\n ├───► {0}('.format(task[0])
for arg in task[1]:
textt += '{0}, '.format(repr(arg))
for kwarg in task[2].items():
textt += '{0}={1}, '.format(repr(kwarg[0]), repr(kwarg[1]))
textt = textt[:-2]
textt += '){0}>──► {1}. (Workload: {2})'.format(' '*(80-len(textt)), task[4], task[3])
text += textt
text = text[::-1].replace('├'[::-1], '└'[::-1], 1)[::-1]
return text
def Launch(title, interval, tpools=[], buttons=[]):
"""Create a "Tmonitor" running."""
Monitor = Tmonitor(title, interval, tpools, buttons)
Monitor.start()
return Monitor