質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.37%
ウィンドウ

コンピューター用語において、ウィンドウとはユーザとプログラムのやり取りを可能にするGUIの枠組みのことをいいます。

リストボックス

ユーザーがリストから1つ以上のアイテムを選択できるようにするGUI要素です。

Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

Q&A

解決済

1回答

3533閲覧

<python> wxpythonで子ウィンドウの値を親ウィンドウのリストに表示・追加する方法

Lups20

総合スコア2

ウィンドウ

コンピューター用語において、ウィンドウとはユーザとプログラムのやり取りを可能にするGUIの枠組みのことをいいます。

リストボックス

ユーザーがリストから1つ以上のアイテムを選択できるようにするGUI要素です。

Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

1グッド

0クリップ

投稿2021/06/19 03:09

編集2021/06/19 12:31

###前提・実現したいこと
Python初心者です。GUIとしてwxpythonを使っています。
親ウィンドウから子ウィンドウを呼び出して、子ウィンドウで選んだ結果(リストや日時データなどの値)を子ウィンドウにあるボタン一つで親ウィンドウにあるリストに表示または追加したいです。

##考えたこと
例えば、下記のようにすれば、親ウィンドウのボタンを押すなどのイベントを起こせば、子の値を受けて表示することはできました。

class Child_Project(): list1=[] def __init__(self, parent): (中略) def button1(self, event): Parent_Project.list1=self.list1 class Parent_Project(): list1=[] def __init__(self, parent): (中略) def button1(self, event): self.listbox.SetItems(self.list1)

しかし、私がしたいことは、わざわざ親ウィンドウでボタンを押さずとも子ウィンドウのボタンを押せば親ウィンドウのリスト(self.listbox)に表示させるようにしたいのです。

調べてみましたがよくわからず…行き詰ってしまいました。
似た質問でBindさせれば良い。というような回答があったのですが、Bindをどう使ってどう表示させるのか全く分からずでした。。もしBindを使うのが正解ということであれば、詳細に伺えれば大変幸いです。。

###実行用ソース
イメージとしては以下のようなものになります。
上記の考えてみたこと(親ウィンドウのボタンを押すなどのイベントを起こせば、子の値を受けて表示することはできました。)の部分についてはコメントアウトしています。

import wx class child_dialog(wx.Dialog): # list1 = [] def __init__(self, parent): wx.Dialog.__init__(self, parent, id=wx.ID_ANY, title=wx.EmptyString, pos=wx.DefaultPosition, size=wx.Size(200, 200), style=wx.DEFAULT_DIALOG_STYLE) self.SetSizeHints(wx.DefaultSize, wx.DefaultSize) bSizer2 = wx.BoxSizer(wx.VERTICAL) bSizer3 = wx.BoxSizer(wx.HORIZONTAL) bSizer4 = wx.BoxSizer(wx.HORIZONTAL) self.text1 = wx.TextCtrl(self, wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, wx.DefaultSize, 0) bSizer3.Add(self.text1, 0, wx.ALL, 5) bSizer2.Add(bSizer3, 1, wx.EXPAND, 5) self.done_button = wx.Button(self, wx.ID_ANY, u"Done", wx.DefaultPosition, wx.DefaultSize, 0) bSizer4.Add(self.done_button, 0, wx.ALL, 5) self.cancel_button = wx.Button(self, wx.ID_ANY, u"Cancel", wx.DefaultPosition, wx.DefaultSize, 0) bSizer4.Add(self.cancel_button, 0, wx.ALL, 5) bSizer2.Add(bSizer4, 0, wx.ALIGN_RIGHT, 5) self.SetSizer(bSizer2) self.Layout() self.Centre(wx.BOTH) self.Show() # Connect Events self.done_button.Bind(wx.EVT_BUTTON, self.done) self.cancel_button.Bind(wx.EVT_BUTTON, self.cancel) def __del__(self): pass def done(self, event): # self.list1 = self.text1.GetValue() # parent_project.list1 = self.list1 parent_project.listbox.SetItems(self.list1) # エラーが出る場所。ここで親リストに表示させたい。 self.Destroy() self.Close() def cancel(self, event): self.Destroy() self.Close() class parent_project(wx.Frame): # list1 = [] def __init__(self, parent): wx.Frame.__init__(self, parent, id=wx.ID_ANY, title=wx.EmptyString, pos=wx.DefaultPosition, size=wx.Size(300, 300), style=wx.DEFAULT_FRAME_STYLE | wx.TAB_TRAVERSAL) self.SetSizeHints(wx.DefaultSize, wx.DefaultSize) bSizer1 = wx.BoxSizer(wx.VERTICAL) m_listBox1Choices = [] self.m_button1 = wx.Button(self, wx.ID_ANY, u"open dialog", wx.DefaultPosition, wx.DefaultSize, 0) bSizer1.Add(self.m_button1, 0, wx.ALL, 5) self.m_button2 = wx.Button(self, wx.ID_ANY, u"write", wx.DefaultPosition, wx.DefaultSize, 0) bSizer1.Add(self.m_button2, 0, wx.ALL, 5) self.m_listBox1 = wx.ListBox(self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, m_listBox1Choices, 0) bSizer1.Add(self.m_listBox1, 0, wx.ALL, 5) self.SetSizer(bSizer1) self.Layout() self.Centre(wx.BOTH) self.m_button1.Bind(wx.EVT_BUTTON, self.button1_click) self.m_button2.Bind(wx.EVT_BUTTON, self.button2_click) def __del__(self): pass # Virtual event handlers, overide them in your derived class def button1_click(self, event): dialog1 = child_dialog(frame) def button2_click(self, event): # self.m_listBox1.Append(self.list1) pass if __name__ == '__main__': app = wx.App(False) frame = parent_project(None) frame.Show(True) app.MainLoop()

