There seems to be some sort of issue in backend_wx with the latest versions of matplotlib and wxPython. We have a figure window with a status bar showing the value of the point currently being hovered over. While this still works as expected, hovering anywhere over the window floods the console with the following trace:
Traceback (most recent call last):
File "C:\Program Files\Python37\lib\site-packages\matplotlib\cbook\__init__.py", line 196, in process
func(*args, **kwargs)
File "C:\Program Files\Python37\lib\site-packages\matplotlib\backend_bases.py", line 2819, in mouse_move
self.set_message(self.mode)
File "C:\Program Files\Python37\lib\site-packages\matplotlib\backends\backend_wx.py", line 1505, in set_message
status_bar.set_function(s)
AttributeError: 'StatusBar' object has no attribute 'set_function'
This functionality worked just fine in matplotlib 3.1.3, but in 3.2.1 we get this error.
Unfortunately I don't have a code fragment to replicate this, sorry! It's something we've noticed while working on CellProfiler. On our end the StatusBar object looks just fine, and there doesn't seem to have been any change in the wxPython API.
I have the same issue. And I think the root cause is here:
def set_message(self, s): status_bar = self.GetTopLevelParent().GetStatusBar() if status_bar is not None: status_bar.set_function(s)
This function uses self.GetTopLevelParent() which returns main Frame. And if main frame has StatusBar (like mine) then it tries to call its set_function
The problem is that returned StatusBar is not StatusBarWx who has such method. It's just wx.StatusBar
In general I don't understand why someone tries to write to the top level status bar? It's kind of small guy tries to beat a big guy :)
Initially when statbar was used it kept its own statusbar which we all saw on the bottom of the canvas with coordinates. But now it uses a top level StatusBar.
To replicate that i guess you just need to have a StatusBar on your frame.
I can fix that in various ways, but I guess the question is, does one expect the global status bar (assuming it's not one set up by matplotlib because you're going your own embedding) to get updated by Matplotlib or not? (better if this behaves consistently across GUIs...)
I would say I'd like to have a chance to disable global status bar updates. Right now I just can't do that, because SetStatusBar(None) for example will set my Global status bar to None, which I don't want. I would say matplotlib must have a reference to its bar.
if self.toolmanager:
self.SetStatusBar(StatusbarWx(self, self.toolmanager))
in FigureFrameWx.init you force setting StatusbarWx for the main Wx frame?
It seems to me too voluntarily. I would expect that matplotlib may allow itself to set status bar in its own window or user can tell matplotlib window to reuse its StatusBar if the user wishes.
But you have to understand that I am just a matplolib user, I'd like gurus to take the floor.
I think this has been the behavior since a long time ago; I'm trying to get rid of it in #17092 (the toolmanager case isn't done yet, but will come after, if we agree on the change).
we have at least two cases where users have replaced that status bar with their own
these new status bars are not StatusBarWx instances (which is our custom sub-class of wx.StatusBar that add theset_function method)
when the mouse motion call back fires, it used to grab our custom status bar and, even if it was no longer displayed, cleanly run. Now in grabs the user set one, fails, and spams the screen.
If it's replacing the status bar on our end that's creating such a problem, I wouldn't expect that the new functionality is inherently wrong. Seeing as this doesn't technically interfere with anything when the bar is replaced, it might be worth simply checking that the fetched statusbar is a StatusBarWx instance before trying to update it? I imagine that would allow you to maintain the current functionality if you do want to keep it.
Yes, we can certainly just add a check that the StatusBar instance is "ours" -- whether with isinstance, or hasattr(statusbar, "set_functions"). We just need to decide the best route to 3.2.2...
Activity
jorikdima commented on Apr 10, 2020
I have the same issue. And I think the root cause is here:
def set_message(self, s): status_bar = self.GetTopLevelParent().GetStatusBar() if status_bar is not None: status_bar.set_function(s)
This function uses self.GetTopLevelParent() which returns main Frame. And if main frame has StatusBar (like mine) then it tries to call its set_function
The problem is that returned StatusBar is not StatusBarWx who has such method. It's just wx.StatusBar
In general I don't understand why someone tries to write to the top level status bar? It's kind of small guy tries to beat a big guy :)
Initially when statbar was used it kept its own statusbar which we all saw on the bottom of the canvas with coordinates. But now it uses a top level StatusBar.
To replicate that i guess you just need to have a StatusBar on your frame.
QuLogic commented on Apr 10, 2020
This GetTopLevelParent change came from #14733; cc @anntzer.
anntzer commented on Apr 10, 2020
I can fix that in various ways, but I guess the question is, does one expect the global status bar (assuming it's not one set up by matplotlib because you're going your own embedding) to get updated by Matplotlib or not? (better if this behaves consistently across GUIs...)
jorikdima commented on Apr 10, 2020
I would say I'd like to have a chance to disable global status bar updates. Right now I just can't do that, because SetStatusBar(None) for example will set my Global status bar to None, which I don't want. I would say matplotlib must have a reference to its bar.
anntzer commented on Apr 10, 2020
What about the proposal at #17092?
jorikdima commented on Apr 10, 2020
Am i right that by
if self.toolmanager: self.SetStatusBar(StatusbarWx(self, self.toolmanager))in FigureFrameWx.init you force setting StatusbarWx for the main Wx frame?
It seems to me too voluntarily. I would expect that matplotlib may allow itself to set status bar in its own window or user can tell matplotlib window to reuse its StatusBar if the user wishes.
But you have to understand that I am just a matplolib user, I'd like gurus to take the floor.
anntzer commented on Apr 10, 2020
I think this has been the behavior since a long time ago; I'm trying to get rid of it in #17092 (the toolmanager case isn't done yet, but will come after, if we agree on the change).
tacaswell commented on Apr 10, 2020
To check my understanding:
I guess our two options are :
@jorikdima The use cases of Matplotlib is definitely much wider than the use cases of the core developers. We definitely value your input on this!
anntzer commented on Apr 10, 2020
I'm absolutely fine with first reverting #14733, but I think #17092 is still worth considering in any case.
DavidStirling commented on Apr 10, 2020
Thanks for investigating this so quickly!
If it's replacing the status bar on our end that's creating such a problem, I wouldn't expect that the new functionality is inherently wrong. Seeing as this doesn't technically interfere with anything when the bar is replaced, it might be worth simply checking that the fetched statusbar is a StatusBarWx instance before trying to update it? I imagine that would allow you to maintain the current functionality if you do want to keep it.
anntzer commented on Apr 11, 2020
Yes, we can certainly just add a check that the StatusBar instance is "ours" -- whether with isinstance, or hasattr(statusbar, "set_functions"). We just need to decide the best route to 3.2.2...
anntzer commented on May 8, 2020
This is closed by #17092 in 3.3. Do we need to partially revert #14733 to fix this in 3.2.2?
FIX: don't try to use non-standard functions on standard status bars
timhoffm commented on May 17, 2020
Not sure why merging #17407 did not auto-close this issue.
QuLogic commented on May 17, 2020
Auto-closing is only for commits/PRs that are on master.