web-dev-qa-db-ja.com

python gtk3アプリケーションでカーソルを砂時計に変更する方法

関数の実行に非常に長い時間がかかり、関数の実行中にカーソルを砂時計に変更しようとしています。ただし、関数が最初に呼び出されたときにのみ機能します。私はこれをやっています(ボタンのon_clickイベントハンドラ内にあります):

from gi.repository import Gtk, Gdk, GObject
import time

def on_grabbtn_clicked(self, button):
    # Change the cursor to hour Glass
    cursor = Gdk.Cursor.new(Gdk.CursorType.WATCH)
    self.window.get_root_window().set_cursor(cursor)

    # lenghty process here
    time.sleep(10)

     # Set the cursor to normal Arrow
    cursor = Gdk.Cursor.new(Gdk.CursorType.ARROW)
    self.window.get_root_window().set_cursor(cursor)

windowは、Glade/GtkBuilderを使用してビルドされ、... windowという名前のウィンドウです。次のように、ウィンドウクラスの__init__()でハンドルを取得します。

self.window = self.builder.get_object('window')

先ほど言ったように、砂時計はボタンを初めてクリックしたときにのみ表示されます。二度目はもう機能しません。だから私はそれを間違っています。しかし、私は何を間違っていますか?

4
Michel Leunen

これがあなたが望むものの実例です。

GUI操作コードを長いブロッキングコードから分離することが重要である理由を理解するには、GTKメインループが何をするかの概念を把握する必要があります。うまくいけば、私の例のコメントとデバッグステートメントが理解に役立つことを願っています。この PyGTK FAQエントリ は有用であり、この概念はGTK3およびGObjectのイントロスペクションを伴うPythonにも適用されます。

サンプルコード:

このコードは、「Click me」というラベルの付いたシンプルなボタンを持つウィンドウを作成します。クリックすると、「Working」に変わり、カーソルが砂時計に変わり、GUIの応答性が維持されます。 10秒が経過すると、ボタンのラベルが「完了」に変わり、カーソルが通常に戻ります。

import time
import threading

from gi.repository import Gtk, Gdk, GObject

window = None

def main():
    GObject.threads_init()
    Gdk.threads_init()

    # Build GUI:
    global window
    window = Gtk.Window()
    button = Gtk.Button(label="Click me")
    window.add(button)
    window.set_default_size(200, 200)
    window.show_all()

    # Connect signals:
    window.connect("delete-event", Gtk.main_quit)
    button.connect("clicked", on_button_click)

    Gtk.main()

def on_button_click(button):
    print "Debug on_button_click: current_thread name:", threading.current_thread().name

    # This is a callback called by the main loop, so it's safe to
    # manipulate GTK objects:
    watch_cursor = Gdk.Cursor(Gdk.CursorType.WATCH)
    window.get_window().set_cursor(watch_cursor)
    button.set_label("Working...")
    button.set_sensitive(False)

    def lengthy_process():
        print "Debug lengthy_process: current_thread name:", threading.current_thread().name
        # We're in a new thread, so we can run lengthy processes without
        # freezing the GUI, but we can't manipulate GTK objects except
        # through GObject.idle_add
        time.sleep(10)
        def done():
            print "Debug done: current_thread name:", threading.current_thread().name
            window.get_window().set_cursor(None)
            button.set_label("Done!")
            return False
        GObject.idle_add(done)

    thread = threading.Thread(target=lengthy_process)
    thread.start()

if __== "__main__":
    main()
3
Flimm