Paepoi » PyGObject Tips » Gtk(PyGObject) Tips | コンテナ(3.10 以降)
Gtk(PyGObject) Tips | コンテナ(3.10 以降)
# 最終更新日 2019.08.17
2019 年現在の仕様に追記と書き換え。
数が非常に多いので 3.10 以前からあるもの は別ページにしています。
小さな画面でもクライアントエリアのサイズを最大限に大きくできるメリットがある
add したコンテナやウイジェットを切り替えして一画面として利用します。
切り替えのエフェクトを GtkStackTransitionType で指定することも可能。
GtkStackSwitcher 又は GtkStackSidebar と合わせて使います。
サンプルコードはそちらで。
諸々の指定は GtkStack 側にしてこちらは切り替えだけを行なう感じになります。
GNOME の設定画面で使われています。
親ウインドウの show_all() に左右されず視覚効果も指定できて便利。
また選択状態になった、クリックされた等のシグナルも受け取れます。
Windows でいうところの LISTBOX とは全然違い上記が可能なコンテナです。
Android, iOS のスマートフォン設定画面を思い浮かべると理解しやすい。
GtkBox にダミーを入れて振り分ける必要もう無い(実際に筆者はやっていた)
最上部は GtkHeaderBar があるので最下部で振り分けたい時に便利。
メニューのような単一用途ではなくどんな Widget でも入れることができる。
常に表示は不要だけど即呼び出したい補助機能に利用すると便利。
ポップアップコンテンツをメニューに特化しサブメニュー機能を追加したものです。
メニューアイテムは GtkMenuItem ではなく GtkModelButton を使います。
この GtkModelButton の action-name プロパティに GAction を指定しハンドラにします。
アプリケーションで利用の場合 [app] のプリフィクスが必要。
ウインドウで利用の場合 [win] のプリフィクスが必要。
下記でテキストビューアを作ってみましたのでこんな感じに。
CSS3 の FlexBox みたいなのを期待したけど少し違います。
どうやら最小横幅を基準にコンテンツの高さを決定するようです。
GtkIconView のセルと違いコチラはどんな Widget でも入れられる。
それと GtkListBox 同様にコンテンツを選択することもできる。
2019 年現在の仕様に追記と書き換え。
数が非常に多いので 3.10 以前からあるもの は別ページにしています。
GtkHeaderBar
タイトルバーをコンテナ化して Widget を置くことができるようにしたもの小さな画面でもクライアントエリアのサイズを最大限に大きくできるメリットがある
#!/usr/bin/env python3 import sys, gi gi.require_version('Gtk', '3.0') from gi.repository import Gtk class Win(Gtk.ApplicationWindow): ''' set_custom_title で中心 pack_start|end で左右にパッキング ''' def __init__(self, app): Gtk.ApplicationWindow.__init__(self, application=app, title='Py') # 進む戻るボタン back = Gtk.Button.new_from_icon_name('go-previous-symbolic', Gtk.IconSize.MENU) forw = Gtk.Button.new_from_icon_name('go-next-symbolic', Gtk.IconSize.MENU) #back.set_valign(Gtk.Align.CENTER) 不要になった #forw.set_valign(Gtk.Align.CENTER) # # Nautilus の進む戻るボタンの同様に合体させる bfbox = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL) bfbox.get_style_context().add_class('linked') bfbox.pack_start(back, False, False, 0) bfbox.pack_start(forw, False, False, 0) # メニューボタンが必要になったので menu = Gtk.Button.new_from_icon_name('open-menu-symbolic', Gtk.IconSize.MENU) # GtkHeaderBar hbar = Gtk.HeaderBar(show_close_button=True) hbar.pack_start(bfbox) hbar.set_custom_title(Gtk.Label(label='タイトル')) hbar.pack_end(menu) # self self.set_titlebar(hbar) self.resize(300, 100) self.show_all() class App(Gtk.Application): def __init__(self): Gtk.Application.__init__(self) def do_startup(self): Gtk.Application.do_startup(self) Win(self) def do_activate(self): self.props.active_window.present() app = App() app.run(sys.argv)
GtkStack
GtkStack はコンテナです、 GtkNotebook のように単独ページを切り替えできます。add したコンテナやウイジェットを切り替えして一画面として利用します。
切り替えのエフェクトを GtkStackTransitionType で指定することも可能。
GtkStackSwitcher 又は GtkStackSidebar と合わせて使います。
サンプルコードはそちらで。
GtkStackSwitcher
GtkStackSwitcher は GtkStack の各ページを切り替えするものです。諸々の指定は GtkStack 側にしてこちらは切り替えだけを行なう感じになります。
#!/usr/bin/env python3 import sys, gi gi.require_version('Gtk', '3.0') from gi.repository import Gtk class Win(Gtk.ApplicationWindow): ''' スイッチャーをあまり増やせないという欠点がある ''' def __init__(self, app): Gtk.ApplicationWindow.__init__(self, application=app, title='Py') # GtkStack stack = Gtk.Stack(transition_type=Gtk.StackTransitionType.SLIDE_LEFT_RIGHT) # GtkStackSwitcher sw = Gtk.StackSwitcher(stack=stack) # page1 label1 = Gtk.Label(label='かっこいい') stack.add_titled(label1, 'suzuki', 'スズキ') # page2 label2 = Gtk.Label(label='平凡') stack.add_titled(label2, 'yamaha', 'ヤマハ') # page2 label3 = Gtk.Label(label='カワサキか...') stack.add_titled(label3, 'kawasaki', 'カワサキ') # GtkHeaderBar hbar = Gtk.HeaderBar(show_close_button=True) hbar.set_custom_title(sw) # self self.set_titlebar(hbar) self.add(stack) self.show_all() class App(Gtk.Application): def __init__(self): Gtk.Application.__init__(self) def do_startup(self): Gtk.Application.do_startup(self) Win(self) def do_activate(self): self.props.active_window.present() app = App() app.run(sys.argv)
GtkStackSidebar
3.16 で追加された GtkStackSwitcher の派生、サイドバーバージョンです。GNOME の設定画面で使われています。
#!/usr/bin/env python3 import sys, gi gi.require_version('Gtk', '3.0') from gi.repository import Gtk class Win(Gtk.ApplicationWindow): ''' ちょっとサイドバーの高さが大きいような CROSSFADE にすると GNOME 設定と同じになる ''' def __init__(self, app): Gtk.ApplicationWindow.__init__(self, application=app, title='Py') # GtkStack stack = Gtk.Stack(transition_type=Gtk.StackTransitionType.CROSSFADE) # GtkStackSwitcher ss = Gtk.StackSidebar(stack=stack) # page1 label1 = Gtk.Label(label='かっこいい') stack.add_titled(label1, 'suzuki', 'スズキ') # page2 label2 = Gtk.Label(label='平凡') stack.add_titled(label2, 'yamaha', 'ヤマハ') # page2 label3 = Gtk.Label(label='カワサキか...') stack.add_titled(label3, 'kawasaki', 'カワサキ') # Paned paned = Gtk.Paned(orientation=Gtk.Orientation.HORIZONTAL) paned.add1(ss) paned.add2(stack) # self self.add(paned) self.resize(300, 200) self.show_all() class App(Gtk.Application): def __init__(self): Gtk.Application.__init__(self) def do_startup(self): Gtk.Application.do_startup(self) Win(self) def do_activate(self): self.props.active_window.present() app = App() app.run(sys.argv)
GtkRevealer
GtkRevealer は Widget の表示非表示をアニメーションするコンテナです。親ウインドウの show_all() に左右されず視覚効果も指定できて便利。
#!/usr/bin/env python3 import sys, gi gi.require_version('Gtk', '3.0') from gi.repository import Gtk TYPES = dict( CrossFade= Gtk.RevealerTransitionType.CROSSFADE, SlideRight=Gtk.RevealerTransitionType.SLIDE_RIGHT, SlideLeft= Gtk.RevealerTransitionType.SLIDE_LEFT, SlideUp= Gtk.RevealerTransitionType.SLIDE_UP, SlideDown= Gtk.RevealerTransitionType.SLIDE_DOWN ) class Win(Gtk.ApplicationWindow): ''' valign, halign を調節しないと上手く動かない デフォルトはどちらも GTK_ALIGN_FILL このサンプルでは一旦 CROSSFADE に変更するとたまに動かなくなる場合あり 通常は途中でアニメーション効果を変えることはないので問題無いけど ''' def __init__(self, app): Gtk.ApplicationWindow.__init__(self, application=app, title='Py', resizable=False) # button button = Gtk.Button(label='Click') button.connect('clicked', self.on_button_clicked) # GtkReveal self.revealer = Gtk.Revealer( transition_duration=2000, transition_type=Gtk.RevealerTransitionType.CROSSFADE, halign = Gtk.Align.START, valign = Gtk.Align.START) # このラベルをアニメーションさせる label = Gtk.Label(label='<span bgcolor="#0ff" size="x-large">Hello\nWorld</span>', use_markup=True) self.revealer.add(label) # パッキング hbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=10) root = None for key, value in TYPES.items(): r = Gtk.RadioButton(label=key, group=root) if not root: root = r r.connect('toggled', self.on_radio_toggled, value) hbox.pack_start(r, False, False, 0) hbox.pack_start(button, False, False, 0) hbox.pack_start(self.revealer, False, False, 0) self.add(hbox) self.show_all() def on_radio_toggled(self, button, user_data): # align の調節 if user_data == Gtk.RevealerTransitionType.SLIDE_UP: self.revealer.props.valign = Gtk.Align.END elif user_data == Gtk.RevealerTransitionType.SLIDE_LEFT: self.revealer.props.halign = Gtk.Align.END else: self.revealer.props.valign = Gtk.Align.START self.revealer.props.halign = Gtk.Align.START # 反映 self.revealer.set_transition_type(user_data) def on_button_clicked(self, widget): # アニメーションさせる self.revealer.set_reveal_child(not self.revealer.props.reveal_child) class App(Gtk.Application): def __init__(self): Gtk.Application.__init__(self) def do_startup(self): Gtk.Application.do_startup(self) Win(self) def do_activate(self): self.props.active_window.present() app = App() app.run(sys.argv)
GtkListBox
GtkListBox は GtkBox をリストビューのように選択可能にするコンテナです。また選択状態になった、クリックされた等のシグナルも受け取れます。
Windows でいうところの LISTBOX とは全然違い上記が可能なコンテナです。
Android, iOS のスマートフォン設定画面を思い浮かべると理解しやすい。
#!/usr/bin/env python3 import sys, gi gi.require_version('Gtk', '3.0') from gi.repository import Gtk DATA = ['YAMAHA', 'HONDA', 'KAWASAKI', 'SUZUKI'] class Win(Gtk.ApplicationWindow): ''' どう動くかだけのサンプルコード 以前は GtkCssProvider の例も書いていたけど繁栄されなくなったので削除 ''' def __init__(self, app): Gtk.ApplicationWindow.__init__(self, application=app, title='Py', resizable=False) # ListBox listbox = Gtk.ListBox() listbox.connect('row-activated', self.on_listbox_row_activated) for i, data in enumerate(DATA): hbox = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL) hbox.pack_start(Gtk.Label(label=data), False, False, 0) hbox.pack_end(Gtk.Switch(), False, False, 0) listbox.insert(hbox, i) # Selection combobox = Gtk.ComboBoxText() combobox.append_text('Gtk.SelectionMode.NONE') combobox.append_text('Gtk.SelectionMode.SINGLE') combobox.set_active(listbox.get_selection_mode()) combobox.connect('changed', self.on_combobox_changed, listbox) # CheckButton checkbutton = Gtk.CheckButton(label='Single Click Mode') checkbutton.set_active(listbox.get_activate_on_single_click()) checkbutton.connect('clicked', self.on_checkbutton_clicked, listbox) # Pack vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=20) vbox.pack_start(listbox, False, False, 0) vbox.pack_start(combobox, False, False, 0) vbox.pack_start(checkbutton, False, False, 0) self.add(vbox) self.set_border_width(10) self.show_all() def on_listbox_row_activated(self, listbox, row): s = DATA[row.get_index()] self.props.title = f'{s} を選択' def on_combobox_changed(self, combo, listbox): b = combo.get_active() == 1 listbox.set_selection_mode(b) def on_checkbutton_clicked(self, button, listbox): b = button.get_active() listbox.set_activate_on_single_click(b) class App(Gtk.Application): def __init__(self): Gtk.Application.__init__(self) def do_startup(self): Gtk.Application.do_startup(self) Win(self) def do_activate(self): self.props.active_window.present() app = App() app.run(sys.argv)
GtkActionBar
GtkActionBar は Widget 配置を左右及び中心に綺麗に振り分けるコンテナ。GtkBox にダミーを入れて振り分ける必要もう無い(実際に筆者はやっていた)
最上部は GtkHeaderBar があるので最下部で振り分けたい時に便利。
#!/usr/bin/env python3 import sys, gi gi.require_version('Gtk', '3.0') from gi.repository import Gtk class Win(Gtk.ApplicationWindow): ''' 関数は GtkHeaderBar と同じ ''' def __init__(self, app): Gtk.ApplicationWindow.__init__(self, application=app, title='Py') # クライアントエリア(ダミー) area = Gtk.DrawingArea() # 中心 c1 = Gtk.Button.new_from_icon_name('go-previous-symbolic', Gtk.IconSize.MENU) c2 = Gtk.Button.new_from_icon_name('camera-photo-symbolic', Gtk.IconSize.MENU) c3 = Gtk.Button.new_from_icon_name('go-next-symbolic', Gtk.IconSize.MENU) hbox = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL) hbox.pack_start(c1, False, False, 0) hbox.pack_start(c2, False, False, 0) hbox.pack_start(c3, False, False, 0) hbox.get_style_context().add_class('linked') # 左右 left = Gtk.Button.new_from_icon_name('user-trash-symbolic', Gtk.IconSize.MENU) right = Gtk.Button.new_from_icon_name('open-menu-symbolic', Gtk.IconSize.MENU) # GtkActionBar bar = Gtk.ActionBar() bar.pack_start(left) bar.set_center_widget(hbox) bar.pack_end(right) # pack vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL) vbox.pack_start(area, True, True, 0) vbox.pack_start(bar, False, False, 0) # self self.add(vbox) self.show_all() class App(Gtk.Application): def __init__(self): Gtk.Application.__init__(self) def do_startup(self): Gtk.Application.do_startup(self) Win(self) def do_activate(self): self.props.active_window.present() app = App() app.run(sys.argv)
GtkPopover
GtkPopover はコンテンツをポップアップする Widget です。メニューのような単一用途ではなくどんな Widget でも入れることができる。
常に表示は不要だけど即呼び出したい補助機能に利用すると便利。
#!/usr/bin/env python3 import sys, gi gi.require_version('Gtk', '3.0') from gi.repository import Gtk class Win(Gtk.ApplicationWindow): ''' 関数は GtkHeaderBar と同じ ''' def __init__(self, app): Gtk.ApplicationWindow.__init__(self, application=app, title='Py') # クライアントエリア(ダミー) area = Gtk.DrawingArea() # このボタンでポップアップさせる button = Gtk.Button(label='アイテム') button.connect('clicked', self.on_option_button_clicked) # GtkActionBar bar = Gtk.ActionBar() bar.pack_end(button) # ポップアップコンテンツ c1 = Gtk.CheckButton(label='ムチを使う') c2 = Gtk.CheckButton(label='ロウソクを使う') c3 = Gtk.CheckButton(label='三角木馬を使う') popbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL) popbox.pack_start(c1, False, False, 0) popbox.pack_start(c2, False, False, 0) popbox.pack_start(c3, False, False, 0) # GtkPopover self.pop = Gtk.Popover(relative_to=button, child=popbox) # パッキング vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL) vbox.pack_start(area, True, True, 0) vbox.pack_start(bar, False, False, 0) self.add(vbox) self.show_all() def on_option_button_clicked(self, widget): ''' コンテンツ Widget は非表示になっている 一括表示でいいなら show_all でいい ''' self.pop.show_all() class App(Gtk.Application): def __init__(self): Gtk.Application.__init__(self) def do_startup(self): Gtk.Application.do_startup(self) Win(self) def do_activate(self): self.props.active_window.present() app = App() app.run(sys.argv)
GtkPopoverMenu
GtkPopoverMenu は GtkPopover のサブクラス。ポップアップコンテンツをメニューに特化しサブメニュー機能を追加したものです。
メニューアイテムは GtkMenuItem ではなく GtkModelButton を使います。
この GtkModelButton の action-name プロパティに GAction を指定しハンドラにします。
アプリケーションで利用の場合 [app] のプリフィクスが必要。
ウインドウで利用の場合 [win] のプリフィクスが必要。
下記でテキストビューアを作ってみましたのでこんな感じに。
#!/usr/bin/env python3 import sys, gi gi.require_version('Gtk', '3.0') from gi.repository import Gtk, Gio class Win(Gtk.ApplicationWindow): ''' サブメニュー不要なら GtkPopover でよかったりする サブメニューについては別ページを後で作る予定 ''' def __init__(self, app): Gtk.ApplicationWindow.__init__(self, application=app, title='Py') # このボタンでポップアップさせる button = Gtk.Button.new_from_icon_name('open-menu-symbolic', Gtk.IconSize.MENU) button.connect('clicked', self.on_option_button_clicked) # GtkModelButton menu_open = Gtk.ModelButton(active=True, action_name='app.new_file_action', text='開く(_O)', use_markup=True) menu_new = Gtk.ModelButton(active=True, action_name='app.new_window_action', text='新しいウインドウ(_N)', use_markup=True) menu_quit = Gtk.ModelButton(active=True, action_name='app.quit_action', text='終了(_Q)', use_markup=True) # ポップアップコンテンツ(メニュー) vbox = Gtk.Box(visible=True, margin=10, orientation=Gtk.Orientation.VERTICAL) vbox.pack_start(menu_open, False, False, 0) vbox.pack_start(menu_new, False, False, 0) vbox.pack_start(Gtk.Separator(orientation=Gtk.Orientation.HORIZONTAL), False, False, 0) vbox.pack_start(menu_quit, False, False, 0) # GtkPopoverMenu self.popovermenu = Gtk.PopoverMenu(relative_to=button, child=vbox) # GtkHeaderBar hbar = Gtk.HeaderBar(show_close_button=True) hbar.pack_end(button) # GtkTextView and GtkScrolledWindow self.view = Gtk.TextView() sw = Gtk.ScrolledWindow(child=self.view) # self self.set_titlebar(hbar) self.add(sw) self.show_all() def on_option_button_clicked(self, widget): self.popovermenu.show_all() def open_dialog(self): dlg = Gtk.FileChooserNative( title='Open', transient_for=self, action=Gtk.FileChooserAction.OPEN) if dlg.run() == Gtk.ResponseType.ACCEPT: #self.set_uri(dlg.get_uri()) with open(dlg.get_filename()) as f: s = f.read() self.view.get_buffer().set_text(s) dlg.destroy() class App(Gtk.Application): def __init__(self): Gtk.Application.__init__(self) def do_startup(self): Gtk.Application.do_startup(self) # GAction 作成 new_window_action = Gio.SimpleAction(name='new_window_action') new_file_action = Gio.SimpleAction(name='new_file_action') quit_action = Gio.SimpleAction(name='quit_action') # 追加 self.add_action(new_window_action) self.add_action(new_file_action) self.add_action(quit_action) # アクセラレーターの指定 self.set_accels_for_action('app.new_window_action', ['<Control>N']) self.set_accels_for_action('app.new_file_action', ['<Control>O']) self.set_accels_for_action('app.quit_action', ['<Control>Q']) # シグナル new_window_action.connect('activate', self.on_new_window_action) new_file_action.connect('activate', self.on_new_file_action) quit_action.connect('activate', self.on_quit_action) # Window を作る Win(self) def on_new_file_action(self, action, parameter): self.props.active_window.open_dialog() def on_new_window_action(self, action, parameter): Win(self) def on_quit_action(self, action, parameter): self.quit() def do_activate(self): self.props.active_window.present() app = App() app.run(sys.argv)
GtkFlowBox
GtkFlowBox は Widget を幅に合わせて自動的に改行するコンテナ。CSS3 の FlexBox みたいなのを期待したけど少し違います。
どうやら最小横幅を基準にコンテンツの高さを決定するようです。
GtkIconView のセルと違いコチラはどんな Widget でも入れられる。
それと GtkListBox 同様にコンテンツを選択することもできる。
#!/usr/bin/env python3 import sys, gi gi.require_version('Gtk', '3.0') from gi.repository import Gtk, Gio, GdkPixbuf # 画像が沢山あるディレクトリに書き換えしてください PICTURES = '/home/sasakima-nao/www/tips/pygobject/container1' class Win(Gtk.ApplicationWindow): ''' サムネイルされた後にウインドウのサイズを変更すると動作が解る valign の指定は必須だったけど不要になったみたい ''' def __init__(self, app): Gtk.ApplicationWindow.__init__(self, application=app, title='Py') # GtkFlowBox flowbox = Gtk.FlowBox()#valign=Gtk.Align.START) # 指定ディレクトリのファイルを探す d = Gio.file_new_for_path(PICTURES) enum = d.enumerate_children(Gio.FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE, 0) for info in enum: content_type = info.get_content_type() if content_type == 'image/jpeg' or content_type == 'image/png': fullpath = f'{PICTURES}/{info.get_name()}' pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_scale(fullpath, 100, 100, True) image = Gtk.Image(pixbuf=pixbuf) flowbox.add(image) scroll = Gtk.ScrolledWindow(child=flowbox) # self self.add(scroll) self.resize(400, 300) self.show_all() class App(Gtk.Application): def __init__(self): Gtk.Application.__init__(self) def do_startup(self): Gtk.Application.do_startup(self) Win(self) def do_activate(self): self.props.active_window.present() app = App() app.run(sys.argv)
Copyright(C) sasakima-nao All rights reserved 2002 --- 2024.