Python GTK+3 チュートリアル

17. ダイアログ

翻訳して勉強するGtkチュートリアル第17章 Dialogs です。説明を読んでもよく分かりません…。実際にコードを書いて動かすことを繰り返さないと、きちんとは分からないでしょうね。


ダイアログウィンドウは標準ウィンドウによく似ており、ユーザと情報のやり取りをするめに使用されます。たとえば環境設定ウィンドウを提供するためによく使われます。ダイアログが持つ大きな違いは、ダイアログを自動的にレイアウトするいくつかのパック済みウィジェットがあることです。その上にラベルやボタン、チェックボタンなどを単純に追加できます。もう一つの大きな違いは、ダイアログが対話された後にアプリケーションがどのように振る舞うべきかを制御するためのレスポンスの処理です。

役立つと思われるいくつかの派生ダイアログクラスがあります。Gtk.MessageDialog はほとんどの単純な通知に使われます。しかし、より複雑な機能を提供するためには、独自のダイアログクラスを派生させる必要がある場合もあるでしょう。

17.1. カスタムダイアログ

カスタムダイアログにウィジェットを入れるには、Gtk.Dialog.get_content_area() を介して利用できる Gtk.Box にウィジェットを入れる必要があります。ダイアログの下部に Gtk.Button を追加するには、Gtk.Dialog.add_button() メソッドを使うことができます。

「モーダル」ダイアログ (つまり、ユーザの入力からアプリケーションの残りの部分をフリーズさせるダイアログ) は、ダイアログ上で Gtk.Dialog.set_modal を呼び出すか、Gtk.Dialog コンストラクタの flags 引数に Gtk.DialogFlags.MODAL フラグを含めるように設定することで作成できます。

ボタンをクリックすると “response” と呼ばれるシグナルを発します。コントロール・フローをコードに返す前にダイアログが戻るのをブロックしたい場合は、Gtk.Dialog.run() を呼び出すことができます。このメソッドは int を返します。これは Gtk.ResponseType の値か、Gtk.Dialog のコンストラクタや Gtk.Dialog.add_button() で指定したカスタムレスポンスの値である場合も有りえます。

最後に、ダイアログを削除するには2つの方法があります。Gtk.Widget.hide() メソッドはダイアログをビューから削除しますが、メモリには保存しておきます。これは、後からアクセスする必要がある場合にダイアログを再構築する必要がないようにするのに便利です。あるいは、Gtk.Widget.destroy() メソッドを使用して、ダイアログが不要になったらメモリから削除することもできます。ダイアログが破棄された後にアクセスする必要がある場合は、再度ダイアログを構築する必要があり、そうしないとダイアログウィンドウが空になってしまうことに注意してください。

17.1.1. 例

動作テスト - カスタムダイアログ
# tut17-01.py
# カスタムダイアログ

import gi

gi.require_version("Gtk", "3.0")
from gi.repository import Gtk


class DialogExample(Gtk.Dialog):
    def __init__(self, parent):
        Gtk.Dialog.__init__(
            self,
            "My Dialog",
            parent,
            0,
            (
                Gtk.STOCK_CANCEL,
                Gtk.ResponseType.CANCEL,
                Gtk.STOCK_OK,
                Gtk.ResponseType.OK,
            ),
        )

        self.set_default_size(150, 100)

        label = Gtk.Label("This is a dialog to display additional information")

        box = self.get_content_area()
        box.add(label)
        self.show_all()


class DialogWindow(Gtk.Window):
    def __init__(self):
        Gtk.Window.__init__(self, title="Dialog Example")

        self.set_border_width(6)

        button = Gtk.Button("Open dialog")
        button.connect("clicked", self.on_button_clicked)

        self.add(button)

    def on_button_clicked(self, widget):
        dialog = DialogExample(self)
        response = dialog.run()

        if response == Gtk.ResponseType.OK:
            print("The OK button was clicked")
        elif response == Gtk.ResponseType.CANCEL:
            print("The Cancel button was clicked")

        dialog.destroy()


win = DialogWindow()
win.connect("destroy", Gtk.main_quit)
win.show_all()
Gtk.main()

17.2. メッセージダイアログ

Gtk.MessageDialog は、メッセージ、アイコン、ユーザが応答するためのボタンを持つ、シンプルで標準的なメッセージダイアログを作成するための便利なクラスです。 Gtk.MessageDialog のコンストラクタでメッセージのタイプとテキストを指定したり、標準的なボタンを指定することができます。

