Search code examples
pythonmultithreadinguser-interfacegtkgtk3

Preventing multiple instances of a thread in GTK


Upon pressing a listbox row, a thread is started which:

  1. Clears the treeview (its liststore)
  2. Then, adds items to the treeview (or rather its liststore).

The row can be pressed multiple times, meaning it can spawn multiple threads, which may end up running simultaneously.

The issue is that if the row is pressed very quickly multiple times, the treeview ends up with duplicate entries. This is caused by the multiple threads running in parallel, all trying to add the same set of items the treeview.

The solution to this issue would be to only ever allow one instance of the thread running at any time. This could be done by either:

  • Preventing new thread instances from starting if the thread is currently running

  • Stop the already running instance of the thread, and only then starting a new one

The problem is that I do not know how to implement either of those solutions.

Code:

class Main(Gtk.Window):
    def __init__(self):
        Gtk.Window.__init__(self)
        self.connect("destroy", Gtk.main_quit)

        self.liststore = Gtk.ListStore(str)
        self.treeview = Gtk.TreeView()
        self.treeview.set_model(self.liststore)
        cellrenderertext = Gtk.CellRendererText()
        treeviewcolumn = Gtk.TreeViewColumn("Items", cellrenderertext, text=0)
        self.treeview.append_column(treeviewcolumn)

        self.button = Gtk.Button(label='Button')

        box = Gtk.Box()
        box.set_orientation(Gtk.Orientation.VERTICAL)

        box.add(self.button)
        box.add(self.treeview)
        self.add(box)

        self.button.connect('clicked', self.button_clicked)


    def button_clicked(self, widget):
        def threaded():
            self.liststore.clear()
            self.liststore.append(['first_item'])
            time.sleep(0.1)
            self.liststore.append(['second_item'])
            time.sleep(0.1)
            self.liststore.append(['third_item'])

        threading.Thread(target=threaded, daemon=True).start()



if __name__ == '__main__':
    window = Main()
    window.show_all()
    Gtk.main()

I added the time.sleep(0.1) to simulate the delay between each item being added. Pressing the button multiple times will result in duplicate entries appearing in the treeview. The expected output is:

first_item

second_item

third_item

However, pressing the button really quickly instead results in:

first_item

third_item

second_item

third_item


Solution

  • You should use variable to keep thread and check this variable.

    In __init__ set variable

    self.thread = None
    

    and when you press button then check this variable

    #if (self.thread in None) or (self.thread.is_alive() is False): 
    if not self.thread or not self.thread.is_alive(): 
        self.thread = threading.Thread(...) 
        self.thread.start()
    

    Instead of checking is_alive() you could set self.thread = None at the end of thread.