The application I am developing contains some buttons
which are Enabled or Disabled according to some status
variables.
Linux, Python 2.5.1, wx 2.8.7.1
1) is it good practice to use <button>.Enable(True|False)
to show that the functionality of the button is
available or not?
2) I observed a strange behavior when using
<button>.Enable(True|False):
- I hold the cursor over an disabled button.
- after a time, the button becomes enabled.
- I click on that button:
-> nothing happens:
-> the button is not activated visually
-> the method to which it is binded to is not executed
- I move and click on another button which was also disabled:
-> it works as expected.
This happens only on buttons that are Disabled/Enabled in a thread.
In the thread status variables are repetitive read from a file
and the buttons are Enabled/Disabled accordingly.
(
Some time ago I solemnly sweared that I will newer use threads,
because of the problems one will run into dead sure.
But for the GUI of this application I found no other way
to make things going (till now).
2) I observed a strange behavior when using
<button>.Enable(True|False):
[...]
This happens only on buttons that are Disabled/Enabled in a thread.
In the thread status variables are repetitive read from a
file and the buttons are Enabled/Disabled accordingly.
You cannot update the gui directly from a thread. The standard way to do it
is to use wx.CallAfter().
So replace <button>.Enable(True|False) with
wx.CallAfter(<button>.Enable,True|False).
Frank Millman schrieb:
> Kurt Mueller wrote:
>> 2) I observed a strange behavior when using
>> <button>.Enable(True|False):
> [...]
>> This happens only on buttons that are Disabled/Enabled in a thread.
>> In the thread status variables are repetitive read from a
>> file and the buttons are Enabled/Disabled accordingly.
> You cannot update the gui directly from a thread. The standard way to do it
> is to use wx.CallAfter().
> So replace <button>.Enable(True|False) with
> wx.CallAfter(<button>.Enable,True|False).
Thanks a lot.
Now I put all the <button>.Enable(True|False) in a <method> and
call this method inside the thread with wx.CallAfter(<method>) (has no args).
The positive is, that the application does not crash anymore,
which it did every once in a while before.
The problem is now,
that the mentioned behavior stays the same.
When I call wx.CallAfter(<method>) outside the thread,
and no thread is running,
all buttons show this strange behavior now.
When I call <method>() outside the thread,
and no thread is running,
the buttons work as expected.
the <method> _set_controls() has a multiple of such statements:
def _set_controls(self): # Enables/Disables controls according to status_run and status_conf
RUN = self.service.status_run # from another class
CONF = self.service.status_conf
print "in _set_controls: RUN=", RUN, "CONF=", CONF
# btn_launch
if RUN == "NONE":
self.btn_launch.Enable(True)
else:
self.btn_launch.Enable(False)
# btn_...
Frank Millman schrieb:
> Kurt Mueller wrote:
>> 2) I observed a strange behavior when using
>> <button>.Enable(True|False):
> [...]
>> This happens only on buttons that are Disabled/Enabled in a thread.
>> In the thread status variables are repetitive read from a
>> file and the buttons are Enabled/Disabled accordingly.
> You cannot update the gui directly from a thread. The standard way to do it
> is to use wx.CallAfter().
> So replace <button>.Enable(True|False) with
> wx.CallAfter(<button>.Enable,True|False).
Thanks a lot.
Now I put all the <button>.Enable(True|False) in a <method> and
call this method inside the thread with wx.CallAfter(<method>) (has no args).
The positive is, that the application does not crash anymore,
which it did every once in a while before.
The problem is now,
that the mentioned behavior stays the same.
When I call wx.CallAfter(<method>) outside the thread,
and no thread is running,
all buttons show this strange behavior now.
When I call <method>() outside the thread,
and no thread is running,
the buttons work as expected.
the <method> _set_controls() has a multiple of such statements:
def _set_controls(self): # Enables/Disables controls according to status_run and status_conf
RUN = self.service.status_run # from another class
CONF = self.service.status_conf
print "in _set_controls: RUN=", RUN, "CONF=", CONF
# btn_launch
if RUN == "NONE":
self.btn_launch.Enable(True)
else:
self.btn_launch.Enable(False)
# btn_...
Thanks for any help or hints.
I'm not sure what is causing the button to become enabled unexpectedly (are you sure that there isn't anything causing Enable to be called on it again?) But there is a better way to do this than having one big function that checks lots of state variables and enables or disables lots of controls. If you bind a EVT_UPDATE_UI handler to each button then you can break that up into one function for each element. For example the handler for the btn_launch shown above could be as simple as this:
def on_update_btn_launch(self, evt):
RUN = self.service.status_run
evt.Enable(RUN == "NONE")
···
--
Robin Dunn
Software Craftsman http://wxPython.org Java give you jitters? Relax with wxPython!
Frank Millman schrieb:
> Kurt Mueller wrote:
>> 2) I observed a strange behavior when using
>> <button>.Enable(True|False):
> [...]
>> This happens only on buttons that are Disabled/Enabled in
a thread.
>> In the thread status variables are repetitive read from a
>> file and the buttons are Enabled/Disabled accordingly.
> You cannot update the gui directly from a thread. The
standard way to do it > is to use wx.CallAfter().
> So replace <button>.Enable(True|False) with >
wx.CallAfter(<button>.Enable,True|False).
Thanks a lot.
Now I put all the <button>.Enable(True|False) in a <method> and
call this method inside the thread with
wx.CallAfter(<method>) (has no
args).
The positive is, that the application does not crash anymore,
which it did every once in a while before.
The problem is now,
that the mentioned behavior stays the same.
When I call wx.CallAfter(<method>) outside the thread,
and no thread is running,
all buttons show this strange behavior now.
When I call <method>() outside the thread,
and no thread is running,
the buttons work as expected.
I saw Robin's post about EVT_UPDATE_UI, and as I have not used this before,
I thought I would kill two birds with one stone - learn how to use it, and
see if I could reproduce your problem.
If you run the attached program, you will see three buttons, and a timer
which toggles their state from enabled to disabled and back again. It runs
every 3 seconds, which gives you plenty of time to hover your mouse over one
of them before it changes state.
On my setup it behaves correctly. I am using version 2.8.7.1 on Windows.
I saw Robin's post about EVT_UPDATE_UI, and as I have not used this before,
I thought I would kill two birds with one stone - learn how to use it, and
see if I could reproduce your problem.
If you run the attached program, you will see three buttons, and a timer
which toggles their state from enabled to disabled and back again. It runs
every 3 seconds, which gives you plenty of time to hover your mouse over one
of them before it changes state.
On my setup it behaves correctly. I am using version 2.8.7.1 on Windows.
What do see when you run it?
Thank you Frank.
When I start fm69.py with "python fm69.py", a window appears with 3 Buttons, only Button 2 is active.
When I do not enter the mouse in the window, nothing happens for a long time (waited 1 min).
When I move the mouse into the window buttons 1 and 3 become active.
As long as I do not move the mouse within the window, there is no change for a long time (waited 1 min).
When I move the mouse constantly in the window, then every 3 [s] the state of the buttons change
as expected.
I move the mouse in the passive button2 and move it around within the button2 area
(to make the change possible)
When it becomes active, I press button2.
Same result as on my application:
- the button does not become "pressed"
- the button shows a gray dotted box.
(Maybe I do not use the correct wording: "active", "pressed", ...)
Linux, Python 2.5.1, wx 2.8.7.1
If you run the attached program, you will see three buttons,
and a timer which toggles their state from enabled to disabled and back
again. It runs every 3 seconds, which gives you plenty of time to hover
your
mouse over one of them before it changes state.
I just noticed a difference between my version and Robin's version, and I am
curious.
In the EVT_UPDATE_UI handler, I have the following line -
It seems that you can access attributes of EventObject from evt itself,
without going via the object. Does this apply to all attributes of the
object? I have not seen this before. If true, you never need to access the
object at all.
When I start fm69.py with "python fm69.py", a window appears
with 3 Buttons, only Button 2 is active.
When I do not enter the mouse in the window, nothing happens
for a long time (waited 1 min).
When I move the mouse into the window buttons 1 and 3 become active.
As long as I do not move the mouse within the window, there
is no change for a long time (waited 1 min).
When I move the mouse constantly in the window, then every 3
[s] the state of the buttons change as expected.
I move the mouse in the passive button2 and move it around within the
button2 area
(to make the change possible)
When it becomes active, I press button2.
Same result as on my application:
- the button does not become "pressed"
- the button shows a gray dotted box.
(Maybe I do not use the correct wording: "active", "pressed",
...) Linux, Python 2.5.1, wx 2.8.7.1
Hi Kurt
To my surprise, I get the same behaviour as you do if I run it on my Linux
box, whereas on Windows it works as expected.
I have no idea what is happening. I think this is one for Robin, unless
anyone else has any ideas.
If you run the attached program, you will see three buttons, and a timer which toggles their state from enabled to disabled and back again. It runs every 3 seconds, which gives you plenty of time to hover
your
mouse over one of them before it changes state.
I just noticed a difference between my version and Robin's version, and I am
curious.
In the EVT_UPDATE_UI handler, I have the following line -
It seems that you can access attributes of EventObject from evt itself,
without going via the object. Does this apply to all attributes of the
object? I have not seen this before. If true, you never need to access the
object at all.
Any comments will be appreciated.
Frank
I did a little introspection on the event object and it seems to acquire the id of the widget that sends the event. If you add the following to your event handler, you can see it for yourself:
If you run the attached program, you will see three buttons, and a timer which toggles their state from enabled to disabled and back again. It runs every 3 seconds, which gives you plenty of time to hover
your
mouse over one of them before it changes state.
I just noticed a difference between my version and Robin's version, and I am
curious.
In the EVT_UPDATE_UI handler, I have the following line -
It seems that you can access attributes of EventObject from evt itself,
without going via the object. Does this apply to all attributes of the
object? I have not seen this before. If true, you never need to access the
object at all.
No, this functionality is specific to the wx.UpdateUIEvent and is basically a convenience for dealing with the widget attributes that are typically managed with EVT_UPDATE_UI. The event object has Enable, Check, Show and SetText methods that are passed on to the widget that is being checked for updates after the event handler returns. One of the reasons it was done this way was to make the event handlers simpler and also be able to easily use the same one for multiple widgets, menu items or toolbar items. (Note that using evt.GetEventObject().Enable as in your code above won't work for enabling menu or toolbar items since you need to go through the menu or toolbar and use the ID, but evt.Enable in a EVT_UPDATE_UI handler will work exactly the same way for any supported type of UI element.
···
--
Robin Dunn
Software Craftsman http://wxPython.org Java give you jitters? Relax with wxPython!
To my surprise, I get the same behaviour as you do if I run it on my Linux
box, whereas on Windows it works as expected.
I have no idea what is happening. I think this is one for Robin, unless
anyone else has any ideas.
EVT_UPDATE_UI events are sent when the app processes an idle event. Apparently on wxGTK the processing of a timer event is not enough to trigger idle processing, and it needs some other kinds of events first. You can workaround this by adding a call to wx.WakeUpIdle to the timer handler, but this shouldn't be necessary so please create a bug ticket about it.
···
--
Robin Dunn
Software Craftsman http://wxPython.org Java give you jitters? Relax with wxPython!
>
> To my surprise, I get the same behaviour as you do if I run
it on my
> Linux box, whereas on Windows it works as expected.
>
> I have no idea what is happening. I think this is one for Robin,
> unless anyone else has any ideas.
EVT_UPDATE_UI events are sent when the app processes an idle event.
Apparently on wxGTK the processing of a timer event is not
enough to trigger idle processing, and it needs some other
kinds of events first.
You can workaround this by adding a call to wx.WakeUpIdle
to the timer handler, but this shouldn't be necessary so
please create a bug ticket about it.
Ok, that explains this particular problem, and I will create a ticket.
However, it does not explain Kurt's original problem, as he was not using
wx.Timer or EVT_UPDATE_UI.
I modified my program so that the 'timer' method changes the state of the
buttons directly. It works on both platforms, but I can now see the problem
that Kurt reported. If the mouse is hovering over a button while it changes
state, it does not seem to detect the change of state. You have to move the
mouse off the object and back again. This applies to GTK only - the problem
does not occur with Windows.
Kurt, if you are still following this thread, can you confirm that this was
your original problem. Is there anything else to add?
To my surprise, I get the same behaviour as you do if I run
it on my
Linux box, whereas on Windows it works as expected.
I have no idea what is happening. I think this is one for Robin,
unless anyone else has any ideas.
EVT_UPDATE_UI events are sent when the app processes an idle event.
Apparently on wxGTK the processing of a timer event is not
enough to trigger idle processing, and it needs some other
kinds of events first.
You can workaround this by adding a call to wx.WakeUpIdle
to the timer handler, but this shouldn't be necessary so
please create a bug ticket about it.
Ok, that explains this particular problem, and I will create a ticket.
However, it does not explain Kurt's original problem, as he was not using
wx.Timer or EVT_UPDATE_UI.
I modified my program so that the 'timer' method changes the state of the
buttons directly. It works on both platforms, but I can now see the problem
that Kurt reported. If the mouse is hovering over a button while it changes
state, it does not seem to detect the change of state. You have to move the
mouse off the object and back again. This applies to GTK only - the problem
does not occur with Windows.
Kurt, if you are still following this thread, can you confirm that this was
your original problem. Is there anything else to add?
Yes, that was my original problem:
- hovering over a button (or anywhere else within the frame) makes the app going.
Not hovering makes the app "standing still", nothing happens.
- mouse hovering inside a "passive" button becoming "active",
then pressing the button, the button does not become "pressed"
and the binded method will not run.
I placed a wx.WakeUpIdle() in the tread. But it did not change the behavior.
Frank Millman schrieb:
>
> I modified my program so that the 'timer' method changes
the state of
> the buttons directly. It works on both platforms, but I can now see
> the problem that Kurt reported. If the mouse is hovering
over a button
> while it changes state, it does not seem to detect the change of
> state. You have to move the mouse off the object and back
again. This
> applies to GTK only - the problem does not occur with Windows.
>
> Kurt, if you are still following this thread, can you confirm that
> this was your original problem. Is there anything else to add?
Yes, that was my original problem:
- hovering over a button (or anywhere else within the frame)
makes the app going.
Not hovering makes the app "standing still", nothing happens.
- mouse hovering inside a "passive" button becoming "active",
then pressing the button, the button does not become "pressed"
and the binded method will not run.
I placed a wx.WakeUpIdle() in the tread. But it did not
change the behavior.
Hi Kurt
Your second problem does occur when you run my test program. We will have to
wait and see what Robin has to say about that.
However, your first problem does not occur. I can see the buttons changing
their state every three seconds, regardless of the position of the mouse.
Is it possible for you to modify my program so that it demonstrates the
behaviour that you are seeing? Alternatively, reduce your program to the
smallest possible size that shows the problem, and can be run 'stand alone'.
If you post the result here, I am sure someone can help get to the bottom of
it.
applies to GTK only - the problem does not occur with Windows.
Kurt, if you are still following this thread, can you confirm that
this was your original problem. Is there anything else to add?
Yes, that was my original problem:
- hovering over a button (or anywhere else within the frame)
makes the app going.
Not hovering makes the app "standing still", nothing happens.
- mouse hovering inside a "passive" button becoming "active",
then pressing the button, the button does not become "pressed"
and the binded method will not run.
I placed a wx.WakeUpIdle() in the tread. But it did not
change the behavior.
Hi Kurt
Your second problem does occur when you run my test program. We will have to
wait and see what Robin has to say about that.
However, your first problem does not occur. I can see the buttons changing
their state every three seconds, regardless of the position of the mouse.
Is it possible for you to modify my program so that it demonstrates the
behaviour that you are seeing? Alternatively, reduce your program to the
smallest possible size that shows the problem, and can be run 'stand alone'.
If you post the result here, I am sure someone can help get to the bottom of
it.
Hi Frank,
Both problems occur in your fm69.py program on my machine:
Linuxi 2.6.22.17-0.1-default, Python 2.5.1, wx 2.8.7.1
1) fm69.py does not run without hovering with mouse in the frame.
2) mouse over a disabled button becoming enabled.
Click on button does not make button "pressed" and binded method will not run.
To see problem 2) I have to hover with the mouse
inside a disabled button to make the app running.
Stay always inside the button!
When the button becomes active, press button.
It will not work, it only becomes a gray dotted border.
In the enabled state of the button, if I go outside the
buttons area and reenter and click.
Then it will work as expected.
Problem 1) does not occur in my app only in your fm69.py
Problem 2) does occur in my app and in your fm69.py
Both problems occur in your fm69.py program on my machine:
Linuxi 2.6.22.17-0.1-default, Python 2.5.1, wx 2.8.7.1
1) fm69.py does not run without hovering with mouse in the frame.
2) mouse over a disabled button becoming enabled.
Click on button does not make button "pressed" and binded
method will not run.
To see problem 2) I have to hover with the mouse inside a
disabled button to make the app running.
Stay always inside the button!
When the button becomes active, press button.
It will not work, it only becomes a gray dotted border.
In the enabled state of the button, if I go outside the
buttons area and reenter and click.
Then it will work as expected.
Problem 1) does not occur in my app only in your fm69.py
Problem 2) does occur in my app and in your fm69.py
Hi Kurt
I was confused for a moment, but then it clicked. You are still running
'fm69.py'. In a subsequent post I attached a revised version called
'fm69a.py'. This one does not use EVT_UPDATE_UI. I think you will find that
in this version problem 1 does not occur.
Therefore the only problem is problem 2, which occurs in your app and in
fm69.py and in fm69a.py. We will have to wait for Robin to see if he has an
answer.
I was confused for a moment, but then it clicked. You are still running
'fm69.py'. In a subsequent post I attached a revised version called
'fm69a.py'. This one does not use EVT_UPDATE_UI. I think you will find
that
in this version problem 1 does not occur.
Sorry, I did not see it.
I ran fm69a.py and problem 1 does not occur on my machine.
Therefore the only problem is problem 2, which occurs in your app and in
fm69.py and in fm69a.py. We will have to wait for Robin to see if he
has an
answer.
I modified my program so that the 'timer' method changes the state of the
buttons directly. It works on both platforms, but I can now see the problem
that Kurt reported. If the mouse is hovering over a button while it changes
state, it does not seem to detect the change of state. You have to move the
mouse off the object and back again. This applies to GTK only - the problem
does not occur with Windows.
Ah, I didn't understand before that it is when the mouse is over the button as it changes state and then the mouse click is not recognized... This is a common GTK problem that I've seen in non-wx apps too, so I don't think there is anything we can do about it. (I've also seen it in version 2.x of Firefox on Mac, so that gives a hint about what code they were looking at when implementing the XUL buttons...
···
--
Robin Dunn
Software Craftsman http://wxPython.org Java give you jitters? Relax with wxPython!
EVT_UPDATE_UI events are sent when the app processes an idle event.
Apparently on wxGTK the processing of a timer event is not
enough to trigger idle processing, and it needs some other
kinds of events first.
You can workaround this by adding a call to wx.WakeUpIdle
to the timer handler, but this shouldn't be necessary so
please create a bug ticket about it.
That was quick. After battling a bit to access Trac, I only created the
ticket on Sunday morning (#10251), and on Sunday afternoon I received an
email reporting that it was fixed.