何が起こったのかをさらに説明する必要があるダイアログでは、二次テキストを追加することができます。この場合、メッセージダイアログの作成時に入力された一次メッセージが大きくなり、太字に設定されます。二次メッセージは Gtk.MessageDialog.format_secondary_text() を呼び出すことで設定できます。

17.2.1. 例

動作テスト - メッセージダイアログ
# tut17-02.py
# メッセージダイアログ

import gi

gi.require_version("Gtk", "3.0")
from gi.repository import Gtk


class MessageDialogWindow(Gtk.Window):
    def __init__(self):
        Gtk.Window.__init__(self, title="MessageDialog Example")

        box = Gtk.Box(spacing=6)
        self.add(box)

        button1 = Gtk.Button("Information")
        button1.connect("clicked", self.on_info_clicked)
        box.add(button1)

        button2 = Gtk.Button("Error")
        button2.connect("clicked", self.on_error_clicked)
        box.add(button2)

        button3 = Gtk.Button("Warning")
        button3.connect("clicked", self.on_warn_clicked)
        box.add(button3)

        button4 = Gtk.Button("Question")
        button4.connect("clicked", self.on_question_clicked)
        box.add(button4)

    def on_info_clicked(self, widget):
        dialog = Gtk.MessageDialog(
            self,
            0,
            Gtk.MessageType.INFO,
            Gtk.ButtonsType.OK,
            "This is an INFO MessageDialog",
        )
        dialog.format_secondary_text(
            "And this is the secondary text that explains things."
        )
        dialog.run()
        print("INFO dialog closed")

        dialog.destroy()

    def on_error_clicked(self, widget):
        dialog = Gtk.MessageDialog(
            self,
            0,
            Gtk.MessageType.ERROR,
            Gtk.ButtonsType.CANCEL,
            "This is an ERROR MessageDialog",
        )
        dialog.format_secondary_text(
            "And this is the secondary text that explains things."
        )
        dialog.run()
        print("ERROR dialog closed")

        dialog.destroy()

    def on_warn_clicked(self, widget):
        dialog = Gtk.MessageDialog(
            self,
            0,
            Gtk.MessageType.WARNING,
            Gtk.ButtonsType.OK_CANCEL,
            "This is an WARNING MessageDialog",
        )
        dialog.format_secondary_text(
            "And this is the secondary text that explains things."
        )
        response = dialog.run()
        if response == Gtk.ResponseType.OK:
            print("WARN dialog closed by clicking OK button")
        elif response == Gtk.ResponseType.CANCEL:
            print("WARN dialog closed by clicking CANCEL button")

        dialog.destroy()

    def on_question_clicked(self, widget):
        dialog = Gtk.MessageDialog(
            self,
            0,
            Gtk.MessageType.QUESTION,
            Gtk.ButtonsType.YES_NO,
            "This is an QUESTION MessageDialog",
        )
        dialog.format_secondary_text(
            "And this is the secondary text that explains things."
        )
        response = dialog.run()
        if response == Gtk.ResponseType.YES:
            print("QUESTION dialog closed by clicking YES button")
        elif response == Gtk.ResponseType.NO:
            print("QUESTION dialog closed by clicking NO button")

        dialog.destroy()


win = MessageDialogWindow()
win.connect("destroy", Gtk.main_quit)
win.show_all()
Gtk.main()

17.3. ファイル選択ダイアログ

Gtk.FileChooserDialog は “File/Open” や “File/Save” メニュー項目の使用に適しています。Gtk.Dialog 用のメソッドと同様に、ファイル選択ダイアログ上で Gtk.FileChooser のすべてのメソッドを使うことができます。

Gtk.FileChooserDialog を作成する際には、ダイアログの目的を定義しなければなりません。

  • File/Open コマンドのように開こうとするファイルを選択するには、Gtk.FileChooserAction.OPEN を使用します。
  • File/Save コマンドのように初めてファイルを保存するには、Gtk.FileChooserAction.SAVE を使い、Gtk.FileChooser.set_current_name() で “Untitled” のような名前を指定します。
  • File/Save As コマンドのように別の名前でファイルを保存するには、Gtk.FileChooserAction.SAVE を使い、Gtk.FileChooser.set_filename() で既存のファイル名を設定します。
  • ファイルの代わりにフォルダを選択するには、Gtk.FileChooserAction.SELECT_FOLDER を使用します。

Gtk.FileChooserDialog Gtk.Dialog を継承しているので、ボタンには Gtk.ResponseType.ACCEPT Gtk.ResponseType.CANCEL のようなレスポンスIDがあり、これらは Gtk.FileChooserDialog のコンストラクタで指定することができます。

