Skip to content

Commit 864b845

Browse files
committed
Trying to fix process list update stuck
1 parent 9a89900 commit 864b845

5 files changed

Lines changed: 86 additions & 59 deletions

File tree

src/constants/threads.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
THREAD_SETTINGS = "settings"
22
THREAD_TRAY = "tray"
3-
THREAD_PROCESS_LIST = "process_list"
3+
THREAD_PROCESS_LIST_DATA = "process_list_data"
4+
THREAD_PROCESS_LIST_ICONS = "process_list_icons"

src/constants/ui.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,3 +49,6 @@ class EditableTreeviewEvents:
4949

5050
class ScrollableTreeviewEvents:
5151
SCROLL: Final[str] = "<<Scroll>>"
52+
53+
54+
ERROR_TRYING_UPDATE_TERMINATED_TKINTER_INSTANCE = 'main thread is not in main loop'

src/ui/tray.py

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,13 @@
77

88
from constants.app_info import APP_NAME_WITH_VERSION, APP_NAME
99
from constants.resources import APP_ICON
10+
from constants.threads import THREAD_SETTINGS
1011
from constants.ui import OPEN_LOG_LABEL, OPEN_CONFIG_LABEL
1112
from constants.updates import UPDATE_URL
1213
from ui.settings import open_settings
1314
from util.files import open_log_file, open_config_file
1415
from util.messages import yesno_error_box, show_error, show_info
16+
from util.scheduler import TaskScheduler
1517
from util.startup import toggle_startup, is_in_startup
1618
from util.updates import check_latest_version
1719
from util.utils import is_portable
@@ -46,6 +48,14 @@ def check_updates():
4648
webbrowser.open(UPDATE_URL, new=0, autoraise=True)
4749

4850

