wxPython, Disabling buttons? - python

I have a list of buttons and I have made a loop to find out what button is pressed, then disable that button on click.
Here is the snippet of code:
def change(self,event):
self.Disable()
for i in enumerate(file_pool):
self.button_pool.append(wx.Button(self.sizer, -1, i[1], pos=(20, i[0]*45),size=(200,40))) #this would create the list of buttons
for i in self.button_pool:
i.Bind(wx.EVT_BUTTON, self.change) #bind each button
However, this will Disable every widget, not just the pressed button. How can I only disable the clicked button?
Thanks

you can get your object from the event:
def change(self, event):
myobject = event.GetEventObject()
myobject.Disable()

Related

Handle mousePressEvent and leaveEvent of QPushButton at the same time

I have a pushButton and want to have a specific behaviour while users interact with that button.
Here is what I want:
user pressed on the button (Without release)
Then the user moved the cursor outside the button (Still the mouse is not released yet).
So, the button should take an active icon and whenever he points outside the button that icon should be changed to inactive-icon
Here is what I get:
I made my own QPushButton and overridden both (leaveEvent(), mouseReleaseEvent(), mousePressEvent()), But the problem is that after the user press and keep pressing on that button no other events are handled, So I need a way to handle other events like the leaveEvent()
Here is my own button class:
class ToggledButton(QPushButton):
def __init__(self, icon: QIcon = None, active_icon: QIcon = None):
super(ToggledButton, self).__init__()
self.active_icon= active_icon
self.inactive_icon = icon
self.setIcon(icon)
self.setCheckable(True)
self.setFlat(False)
def leaveEvent(self, event):
super(ToggledButton, self).leaveEvent(event)
self.setIcon(self.inactive_icon )
def mousePressEvent(self, event):
super(ToggledButton, self).mousePressEvent(event)
self.setIcon(self.active_icon)
def mouseReleaseEvent(self, event):
super(ToggledButton, self).mouseReleaseEvent(event)
self.setIcon(self.inactive_icon )
When a widget receives a mouse button press, it normally becomes the mouse grabber. When that happens, no widget will ever receive an enter or leave event, including that same widget.
A widget that is the mouse grabber will always receive mouse move events, so that is the function that you need to override:
def mouseMoveEvent(self, event):
super().mouseMoveEvent(event)
if event.buttons() == Qt.LeftButton:
if event.pos() in self.rect():
self.setIcon(self.active_icon)
else:
self.setIcon(self.inactive_icon)
Note: you should check the button() in the mouse press event, otherwise it will change icon also when the user presses a different mouse button:
def mousePressEvent(self, event):
super().mousePressEvent(event)
if event.button() == Qt.LeftButton:
self.setIcon(self.active_icon)
Note the difference between buttons() (the pressed mouse buttons at the time of the event) and button(), which is the mouse button that caused the event, which is obviously meaningless for mouse move events, since the event is not caused by mouse buttons but by the movement.

Disabled button still catching clicks during long tasks wxpython

Disabled button still catch clicks during the long task. During the long tasks the button is grayed out but if you click it during the long task, click event fires after the long task has finished. e.g.
def onClick(self, evt):
self.btn.Disable()
for i in range (1000):
print i
self.btn.Enable()
Button disables itself before executing the long for loop, but if we click the button during for loop, it starts the for loop again, because it calls the onClick function again, after the for loop finishes.
Any idea how to disable the click event as well ?
Although I have my doubts as to whether you should be coding your long running event this way, you can achieve what you want by using Unbind on the button click, perform the long running task, using Yield to use up any subsequent button clicks and then at the end of the task Bind to the button again.
i.e.
import wx
import time
class ButtonFrame(wx.Frame):
def __init__(self):
wx.Frame.__init__(self,None)
self.btn = wx.Button(self, -1, "Click Me")
self.btn.Bind(wx.EVT_BUTTON, self.onClick)
self.Centre()
self.Show()
def onClick(self, event):
self.btn.Unbind(wx.EVT_BUTTON)
for i in range (10):
time.sleep(1)
print( i )
wx.GetApp().Yield() # Yielding allows button events to be used up
self.btn.Bind(wx.EVT_BUTTON, self.onClick)
print ("Accepting clicks again")
if __name__ == "__main__":
app = wx.App()
ButtonFrame()
app.MainLoop()
To be honest I didn't really get what you are asking.
Your code works as follows:
When you click on the button, the button (i.e. self.btn) is disabled
It will stayed disabled and execute the for loop
Once done executing the for loop, the button goes back to live
If you would like to disable the button, you should do it outside of the onclick event.
For example:
self.btn.Disable() # This will grey out the button, you can't click it, so the following onClick function wouldn't be triggered
def onClick(self, evt):
# do something
If you would like to use the button to trigger a task execution, and disable the button that triggers the task when the task is in the middle of execution, the best way is to use multi-thread. You can take a look at the following two links for more information:
http://www.blog.pythonlibrary.org/2010/05/22/wxpython-and-threads/
https://wiki.wxpython.org/LongRunningTasks
In fact it's easier than my first answer suggested. There is no reason to UnBind, simply using Yield before re-enabling the button will suffice:
import wx
import time
class ButtonFrame(wx.Frame):
def __init__(self):
wx.Frame.__init__(self,None,-1,"Disable Button Events")
panel = wx.Panel(self, -1)
self.btn = wx.Button(panel, -1, "Click Me", pos=(10,10), size=(80,30))
self.btn.Bind(wx.EVT_BUTTON, self.onClick)
self.Show()
def onClick(self, event):
self.btn.Disable()
for i in range (10):
time.sleep(1)
print("Long task running",i)
wx.GetApp().Yield() # Yielding allows button events to be used up
self.btn.Enable()
print("Accepting clicks again")
if __name__ == "__main__":
app = wx.App()
ButtonFrame()
app.MainLoop()