Gtk.Dialog とは対照的に、Gtk.FileChooserDialog ではカスタム・レスポンスコードを使用することはできません。

Gtk.Dialog は、少なくとも1つのボタンが以下のレスポンスIDのいずれかを持つことを要求します。

  • Gtk.ResponseType.ACCEPT
  • Gtk.ResponseType.OK
  • Gtk.ResponseType.YES
  • Gtk.ResponseType.APPLY

ユーザがファイルの選択を終えると、プログラムは選択された名前をファイル名 (Gtk.FileChooser.get_filename() )または URI (Gtk.FileChooser.get_uri()) として取得することができます。

デフォルトでは、Gtk.FileChooser は一度に一つのファイルしか選択できません。複数のファイルを選択できるようにするには、Gtk.FileChooser.set_select_multiple() を使用します。選択されたファイルの一覧を取得するには Gtk.FileChooser.get_filenames() あるいは Gtk.FileChooser.get_uris() を使用します。

Gtk.FileChooser は、ファイルやフォルダをより設定しやすく、アクセスしやすくするための様々なオプションもサポートしています。

  • Gtk.FileChooser.set_local_only():ローカルファイルのみを選択できるようになります。
  • Gtk.FileChooser.show_hidden():隠しファイルやフォルダを表示します。
  • Gtk.FileChooser.set_do_overwrite_confirmation()Gtk.FileChooserAction.SAVE モードでファイルセレクタが設定されている場合、ユーザが既に存在するファイル名を入力した場合に確認ダイアログを表示します。

さらに、Gtk.FileFilter オブジェクトを作成して Gtk.FileChooser.add_filter() を呼び出すことで、表示するファイルの種類を指定することができます。そして、ユーザは追加されたフィルタをファイルセレクタの下部にあるコンボボックスから選択することができます。

17.3.1. 例

動作テスト - ファイル選択ダイアログ
# tut17-03.py
# ファイル選択ダイアログ

import gi

gi.require_version("Gtk", "3.0")
from gi.repository import Gtk


class FileChooserWindow(Gtk.Window):
    def __init__(self):
        Gtk.Window.__init__(self, title="FileChooser Example")

        box = Gtk.Box(spacing=6)
        self.add(box)

        button1 = Gtk.Button("Choose File")
        button1.connect("clicked", self.on_file_clicked)
        box.add(button1)

        button2 = Gtk.Button("Choose Folder")
        button2.connect("clicked", self.on_folder_clicked)
        box.add(button2)

    def on_file_clicked(self, widget):
        dialog = Gtk.FileChooserDialog(
            "Please choose a file",
            self,
            Gtk.FileChooserAction.OPEN,
            (
                Gtk.STOCK_CANCEL,
                Gtk.ResponseType.CANCEL,
                Gtk.STOCK_OPEN,
                Gtk.ResponseType.OK,
            ),
        )

        self.add_filters(dialog)

        response = dialog.run()
        if response == Gtk.ResponseType.OK:
            print("Open clicked")
            print("File selected: " + dialog.get_filename())
        elif response == Gtk.ResponseType.CANCEL:
            print("Cancel clicked")

        dialog.destroy()

    def add_filters(self, dialog):
        filter_text = Gtk.FileFilter()
        filter_text.set_name("Text files")
        filter_text.add_mime_type("text/plain")
        dialog.add_filter(filter_text)

        filter_py = Gtk.FileFilter()
        filter_py.set_name("Python files")
        filter_py.add_mime_type("text/x-python")
        dialog.add_filter(filter_py)

        filter_any = Gtk.FileFilter()
        filter_any.set_name("Any files")
        filter_any.add_pattern("*")
        dialog.add_filter(filter_any)

    def on_folder_clicked(self, widget):
        dialog = Gtk.FileChooserDialog(
            "Please choose a folder",
            self,
            Gtk.FileChooserAction.SELECT_FOLDER,
            (Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL, "Select", Gtk.ResponseType.OK),
        )
        dialog.set_default_size(800, 400)

        response = dialog.run()
        if response == Gtk.ResponseType.OK:
            print("Select clicked")
            print("Folder selected: " + dialog.get_filename())
        elif response == Gtk.ResponseType.CANCEL:
            print("Cancel clicked")

        dialog.destroy()


win = FileChooserWindow()
win.connect("destroy", Gtk.main_quit)
win.show_all()
Gtk.main()


関連記事