system-monitor

Форк
0
/
monitor.py 
343 строки · 13.1 Кб
1
import psutil
2
import platform
3
import tkinter as tk
4
from tkinter import ttk, filedialog
5
import os
6
import datetime
7
import threading
8
import pynvml
9

10
# Initialize NVML
11
pynvml.nvmlInit()
12

13
output_file = ""  # Глобальная переменная для пути сохранения файла
14
dynamic_update_enabled = False  # Флаг для отслеживания состояния динамического обновления
15
current_sort_column = 'PID'  # Колонка, по которой сортируем в данный момент
16
current_sort_order = 'asc'  # Порядок сортировки: 'asc' или 'desc'
17

18
def clear_console():
19
    os.system('cls' if os.name == 'nt' else 'clear')
20

21

22
def sort_processes(processes):
23
    global current_sort_column, current_sort_order
24
    if current_sort_column == 'PID':
25
        processes.sort(key=lambda x: int(x[0]), reverse=(current_sort_order == 'desc'))
26
    else:
27
        index = {'Name': 1, 'Memory%': 2, 'CPU%': 3}[current_sort_column]
28
        processes.sort(key=lambda x: x[index], reverse=(current_sort_order == 'desc'))
29
    return processes
30

31

32
def update_treeview(tree, processes):
33
    selected_item = tree.selection()
34
    selected_pid = None
35
    if selected_item:
36
        selected_pid = tree.item(selected_item, 'values')[0]
37

38
    for row in tree.get_children():
39
        tree.delete(row)
40

41
    for pid, name, memory_percent, cpu_percent in processes:
42
        tree.insert('', 'end', values=(pid, name, f"{memory_percent:.2f}", f"{cpu_percent:.2f}"))
43

44
    if selected_pid:
45
        for row in tree.get_children():
46
            if tree.item(row, 'values')[0] == selected_pid:
47
                tree.selection_set(row)
48
                break
49

50

51
def fetch_system_usage():
52
    cpu_usage = psutil.cpu_percent()
53
    memory_info = psutil.virtual_memory()
54
    disk_usage = psutil.disk_usage('/')
55
    net_io = psutil.net_io_counters()
56
    return cpu_usage, memory_info, disk_usage, net_io
57

58

59
def fetch_processes():
60
    processes = []
61
    for proc in psutil.process_iter(['pid', 'name', 'memory_percent', 'cpu_percent']):
62
        try:
63
            processes.append(
64
                (proc.info['pid'], proc.info['name'], proc.info['memory_percent'], proc.info['cpu_percent']))
65
        except (psutil.NoSuchProcess, psutil.AccessDenied, psutil.ZombieProcess):
66
            continue
67
    return processes
68

69

70
def update_system_info():
71
    cpu_usage, memory_info, disk_usage, net_io = fetch_system_usage()
72

73
    # Добавляем информацию о процессоре
74
    cpu_info = f"Processor: {platform.processor()}"
75

76
    # Добавляем информацию об оперативной памяти
77
    memory_info_str = f"Memory: Total = {get_size(memory_info.total)}, " \
78
                      f"Available = {get_size(memory_info.available)}"
79

80
    # Добавляем информацию о видеокарте
81
    gpu_info = ""
82
    try:
83
        import pynvml
84

85
        pynvml.nvmlInit()
86
        device_count = pynvml.nvmlDeviceGetCount()
87
        for i in range(device_count):
88
            handle = pynvml.nvmlDeviceGetHandleByIndex(i)
89
            gpu_name = pynvml.nvmlDeviceGetName(handle)
90
            mem_info = pynvml.nvmlDeviceGetMemoryInfo(handle)
91
            gpu_info += f"GPU {i + 1}: {gpu_name}, Memory Total = {get_size(mem_info.total)}, " \
92
                        f"Memory Used = {get_size(mem_info.used)}, Memory Free = {get_size(mem_info.free)}\n"
93

94
        pynvml.nvmlShutdown()
95
    except ImportError:
96
        gpu_info = "pynvml library not found"
97

98
    # Добавляем информацию о дисках
99
    disk_info = ""
100
    partitions = psutil.disk_partitions()
101
    for partition in partitions:
102
        try:
103
            partition_usage = psutil.disk_usage(partition.mountpoint)
104
            disk_info += f"Device: {partition.device}, Total: {get_size(partition_usage.total)}, " \
105
                         f"Used: {get_size(partition_usage.used)}, Free: {get_size(partition_usage.free)}\n"
106
        except PermissionError:
107
            disk_info = "Permission denied"
108

109
    # Добавляем информацию об операционной системе
110
    os_info = f"OS: {platform.system()} {platform.release()} {platform.version()}"
111

112
    label_cpu.config(text=f"CPU Usage: {cpu_usage}%\n{cpu_info}")
113
    label_memory.config(text=f"Memory Usage: {memory_info.percent}%\n{memory_info_str}")
114
    label_disk.config(text=f"Disk Usage: {disk_usage.percent}%\n{disk_info}")
