Ipywidgetsで「ポップアップウインドウもどき」を作ってみる

簡単なPythonのプログラムを試すのに、Jupyter notebookを使っているのですが、ちょっとしたGUIが欲しくなったので、ipywidgetsと言うライブラリを試しています。ちなみに使っている環境は以下の通りです。

  • OS :  macOS-13.5.2

  • Python :  ver. 3.11.4

  • Jupyter Notebook :  ver. 6.5.4

  • ipywidgets :  ver. 8.0.6

 pywidgetsは、お手軽に使えるウイジェットセットで、見たところ一通りのものは揃っていますが、あまり細かい凝ったことはできなさそうな感じです。 Ipywidgetsができないことの中に、メッセージやメニューなどの「ポップアップができない」というのががあります。ポップアップはできませんが、メッセージやメニューなどの表示・非表示を切り替えてそれっぽく見せることはできそうです。

IpywidgetsにはOutputと言うウィジェットが用意されています。これは、表示出力先となるエリアとなるウィジェットで標準出力、標準エラー出力、動画などの出力先として使えますし、各種ウィジェットの表示先としても使えます。Outputに何かを出力する時には、Outputをコンテキストマネージャとして使い、with文の中でdisplay()やprint()などを呼び出します。

import ipywidgets as widgets

out = widgets.Output()
display(out)

with out :
    display(widgets.Button(description ="SAMPLE"))

Output上の表示を消去する場合は、Outputウィジェットのメソッドclear_output()を呼び出します。

out.clear_output()

この仕組みを使って、表示されているラベルの文字列と背景色を設定するメニューを「ポップアップっぽく」表示するプログラムを作ってみます。

import ipywidgets as widgets

class PropertyEditor:   
    
    # カテゴリーの属性を定義するエディター
    # 動的に表示・非表示が可能

    def __init__(self):
        self.caller = None
        self.name = "Blank"
        self.color = "white"
        self._initialize_widget()
        
    def _initialize_widget(self):
        self.name_text = widgets.Text(
                            value = self.name,
                            description='Label Strings:')

        self.color_picker = widgets.ColorPicker(
                            concise=False,
                            description='Color:',
                            value=self.color)
        save_button = widgets.Button(
                            description ='Save',
                            layout = widgets.Layout(width = '45pt') )
    
        save_button.on_click(self._closeEditPanel)
    
        save_button_panel = widgets.HBox(
                            [save_button],
                            layout=widgets.Layout(display='flex', flex_flow='row',
                                                justify_content='center'))

        self.widget = widgets.VBox([self.name_text, self.color_picker,save_button_panel],
                            layout=widgets.Layout(border='1px solid black', 
                                                display='flex', flex_flow='colomun',
                                                justify_content='center'))
        # 動的に表示・非表示を切り替えるためにOutputウィジェットを作る
        self.out = widgets.Output()



    def showEditPanel(self, caller):
        self.caller = caller
        self.name_text.value = caller.name
        self.color_picker.value = caller.color
        # Outputウイジェットをクリア
        self.out.clear_output()
        # Outputウイジェットの上にエディタを表示
        with self.out:
            display(self.widget)


    def _closeEditPanel(self, x):
        self.caller.setProperties(self.name_text.value, self.color_picker.value)
        #Outputウイジェットをクリアし、エディタを消去
        self.out.clear_output()
        
           
class SamplePanel:   
    # 最初から表示される、ラベルとボタンを含むパネル
    
    def __init__(self):
        self.name ="Default String"
        self.color = "lightgray"
        self.editor = None
        

        self._label = widgets.Label(
                            value=self.name,
                            layout = widgets.Layout(width = '180pt',height ='22pt'),
                            style={"background": self.color}
                            )

        self._edit_button = widgets.Button(description ='Edit',
                                            disabled = False,
                                    layout = widgets.Layout(width = '36pt'),
                                    )
        self._edit_button.on_click(self._showPropertyEditor)
        self.widget = widgets.HBox([self._label, self._edit_button])
        

 
    def setProperties(self, name, color):
        self.name = name
        self.color = color
        self._label.value = name
        self._label.style.background = color
    
    def _showPropertyEditor(self, x):
        self.editor.showEditPanel(self)

        
# ここからメインプログラム
        
editor = PropertyEditor()
sample_panel = SamplePanel()
sample_panel.editor = editor
sample_box = widgets.HBox([sample_panel.widget, editor.out])

display(sample_box)

こんな感じで設定パネルが表示されたり消えたりします。

表示例

仕組みを絵で書くとこんな感じです。参考まで。

表示の仕組み

いいなと思ったら応援しよう!