Opened 7 years ago

Closed 7 years ago

#3402 closed defect (fixed)

g.gui.gmodeler: trying to add command pushes CPU to 100% and provokes freeze

Reported by: mlennert Owned by: grass-dev@…
Priority: major Milestone: 7.2.2
Component: wxGUI Version: svn-trunk
Keywords: gmodeler freeze Cc:
CPU: Unspecified Platform: Unspecified

Description

Trying to add a module call ("Add command") to the modeler leads to 100% CPU usage and a freeze of the modeler, before the module addition window content is visible.

I can add data and loops and I can load an existing model and access the module dialogs, but I cannot add a new module call.

I've tried with trunk, release72, 7.2.1 and release70, all cause the same problem, so I imagine that it probably has to do with some changes in relevant external libraries, not in the gmodeler code itself, but I don't know how to debug this.

I'm on Debian testing, so python-wx 3.0.2.0 and python 2.7.

Can anyone confirm this issue ? It makes the modeler absolutely useless to me, so I classify this as major, although it only concerns this particular module.

Change History (18)

comment:1 by annakrat, 7 years ago

It seems to work on Ubuntu. Do you use GTK3?

in reply to:  1 ; comment:2 by mlennert, 7 years ago

Replying to annakrat:

It seems to work on Ubuntu. Do you use GTK3?

I have both 2 and 3 installed on my machine:

libgtk-3-0:amd64                 3.22.18-1
libgtk2.0-0:amd64                2.24.31-2

But the wx packages are for gtk 3 and python-wx depends on those:

dpkg -s python-wxgtk3.0
Package: python-wxgtk3.0
[...]
Version: 3.0.2.0+dfsg-4
Provides: python2.7-wxgtk3.0
Depends: python-wxversion, python (<< 2.8), python (>= 2.7~), python:any (<< 2.8), python:any (>= 2.7.5-5~), libc6 (>= 2.14), libgcc1 (>= 1:3.0), libstdc++6 (>= 5.2), libwxbase3.0-0v5 (>= 3.0.2+dfsg), libwxgtk3.0-0v5 (>= 3.0.2+dfsg)

python-wxgtk2.8 is not available in Debian testing anymore.

in reply to:  2 comment:3 by mlennert, 7 years ago

Replying to mlennert:

Replying to annakrat:

It seems to work on Ubuntu. Do you use GTK3?

I have both 2 and 3 installed on my machine:

libgtk-3-0:amd64                 3.22.18-1
libgtk2.0-0:amd64                2.24.31-2

But the wx packages are for gtk 3 and python-wx depends on those:

dpkg -s python-wxgtk3.0
Package: python-wxgtk3.0
[...]
Version: 3.0.2.0+dfsg-4
Provides: python2.7-wxgtk3.0
Depends: python-wxversion, python (<< 2.8), python (>= 2.7~), python:any (<< 2.8), python:any (>= 2.7.5-5~), libc6 (>= 2.14), libgcc1 (>= 1:3.0), libstdc++6 (>= 5.2), libwxbase3.0-0v5 (>= 3.0.2+dfsg), libwxgtk3.0-0v5 (>= 3.0.2+dfsg)

python-wxgtk2.8 is not available in Debian testing anymore.

Actually, if I'm not completely mistaken, it's been gtk3 for a while. Do you think gtk3 is the problem, or is it the minimum version necessary ?

Anything I could do to debug this ?

comment:4 by annakrat, 7 years ago

what does this give you?

  import wx
  wx.version()

I have '3.0.2.0 gtk2 (classic)'. I just want to make sure it's GTK3, the '3' in your packages might refer to wxpython 3, not gtk 3.

You can test example OGL in wxpython demo, I suspect there could be a problem related to this. If the problem is in the dialog, you could try to remove certain widgets and see if the problem persists. I see it creates a long list of modules in the wx.Choice widget, maybe that could an issue?

in reply to:  4 ; comment:5 by mlennert, 7 years ago

Replying to annakrat:

what does this give you?

  import wx
  wx.version()

I have '3.0.2.0 gtk2 (classic)'. I just want to make sure it's GTK3, the '3' in your packages might refer to wxpython 3, not gtk 3.

Once again (as always ;-) ) you were right:

>>> import wx
>>> wx.version()
'3.0.2.0 gtk2 (classic)'

And looking better than before, I see that the dependency chain of packages is:

python-wxgtk3.0 -> libwxgtk3.0-0v5 -> libgtk2.0-0

So, definitely gtk2...

You can test example OGL in wxpython demo, I suspect there could be a problem related to this. If the problem is in the dialog, you could try to remove certain widgets and see if the problem persists. I see it creates a long list of modules in the wx.Choice widget, maybe that could an issue?

