From 206f67f2f01328277194f83010c350c119b5b8a7 Mon Sep 17 00:00:00 2001 From: itdominator <1itdominator@gmail.com> Date: Thu, 6 Oct 2022 20:48:44 -0500 Subject: [PATCH] Finally resolved UI thread overloads --- plugins/searcher/mixins/file_search_mixin.py | 22 ++--- plugins/searcher/mixins/grep_search_mixin.py | 22 ++--- plugins/searcher/plugin.py | 37 ++++++-- plugins/searcher/search_dialog.glade | 92 +++++++++++++++----- plugins/searcher/utils/search.py | 28 +++--- 5 files changed, 132 insertions(+), 69 deletions(-) diff --git a/plugins/searcher/mixins/file_search_mixin.py b/plugins/searcher/mixins/file_search_mixin.py index 51cddce..ffcc32a 100644 --- a/plugins/searcher/mixins/file_search_mixin.py +++ b/plugins/searcher/mixins/file_search_mixin.py @@ -12,12 +12,14 @@ from ..widgets.file_preview_widget import FilePreviewWidget def threaded(fn): def wrapper(*args, **kwargs): threading.Thread(target=fn, args=args, kwargs=kwargs, daemon=False).start() + return wrapper # NOTE: Threads WILL die with parent's destruction. def daemon_threaded(fn): def wrapper(*args, **kwargs): threading.Thread(target=fn, args=args, kwargs=kwargs, daemon=True).start() + return wrapper @@ -25,6 +27,7 @@ class FileSearchMixin: def _run_find_file_query(self, widget=None, eve=None): self._handle_find_file_query(query=widget) + # TODO: Merge this logic with nearly the exact same thing in grep_search_mixin @daemon_threaded def _handle_find_file_query(self, widget=None, eve=None, query=None): # NOTE: Freeze IPC consumption @@ -33,19 +36,15 @@ class FileSearchMixin: # NOTE: Kill the former process if self._list_proc: - if self._list_proc.poll(): - self._list_proc.send_signal(signal.SIGKILL) - while self._list_proc.poll(): - pass + if self._list_proc.poll() == None: + self._list_proc.terminate() + while self._list_proc.poll() == None: + ... - self._list_proc = None - else: - self._list_proc = None + self._list_proc = None # NOTE: Clear children from ui and make sure ui thread redraws - GLib.idle_add(self.clear_children, self._file_list) - while len(self._file_list.get_children()) > 0: - time.sleep(0.2) + GLib.idle_add(self.reset_file_list_box) # NOTE: If query create new process and do all new loop. self.pause_fifo_update = False @@ -62,9 +61,6 @@ class FileSearchMixin: self._list_proc = subprocess.Popen(command, cwd=self.path, stdin=None, stdout=None, stderr=None) def _load_file_ui(self, data): - if self.pause_fifo_update: - return - if not data in ("", None): jdata = json.loads( data ) target = jdata[0] diff --git a/plugins/searcher/mixins/grep_search_mixin.py b/plugins/searcher/mixins/grep_search_mixin.py index a83a24c..dc8372d 100644 --- a/plugins/searcher/mixins/grep_search_mixin.py +++ b/plugins/searcher/mixins/grep_search_mixin.py @@ -12,12 +12,14 @@ from ..widgets.grep_preview_widget import GrepPreviewWidget def threaded(fn): def wrapper(*args, **kwargs): threading.Thread(target=fn, args=args, kwargs=kwargs, daemon=False).start() + return wrapper # NOTE: Threads WILL die with parent's destruction. def daemon_threaded(fn): def wrapper(*args, **kwargs): threading.Thread(target=fn, args=args, kwargs=kwargs, daemon=True).start() + return wrapper @@ -25,6 +27,7 @@ class GrepSearchMixin: def _run_grep_query(self, widget=None, eve=None): self._handle_grep_query(query=widget) + # TODO: Merge this logic with nearly the exact same thing in file_search_mixin @daemon_threaded def _handle_grep_query(self, widget=None, eve=None, query=None): # NOTE: Freeze IPC consumption @@ -33,19 +36,15 @@ class GrepSearchMixin: # NOTE: Kill the former process if self._grep_proc: - if self._grep_proc.poll(): - self._grep_proc.send_signal(signal.SIGKILL) - while self._grep_proc.poll(): - pass + if self._grep_proc.poll() == None: + self._grep_proc.terminate() + while self._grep_proc.poll() == None: + ... - self._grep_proc = None - else: - self._grep_proc = None + self._grep_proc = None # NOTE: Clear children from ui and make sure ui thread redraws - GLib.idle_add(self.clear_children, self._grep_list) - while len(self._grep_list.get_children()) > 0: - time.sleep(0.2) + GLib.idle_add(self.reset_grep_box) # NOTE: If query create new process and do all new loop. self.pause_fifo_update = False @@ -62,9 +61,6 @@ class GrepSearchMixin: self._grep_proc = subprocess.Popen(command, cwd=self.path, stdin=None, stdout=None, stderr=None) def _load_grep_ui(self, data): - if self.pause_fifo_update: - return - if not data in ("", None): jdata = json.loads( data ) jkeys = jdata.keys() diff --git a/plugins/searcher/plugin.py b/plugins/searcher/plugin.py index d506e21..4b66a63 100644 --- a/plugins/searcher/plugin.py +++ b/plugins/searcher/plugin.py @@ -40,6 +40,8 @@ class Plugin(IPCServer, FileSearchMixin, GrepSearchMixin, PluginBase): self._search_dialog = None self._active_path = None + self.file_list_parent = None + self.grep_list_parent = None self._file_list = None self._grep_list = None self._grep_proc = None @@ -72,10 +74,11 @@ class Plugin(IPCServer, FileSearchMixin, GrepSearchMixin, PluginBase): self._builder.connect_signals(handlers) self._search_dialog = self._builder.get_object("search_dialog") - self._grep_list = self._builder.get_object("grep_list") - self._file_list = self._builder.get_object("file_list") self.fsearch = self._builder.get_object("fsearch") + self.grep_list_parent = self._builder.get_object("grep_list_parent") + self.file_list_parent = self._builder.get_object("file_list_parent") + self._event_system.subscribe("update-file-ui", self._load_file_ui) self._event_system.subscribe("update-grep-ui", self._load_grep_ui) @@ -92,9 +95,29 @@ class Plugin(IPCServer, FileSearchMixin, GrepSearchMixin, PluginBase): response = self._search_dialog.run() self._search_dialog.hide() + # TODO: Merge the below methods into some unified logic + def reset_grep_box(self) -> None: + try: + child = self.grep_list_parent.get_children()[0] + self._grep_list = None + self.grep_list_parent.remove(child) + except Exception: + ... - def clear_children(self, widget: type) -> None: - ''' Clear children of a gtk widget. ''' - for child in widget.get_children(): - widget.remove(child) - time.sleep(0.01) + self._grep_list = Gtk.Box() + self._grep_list.set_orientation(Gtk.Orientation.VERTICAL) + self.grep_list_parent.add(self._grep_list) + self.grep_list_parent.show_all() + + def reset_file_list_box(self) -> None: + try: + child = self.file_list_parent.get_children()[0] + self._file_list = None + self.file_list_parent.remove(child) + except Exception: + ... + + self._file_list = Gtk.Box() + self._file_list.set_orientation(Gtk.Orientation.VERTICAL) + self.file_list_parent.add(self._file_list) + self.file_list_parent.show_all() diff --git a/plugins/searcher/search_dialog.glade b/plugins/searcher/search_dialog.glade index aaa3b09..8578e51 100644 --- a/plugins/searcher/search_dialog.glade +++ b/plugins/searcher/search_dialog.glade @@ -74,16 +74,41 @@ False vertical - + True - True - Query... - edit-find-symbolic - False - False - Search for file... - - + False + + + True + True + Query... + edit-find-symbolic + False + False + Search for file... + + + + True + True + 0 + + + + + gtk-stop + True + True + True + True + + + + False + True + 1 + + False @@ -103,7 +128,7 @@ True False - + True False vertical @@ -141,16 +166,41 @@ False vertical - + True - True - Query... - edit-find-symbolic - False - False - Query string in file... - - + False + + + True + True + Query... + edit-find-symbolic + False + False + Query string in file... + + + + True + True + 0 + + + + + gtk-stop + True + True + True + True + + + + False + True + 1 + + False @@ -170,12 +220,10 @@ True False - + True False vertical - 5 - top diff --git a/plugins/searcher/utils/search.py b/plugins/searcher/utils/search.py index 31bdcc6..5c1efca 100755 --- a/plugins/searcher/utils/search.py +++ b/plugins/searcher/utils/search.py @@ -2,7 +2,7 @@ # Python imports -import os, traceback, argparse, threading, json, base64, time +import os, traceback, argparse, threading, json, base64, time, pickle from setproctitle import setproctitle from multiprocessing.connection import Client @@ -37,19 +37,18 @@ def send_ipc_message(message) -> None: conn.send(message) conn.close() - time.sleep(0.05) + # NOTE: Kinda important as this prevents overloading the UI thread + time.sleep(0.04) def file_search(path, query): try: - for file in os.listdir(path): - target = os.path.join(path, file) - if os.path.isdir(target): - file_search(target, query) - else: - if query in file.lower(): - data = f"SEARCH|{json.dumps([target, file])}" - send_ipc_message(data) + for _path, _dir, _files in os.walk(path, topdown = True): + for file in _files: + if query in file.lower(): + target = os.path.join(_path, file) + data = f"SEARCH|{json.dumps([target, file])}" + send_ipc_message(data) except Exception as e: print("Couldn't traverse to path. Might be permissions related...") traceback.print_exc() @@ -59,8 +58,9 @@ def _search_for_string(file, query): grep_result_set = {} padding = 15 with open(file, 'r') as fp: - # NOTE: I know there's an issue if there's a very large file with content all on one line will lower and dupe it. - # And, yes, it will only return one instance from the file. + # NOTE: I know there's an issue if there's a very large file with content + # all on one line will lower and dupe it. And, yes, it will only + # return one instance from the file. for i, raw in enumerate(fp): line = None llower = raw.lower() @@ -87,11 +87,11 @@ def _search_for_string(file, query): data = f"GREP|{json.dumps(grep_result_set)}" send_ipc_message(data) + @daemon_threaded def _search_for_string_threaded(file, query): _search_for_string(file, query) - def grep_search(path, query): try: for file in os.listdir(path): @@ -101,7 +101,7 @@ def grep_search(path, query): else: if target.lower().endswith(filter): size = os.path.getsize(target) - if size < 5000: + if not size > 5000: _search_for_string(target, query) else: _search_for_string_threaded(target, query)