Paepoi

Paepoi » GTK4(Python) Tips » GTK4(Python) Tips | ダイアログ

GTK4(Python) Tips | ダイアログ

# 最終更新日 2022.04.24

GtkDialog
GTK3 ではメインループ処理は不要で run() で表示し destroy() で破棄という流れでした。
GTK4 ではレスポンスはシグナルで受け取るように、伴ってメインループ必須になりました。
Esc キーを押すと自動でキャンセル扱いになるという特徴は引き継いでいます。

GTK3 同様に何故か new_with_buttons() 関数は利用できません。
基本的なプロパティも存在しませんので関数で一つづつボタン等を追加する必要がある。
GtkDialog をそのまま使うには作成した後に関数を使ってボタンを追加する必要があります。
GtkWindow のサブクラスなので GtkWindow のプロパティはそのまま使えます。
#!/usr/bin/env python3

import gi, sys
gi.require_version('Gtk', '4.0')
from gi.repository import Gtk, GLib

'''
    この方法では「親ウインドウが無い」という警告メッセージが出ます
    実際に利用する場合はウインドウから呼び出すようにしましょう
'''

def on_message_response(dialog, response_id):
    if response_id == Gtk.ResponseType.YES:
        print('やっぱりマイクロフォーサーズだよね')
    elif response_id == Gtk.ResponseType.NO:
        print('フルサイズもいいけど重いよね')
    else:
        print('Esc 押したのね')
    # このシグナルでダイアログは破棄する
    dialog.destroy()
    loop.quit()

# new_with_buttons は無いので作成後にボタンを追加
dlg = Gtk.MessageDialog()
dlg.add_buttons('OM SYSTEM', Gtk.ResponseType.YES, 'CANON', Gtk.ResponseType.NO)

# vbox メソッドはさすがに無くなったので C 同様に
vbox = dlg.get_content_area()
vbox.append(Gtk.Label(label='どっちが好き?'))

# dlg.run() は廃止、シグナルにて
dlg.connect('response', on_message_response)
dlg.show()

# 実際に利用する時は GtkApplication がメインループを受け持ちます
loop = GLib.MainLoop.new(None, False)
loop.run()

GtkMessageDialog
GtkMessageDialog は GtkDialog のサブクラスで文字列の表示に特化した Widget です。
GtkDialog を使って同じことが可能ですが警告文の表示等に手間がかからず便利です。
response シグナルで破棄が必須なのは GtkDialog と同様。

こちらにはプロパティが沢山あるので使い易い、プロパティ名で何がドレなのか大体解ると思う。
GtkButtonsType は GtkDialog より簡素化されて ResponseType や複数ボタンもセットです。
NONE, OK, CLOSE, CANCEL, YES_NO, OK_CANCEL
#!/usr/bin/env python3

import gi
gi.require_version('Gtk', '4.0')
from gi.repository import Gtk, Gio, GLib

class Win(Gtk.ApplicationWindow):
    def __init__(self, a):
        Gtk.ApplicationWindow.__init__(self, application=a)
        btn = Gtk.Button(label='メッセージを表示する')
        btn.connect('clicked', self.on_button_clicked)
        self.set_child(btn)
        self.present()

    def on_button_clicked(self, button):
        msg = Gtk.MessageDialog(
            buttons = Gtk.ButtonsType.OK,
            modal = True,
            #secondary_text = '詳細',
            text = 'メッセージ',
            transient_for = self)
        # 戻り値を処理しないならラムダ式で簡素に
        msg.connect('response', lambda d,i: d.destroy())
        msg.show()

def app_activate(a):
    w = Win(a)
    w.present()

app = Gtk.Application()
app.connect('activate', app_activate)
app.run()

GtkAboutDialog
GtkAboutDialog は GTK3 の時と一見何も変わっていません。
しかし GTK3 では GtkDialog ベースだったのが GtkWindow ベースに変わっています。
なので response シグナルはありませんし処理する必要もありません。
全部プロパティだけで設定可能、transient_for と modal の指定を忘れずに。
後は show() するだけで使えます。
#!/usr/bin/env python3

import gi
gi.require_version('Gtk', '4.0')
from gi.repository import Gtk, Gio, GLib

class Win(Gtk.ApplicationWindow):
    def __init__(self, a):
        Gtk.ApplicationWindow.__init__(self, application=a)
        btn = Gtk.Button(label='このアプリについて')
        btn.connect('clicked', self.on_button_clicked)
        self.set_child(btn)
        self.present()

    def on_button_clicked(self, button):
        about = Gtk.AboutDialog(
            transient_for=self,
            modal = True,
            #logo = ***, # アイコン
            program_name = 'OM SYSTEM E-M5',
            version = '3.0.0',
            comments = 'OLYMPUS のカメラ事業は OM SYSTEM に変わりました',
            website = 'http://localhost/',
            website_label = 'localhost', # 指定しなければ「ホームページ」と表示
            copyright = 'Copyright(C) 2022 sasakima-nao',
            # 以下ライセンスボタン、指定しなければボタンは出ない
            license = 'GPL',
            # 以下クレジットボタン、指定しなければボタンは出ない、配列に注意
            authors = ['sasakima-nao'],     # 開発担当
            artists = ['sasakima-nao'],     # アートワーク担当
            documenters = ['sasakima-nao'], # ドキュメント担当
            translator_credits = 'sasakima-nao' # 翻訳担当、ここは str
        )
        about.show()

