3

GUIに表示されるテキストを特定の時間間隔で変更したい。多くのアプローチの結果、特に私の要件に対して、time.sleep()の代わりに使用する必要があることがわかりましたwx.Timerが、 time.sleep()GUI全体をフリーズします。これが私のコードの例です:

import wx
import time

DWELL_TIMES = [1, 2, 1, 3]
SCREEN_STRINGS = ['nudge nudge', 'wink wink', 'I bet she does', 'say no more!']

class DM1(wx.Frame):
    def __init__(self, *args, **kwargs):
        wx.Frame.__init__(self, *args, **kwargs)
        panel = wx.Panel(self)
        text_display = wx.StaticText(panel, pos = (400, 150))

        for dwell_time in DWELL_TIMES:
            text_display.SetLabel(SCREEN_STRINGS[dwell_time])
            time.sleep(float(DWELL_TIMES[dwell_time]))


app = wx.App()
DM1Frame = DM1(None, size = (800, 600))
DM1Frame.Center()
DM1Frame.Show()
app.MainLoop()

なぜこれが起こるのか、そしてGUIをブロックしないようにする方法を誰かが知っていますか?それは私を助けることができると思いThreadingますね?もしそうなら、このコード内にスレッドを配置する正しい方法はどれですか?に代わるものはありThreadingますか?

どうもありがとう!

4

6 に答える 6

3

他の人が言ったように、wx.CallAfterそしてwx.CallLaterあなたの友達です。それらを研究し、それらを学びます。これは、を使用した完全な実例wx.CallLaterです。適切と思われる他のリファクタリングを含めました。

import wx

DATA = [
    (1, 'nudge nudge'),
    (2, 'wink wink'),
    (1, 'I bet she does'),
    (3, 'say no more!'),
]

class Frame(wx.Frame):
    def __init__(self):
        super(Frame, self).__init__(None)
        panel = wx.Panel(self)
        self.text = wx.StaticText(panel)
        sizer = wx.BoxSizer(wx.VERTICAL)
        sizer.AddStretchSpacer(1)
        sizer.Add(self.text, 0, wx.ALIGN_CENTER)
        sizer.AddStretchSpacer(1)
        panel.SetSizer(sizer)
        self.index = 0
        self.update()
    def update(self):
        duration, label = DATA[self.index]
        self.text.SetLabel(label)
        self.index = (self.index + 1) % len(DATA)
        wx.CallLater(int(duration * 1000), self.update)

if __name__ == '__main__':
    app = wx.App(None)
    frame = Frame()
    frame.SetTitle('Example')
    frame.SetSize((400, 300))
    frame.Center()
    frame.Show()
    app.MainLoop()
于 2012-06-28T13:46:21.100 に答える
2

このコード内にスレッドを配置する正しい方法はどれですか?

を使用するwx.Timerことはこの単純化された例の正しい解決策ですが、実際の目標がワーカースレッドを使用して長いタスクを実行し、アプリケーション全体をフリーズせずにメインGUIを更新する方法を知ることである場合、その方法は次のとおりです。

import wx
import threading
import time

class WorkerThread(threading.Thread):
    DWELL_TIMES = [1, 2, 1, 3]
    SCREEN_STRINGS = ['nudge nudge', 'wink wink', 'I bet she does', 'say no more!']

    def __init__(self, window):
        threading.Thread.__init__(self)
        self.window = window

    def run(self):
        for i in range(len(WorkerThread.DWELL_TIMES)):
            wx.CallAfter(self.window.set_text, WorkerThread.SCREEN_STRINGS[i])
            time.sleep(float(WorkerThread.DWELL_TIMES[i]))
        wx.CallAfter(self.window.close)


class DM1(wx.Frame):
    def __init__(self, *args, **kwargs):
        wx.Frame.__init__(self, *args, **kwargs)
        panel = wx.Panel(self)
        self.text_display = wx.StaticText(panel, pos = (400, 150))
        self.kickoff_work()

    def kickoff_work(self):        
        t = WorkerThread(self)
        t.start()

    def set_text(self, text):
        self.text_display.SetLabel(text)

    def close(self):
        self.Close()

app = wx.App()
DM1Frame = DM1(None, size = (800, 600))
DM1Frame.Center()
DM1Frame.Show()
app.MainLoop()
于 2012-06-28T02:59:44.167 に答える
2

time.sleep()のドキュメントを見ると、基本的に、指定された間隔でそのスレッドの実行がブロックされていることがわかります。問題は、現在GUIにスレッドが1つしかないため、スレッドをブロックすると、そのスレッドでのすべての実行がブロックされることです。これは、経験したように、スリープ中はGUIを使用できないことを意味します。

スレッドを使用している場合でも、time.sleep()呼び出しをGUIと同じスレッドにすることはできないため、スリープが終了した後にGUIを更新しようとすると非常に複雑になります。それを超えて、それは基本的に再実装wx.Timerです!すでに行われていることをやり直すのは無駄です。

あなたの質問は「どうやって睡眠を機能させるのか」よりも少なくすべきだと私には思えます。その他「なぜwx.Timer正しく機能しないのですか?」あなたが抱えている問題をwx.Timer詳しく説明してください。なぜうまくいかないのですか?たぶん、いくつかのコードを投稿してください。私の推測では、おそらくwx.EVT_TIMER適切にバインドされていません。このチュートリアルをご覧ください。

于 2012-06-27T02:19:33.370 に答える
0

最初に開始した時刻を取得するグローバル変数を作成してから、2番目の変数に現在の時刻を取得させて、2つの時間が十分に離れているかどうかを確認してみてください。このようなもの:

テキストが何か新しいものに変わるとき、

global timestart

timestart = gettime()

次に、コードを変更しているかどうかを確認します。

timestop = gettime()

if timestop - timestart >= timebetweenchanges:
    change code
于 2012-06-26T21:41:56.653 に答える
0

なぜタイマーが使えないのかわかりません。それらはあなたがそれらを必要とする正確な目的のために作られているようです。すでに述べたように、私はこのテーマに関するチュートリアルを書きました。

しかし、彼は完全に正しいです。time.sleep()を使用すると、wxのメインイベントループがブロックされるため、GUIがフリーズします。どうしてもtime.sleep()を使用する必要がある場合(これは疑わしいです)、スレッドを使用できます。私もそのテーマに関するチュートリアルを書きました。実際、私は実際にその例でtime.sleep()を使用しています。

于 2012-06-27T19:59:03.863 に答える
0

私はあなたが使用することを提案するかもしれませんwx.CallLater。公式ドキュメントを参照してください:http ://wxpython.org/docs/api/wx.CallLater-class.html

wx.Timerの便利なクラスで、指定されたミリ秒数の後に指定された呼び出し可能オブジェクトを1回呼び出し、位置引数またはキーワード引数を渡します。呼び出し可能オブジェクトの戻り値は、GetResultメソッドで実行された後に使用できます。

戻り値を取得したり、タイマーを再起動したりする必要がない場合は、このオブジェクトへの参照を保持する必要はありません。タイマーの実行中はそれ自体への参照を保持しますが(タイマーにはself.Notifyへの参照があります)、タイマーが完了するとサイクルが中断され、wx.CallLaterオブジェクトが自動的にクリーンアップされます。

可能なさらなる参照は、この質問で見つけることができます:wxPythonでのwx.CallLaterの使用

于 2012-06-28T03:36:31.137 に答える