115
    label_network.config(
116
        text=f"Network: Sent = {net_io.bytes_sent / (1024 * 1024):.2f} MB, Received = {net_io.bytes_recv / (1024 * 1024):.2f} MB")
117
    label_gpu.config(text=gpu_info)
118
    label_os.config(text=os_info)
119

120

121
def get_size(bytes, suffix="B"):
122
    # Функция для форматирования размера файла
123
    factor = 1024
124
    for unit in ["", "K", "M", "G", "T", "P"]:
125
        if bytes < factor:
126
            return f"{bytes:.2f} {unit}{suffix}"
127
        bytes /= factor
128
    return f"{bytes:.2f} {unit}{suffix}"
129

130

131
def refresh_data():
132
    processes = fetch_processes()
133
    processes = sort_processes(processes)
134
    update_treeview(tree, processes)
135

136

137
def print_system_usage():
138
    global dynamic_update_enabled
139

140
    update_system_info()
141
    refresh_data()
142

143
    if dynamic_update_enabled:
144
        root.after(1000, print_system_usage)  # Планируем следующее обновление через 1 секунду
145

146

147
def toggle_dynamic_update():
148
    global dynamic_update_enabled
149
    dynamic_update_enabled = not dynamic_update_enabled
150
    if dynamic_update_enabled:
151
        label_update_status.config(text="Dynamic update: ON", foreground="green")
152
        btn_toggle_update.config(text="Disable Dynamic Update\n(Enabled)")
153
        btn_manual_update.config(state=tk.DISABLED)  # Блокируем кнопку ручного обновления
154
        threading.Thread(target=print_system_usage).start()  # Начать динамическое обновление в отдельном потоке
155
    else:
156
        label_update_status.config(text="Dynamic update: OFF", foreground="red")
157
        btn_toggle_update.config(text="Enable Dynamic Update\n(Disabled)")
158
        btn_manual_update.config(state=tk.NORMAL)  # Разблокируем кнопку ручного обновления
159

160

161
def manual_update():
162
    if dynamic_update_enabled:
163
        return  # Игнорируем ручное обновление, если включено динамическое
164
    update_system_info()  # Обновляем системную информацию
165
    refresh_data()  # Выполняем ручное обновление данных
166

167

168
def save_to_file():
169
    global output_file
170
    if not output_file:
171
        output_file = filedialog.asksaveasfilename(defaultextension=".txt",
172
                                                   filetypes=[("Text files", "*.txt"), ("All files", "*.*")])
173
        if not output_file:
174
            return  # Если пользователь отменил выбор файла, выходим из функции
175

176
    current_datetime = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
177

178
    with open(output_file, 'a') as f:
179
        f.write(f"=== Data saved at {current_datetime} ===\n")
180
        f.write(label_cpu.cget("text") + "\n")
181
        f.write(label_memory.cget("text") + "\n")
182
        f.write(label_disk.cget("text") + "\n")
183
        f.write(label_network.cget("text") + "\n")
184
        f.write(label_gpu.cget("text") + "\n")
185
        f.write(label_os.cget("text") + "\n")
186

187
        f.write("\nRunning Processes:\n")
188
        f.write(f"{'PID':>6} {'Name':<25} {'Memory%':>8} {'CPU%':>8}\n")
189

190
        processes = fetch_processes()
191
        for pid, name, memory_percent, cpu_percent in processes:
192
            f.write(f"{pid:6} {name:<25} {memory_percent:>8.2f} {cpu_percent:>8.2f}\n")
193

194
        f.write("\n")
195

196

197
def sort_column(tree, col):
198
    global current_sort_column, current_sort_order
199
    if current_sort_column == col:
200
        current_sort_order = 'desc' if current_sort_order == 'asc' else 'asc'
201
    else:
202
        current_sort_column = col
203
        current_sort_order = 'asc'
204
    refresh_data()
205
    update_column_headings(tree)
206

207

208
def update_column_headings(tree):
209
    columns = ['PID', 'Name', 'Memory%', 'CPU%']
210
    for col in columns:
211
        heading = col
212
        if col == current_sort_column:
213
            if current_sort_order == 'asc':
214
                heading += " ↑"
215
            else:
216
                heading += " ↓"
217
        tree.heading(col, text=heading, command=lambda _col=col: sort_column(tree, _col))
218

219

220
def kill_process():
221
    selected_item = tree.selection()
222
    if not selected_item:
223
        return
224

225
    pid = tree.item(selected_item, 'values')[0]
226
    try:
227
        pid = int(pid)  # Преобразуем PID к целочисленному типу
228
        if pid < 0:
229
            return
230

231
        proc = psutil.Process(pid)
232

233
        # Рекурсивно завершаем процесс и все его дочерние процессы
234
        for child in proc.children(recursive=True):
235
            child.terminate()
236
        proc.terminate()
237

238
        refresh_data()
239
    except ValueError:
240
        print(f"Invalid PID value: {pid}")
241
    except psutil.NoSuchProcess:
242
        print(f"Process with PID {pid} no longer exists.")
