Tkinter button not responding via thread but works otherwise

61 Views Asked by At

I am working on small tkinter gui app in which I am fetching real time stock prices and place orders at the same time, I have created couple of Daemon threads that are fetching real time data over websocket as follows :

def start_feed(api, subscribe_list):
    print("start_feed function is called....")

    def event_handler_feed_update(tick_data):
        global gData=tick_data
        #This is where I receive live data over websocket

        
    def event_handler_order_update(orderNo):
        print(orderNo)

    def open_callback(threaded=True):
        global feed_opened
        feed_opened = True

    api.start_websocket(
        order_update_callback=event_handler_order_update,
        subscribe_callback=event_handler_feed_update,
        socket_open_callback=open_callback,
    )

    while feed_opened == False:
        pass

    api.subscribe(subscribe_list)
    time.sleep(2)

def live_feed():
    print("Inside live_feed function")    
    global myLabel1
    print()
    print(gData)        
    time.sleep(0.15)
    #this is an infinite loop in which I fetching new data after every 0.15 seconds and implementing logic 

t2 = Thread(target=start_feed(api, subscribe_list))
t2.setDaemon(True)
t2.start()
t1 = Thread(target=live_feed)
t1.setDaemon(True)
t1.start()

Then I have created a basic tkinter gui with button n other widgets, below is the screenshot when button pressed it's working as expected when used non threaded code to call the function
This is how gui looks like and you can see live feed in output window on the rhs

The working button code -

buy_PE = tk.Button(root, text="Buy PE", command=buy_PE).pack()

But when I use threading in it button is not calling the function at all, only press action happens n no function call, here is the code -

buy_PE = tk.Button(root, text="Buy PE", command=Thread(target=buy_PE).start()).pack()

Not working screenshot

Here is the function that I am trying to call via button press -

# Buy PE function -
def buy_PE():
    global pe_tradingSymbol
    global final_quantity
    PE_buy_order_placed.config(text="inside buy_PE()")
    orderNo = api.place_order(
        buy_or_sell="B",        
        tradingsymbol=pe_tradingSymbol,
        quantity=final_quantity,        
        price_type="MKT",        
    )

    PE_buy_order_placed.config(text=orderNo)

Here api is a global object

So my question, why this button is not calling the function when used thread but working well otherwise ? is it because of the already running t1,t2 threads in the background ? is there any way I can speed up the button response time in tkinter, it feels a bit laggy when I press a button, almost takes a second to respond back, anyway to speed that up ?

2

There are 2 best solutions below

3
On

I couldn't run the code and check because I have to type all the missing parts and recheck. I assume this could be the problem.

When you call a Thread, it takes a target function as its argument. Since you can't call function with argument directly, Try as follows:

buy_PE = tk.Button(root, text="Buy PE", command= lamda :Thread(target=buy_PE).start()).pack()

Implement the same kind of changes with all threads called by any buttons or any other widgets.

Kindly confirm in comments if it works or otherwise also. I'm following this question manually.

1
On

I can't run code but I see three problems in one line

buy_PE = tk.Button(root, text="Buy PE", command=Thread(target=buy_PE).start()).pack()

The most important: command= needs callback - it means function's name without () and when you press button then it will use () to run it.
So you need

command=Thread(target=buy_PE).start

without () after start.

Your current code starts thread when you start program. And command has nothing to run. Eventually it may raise error if start() returns something strange because tkinter will try to use it as function.

Second: you use the same name buy_PE to keep button buy_PE = tk.Button(...) and as function's name def buy_PE() and sometimes it can make problem because thread may try to run your button instead of function. You should rename one of them.

Third: code buy_PE = tk.Button(...).pack() assigns None to variable because pack() (and grid(), place()) always gives None. You should do it in two steps

buy_PE = tk.Button(...)
buy_PE.pack()

Maybe you may have another problem. tkinter (like many GUIs) doesn't like to use widgets in threads - so you may have problem with PE_buy_order_placed.config(text=...). You may have to send values to main thread using queue, and tkinter may need after(miliisecond, callback) to run periodically function which will check queue and update widget.