Просмотр исходного кода

wxGUI/datalog: fix crashing due to accessing GUI from other thread (#1027)

Anna Petrasova 4 лет назад
Родитель
Сommit
1962602b4a

+ 2 - 15
gui/wxpython/datacatalog/catalog.py

@@ -18,7 +18,6 @@ for details.
 import wx
 import os
 
-from core.gthread import gThread
 from core.debug import Debug
 from datacatalog.tree import DataCatalogTree
 from datacatalog.toolbars import DataCatalogToolbar
@@ -45,8 +44,6 @@ class DataCatalog(wx.Panel):
 
         # tree with layers
         self.tree = DataCatalogTree(self, giface=giface)
-        self.thread = gThread()
-        self._loaded = False
         self.tree.showNotification.connect(self.showNotification)
 
         # some layout
@@ -68,21 +65,11 @@ class DataCatalog(wx.Panel):
         self.Layout()
 
     def LoadItems(self):
-        if self._loaded:
-            return
-
-        self.thread.Run(callable=self.tree.ReloadTreeItems,
-                        ondone=lambda event: self.LoadItemsDone())
-
-    def LoadItemsDone(self):
-        self._loaded = True
-        self.tree.UpdateCurrentDbLocationMapsetNode()
-        self.tree.ExpandCurrentMapset()
+        self.tree.ReloadTreeItems()
 
     def OnReloadTree(self, event):
         """Reload whole tree"""
-        self.tree.ReloadTreeItems()
-        self.tree.ExpandCurrentMapset()
+        self.LoadItems()
 
     def OnReloadCurrentMapset(self, event):
         """Reload current mapset tree only"""

+ 0 - 3
gui/wxpython/datacatalog/frame.py

@@ -53,8 +53,6 @@ class DataCatalogFrame(wx.Frame):
         # tree
         self.tree = DataCatalogTree(parent=self.panel, giface=self._giface)
         self.tree.ReloadTreeItems()
-        self.tree.UpdateCurrentDbLocationMapsetNode()
-        self.tree.ExpandCurrentMapset()
 
         # buttons
         self.btnClose = Button(parent=self.panel, id=wx.ID_CLOSE)
@@ -94,7 +92,6 @@ class DataCatalogFrame(wx.Frame):
     def OnReloadTree(self, event):
         """Reload whole tree"""
         self.tree.ReloadTreeItems()
-        self.tree.ExpandCurrentMapset()
 
     def OnReloadCurrentMapset(self, event):
         """Reload current mapset tree only"""

+ 20 - 12
gui/wxpython/datacatalog/tree.py

@@ -28,6 +28,7 @@ import wx
 from core.gcmd import RunCommand, GError, GMessage, GWarning
 from core.utils import GetListOfLocations
 from core.debug import Debug
+from core.gthread import gThread
 from gui_core.dialogs import TextEntryDialog
 from core.giface import StandaloneGrassInterface
 from core.treemodel import TreeModel, DictNode
@@ -278,6 +279,7 @@ class DataCatalogTree(TreeView):
         self._iconTypes = ['grassdb', 'location', 'mapset', 'raster',
                            'vector', 'raster_3d']
         self._initImages()
+        self.thread = gThread()
 
         self._resetSelectVariables()
         self._resetCopyVariables()
@@ -478,8 +480,6 @@ class DataCatalogTree(TreeView):
                 queue_list = []
                 loc_list = []
                 location_nodes = []
-                # refresh after each chunk to make GUI more responsive
-                self.RefreshItems()
 
         for node in all_location_nodes:
             self._model.SortChildren(node)
@@ -489,7 +489,10 @@ class DataCatalogTree(TreeView):
 
     def _reloadTreeItems(self):
         """Updates grass databases, locations, mapsets and layers in the tree.
-        Saves resulting data and error."""
+
+        It runs in thread, so it should not directly interact with GUI.
+        In case of any errors it returns the errors as a list of strings, otherwise None.
+        """
         errors = []
         for grassdatabase in self.grassdatabases:
             grassdb_nodes = self._model.SearchNodes(name=grassdatabase,
@@ -505,12 +508,8 @@ class DataCatalogTree(TreeView):
                 errors += error
 
         if errors:
-            # WriteWarning/Error results in crash
-            self._giface.WriteLog('\n'.join(errors))
-        Debug.msg(1, "Tree filled")
-
-        self.UpdateCurrentDbLocationMapsetNode()
-        self.RefreshItems()
+            return errors
+        return None
 
     def _renameNode(self, node, name):
         """Rename node (map, mapset, location), sort and refresh.
@@ -543,9 +542,18 @@ class DataCatalogTree(TreeView):
 
     def ReloadTreeItems(self):
         """Reload dbs, locations, mapsets and layers in the tree."""
-        busy = wx.BusyCursor()
-        self._reloadTreeItems()
-        del busy
+        self.busy = wx.BusyCursor()
+        self.thread.Run(callable=self._reloadTreeItems,
+                        ondone=self._loadItemsDone)
+
+    def _loadItemsDone(self, event):
+        Debug.msg(1, "Tree filled")
+        del self.busy
+        if event.ret is not None:
+            self._giface.WriteWarning('\n'.join(event.ret))
+        self.UpdateCurrentDbLocationMapsetNode()
+        self.RefreshItems()
+        self.ExpandCurrentMapset()
 
     def ReloadCurrentMapset(self):
         """Reload current mapset tree only."""