Python using Tk button

I'm using python GUI (TK). I have a button that runs a long procedure - but I want it to be disabled immediately after clicking it.
It looks like this:
button = tk.Button(self, text="blabla", command= lambda: self.foo(param, button)
def foo(self, button):
button.configure (state = "disabled")
#now call the function that takes time
goo()
def goo():
doLongAction()
The problem is the button is disabled only after goo() returns and then foo returns.
Is there a way to disable it right away?
Thanks
You need to update the button widget after you disable it by calling the update_idletasks method:
button = tk.Button(self, text="blabla", command= lambda: self.foo(param, button)
def foo(self, button):
button.configure (state = "disabled")
##########################
button.update_idletasks()
##########################
#now call the function that takes time
goo()
def goo():
doLongAction()

Tk/Tkinter: Detect application lost focus

I am making a Tkinter application. In the application, I want to pop up a context menu, which I do using Tk.Menu.post().
I don't know how to make this menu disappear when the application loses focus. I need to do this because the menu stays on top, even when switching to another window, leaving a menu 'artifact'.
I put a <FocusOut> event on the menu, which is triggered if the menu has focus and the user moves focus to another application. This works well.
What do I do if the main application window has focus? I could put a <FocusOut> event on the application window, which closes the menu; however, this ends up being called when I give focus to the menu, which closes the menu. The menu is created with parent as the main application, so I am not sure why <FocusOut> on the main app is even triggered when the menu gets focus.
How do I distinguish between the main application window losing focus to a different application vs losing focus to my menu?
I don't want to use tk_popup() because I want the user to continue to provide input to the main window. (use of the menu is optional).
Thanks to #Brad Lanam I came up with a SOLUTION, which I have included:
from Tkinter import *
class App(Tk):
def __init__(self, *args, **kwargs):
Tk.__init__(self, *args, **kwargs)
self.entry = Entry(self)
self.entry.grid(padx=30, pady=30)
self.entry.focus_set()
self.entry.bind("<Tab>", self.put_menu)
self.entry.bind("<Down>", self.set_focus_on_menu)
self.menu = Menu(self, tearoff=False)
self.menu.add_command(label="test")
self.menu.bind("<FocusOut>", self.destroy_menu)
self.bind("<FocusIn>", self.app_got_focus)
self.bind("<FocusOut>", self.app_lost_focus)
self.bind("<3>", self.put_menu)
def app_got_focus(self, event):
self.config(background="red")
def app_lost_focus(self, event):
self.config(background="grey")
######## SOLUTION BEGIN #########
if self.focus_get() != self.menu:
self.destroy_menu(event)
######## SOLUTION END ###########
def set_focus_on_menu(self, event):
self.menu.focus_set()
def menu_got_focus(self, event):
self.menu.activate(0)
def put_menu(self, event):
self.menu.post(self.winfo_x() + self.entry.winfo_x(), self.winfo_y() + self.entry.winfo_y()+20)
def destroy_menu(self, event):
self.menu.destroy()
app = App()
app.mainloop()
self.focus_get() will return the object that has focus, which can be used to distinguish between the menu receiving focus, vs some other application.
For example, to remove the menu when the focus moves to another application:
def app_lost_focus(self, event):
if self.focus_get() != self.menu:
self.destroy_menu(event)

check which button is pressed gtk3 using python

i have 10 button, which correspond to the same method. how am i going to check which button was clicked in the corresponding method? i tried to check for the button press of a particular button in the list by the following code, but i got segmentation fault error:
for i in range(0,10):
if button_list[i].clicked():
break
break
#operation with respect to the button clicked
Here's a sample code that illustrates knowing what button triggered the event by using the label of the button:
from gi.repository import Gtk
class ButtonWindow(Gtk.Window):
def __init__(self):
Gtk.Window.__init__(self, title="Button Demo")
self.set_border_width(10)
hbox = Gtk.Box(spacing=6)
self.add(hbox)
#Lets create 10 buttons for this demo
#You could create and set the label for
#each of the buttons one by one
#but in this case we just create 10
#and call them Button0 to Button9
for i in range(10):
name = "Button{}".format(i)
button = Gtk.Button(name)
button.connect("clicked", self.on_button_clicked)
hbox.pack_start(button, True, True, 0)
def on_button_clicked(self, button):
print button.get_label()
def on_close_clicked(self, button):
print "Closing application"
Gtk.main_quit()
win = ButtonWindow()
win.connect("delete-event", Gtk.main_quit)
win.show_all()
Gtk.main()
So you could just check what the label is and act accordingly.
Once you have connected all the buttons to the same callback, I assume the callback will have this signature: callback(button) where button is the button that emitted the clicked signal.
Inside that callback should be easy to check which button was clicked using something like:
button_list.index(button)
This will return the index of the button inside your list.

Categories