0

I have a simple Python/GTK app. I have a function that does stuff and until said function is done doing stuff, the GtkSpinner should be spinning.

Here is my code:

import time
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk

class Handler:

def on_main_window_destroy(self, *args):
    Gtk.main_quit()

def on_button_clicked(self, *args):
    print("button clicked")
    self.dostuff()

def dostuff(self):
    app.spinner.start()
    print("I'm doing stuff")
    time.sleep(3)
    print("I'm done")
    app.spinner.stop()

class MainApp():

def __init__(self):
    self.builder = Gtk.Builder()
    self.builder.add_from_file("test.glade")
    self.builder.connect_signals(Handler())
    self.main_window = self.builder.get_object("main_window")
    self.main_window.show()

    self.spinner = self.builder.get_object("spinner")

def main(self):
    Gtk.main()

if __name__ == "__main__":
app = MainApp()
app.main()

The time.sleep() command is not the problem. If I replace it with actual "work" the same thing happens. The Spinner is not started/activated.

Why is that? Every command is handled line-by-line. So how can anything be blocking? If I replace the Spinner-code by simple print() statements, they are being outputted just as you'd expect it. I don't have a CS degree and I'm just a hobbyist. Is there some concept I'm not getting?

What do I need to change so that my Spinner starts before the task is started and stops after the task has been finished?

Thanks so much in advance!!

EDIT: This is my Glade file: https://pastebin.com/UDUinH1d

NoBugs
  • 9,310
  • 13
  • 80
  • 146
casualcoder
  • 231
  • 2
  • 11
  • Your program is not possible to execute so it is difficult to know what's wrong. Is the spinner visible at all? Please create a [Minimal, Complete, and Verifiable example](https://stackoverflow.com/help/mcve). – bohrax Aug 30 '18 at 21:44
  • I added the Glade file to my original posting. It should be working now! – casualcoder Aug 30 '18 at 22:23
  • No it doesn't without any effort, you should post a very minimal example that works out of the box – gianmt Aug 31 '18 at 05:48
  • I'm sorry, I don't understand. This already is the most minimal example possible. I have a much larger app and just made this simple demo to show what is wrong. There's really no way to make it any more minimal. You need to files. The *.py file as posted in my original post and the test.glade file which you can find in the pastebin link. Then put both files in one directory and just run the Python file. (Obviously you need to be in a GTK Linux environment though) – casualcoder Aug 31 '18 at 13:09
  • Your Python code example was not possible to execute due to incorrect indentation. Glad that you fixed your own problem though! – bohrax Aug 31 '18 at 16:21

2 Answers2

0

I figured it out myself. For some reason (which I do not quite understand) the GTK GUI is unresponsive even though the Python script should be executed line by line. For some reason this doesn't seem to be the case with PyGObject. So the only solution was to just thread it, which now looks like this:

    t = threading.Thread(target=dostuff, args=(optional, arguments, go, here))
    app.spinner.start()
    t.start()
casualcoder
  • 231
  • 2
  • 11
  • This makes sense, as your button click handler is executed in the Gtk main thread. This will freeze any other Gtk related things unless you yield execution using e.g. `Gtk.main_iteration_do()` once in a while. Maybe this question can be useful as well: https://stackoverflow.com/questions/11923008/threading-in-gtk-python – bohrax Aug 31 '18 at 16:37
  • This is true for every GUI framework. Since your `dostuff` function is run inside the main loop (`Gtk.main()`) it runs every line at a time in the same thread, so, while it's `time.sleep`ing it can't spin the Spinner. Your solution is (AFAIK) the proper way to do it. – Daniel F. Aug 31 '18 at 23:05
  • Yes, just be sure to use `Gdk.threads_add_idle` or similar if you are manipulating Gtk stuff from another thread (in this case you are not). – bohrax Sep 01 '18 at 10:08
  • 'line by line' means 100 lines in C. Gtk.main() is not „one line”. You should consider using Gtk's events_pending and main_iteration (https://developer.gnome.org/gtk3/stable/gtk3-General.html) – cox Sep 04 '18 at 17:49
  • But if I replace the spinner-coed via a simple print() statement, the print() statement is actually printing out something. So why does print() actually print something, but self.spinner.start() not start the spinner? What am I not getting here? – casualcoder Sep 18 '18 at 03:04
0

You using ( time.sleep ) in main code, but ( sleep ) stoped all script, and spinning. I used ( thread ) to start it as a second process.

Check this.

#!/usr/bin/env python
# -*- coding: utf-8 -*-

import time, gi, thread
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk

class MainApp():
    def __init__(self):
        self.builder = Gtk.Builder()
        self.builder.add_from_file("test.glade")
        self.main_window = self.builder.get_object("main_window")
        self.main_window.connect("destroy", self.quit);

        self.spinner = self.builder.get_object("spinner")

        self.button = self.builder.get_object("button")
        self.button.connect("clicked", self.on_button_clicked);

        self.main_window.show()

    def main(self): Gtk.main()
    def quit(self, widget): Gtk.main_quit();

    def sleep_and_stop(self, data = None):
        time.sleep(3);
        print("I'm done");
        self.spinner.stop();

    def on_button_clicked(self, widget = None, data = None):
        self.spinner.start();
        print("I'm doing stuff");
        thread.start_new_thread(self.sleep_and_stop, (None,));

if __name__ == "__main__": MainApp().main();
GUIMish
  • 23
  • 6