###補足
Anacondaで構築。
Pythonは3.8.10
WxPythonは4.1.1

teamikl👍を押しています

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

teamikl

2021/06/19 10:47

実行して「エラーを再現できる」最小限のコードを掲載してください。 小ウィンドウは、開きっぱなしのまま別ウィンドウに選択されたデータを送るのですか? それとも、ダイアログの様に選択すれば閉じるような使い方でしょうか。 wx のウィジェット間でのデータのやり取りになら、 イベントの枠組を用いてデータの受け渡しができますが、 提示されたコードは通常のPythonのクラスで、 「親ウィンドウ」「子ウィンドウ」が何処にあるのか不明瞭です。
Lups20

2021/06/19 12:43 編集

すみません。意図としてはこのエラーを解決したいというより、自分なりにあれこれ試したり考えてみたもののどうすれば良いのか皆目見当付かなかった経緯を簡潔に伝えた方がよいかと思い、先の質問文のように致しました。 しかし、自分なりに試したものや考えたものを中途半端に載せてしまったために混乱させてしまう結果となってしまいました。。申し訳ないです。 質問文を修正いたしました。次回から基本的に実行可能なものを作って質問するように致します。 >ダイアログの様に選択すれば閉じるような使い方でしょうか。 仰る通りです。
teamikl

2021/06/19 14:44

「小ウィンドウから親ウィンドウのウィジェットを参照したい」という意図だと思いますが、 質問のコードを見ての印象だと、クラスとインスタンスの区別の認識を 問題の前に、実際のコードを見て確認した方が良いかなと思いました。 もう一点確認で、ダイアログはモーダルダイアログ (小ウィンドウが出ている間は、親ウィンドウの操作はできない)ですか? 場合によっては、親ウィンドウ側から小ウィンドウを参照したほうが良い事もあります。
Lups20

2021/06/19 21:07

ご回答ありがとうございます。後ほど回答側にもコメント記載いたしますが、先にこちらでお返事させて頂きます。 御指摘の通り、お恥ずかしながらクラスとインスタンスの区別の認識は明確にできている訳ではないと思います。。お気遣い頂きありがとうございます。 ダイアログはモーダルダイアログのつもりでした。
teamikl

2021/06/20 03:56

>ダイアログはモーダルダイアログのつもりでした。 ここは確認でした。モーダルダイアログであれば、親側の ShowModal 前後にコード記述出来る為。 もし、モーダレスな場合は、親側でダイアログを閉じるタイミングを把握できないので、 イベントでの通知が必要になってきます。⇛ 回答に書いたカスタムイベントを使う方法が候補。 ---- ちなみに、Pythonでは、クラス名は大文字開始のキャメルケース(単語の区切りを大文字に) という慣習があります。例: ParentWindow, ChildWindow 通常の変数は、大抵の場合小文字開始 parentWindow 等
Lups20

