Upon pressing a listbox row, a thread is started which:
- Clears the treeview (its liststore)
- 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
CodePudding user response:
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.
CodePudding user response:
Note that all of the comments or answers that tell you how do deal with threading in Python might be partially correct for the narrow question on multi-threading, but they're not really what you want here.
You should realize that almost none of the GTK/GLib API (like Gtk.ListStore
) is not thread-safe unless explicitly mentioned. If you're going to call those functions from different threads than the main one, you should be prepared to encounter random and hard-to-debug issues.
In this case, if you want to update your Gtk.ListStore from a different thread, you want to make sure you use a function like GLib.idle_add()
to update the store in the main thread.
You might also want to read up on the PyGObject tutorial on threading and concurrency