243
    except psutil.AccessDenied:
244
        print(f"Access to terminate process with PID {pid} denied.")
245

246

247
# Создание GUI
248
root = tk.Tk()
249
root.title("System Monitor")
250

251
# Фрейм с основными метками
252
frame_stats = ttk.Frame(root, padding="10")
253
frame_stats.grid(row=0, column=0, sticky="nsew")
254

255
label_cpu = ttk.Label(frame_stats, text="CPU Usage: ")
256
label_cpu.grid(row=0, column=0, sticky="w")
257

258
label_memory = ttk.Label(frame_stats, text="Memory Usage: ")
259
label_memory.grid(row=1, column=0, sticky="w")
260

261
label_disk = ttk.Label(frame_stats, text="Disk Usage: ")
262
label_disk.grid(row=2, column=0, sticky="w")
263

264
label_network = ttk.Label(frame_stats, text="Network: ")
265
label_network.grid(row=3, column=0, sticky="w")
266

267
# Добавляем метки для информации об устройствах
268
label_gpu = ttk.Label(frame_stats, text="GPU: ")
269
label_gpu.grid(row=4, column=0, sticky="w")
270

271
label_os = ttk.Label(frame_stats, text="OS: ")
272
label_os.grid(row=5, column=0, sticky="w")
273

274
# Фрейм с таблицей процессов
275
frame_processes = ttk.Frame(root, padding="10")
276
frame_processes.grid(row=0, column=1, sticky="nsew")
277

278
tree = ttk.Treeview(frame_processes, columns=('PID', 'Name', 'Memory%', 'CPU%'), show='headings')
279
tree.heading('PID', text='PID', command=lambda: sort_column(tree, 'PID'))
280
tree.heading('Name', text='Name', command=lambda: sort_column(tree, 'Name'))
281
tree.heading('Memory%', text='Memory%', command=lambda: sort_column(tree, 'Memory%'))
282
tree.heading('CPU%', text='CPU%', command=lambda: sort_column(tree, 'CPU%'))
283

284
tree.column('#0', stretch=tk.YES)
285
tree.column('#1', stretch=tk.YES)
286
tree.column('#2', stretch=tk.YES)
287
tree.column('#3', stretch=tk.YES)
288
tree.grid(row=0, column=0, sticky="nsew")
289

290
scrollbar = ttk.Scrollbar(frame_processes, orient="vertical", command=tree.yview)
291
scrollbar.grid(row=0, column=1, sticky='ns')
292
tree.configure(yscrollcommand=scrollbar.set)
293

294
# Кнопка для включения/отключения динамического обновления
295
btn_toggle_update = ttk.Button(root, text="Enable Dynamic Update\n(Disabled)", command=toggle_dynamic_update)
296
btn_toggle_update.grid(row=1, column=0, sticky="ew", padx=10, pady=10)
297

298
# Кнопка для ручного обновления данных
299
btn_manual_update = ttk.Button(root, text="Manual Update", command=manual_update)
300
btn_manual_update.grid(row=1, column=1, sticky="ew", padx=10, pady=10)
301

302
# Кнопка для завершения процесса
303
btn_kill_process = ttk.Button(root, text="Kill Process", command=kill_process)
304
btn_kill_process.grid(row=1, column=2, sticky="ew", padx=10, pady=10)
305

306
# Метка для отображения состояния динамического обновления
307
label_update_status = ttk.Label(root, text="Dynamic update: OFF", foreground="red")
308
label_update_status.grid(row=2, column=0, columnspan=3, pady=5)
309

310
# Настройка растягиваемости и выравнивания элементов
311
root.columnconfigure(0, weight=1)
312
root.columnconfigure(1, weight=1)
313
root.columnconfigure(2, weight=1)
314
root.rowconfigure(0, weight=1)
315

316
frame_stats.columnconfigure(0, weight=1)
317
frame_stats.rowconfigure(0, weight=1)
318
frame_stats.rowconfigure(1, weight=1)
319
frame_stats.rowconfigure(2, weight=1)
320
frame_stats.rowconfigure(3, weight=1)
321
frame_stats.rowconfigure(4, weight=1)
322
frame_stats.rowconfigure(5, weight=1)
323

324
frame_processes.columnconfigure(0, weight=1)
325
frame_processes.rowconfigure(0, weight=1)
326

327
# При запуске приложения сразу подтягиваем данные
328
print_system_usage()  # Обновление данных
329
update_column_headings(tree)  # Обновить заголовки столбцов
330

331
# Меню для выбора места сохранения лог-файла
332
menu_bar = tk.Menu(root)
333
root.config(menu=menu_bar)
334

335
file_menu = tk.Menu(menu_bar, tearoff=0)
336
menu_bar.add_cascade(label="File", menu=file_menu)
337
file_menu.add_command(label="Save Log As...", command=save_to_file)
338

339
# Запуск основного цикла GUI
340
root.mainloop()
341

342
# Выход из NVML
343
pynvml.nvmlShutdown()
344

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.