51+
def close_app(item):
52+
if TaskScheduler.check_task(THREAD_SETTINGS):
53+
show_info(APP_NAME_WITH_VERSION, "Please close the settings window before closing the application.")
54+
return
55+
56+
return item.stop()
57+
58+
4959
def init_tray() -> Icon:
5060
"""
5161
Initializes and returns a system tray icon.
@@ -75,7 +85,7 @@ def init_tray() -> Icon:
7585
),
7686
Menu.SEPARATOR,
7787

78-
MenuItem('Quit', lambda item: item.stop()),
88+
MenuItem('Quit', close_app),
7989
)
8090

8191
return pystray.Icon("tray_icon", image, APP_NAME, menu)

src/ui/widget/settings/tabs/processes/process_list.py

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,16 +5,19 @@
55
from PIL import ImageTk
66
from pydantic import BaseModel
77

8+
from constants.log import LOG
89
from constants.resources import UI_ADD_PROCESS_RULE, UI_ADD_SERVICE_RULE, UI_COPY, UI_SERVICE, \
910
UI_PROCESS
11+
from constants.threads import THREAD_PROCESS_LIST_ICONS
1012
from constants.ui import CMENU_ADD_PROCESS_RULE_LABEL, CMENU_ADD_SERVICE_RULE_LABEL, COLUMN_WIDTH_WITH_ICON, \
11-
CMENU_COPY_LABEL
13+
CMENU_COPY_LABEL, ERROR_TRYING_UPDATE_TERMINATED_TKINTER_INSTANCE
1214
from enums.filters import FilterByProcessType
1315
from enums.rules import RuleType
1416
from enums.selector import SelectorType
1517
from model.process import Process
1618
from ui.widget.common.treeview.pydantic import PydanticTreeviewLoader
1719
from ui.widget.common.treeview.sortable import SortableTreeview
20+
from util.scheduler import TaskScheduler
1821
from util.ui import load_img, trim_cmenu_label
1922
from util.utils import get_icon_from_exe
2023

@@ -157,10 +160,23 @@ def update_ui(self):
157160
self._update_process_icons()
158161

159162
def _update_process_icons(self):
160-
for row_id in self.get_children():
161-
model = self.as_model(row_id)
162-
icon = self.get_process_icon(model)
163-
self.item(row_id, image=icon)
163+
def set_icons(icons):
164+
try:
165+
for row_id, icon in icons:
166+
self.item(row_id, image=icon)
167+
except BaseException as e:
168+
if ERROR_TRYING_UPDATE_TERMINATED_TKINTER_INSTANCE not in str(e):
169+
LOG.exception("Update process icons error")
170+
171+
def get_icons():
172+
try:
173+
icons = {row_id: self.get_process_icon(self.as_model(row_id)) for row_id in self.get_children()}
174+
self.after(0, lambda: set_icons(icons.items()))
175+
except BaseException as e:
176+
if ERROR_TRYING_UPDATE_TERMINATED_TKINTER_INSTANCE not in str(e):
177+
LOG.exception("Get process icons error")
178+
179+
TaskScheduler.schedule_task(THREAD_PROCESS_LIST_ICONS, get_icons)
164180

165181
def _get_filtered_data(self, filter_by_type, search_query):
166182
data = []

src/ui/widget/settings/tabs/processes/process_tab.py

Lines changed: 49 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@
44

55
from constants.log import LOG
66
from constants.resources import UI_PROCESS_LIST
7-
from constants.threads import THREAD_PROCESS_LIST
8-
from constants.ui import UI_PADDING, ActionEvents
7+
from constants.threads import THREAD_PROCESS_LIST_DATA
8+
from constants.ui import UI_PADDING, ActionEvents, ERROR_TRYING_UPDATE_TERMINATED_TKINTER_INSTANCE
99
from enums.rules import RuleType
1010
from enums.selector import SelectorType
1111
from model.process import Process
@@ -43,9 +43,6 @@ def __init__(self, master: Notebook, process_rules_list: RulesList, service_rule
4343

4444
super().__init__(master)
4545

46-
self._is_close = False
47-
self.bind("<Destroy>", self._on_close)
48-
4946
self._create_process_list()
5047
self._create_progress_bar()
5148
self._create_actions()
@@ -74,64 +71,64 @@ def load_from_config(self, config: dict):
7471
self._refresh()
7572

7673
def _update_process_list(self):
77-
filter_by_type = self.actions.filterByType.get_enum_value()
78-
search_query = self.actions.search.get().strip().lower()
79-
80-
self.process_list.set_filter(filter_by_type, search_query)
81-
self.process_list.update_ui()
74+
try:
75+
filter_by_type = self.actions.filterByType.get_enum_value()
76+
search_query = self.actions.search.get().strip().lower()
8277

83-
def _on_close(self, _=None):
84-
self._is_close = True
78+
self.process_list.set_filter(filter_by_type, search_query)
79+
self.process_list.update_ui()
80+
except BaseException as e:
81+
if ERROR_TRYING_UPDATE_TERMINATED_TKINTER_INSTANCE not in str(e):
82+
LOG.exception("Update process list error")
8583

8684
def _refresh(self):
87-
main_thread_not_in_mainloop_message = 'main thread is not in main loop'
85+
LOG.info("Refreshing process list...")
86+
87+
def update_process_list():
88+
LOG.info("Updating process list...")
89+
90+
try:
91+
self._update_process_list()
92+
finally:
93+
self._refresh_state()
94+
95+
def load_data():
96+
LOG.info("Loading data...")
97+
98+
try:
99+
self.process_list.set_data(ProcessesInfoService.get_processes(False))
100+
self.after(0, update_process_list)
101+
except BaseException as e:
102+
if ERROR_TRYING_UPDATE_TERMINATED_TKINTER_INSTANCE not in str(e):
103+
LOG.exception("Load data error")
104+
105+
self._refresh_state()
88106

89107
try:
90108
self._refresh_state(True)
91109
self.process_list.clear()
92110

93-
def load():
94-
try:
95-
try:
96-
self.process_list.set_data(ProcessesInfoService.get_processes(False))
97-
98-
def do_ui_update():
99-
if self._is_close:
100-
return
101-
102-
try:
103-
try:
104-
self._update_process_list()
105-
finally:
106-
self._refresh_state()
107-
except BaseException as e:
108-
if str(e) != main_thread_not_in_mainloop_message:
109-
LOG.exception("Refresh error")
110-
111-
if self._is_close:
112-
return
113-
114-
self.after(0, do_ui_update)
115-
finally:
116-
self._refresh_state()
117-
except BaseException as e:
118-
if str(e) != main_thread_not_in_mainloop_message:
119-
LOG.exception("Refresh error")
120-
121-
TaskScheduler.schedule_task(THREAD_PROCESS_LIST, load)
122-
except:
111+
TaskScheduler.schedule_task(THREAD_PROCESS_LIST_DATA, load_data)
112+
except BaseException as e:
113+
if ERROR_TRYING_UPDATE_TERMINATED_TKINTER_INSTANCE not in str(e):
114+
LOG.exception("Refresh error")
115+
123116
self._refresh_state()
124-
LOG.exception("Refresh error")
125117

126118
def _refresh_state(self, lock: bool = False):
127-
actions = self.actions
128-
actions.refresh['state'] = DISABLED if lock else NORMAL
129-
130-
progress_bar = self._progress_bar
131-
if lock:
132-
progress_bar.place(relx=0.5, rely=0.5, anchor=CENTER)
133-
else:
134-
progress_bar.place_forget()
119+
try:
120+
actions = self.actions
121+
actions.refresh['state'] = DISABLED if lock else NORMAL
122+
123+
progress_bar = self._progress_bar
124+
125+
if lock:
126+
progress_bar.place(relx=0.5, rely=0.5, anchor=CENTER)
127+
else:
128+
progress_bar.place_forget()
129+
except BaseException as e:
130+
if ERROR_TRYING_UPDATE_TERMINATED_TKINTER_INSTANCE not in str(e):
131+
LOG.exception("Refresh state error")
135132

136133
def save_to_config(self, config: dict):
137134
pass

0 commit comments

Comments
 (0)