Varun Sharma
Varun Sharma

Reputation: 1869

Python GUI freezes or closes while trying to update the button label text

I am trying to read a string from the ubuntu terminal and set that string as a label of a button. It works perfectly for some iteration and then freezes or closes with error. I couldn't find any pattern about when it freezes or closes. I am using gtk libraries and python 2.7.

A screenshot of the UI after it has frozen can be seen below.

Screenshot of the frozen UI(with error)

As seen in the above screenshot, it has successfully updated the value 234, 56 and then exited with error after receiving 213 string. You can also observe that the button in the UI also has 213 value.

Sometimes the UI just freezes without displaying any errors or exiting.

I have used the below codes

1. thread.py ( main program called from terminal )

import thread
import time
import gui2
import vkeys1
import os   
try:
    thread.start_new_thread( vkeys1.main, ( ) )
    thread.start_new_thread( gui2.main, ( ) )  
except:
   print "Error: unable to start thread"

# To stop this script from closing
os.system("mkfifo d1 2> error.log")
fd = os.open('d1', os.O_RDONLY)
ch = os.read(fd,1)   # No writer

2. vkeys1.py ( It reads the input from terminal and calls textinit() )

import gui2
def main() :
    while True:
        try :           
            gui2.ch = str(input('\nInput a string :   '))
            gui2.textinit()
        except :
            print(" \n\n Exception!! \n\n")

3. gui2.py ( Updates the button label )

from gi.repository import Gtk, GdkPixbuf, Gdk, GLib
import Image
import os, sys
import time
import vkeys1
import threading

global ch   # ch is used at vkeys1.py to store the input
ch = 'dummy content'

button0 = Gtk.Button(label="Initially empty")

class TableWindow(Gtk.Window):

    def __init__(self):
        Gtk.Window.__init__(self, title="String retrieval widget")  
        self.set_size_request(500,200)
        self.connect_after('destroy', self.destroy) 
        self.main_box=Gtk.VBox()
        self.main_box.set_spacing(5)

        self.label = Gtk.Label(" ") 

        table = Gtk.Table(7,4, True)
        self.add(self.main_box)
        self.main_box.pack_start(self.label, False, False, 0)
        self.main_box.pack_start(table, False, False, 0) 

        table.attach(button0, 0, 4, 0, 1)
        self.show_all()

    def destroy(window, self):
        Gtk.main_quit()


def textinit():     # called from vkeys1.py
        class Thrd(threading.Thread) :
            def __init__(self) :
                threading.Thread.__init__(self)
                print('\nReceived string')
                print(str(ch))
                print('\n')
                button0.set_label(str(ch))  # Button label updated here         
    Thrd2 = Thrd()
    Thrd2.start()
    return          

def main():
    app=TableWindow()   
    app.set_keep_above(True)    
    app.set_gravity(Gdk.Gravity.SOUTH_WEST) 
    Gtk.main()

if __name__ == "__main__":# for any error exit
    sys.exit(main())

The above codes can be run by typing python thread.py (after creating the above 3 files off-course). Please suggest any solution to overcome this freezing problem.

Upvotes: 2

Views: 853

Answers (1)

user4815162342
user4815162342

Reputation: 154916

The most likely cause of the crash is that your code invokes GTK code from threads other than the thread that runs the main loop, which the documentation states is not allowed.

To resolve the issue, replace the call of gui2.textinit() with GLib.idle_add(gui2.textinit) (note the lack of parentheses after textinit).

Several remarks about the code:

  • The generic exception handler is masking exceptions that occur. Remove it, and you will see a useful traceback when something goes wrong.

  • If you are running under Python 2, you probably want to change input to raw_input, otherwise the code chokes on any input that is not a valid Python expression.

  • textinit creates a thread object that never runs an actual thread. When inheriting from threading.Thread, one must override the run function, which will be invoked in the new thread once start() is called. Doing the work in the constructor accomplishes nothing.

  • thread.start_new_thread is a low-level API that should not be used in normal circumstances and that is demoted to _thread in Python 3. Instead of thread.start_new_thread(fn, ()), use threading.Thread(target=fn), which has the same meaning, and also returns a Thread object.

Upvotes: 2

Related Questions