1

メニューとプログレスバーを備えたメインウィンドウがあります。メニューコマンドでOKボタンのある通信ウィンドウが開き、OKボタンでプロセスが開始されます(ここでは3秒スリープ)。通信ウィンドウは、ここで提供していないクラスから継承して作成されます (回答が必要な場合はお知らせください)。メソッドは、マザー クラスの既存のメソッドapplyをオーバーライドします。ok

今私の問題: プログレスバーがメイン ウィンドウ (クラス App) とprogressbar(start)対応progressbar(stop)ウィンドウにあるため、マザー クラス tkSimpleDialog.Dialog を介してクラス App に (開始) と (停止) を渡す必要があります。だから私も__init__(self..)メソッドをオーバーライドし、self.プログレスバーに提供すると思いました。

どうすればこれを機能させることができますか?

import Tkinter, ttk, tkFileDialog, tkSimpleDialog, time, threading

class App:
  def __init__(self, master, progressbar):
    self.progress_line(master)

  def progress_line (self, master):
    self.progressbar = ttk.Progressbar(master, mode='indeterminate')
    self.progressbar.place(anchor = 'ne', height = "20", width = "150", x = "175", y = "30")

class AppMenu(object):

  def __init__(self, master, progressbar):
    self.master = master
    self.menu_bar()

  def menu_bar(self):
    menu_bar = Tkinter.Menu(self.master)
    self.menu_bar = Tkinter.Menu(self.master)
    self.master.config(menu=self.menu_bar)
    self.create_menu = Tkinter.Menu(self.menu_bar, tearoff = False)
    self.create_menu.add_command(label = "do", command = self.do)
    self.menu_bar.add_cascade(label = "now", menu = self.create_menu)

  def do(self):
    do1 = Dialog(self.master, progressbar)    

class Dialog(tkSimpleDialog.Dialog):

  def __init__(self, parent, progressbar):

    tkSimpleDialog.Dialog.__init__(self, parent, progressbar)
    self.transient(parent)

    self.parent = parent
    self.result = None

    self.progressbar = progressbar

    body = Frame(self)
    self.initial_focus = self.body(body)
    body.pack(padx=5, pady=5)

    self.buttonbox()
    self.grab_set()

    if not self.initial_focus:
        self.initial_focus = self

    self.protocol("WM_DELETE_WINDOW", self.cancel)
    self.geometry("+%d+%d" % (parent.winfo_rootx()+50, parent.winfo_rooty()+50))
    self.initial_focus.focus_set()
    self.wait_window(self)

  def ok(self, event=None):
    self.withdraw()
    self.start_foo_thread()
    self.cancel()
  def apply(self):
    time.sleep(5)

  def start_foo_thread(self):
    global foo_thread
    self.foo_thread = threading.Thread(target=self.apply)
    self.foo_thread.daemon = True
    self.progressbar.start()
    self.foo_thread.start()
    master.after(20, check_foo_thread)

  def check_foo_thread(self):
    if self.foo_thread.is_alive():
        root.after(20, self.check_foo_thread)
    else:
        self.progressbar.stop()    

master = Tkinter.Tk()
progressbar = None
app = App(master, progressbar)
appmenu = AppMenu(master, progressbar)
master.mainloop()

エラーメッセージ: 最初に [OK] をクリックした後:

Exception in Tkinter callback
Traceback (most recent call last):
  File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/lib-tk/Tkinter.py", line 1410, in __call__
  File "ask-progressbar.py", line 57, in ok
    self.start_foo_thread()
  File "ask-progressbar.py", line 66, in start_foo_thread
    self.progressbar.start()
AttributeError: Dialog2 instance has no attribute 'progressbar'

2番目:アプリを閉じた後

Exception in Tkinter callback
Traceback (most recent call last):
  File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/lib-tk/Tkinter.py", line 1410, in __call__
  File "ask-progressbar.py", line 26, in do
    do1 = Dialog2(self.master, progressbar)
  File "ask-progressbar.py", line 33, in __init__
    self.transient(parent)
  File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/lib-tk/Tkinter.py", line 1652, in wm_transient
TclError: can't invoke "wm" command:  application has been destroyed
4

2 に答える 2

2

以下は、コードの作業バージョンです。プログレスバーに関する他の質問に対する私の回答からコード内の多くのことを変更しなかったため、修正しなければならない問題がいくつかありました。

ここでの主な質問への答えは、基本的に、インスタンスを渡し、関連するさまざまなクラスインスタンスで必要に応じてそれを記憶し、必要なときにメソッドがself引数を介してインスタンスを利用できるようにする必要があるということです。また、tkSimpleDialog.Dialog基本クラスのメソッドを派生させてオーバーライドしようとしていた方法も、複雑すぎて正しくありませんでした。