def app_activate(a):
    w = Win(a)
    w.set_default_size(600, 500) # モーダル化が判るようにリサイズ
    w.present()

app = Gtk.Application()
app.connect('activate', app_activate)
app.run()

GtkAssistant
GtkAssistant は Windows でいうウィザードです。
Python3 のインストーラー等で macOS の人もお馴染だと思います。
対話形式で質問に答えていくことで複雑な処理を簡易に行う一つの手段である。

特化しすぎているためか GTK2 時代から何も変わっていない稀有な Widget です。
部品を GTK4 の掟に従って配置するだけで同じように使えるはずです。

ただ一つ、GtkRadioButton を以前使っていた場合は注意。
GtkCheckButton に書き換えるだけで同じように使えるはずです。
#!/usr/bin/env python3

import gi
gi.require_version('Gtk', '4.0')
from gi.repository import Gtk

class AssistantWin(Gtk.Assistant):
    def __init__(self, a):
        '''
            4 ページあるサンプル
        '''
        Gtk.Assistant.__init__(self, application=a)
        self.connect('cancel', self.on_close)
        self.connect('close', self.on_close)
        self.connect('apply', self.on_apply)
        # Pages
        for title in ['最初に', 'コンテンツ', '進展状況', '完了']:
            vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
            self.append_page(vbox)
            self.set_page_title(vbox, title)
        # 最初に
        label0 = Gtk.Label(label='導入ページ - 進んでください')
        page = self.get_nth_page(0)
        page.append(label0)
        self.set_page_type(page, Gtk.AssistantPageType.INTRO)
        self.set_page_complete(page, True)
        # コンテンツ
        label1 = Gtk.Label(label='コンテンツページ - チェックすると次に進めます\n完了を間違えて押さないで')
        check = Gtk.CheckButton(label='カメラの画質はセンサーサイズではない')
        check.connect('toggled', self.on_checkbutton_toggled)
        page = self.get_nth_page(1)
        page.append(label1)
        page.append(check)
        self.set_page_type(page, Gtk.AssistantPageType.CONTENT)
        # 進展状況
        label2 = Gtk.Label(label='進度ページ - 本当ですか?')
        #r1 = Gtk.RadioButton(label='いいえ', active=True)) # GTK3
        #r2 = Gtk.RadioButton(label='はい', group=r1)
        r1 = Gtk.CheckButton(label='いいえ', active=True)
        r2 = Gtk.CheckButton(label='はい', group=r1)
        r2.connect('toggled', self.on_radiobutton_toggled)
        page = self.get_nth_page(2)
        page.append(label2)
        page.append(r1)
        page.append(r2)
        self.set_page_type(page, Gtk.AssistantPageType.CONTENT)
        self.set_page_complete(page, True)
        # 完了
        self.last_label = Gtk.Label(label='そうですか...')
        page = self.get_nth_page(3)
        page.append(self.last_label)
        self.set_page_type(page, Gtk.AssistantPageType.SUMMARY)
        self.set_page_complete(page, True)
 
    def on_close(self, assistant):
        '''
            delete-event もココにくる
        '''
        app.quit()
 
    def on_apply(self, assistant):
        '''
            コンプリート(CONFIRM)でココにくる
            close シグナルも発生するので main_quit 処理は不要
        '''
        print('やっぱりマイクロフォーサーズが最高!')
 
    def on_checkbutton_toggled(self, widget):
        '''
            次に進むボタンの ON/OFF
        '''
        self.set_page_complete(self.get_nth_page(1), widget.get_active())
 
    def on_radiobutton_toggled(self, widget):
        '''
            CONFIRM, SUMMARY の振り分け
        '''
        if widget.get_active():
            self.last_label.set_text('適用してください')
            self.set_page_type(self.get_nth_page(3), Gtk.AssistantPageType.CONFIRM)
        else:
            self.last_label.set_text('そうですか...')
            self.set_page_type(self.get_nth_page(3), Gtk.AssistantPageType.SUMMARY)

def app_activate(a):
    # GtkWindow ベースなのでウインドウとして使える
    w = AssistantWin(a)
    w.present()

app = Gtk.Application()
app.connect('activate', app_activate)
app.run()
Copyright(C) sasakima-nao All rights reserved 2002 --- 2023.