Commenting out the SearchModuleWidget seems to do the trick:

Index: gui/wxpython/gmodeler/dialogs.py
===================================================================
--- gui/wxpython/gmodeler/dialogs.py	(révision 71421)
+++ gui/wxpython/gmodeler/dialogs.py	(copie de travail)
@@ -201,11 +201,11 @@
         self.cmd_prompt.promptRunCmd.connect(self.OnCommand)
         self.cmd_prompt.commandSelected.connect(
             lambda command: self.label.SetValue(command))
-        self.search = SearchModuleWidget(parent=self.panel,
+        '''self.search = SearchModuleWidget(parent=self.panel,
                                          model=menuModel.GetModel(),
                                          showTip=True)
         self.search.moduleSelected.connect(
-            lambda name: self.cmd_prompt.SetTextAndFocus(name + ' '))
+            lambda name: self.cmd_prompt.SetTextAndFocus(name + ' '))'''
         wx.CallAfter(self.cmd_prompt.SetFocus)
 
         self.label = wx.TextCtrl(parent=self.panel, id=wx.ID_ANY)
@@ -249,8 +249,8 @@
         btnSizer.Realize()
 
         mainSizer = wx.BoxSizer(wx.VERTICAL)
-        mainSizer.Add(self.search, proportion=0,
-                      flag=wx.EXPAND | wx.ALL, border=3)
+        '''mainSizer.Add(self.search, proportion=0,
+                      flag=wx.EXPAND | wx.ALL, border=3)'''
         mainSizer.Add(cmdSizer, proportion=1,
                       flag=wx.EXPAND | wx.LEFT | wx.RIGHT | wx.TOP, border=3)
         mainSizer.Add(labelSizer, proportion=1,

I now get a functioning window and when I start entering a module name, I get the incremental search, can select a module and add it. Will need to test some more.

Trying to find and test SearchModuleWidget elsewhere in the GUI, I stumbled upon another encoding issue: #3405. Could this maybe be linked ?

in reply to:  5 ; comment:6 by mlennert, 7 years ago

Replying to mlennert:

Commenting out the SearchModuleWidget seems to do the trick:

Index: gui/wxpython/gmodeler/dialogs.py
===================================================================
--- gui/wxpython/gmodeler/dialogs.py	(révision 71421)
+++ gui/wxpython/gmodeler/dialogs.py	(copie de travail)
@@ -201,11 +201,11 @@
         self.cmd_prompt.promptRunCmd.connect(self.OnCommand)
         self.cmd_prompt.commandSelected.connect(
             lambda command: self.label.SetValue(command))
-        self.search = SearchModuleWidget(parent=self.panel,
+        '''self.search = SearchModuleWidget(parent=self.panel,
                                          model=menuModel.GetModel(),
                                          showTip=True)
         self.search.moduleSelected.connect(
-            lambda name: self.cmd_prompt.SetTextAndFocus(name + ' '))
+            lambda name: self.cmd_prompt.SetTextAndFocus(name + ' '))'''
         wx.CallAfter(self.cmd_prompt.SetFocus)
 
         self.label = wx.TextCtrl(parent=self.panel, id=wx.ID_ANY)
@@ -249,8 +249,8 @@
         btnSizer.Realize()
 
         mainSizer = wx.BoxSizer(wx.VERTICAL)
-        mainSizer.Add(self.search, proportion=0,
-                      flag=wx.EXPAND | wx.ALL, border=3)
+        '''mainSizer.Add(self.search, proportion=0,
+                      flag=wx.EXPAND | wx.ALL, border=3)'''
         mainSizer.Add(cmdSizer, proportion=1,
                       flag=wx.EXPAND | wx.LEFT | wx.RIGHT | wx.TOP, border=3)
         mainSizer.Add(labelSizer, proportion=1,

I now get a functioning window and when I start entering a module name, I get the incremental search, can select a module and add it. Will need to test some more.

However: I can only get one module onto the modeler canvas that way. Afterwards, when I click on the button to add a module again, nothing happens at all.

comment:7 by neteler, 7 years ago

Just FYI: I have made a test on my Fedora 26 box, no CPU issue here (which I do remember from older wxpython versions, see trac and Fedora's bugzilla). My current version which seems to work fine:

Python 2.7.13 (default, Jun 26 2017, 10:20:05) 
[GCC 7.1.1 20170622 (Red Hat 7.1.1-3)] on linux2
Type "help", "copyright", "credits" or "license" for more information.

import wx
wx.version()
3.0.2.0 gtk3 (classic)

in reply to:  6 ; comment:8 by mlennert, 7 years ago

Replying to mlennert:

Replying to mlennert:

Commenting out the SearchModuleWidget seems to do the trick:

Index: gui/wxpython/gmodeler/dialogs.py
===================================================================
--- gui/wxpython/gmodeler/dialogs.py	(révision 71421)
+++ gui/wxpython/gmodeler/dialogs.py	(copie de travail)
@@ -201,11 +201,11 @@
         self.cmd_prompt.promptRunCmd.connect(self.OnCommand)
         self.cmd_prompt.commandSelected.connect(
             lambda command: self.label.SetValue(command))
-        self.search = SearchModuleWidget(parent=self.panel,
+        '''self.search = SearchModuleWidget(parent=self.panel,
                                          model=menuModel.GetModel(),
                                          showTip=True)
         self.search.moduleSelected.connect(
-            lambda name: self.cmd_prompt.SetTextAndFocus(name + ' '))
+            lambda name: self.cmd_prompt.SetTextAndFocus(name + ' '))'''
         wx.CallAfter(self.cmd_prompt.SetFocus)
 
         self.label = wx.TextCtrl(parent=self.panel, id=wx.ID_ANY)
@@ -249,8 +249,8 @@
         btnSizer.Realize()
 
         mainSizer = wx.BoxSizer(wx.VERTICAL)
-        mainSizer.Add(self.search, proportion=0,
-                      flag=wx.EXPAND | wx.ALL, border=3)
+        '''mainSizer.Add(self.search, proportion=0,
+                      flag=wx.EXPAND | wx.ALL, border=3)'''
         mainSizer.Add(cmdSizer, proportion=1,
                       flag=wx.EXPAND | wx.LEFT | wx.RIGHT | wx.TOP, border=3)
         mainSizer.Add(labelSizer, proportion=1,

I now get a functioning window and when I start entering a module name, I get the incremental search, can select a module and add it. Will need to test some more.

However: I can only get one module onto the modeler canvas that way. Afterwards, when I click on the button to add a module again, nothing happens at all.

This was because of the attempt to reset the non-existant search.

However, I think I found the culprit: the tooltip ! Disabling it makes the widget work again:

Index: gui/wxpython/gmodeler/dialogs.py
===================================================================
--- gui/wxpython/gmodeler/dialogs.py	(révision 71430)
+++ gui/wxpython/gmodeler/dialogs.py	(copie de travail)
@@ -203,7 +203,7 @@
             lambda command: self.label.SetValue(command))
         self.search = SearchModuleWidget(parent=self.panel,
                                          model=menuModel.GetModel(),
-                                         showTip=True)
+                                         showTip=False)
         self.search.moduleSelected.connect(
             lambda name: self.cmd_prompt.SetTextAndFocus(name + ' '))
         wx.CallAfter(self.cmd_prompt.SetFocus)

Could it be that there's some encoding needed here ? This said, I have the same error even if I set all locale settings to 'C'.

in reply to:  8 comment:9 by mlennert, 7 years ago

Replying to mlennert:

However, I think I found the culprit: the tooltip ! Disabling it makes the widget work again:

Index: gui/wxpython/gmodeler/dialogs.py
===================================================================
--- gui/wxpython/gmodeler/dialogs.py	(révision 71430)
+++ gui/wxpython/gmodeler/dialogs.py	(copie de travail)
@@ -203,7 +203,7 @@
             lambda command: self.label.SetValue(command))
         self.search = SearchModuleWidget(parent=self.panel,
                                          model=menuModel.GetModel(),
-                                         showTip=True)
+                                         showTip=False)
         self.search.moduleSelected.connect(
             lambda name: self.cmd_prompt.SetTextAndFocus(name + ' '))
         wx.CallAfter(self.cmd_prompt.SetFocus)

Just to confirm, I tried add showTip=True to the call to the same widget in gui/wxpython/gui_core/menu.py and it creates the same kind of issue, only that now the entire GUI freezes as soon as I call g.gui...

comment:10 by mlennert, 7 years ago

Could someone for whom this works tell me what the tooltip actually says ? Or where in the code I can see the content of the tooltip ?

comment:11 by annakrat, 7 years ago

It takes short description of the module and display is using StaticWrapText which is defined in widgets.py. I suspect that widget is the problem, it runs into some infinite loop, could you test, where exactly is it stuck?

comment:12 by annakrat, 7 years ago

Try this if it helps:

Index: gui_core/widgets.py
===================================================================
--- gui_core/widgets.py	(revision 71422)
+++ gui_core/widgets.py	(working copy)
@@ -1087,7 +1087,7 @@
         self._search.Bind(wx.EVT_TEXT_ENTER, self.OnEnter)
 
         if self._showTip:
-            self._searchTip = StaticWrapText(parent=self, id=wx.ID_ANY,
+            self._searchTip = wx.StaticText(parent=self, id=wx.ID_ANY,
                                              size=(-1, 35))
 
         if self._showChoice:
@@ -1160,6 +1160,7 @@
         label = _("%d modules match") % len(commands)
         if self._showTip:
             self._searchTip.SetLabel(label)
+            self._searchTip.Wrap(self.GetSize()[0])
 
         self.showNotification.emit(message=label)
 
@@ -1193,6 +1194,7 @@
             for module in self._results:
                 if cmd == module.data['command']:
                     self._searchTip.SetLabel(module.data['description'])
+                    self._searchTip.Wrap(self.GetSize()[0])
                     break

in reply to:  12 comment:13 by mlennert, 7 years ago

Replying to annakrat:

Try this if it helps:

Index: gui_core/widgets.py
===================================================================
--- gui_core/widgets.py	(revision 71422)
+++ gui_core/widgets.py	(working copy)
@@ -1087,7 +1087,7 @@
         self._search.Bind(wx.EVT_TEXT_ENTER, self.OnEnter)
 
         if self._showTip:
-            self._searchTip = StaticWrapText(parent=self, id=wx.ID_ANY,
+            self._searchTip = wx.StaticText(parent=self, id=wx.ID_ANY,
                                              size=(-1, 35))
 
         if self._showChoice:
@@ -1160,6 +1160,7 @@
         label = _("%d modules match") % len(commands)
         if self._showTip:
             self._searchTip.SetLabel(label)
+            self._searchTip.Wrap(self.GetSize()[0])
 
         self.showNotification.emit(message=label)
 
@@ -1193,6 +1194,7 @@
             for module in self._results:
                 if cmd == module.data['command']:
                     self._searchTip.SetLabel(module.data['description'])
+                    self._searchTip.Wrap(self.GetSize()[0])
                     break

It does ! Everything seems to work fine with this patch applied.

Thanks, once again, Anna !

How much risk we break something in applying this, including to release72 ?

comment:14 by annakrat, 7 years ago

This is safe, please apply it. But the mystery is that this widget is in other places, like in every module dialog.

in reply to:  14 comment:15 by mlennert, 7 years ago

Replying to annakrat:

This is safe, please apply it. But the mystery is that this widget is in other places, like in every module dialog.

All the other examples of usages I've found define a label parameter in the call to the widget. The call in SearchModuleWidget seems to be the only one that doesn't.

And if I add a label parameter to the call, such as:

Index: gui/wxpython/gui_core/widgets.py
===================================================================
--- gui/wxpython/gui_core/widgets.py	(révision 71437)
+++ gui/wxpython/gui_core/widgets.py	(copie de travail)
@@ -1088,7 +1088,7 @@
 
         if self._showTip:
             self._searchTip = StaticWrapText(parent=self, id=wx.ID_ANY,
-                                             size=(-1, 35))
+                                    label="Choose a module", size=(-1, 35))
 
         if self._showChoice:
             self._searchChoice = wx.Choice(parent=self, id=wx.ID_ANY)

the modeler runs perfectly again.

So, before I apply your patch, I'd rather you tell me whether is it better to apply your patch, my patch, here, in order to be able to keep using StaticWrapText, or would it be better to find a solution in StaticWrapText which allows calling it with an empty string ?

comment:16 by mlennert, 7 years ago

To be complete, my patch has to be

Index: gui/wxpython/gui_core/widgets.py
===================================================================
--- gui/wxpython/gui_core/widgets.py	(révision 71437)
+++ gui/wxpython/gui_core/widgets.py	(copie de travail)
@@ -1088,7 +1088,7 @@
 
         if self._showTip:
             self._searchTip = StaticWrapText(parent=self, id=wx.ID_ANY,
-                                             size=(-1, 35))
+                                    label="Choose a module", size=(-1, 35))
 
         if self._showChoice:
             self._searchChoice = wx.Choice(parent=self, id=wx.ID_ANY)
@@ -1199,7 +1199,7 @@
         """Reset widget"""
         self._search.SetValue('')
         if self._showTip:
-            self._searchTip.SetLabel('')
+            self._searchTip.SetLabel('Choose a module')
 
 
 class ManageSettingsWidget(wx.Panel):

Otherwise, you cannot launch the widget a second time...

comment:17 by annakrat, 7 years ago

That sounds good, I would apply your patch.

in reply to:  17 comment:18 by mlennert, 7 years ago

Resolution: fixed
Status: newclosed

Replying to annakrat:

That sounds good, I would apply your patch.

Done in r71439 and r71440.

Closing this for now.

Note: See TracTickets for help on using tickets.