2021/06/20 12:47

カスタムイベントを使ってのイベントでの通知は難しそうですが、こちらも勉強のためやってみたいと思います。 クラス名の慣習について、ありがとうございます。意識して名付けるよう気を付けていきます。 ありがとうございました!
guest

回答1

0

ベストアンサー

python

1# list1=[] は、クラス全体で共有するデータになってます。 2# インスタンスで設定できるように、__init__ 内で初期化 3 4 def __init__(self, parent): 5 self.list1 = [] 6 7 (中略) 8 9# Parent_Project.list1 の様にクラス変数を使っているところは 10# self.list1 と、インスタンス変数に変更

もし記載ミスでなく、そのままのコードで動いてるのだとすれば、一般的な使い方ではないので、
現状のコードがどうなっているのかが、第三者視点で把握できません。
(中略されてる場所、もしくは省かれた箇所に問題解決に必要な情報があります)


2つのウィンドウ間でデータのやり取りをするのは、
カスタムイベントを使う方法だと思います。検索するキーワードとしては wx PostEvent

流れだけ説明すると:

  • イベントを定義する
  • 親ウィンドウでイベント種別と呼び出すメソッドを関連付ける Bind

 ※ サンプルコードには新旧の2つの方法で書かれてますが、上の新しい方法で Bindします。
下は互換性の為の古いコードなので不要です。

  • 子ウィンドウのボタンクリック時に、イベント作成してを送る。wx.PostEvent(parentWindow, event)

 イベントに選択したデータを持たせます。

  • 親ウィンドウには受け取りのメソッドを実装する

現状のコードにどう適応するのかは、質問に提示されている情報では解りません。

  • ウィンドウを2つ表示
  • 片方でアイテム選択のボタン

辺りまでの最小限の動くコードを、質問に掲載して頂ければ、
もう少し具体的な部分の説明が出来ると思います。

現段階では詳細に説明しても、それ以外の部分で齟齬が出そうです。
「インスタンス」が正しく使われていないので、
今動かしてるコードにどう適応するかの部分で問題になりそう。
Bind ~の前に、クラス・インスタンスの関係が正しく機能するかを確認しましょう。


カスタムイベントを使った例ではありませんが、動作確認用サンプル

python

1import wx 2 3app = wx.App() 4win1 = wx.Frame(None, title="parent") 5listbox1 = wx.ListBox(win1) 6win1.Show() 7 8def onItem(event): 9 # single select example 10 listbox1.Append(event.GetString()) 11 12def onClick(event): 13 # multi select example 14 idxs = listbox2.GetSelections() 15 items = list(map(listbox2.GetString, idxs)) 16 listbox1.SetItems(items) 17 18win2 = wx.Frame(win1, title="child") 19listbox2 = wx.ListBox(win2, choices=["A", "B", "C"], style=wx.LB_MULTIPLE) 20listbox2.Bind(wx.EVT_LISTBOX_DCLICK, onItem) # ダブルクリック時 21 22button = wx.Button(win2, label="Click") 23button.Bind(wx.EVT_BUTTON, onClick) # ボタンクリック 24 25sizer = wx.BoxSizer(orient=wx.VERTICAL) 26sizer.Add(listbox2, wx.ALL, wx.EXPAND) 27sizer.Add(button, wx.LEFT|wx.RIGHT, wx.EXPAND) 28win2.SetSizer(sizer) 29win2.Show() 30 31app.MainLoop()

追記

取り急ぎ解決策。Append の部分は用途に合わせて下さい。追加か変更。
SetItems の場合は、リストを渡す必要があります。

python

1 2 def done(self, event): 3 self.GetParent().m_listBox1.Append(self.text1.GetValue()) 4 self.Destroy() 5 self.Close() 6

問題点:

  • parent_project は、インスタンスではなくクラスなので、

 クラス自身は、インスタンス化しないとウィジェット等の実態を持ちません。
