CustomTkinter
CustomTkinter copied to clipboard
Changing cursor on click for unresponsive gap
My aplication has a button that triggers a timeconsuming interaction with some hardware. During that time the ui is unresponsive whitch is fine but I want to give the user a clue by showing the watch cursor.
When I tryed this at app level in the the function given to the button as command nothing changes no matter how often i call self.update() after changing the cursor to 'watch'. So I changed the CTkButton classes clicked method and added the respective instance attributes like so:
def clicked(self, event=None):
if self.command is not None:
if self.state != tkinter.DISABLED:
# click animation: change color with .on_leave() and back to normal after 100ms with click_animation()
if self.clickedCursor is not None:
self.config(cursor=self.clickedCursor)
self.update()
self.update() # for some reason it only works with a second self.update()
self.on_leave()
self.click_animation_running = True
self.after(100, self.click_animation)
self.command()
if self.clickedCursor is not None:
self.config(cursor='')
this works halfway decent (somtimes not at first click) and only when I click and stay on the button long enough. If I move the mouse away from the button fast enough after clicking it, it changes to arrow (probably from the surrounding frame).
Is there a more general way to overwrite the cursor, maybe at toplevel, for as long as I need and than give the controll back to the framework?
@EN20M This is happening because the whole program becomes unresponsive. What I recommend is to use a simple threading in the 'clicked' function.
Here is a basic example:
import threading
def thread_this():
threading.Thread(target=clicked).start() # target will be your 'clicked' function.
def clicked(self, event=None):
# your main function here
self.button = customtkinter.CTkButton(self, command=thread_this) # command that button to 'thread_this' first.
Everything will work normally in the main window, and the program will never freeze.
@Akascape I understand where you are comming from. I know it feel counterintuitive but in my specific situation unless the hardware interaction is done I don't even wand the UI to be responsive since without the hardware beeing the same the UI is just an empty shell.
If I would use threading it would introduce two new problems:
- The UI would look like everything is working while in reality the hardware is still busy.
- I would have to overwrite the defaultcursor for every singel widget in my whole UI and deactivate avery single button.
So in my case I kind of use the unresponsiveness as a feature but still I would like to be able to generally/reliably overwrite the cursor.
@EN20M Ok, In that case you have to update the cursor immediately after clicking the button. Try this:
self.button.configure(cursor='watch') # change the cursor to watch
self.update() # immediately update it
Then you can add this in the end:
self.button.configure(cursor='hand2') # back to normal
Instead of changing the cursor in self, we are changing it after pressing the button.
Thats exactly what the code in my initial issue description does. As I tried to explain the shown code snippet is what I overwrote the CTkButton class with. But as I said this comes with the drawback of only working if I keep the mouse on the button long enouth to take effect after clicking.
But as I said this comes with the drawback of only working if I keep the mouse on the button long enouth to take effect after clicking.
In that case you can change the surrounding frame/window's cursor too.
self.button.configure(cursor='watch')
self.frame.configure(cursor='watch')
self.update()
Or you can also make the button a little bit bigger.
Obviously it will be very rare that the mouse is moved immediately other than the button and surrounding frame.