2017-06-28 61 views
2

起動時にアプリケーションがリモートクラウドストレージからデータベースをロードしている間に表示されるスプラッシュ画面を実装しました。 .update()を呼び出すとスプラッシュ画面が生きています(進行状況バーがあります)。また、別の読み込みプロセスが終了すると破棄されます。その後、メインループが開始され、アプリケーションは正常に実行されます。メインループ外のTkinterスプラッシュ画面とマルチプロセッシング

以下のコードは、私のMacでpython 3.6とtcl/tk 8.5.9で正常に動作していました。しかし、Sierraのアップデート後、私はtkをActiveTcl 8.5.18にアップデートすることを余儀なくされました。さて、スプラッシュ画面は、別のプロセスが終了するまで表示されませんが、表示され、(たとえその.destroy()メソッドが呼び出されていても)ルートウィンドウとともに画面にとどまります。

import tkinter as tk 
import tkinter.ttk as ttk 
import multiprocessing 
import time 


class SplashScreen(tk.Toplevel): 
    def __init__(self, root): 
     tk.Toplevel.__init__(self, root) 
     self.geometry('375x375') 
     self.overrideredirect(True) 

     self.columnconfigure(0, weight=1) 
     self.rowconfigure(0, weight=1) 

     self.label = ttk.Label(self, text='My Splashscreen', anchor='center') 
     self.label.grid(column=0, row=0, sticky='nswe') 

     self.center_splash_screen() 
     print('initialized splash') 

    def center_splash_screen(self): 
     w = self.winfo_screenwidth() 
     h = self.winfo_screenheight() 
     x = w/2 - 375/2 
     y = h/2 - 375/2 
     self.geometry("%dx%d+%d+%d" % ((375, 375) + (x, y))) 

    def destroy_splash_screen(self): 
     self.destroy() 
     print('destroyed splash') 


class App(tk.Tk): 
    def __init__(self): 
     tk.Tk.__init__(self) 

     self.start_up_app() 

     self.title("MyApp") 
     self.columnconfigure(0, weight=1) 
     self.rowconfigure(0, weight=1) 

     self.application_frame = ttk.Label(self, text='Rest of my app here', anchor='center') 
     self.application_frame.grid(column=0, row=0, sticky='nswe') 

     self.mainloop() 

    def start_up_app(self): 
     self.show_splash_screen() 

     # load db in separate process 
     process_startup = multiprocessing.Process(target=App.startup_process) 
     process_startup.start() 

     while process_startup.is_alive(): 
      # print('updating') 
      self.splash.update() 

     self.remove_splash_screen() 

    def show_splash_screen(self): 
     self.withdraw() 
     self.splash = SplashScreen(self) 

    @staticmethod 
    def startup_process(): 
     # simulate delay while implementation is loading db 
     time.sleep(5) 

    def remove_splash_screen(self): 
     self.splash.destroy_splash_screen() 
     del self.splash 
     self.deiconify() 

if __name__ == '__main__': 
    App() 

なぜこれが起こっているのか、それを解決する方法がわかりません。誰でも助けることができますか?ありがとう!

更新:あなたが行self.overrideredirect(True)をoutcomment場合

スプラッシュ画面が正しく表示されます。しかし、私は窓の飾りが必要ではなく、スクリプトの終わりにはまだ画面上にとどまっています。しかし、それは内部的に破壊されており、self.splash(例えば、.winfo_... -methods)のメソッド呼び出しは_tkinter.TclError: bad window path name ".!splashscreen"になります。

また、このコードはwindowsおよびtcl/tk 8.6でも正常に動作します。これはMacのtcl/tk 8.5.18のウィンドウ管理に関するバグ/問題ですか?

+0

あなたのコードは私の目的のとおりに動作します。私は2017-03-21でリリースされたPython 3.6.1を使用しています。 –

+0

あなたはtkinterで 'sleep()'を使ってはいけません。代わりに 'after()'を使用してください –

+0

@SierraMountainTechありがとうございます、私は現在Python 3.6.0で更新を試みます。 after()/ sleep():私はこれを認識しています。これは、シミュレーション目的のために、別個のプロセスでのみ使用されていました。 – Sam

答えて

0

明らかに、これは、overrideredirect(True)を呼び出した後、ウィンドウがウィンドウマネージャによって装飾されていないときのウィンドウの積み重ね順序に問題があるためです。それは他のプラットフォームでも発生しているようです。

のPython 3.6.1とMacOSの10.12.5に次のコードを実行し、Tcl/Tk 8.5.18は、トップレベルのウィンドウがボタンの後に表示されない「開く」をクリックします

行をコメントアウト
import tkinter as tk 

class TL(tk.Toplevel): 
    def __init__(self): 
     tk.Toplevel.__init__(self) 
     self.overrideredirect(True) 
     # self.after_idle(self.lift) 
     tl_label = tk.Label(self, text='this is a undecorated\ntoplevel window') 
     tl_label.grid(row=0) 
     b_close = tk.Button(self, text='close', command=self.close) 
     b_close.grid(row=1) 

    def close(self): 
     self.destroy() 

def open(): 
    TL() 

root = tk.Tk() 
label = tk.Label(root, text='This is the root') 
label.grid(row=0) 
b_open = tk.Button(root, text='open', command=open) 
b_open.grid(row=1) 
root.mainloop() 

self.after_idle(self.lift)は問題を解決します(単にself.lift()を呼び出すだけですが、after_idle()を使用すると、ウィンドウがその位置に移動されてサイズ変更される前にウィンドウが点滅するのを防ぐことができます。これはtkinterを繰り返し経験して、私がPyQTやPySide2を学ぶべきかどうか...)。

私の元の質問では、デコレートされていないウィンドウを閉じることに関する問題について:window.destroy()の代わりにafter_idle(window.destroy())を呼び出すと、それも修正されているようです。私はなぜなのか理解していない。

これを他の人が再現し、誰かが私にこのことをバグとして報告したいと考えている場合は、私はそうすることを喜んでします。

関連する問題