インスタンスを参照する必要があります。
⇛ 親子関係がある場合は、Parent や GetParent() で参照できます。

  • リストボックスの属性 list1 -> m_listBox1
  • リストボックスのメソッド SetItems -> Append

モーダルダイアログの場合は、親ウィンドウ側から小ウィンドウを参照したほうが良いです。

理由: クラスの再利用性の為。小ウィンドウから親を参照すると
上のコードの場合は、親に m_listBox1 というリストボックスがないと使えません。
親から小ウィンドウの情報を参照すると、他の場所でも使えるようになります。


追記2: モーダルダイアログ

python

1# 親ウィンドウ側のクラスで 2 3 def button1_click(self, event): 4 with child_dialog(self) as dialog1: 5 if dialog1.ShowModal() == wx.ID_OK: 6 self.m_listBox1.Append(dialog1.text1.GetValue())

投稿2021/06/19 10:49

編集2021/06/19 15:23
teamikl

総合スコア8681

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

Lups20

2021/06/19 22:03 編集

ご回答ありがとうございます。 解決いたしました。どちらの回答も大変勉強になりました。 ただ一点伺いたいことがあります。 仰るように再利用性がないため親が子を参照するようにしたいと思って試したみたのですが、 dialog1.ShowModal() == wx.ID_OK: の所がTrueにならず上手くいきませんでした。 self.done_button = wx.Button(self, wx.ID_OK, …)とwx.ID_ANYをwx.ID_OKにしないといけないのか?と試してみましたが、ダメでした。 https://wxpython.org/Phoenix/docs/html/wx.StandardID.enumeration.html を見ると、" Standard button and menu IDs."とあるので、固有のIDが割り当てられるものなのか?と思い、 調べていくうちにwx.StdDialogButtonSizer()を使えばいいのだろうかと、以下をchild_dialogに追加した所、上手くいきました。 ``` m_sdbSizer1 = wx.StdDialogButtonSizer() self.m_sdbSizer1OK = wx.Button(self, wx.ID_OK) m_sdbSizer1.AddButton(self.m_sdbSizer1OK) self.m_sdbSizer1Cancel = wx.Button(self, wx.ID_CANCEL) m_sdbSizer1.AddButton(self.m_sdbSizer1Cancel) m_sdbSizer1.Realize() bSizer2.Add(m_sdbSizer1, 1, wx.EXPAND, 5) ``` このあと、ではwx.ID_OKではなくYES等使えばいいのかと思い、ボタンはself.done_button = wx.Button(self, wx.ID_YES, …)、dialog1.ShowModal() == wx.ID_YES:でもダメで、IDを直接数値で入力してもダメでした。 結局、print(dialog1.ShowModal)とすると、何をしても5101で、この数値はwx.ID_CANCELの値と同じでした。つまり、self.Close()もしくはself.Destroyでダイアログを閉じるとキャンセルボタン扱いになるようでした。 大変長くなってしまい申し訳ありませんが、上記を踏まえてここで質問です。 教えて頂いたdialog1.ShowModal()の値(戻り値?)を自分で変更・設定できないのでしょうか。。。 今回のようにOKとキャンセルボタンくらいしかないのであれば、wx.StdDialogButtonSizer()で解決となりますが、閉じた後の動作が2種類以上ある場合(例えば、片方のボタンを押せば子の値をリスト1に表示させるが、片方のボタンを押せば別リスト2に表示させる等)、使えなくなってしまうように思います。。
teamikl

2021/06/20 03:35 編集

dialog1.ShowModal() == wx.ID_OK の所は、見落としてました。 ダイアログ側が終了時に適切な値を返す必要がありますね。 >(例えば、片方のボタンを押せば子の値をリスト1に表示させるが、片方のボタンを押せば別リスト2に表示させる等)、使えなくなってしまうように思います。。 解決策: - SetReturnCode で値は変更できます。 - EndModal により、閉じると同時に任意の値を設定出来ます。 残念ながら、StdDialogButtonSizerのAddButton には、 「OK」「YES」「NO」「SAVE」~等の定型以外 任意のボタンは追加できないので、独自にボタンを追加する必要はあり。
Lups20

2021/06/20 12:42

ご回答ありがとうございます。 EndModalで無事上手くいきました。 度々ご回答頂きありがとうございます。本当に助かりました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.37%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問