通常、最善の (そして最も簡単な) 方法は、独自のメソッドvalidate()apply()メソッドを提供することです。これが動作するように設計されているためです。独自のコンストラクターも必要な場合__init__()は、サブクラスのメソッド内から理解できるパラメーターのみを基本クラスのメソッドに渡すことが重要です。さらに機能が必要な場合は、通常、追加の派生クラスのみのメソッドを介して提供できます。これは、そのクラスまたは作成した他のクラスだけが知っているものです。

とにかく、これが私が最終的に得たものです:

import Tkinter, ttk, tkFileDialog, tkSimpleDialog, time, threading

class App:
    def __init__(self, master):
        self.progress_line(master)

    def progress_line(self, master):
        # the value of "maximum" determines how fast progressbar moves
        self._progressbar = ttk.Progressbar(master, mode='indeterminate', 
                                            maximum=4) # speed of progressbar
        self._progressbar.place(anchor='ne', height="20", width="150", 
                                x="175", y="30")
    @property
    def progressbar(self):
        return self._progressbar # return value of private member

class AppMenu(object):
  def __init__(self, master, progressbar):
      self.master = master
      self.menu_bar()
      self.progressbar = progressbar

  def menu_bar(self):
      self.menu_bar = Tkinter.Menu(self.master)
      self.master.config(menu=self.menu_bar)
      self.create_menu = Tkinter.Menu(self.menu_bar, tearoff=False)
      self.create_menu.add_command(label="do", command=self.do)
      self.menu_bar.add_cascade(label="now", menu=self.create_menu)

  def do(self):
      Dialog(self.master, self.progressbar) # display the dialog box

class Dialog(tkSimpleDialog.Dialog):
    def __init__(self, parent, progressbar):
        self.progressbar = progressbar
        tkSimpleDialog.Dialog.__init__(self, parent, title="Do foo?")

    def apply(self):
        self.start_foo_thread()

    # added dialog methods...
    def start_foo_thread(self):
        self.foo_thread = threading.Thread(target=self.foo)
        self.foo_thread.daemon = True
        self.progressbar.start()
        self.foo_thread.start()
        master.after(20, self.check_foo_thread)

    def check_foo_thread(self):
        if self.foo_thread.is_alive():
            master.after(20, self.check_foo_thread)
        else:
            self.progressbar.stop()

    def foo(self): # some time-consuming function...
        time.sleep(3)


master = Tkinter.Tk()
master.title("Foo runner")
app = App(master)
appmenu = AppMenu(master, app.progressbar)
master.mainloop()

お役に立てれば。

于 2013-05-07T17:53:22.327 に答える
2

これは、スレッドの使用を必要としない別のより単純なソリューションです。そのため、ケースでの使用/適応が容易になる可能性があります。update_idletasks()時間のかかるfoo()関数の実行中に、プログレスバー ウィジェットのメソッドを複数回呼び出します。繰り返しますが、プログレスバーを必要とするコードのさまざまな部分に渡す方法を示しています。

import Tkinter, ttk, tkFileDialog, tkSimpleDialog, time

class App:
    def __init__(self, master):
        self.progress_line(master)

    def progress_line(self, master):
        self._progressbar = ttk.Progressbar(master, mode='indeterminate')
        self._progressbar.place(anchor='ne', height="20", width="150", 
                                x="175", y="30")
    @property
    def progressbar(self):
        return self._progressbar # return value of private member

class AppMenu(object):
    def __init__(self, master, progressbar):
        self.master = master
        self.menu_bar()
        self.progressbar = progressbar

    def menu_bar(self):
        self.menu_bar = Tkinter.Menu(self.master)
        self.master.config(menu=self.menu_bar)
        self.create_menu = Tkinter.Menu(self.menu_bar, tearoff=False)
        self.create_menu.add_command(label="do foo", command=self.do_foo)
        self.menu_bar.add_cascade(label="now", menu=self.create_menu)

    def do_foo(self):
        confirm = ConfirmationDialog(self.master, title="Do foo?")
        self.master.update() # needed to completely remove conf dialog
        if confirm.choice:
            foo(self.progressbar)

class ConfirmationDialog(tkSimpleDialog.Dialog):
    def __init__(self, parent, title=None):
        self.choice = False
        tkSimpleDialog.Dialog.__init__(self, parent, title=title)

    def apply(self):
        self.choice = True

def foo(progressbar):
    progressbar.start()
    for _ in range(50):
        time.sleep(.1) # simulate some work
        progressbar.step(10)
        progressbar.update_idletasks()
    progressbar.stop()

master = Tkinter.Tk()
master.title("Foo runner")
app = App(master)
appmenu = AppMenu(master, app.progressbar)
master.mainloop()
于 2013-05-11T21:29:38.440 に答える