wxPythonでの画面遷移について
やりたいこと
wxPythonで画面遷移がしたい。
新しくWindowを開き直すのではなく、内部のPanelだけを切り替えるような方法で画面遷移をさせたい。
なお、画面生成にはwxGlade を利用する。
ディレクトリ構成
root ┣ view ┃ ┣ main_window.py ┃ ┣ sub_window_01.py ┃ ┗ sub_window_02.py ┗ main.py
main.py
main_window.pyで定義しているMainWindowを起動させるだけ。
基本的に何もしない。
import view.main_window if __name__ == '__main__': app = view.main_window.MainWindow(0) app.MainLoop()
main_window.py
MainWindowを定義。
MainWindow・MainFrame・MainPanelとそれぞれを設定しているが、装飾などは一切せず、何も表示物のない画面を生成する。
#!/usr/bin/env python # -*- coding: UTF-8 -*- # # generated by wxGlade 0.9.9pre on Fri Oct 30 16:27:31 2020 # import wx # begin wxGlade: dependencies # end wxGlade # begin wxGlade: extracode # end wxGlade class MainPanel(wx.Panel): def __init__(self, *args, **kwds): # begin wxGlade: MainPanel.__init__ self.main_frame = args[0] kwds["style"] = kwds.get("style", 0) wx.Panel.__init__(self, *args, **kwds) self.sizer = wx.BoxSizer(wx.VERTICAL) self.sizer.Add((0, 0), 0, 0, 0) self.SetSizer(self.sizer) self.Layout() self.init_screen() # end wxGlade def screen_transition(self, panel): self.sizer.Clear(False) self.DestroyChildren() new_panel = panel(self) self.sizer.Add(new_panel, 1, wx.EXPAND) self.sizer.Layout() def change_title(self, title): self.main_frame.SetTitle(title) def init_screen(self): from view.sub_window_01 import SubPanel01 self.screen_transition(SubPanel01) self.change_title(u'子画面01') # end of class MainPanel class MainFrame(wx.Frame): def __init__(self, *args, **kwds): # begin wxGlade: MainFrame.__init__ kwds["style"] = kwds.get("style", 0) | wx.DEFAULT_FRAME_STYLE wx.Frame.__init__(self, *args, **kwds) self.SetSize((400, 300)) self.SetTitle(u"主画面") self.main_panel = MainPanel(self, wx.ID_ANY) self.Layout() # end wxGlade # end of class MainFrame class MainWindow(wx.App): def OnInit(self): self.main_frame = MainFrame(None, wx.ID_ANY, "") self.SetTopWindow(self.main_frame) self.main_frame.Show() return True # end of class MainWindow if __name__ == "__main__": main_window = MainWindow(0) main_window.MainLoop()
MainPanelクラスで定義された各関数について確認してみる。
__init__
以下の二点だけ押さえておけばOK
MainFrameのインスタンスを取得しておく
コマンドライン引数の **args の最初の要素にMainFrameのインスタンスが格納されているのでそれを取得しておく。
後に画面タイトルの変更で使用するため。
self.main_frame = args[0]
Frameを変更する予定がない場合はいらない。
初期画面表示
一番初めに表示させておきたいPanelを指定しておく。
そうしないと何もない画面がただ表示されて終わりという悲しい現実を叩きつけられるため。
今回はその処理を関数に纏めているので、ここではその関数を呼び出せばOK
self.init_screen()
screen_transition
画面遷移を実行する関数。
引数で遷移先のPanelの情報を渡すことで、現在表示されているPanelから新しいPanelの情報へ切り替える。
change_title
MainFrameで定義されたタイトルを任意のものへ変更するための関数。
予め__init__でMainFrameのインスタンスを格納させておく必要があるので注意。
画面タイトルを変更させる必要が無いのであればこれはいらない。
init_screen
初期画面を表示する関数。
今回は sub_window_01.py で定義されている SubPanle01 を最初に表示するようにしている。
関数内でimportをしているのは ImportError: cannot import name
を避けるため。
互いにモジュールを呼び合うとこのエラーが発生する。
sub_window_01.py
子画面01を定義。
子画面02を呼び出すためのボタンがあるだけのシンプルな画面。
#!/usr/bin/env python # -*- coding: UTF-8 -*- # # generated by wxGlade 0.9.9pre on Fri Oct 30 16:23:30 2020 # import wx # begin wxGlade: dependencies # end wxGlade # begin wxGlade: extracode # end wxGlade class SubPanel01(wx.Panel): def __init__(self, *args, **kwds): # begin wxGlade: SubPanel01.__init__ self.parent = args[0] kwds["style"] = kwds.get("style", 0) wx.Panel.__init__(self, *args, **kwds) sizer_1 = wx.BoxSizer(wx.VERTICAL) sizer_2 = wx.BoxSizer(wx.HORIZONTAL) sizer_1.Add(sizer_2, 1, wx.EXPAND, 0) static_text_1 = wx.StaticText(self, wx.ID_ANY, u"子画面01") static_text_1.SetFont(wx.Font(20, wx.FONTFAMILY_DEFAULT, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL, 0, "")) sizer_2.Add(static_text_1, 1, wx.ALIGN_CENTER_VERTICAL | wx.ALL, 20) sizer_3 = wx.BoxSizer(wx.HORIZONTAL) sizer_1.Add(sizer_3, 1, wx.ALL | wx.EXPAND, 20) self.move_window_button = wx.Button(self, wx.ID_ANY, u"子画面02へ遷移") self.move_window_button.SetFont(wx.Font(20, wx.FONTFAMILY_DEFAULT, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL, 0, "")) sizer_3.Add(self.move_window_button, 1, wx.ALIGN_CENTER_VERTICAL | wx.ALL, 20) self.SetSizer(sizer_1) self.Layout() self.Bind(wx.EVT_BUTTON, self.transition_sub_window_02, self.move_window_button) # end wxGlade def transition_sub_window_02(self, event): # wxGlade: SubPanel01.<event_handler> from view.sub_window_02 import SubPanel02 self.parent.screen_transition(SubPanel02) self.parent.change_title(u'子画面02') # end of class SubPanel01 class SubFrame_01(wx.Frame): def __init__(self, *args, **kwds): # begin wxGlade: SubFrame_01.__init__ kwds["style"] = kwds.get("style", 0) | wx.DEFAULT_FRAME_STYLE wx.Frame.__init__(self, *args, **kwds) self.SetSize((400, 300)) self.SetTitle(u"子画面01") self.sub_panel_01 = SubPanel01(self, wx.ID_ANY) self.Layout() # end wxGlade # end of class SubFrame_01 class SubWindow01(wx.App): def OnInit(self): self.sub_frame_01 = SubFrame_01(None, wx.ID_ANY, "") self.SetTopWindow(self.sub_frame_01) self.sub_frame_01.Show() return True # end of class SubWindow01 if __name__ == "__main__": sub_window_01 = SubWindow01(0) sub_window_01.MainLoop()
ごちゃごちゃ書いているけれど、実際に使うのはSubPanel01クラスだけで、あとはおまけ。
SubPanel01クラスで定義された各関数について確認してみる。
__init__
親のインスタンス(MainPanel)を格納する箇所だけ押さえておけば良いと思う。
self.parent = args[0]
transition_sub_window_02
子画面02へ遷移させる関数。
importを関数内で実施しているのはmain_window.pyで説明している通り。
sub_window_02.py
内容はsub_window_01.pyのコピペ。説明は省略。
#!/usr/bin/env python # -*- coding: UTF-8 -*- # # generated by wxGlade 0.9.9pre on Fri Oct 30 16:25:03 2020 # import wx # begin wxGlade: dependencies # end wxGlade # begin wxGlade: extracode # end wxGlade class SubPanel02(wx.Panel): def __init__(self, *args, **kwds): # begin wxGlade: SubPanel02.__init__ self.parent = args[0] kwds["style"] = kwds.get("style", 0) wx.Panel.__init__(self, *args, **kwds) sizer_1 = wx.BoxSizer(wx.VERTICAL) sizer_2 = wx.BoxSizer(wx.HORIZONTAL) sizer_1.Add(sizer_2, 1, wx.EXPAND, 0) static_text_1 = wx.StaticText(self, wx.ID_ANY, u"子画面02") static_text_1.SetFont(wx.Font(20, wx.FONTFAMILY_DEFAULT, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL, 0, "")) sizer_2.Add(static_text_1, 1, wx.ALIGN_CENTER_VERTICAL | wx.ALL, 20) sizer_3 = wx.BoxSizer(wx.HORIZONTAL) sizer_1.Add(sizer_3, 1, wx.ALL | wx.EXPAND, 20) self.move_window_button = wx.Button(self, wx.ID_ANY, u"子画面01へ遷移") self.move_window_button.SetFont(wx.Font(20, wx.FONTFAMILY_DEFAULT, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL, 0, "")) sizer_3.Add(self.move_window_button, 1, wx.ALIGN_CENTER_VERTICAL | wx.ALL, 20) self.SetSizer(sizer_1) self.Layout() self.Bind(wx.EVT_BUTTON, self.transition_sub_window_02, self.move_window_button) # end wxGlade def transition_sub_window_02(self, event): # wxGlade: SubPanel02.<event_handler> from view.sub_window_01 import SubPanel01 self.parent.screen_transition(SubPanel01) self.parent.change_title(u'子画面01') # end of class SubPanel02 class SubFrame_02(wx.Frame): def __init__(self, *args, **kwds): # begin wxGlade: SubFrame_02.__init__ kwds["style"] = kwds.get("style", 0) | wx.DEFAULT_FRAME_STYLE wx.Frame.__init__(self, *args, **kwds) self.SetSize((400, 300)) self.SetTitle(u"子画面02") self.sub_panel_02 = SubPanel02(self, wx.ID_ANY) self.Layout() # end wxGlade # end of class SubFrame_02 class SubWindow02(wx.App): def OnInit(self): self.sub_frame_02 = SubFrame_02(None, wx.ID_ANY, "") self.SetTopWindow(self.sub_frame_02) self.sub_frame_02.Show() return True # end of class SubWindow02 if __name__ == "__main__": sub_window_02 = SubWindow02(0) sub_window_02.MainLoop()