Paepoi » GTK4(Python) Tips » GTK4(Python) Tips | GtkApplicationWindow
GTK4(Python) Tips | GtkApplicationWindow
# 最終更新日 2022.04.24
GMainLoop でも一応動くようですがどこにも解説されておらず非推奨のようです。
GUI アプリを作る場合は必ず GtkApplication を使う必要があります。
ウインドウは GtkApplicationWindow という GtkWindow のサブクラスを使います。
application プロパティは GtkWindow 側にありますがコチラを使えということみたいです。
GTK4 では GtkWindow 自体は継承して使う Interface として提供されている感じです。
ウインドウ側にて close-request シグナルで破棄を行う処理も必要もありません。
閉じるボタンを押す(close-request 発生)で GtkApplication と切り離され自動的に破棄されます。
関数も GTK3 の時といくつか変わっているので以下に基本的サンプルを。
親コンテナ側で指定していたパッキング情報は子 Widget のプロパティに移管されています。
ウインドウサイズを変更しないアプリなら単純になったとも言えます。
ただしそれだけではなく CSS にて透過させる処理が必要になりました。
デスクトップマスコット等の背景透過画像を表示させるには以下のようにします。
又タイトルバーが無い状態でもマウスで動かせるようにする処理も入れています。
GtkApplicationWindow
GTK4 になりメインループを開始する gtk_main 関数が廃止されました。GMainLoop でも一応動くようですがどこにも解説されておらず非推奨のようです。
GUI アプリを作る場合は必ず GtkApplication を使う必要があります。
ウインドウは GtkApplicationWindow という GtkWindow のサブクラスを使います。
application プロパティは GtkWindow 側にありますがコチラを使えということみたいです。
GTK4 では GtkWindow 自体は継承して使う Interface として提供されている感じです。
ウインドウ側にて close-request シグナルで破棄を行う処理も必要もありません。
閉じるボタンを押す(close-request 発生)で GtkApplication と切り離され自動的に破棄されます。
関数も GTK3 の時といくつか変わっているので以下に基本的サンプルを。
#!/usr/bin/env python3 import gi, sys gi.require_version('Gtk', '4.0') from gi.repository import Gtk, Gio class Win(Gtk.ApplicationWindow): ''' GTK4 でウインドウを作るサンプル ''' def __init__(self, app): Gtk.ApplicationWindow.__init__(self, title='Title', application=app) self.button = Gtk.Button(label='ハローワールド') self.button.connect('clicked', self.on_button_connect) # add は set_child に変わりました self.set_child(self.button) # resize は set_default_size に変わりました self.set_default_size(320, 240) # show_all は無くなり widget はデフォルトで表示に変わりました # delete-event は close-request に変わりましたが処理不要です def on_button_connect(self, button): ''' ボタンを押す毎にハローワールドが増える ''' button.props.label += '\nハローワールド' class App(Gtk.Application): ''' GTK4 では gtk_main は使えません ''' def __init__(self): ''' 初期化、引数を処理しないならコレだけでいい ''' Gtk.Application.__init__(self) def do_startup(self): ''' 最初のウインドウは基本的にココで作ります ''' Gtk.Application.do_startup(self) # startup をオーバーライドする場合必須 Win(self) def do_activate(self): ''' アプリケーションがアクティブになったらココにきます HANDLES_COMMAND_LINE 指定の場合は無視されるので注意 ''' self.props.active_window.present() # ウインドウのアクティブ化 app = App() app.run(sys.argv)
パッキング
GtkBox を使ったパッキングは GTK4 で完全に変更されました。親コンテナ側で指定していたパッキング情報は子 Widget のプロパティに移管されています。
pack_start(child, expand, fill, padding) # 上記パッキング情報は子 Widget の以下プロパティで指定 expand: hexpand|vexpand fill: halign|valign padding: margin-top|margin-botom|margin-start|margin-end何をどう指定すればどういう動作になるのかは御自身で色々試してみてください。
ウインドウサイズを変更しないアプリなら単純になったとも言えます。
#!/usr/bin/env python3 import gi gi.require_version('Gtk', '4.0') from gi.repository import Gtk class Win(Gtk.ApplicationWindow): ''' 縦: Gtk.Orientation.VERTICAL 横: Gtk.Orientation.HORIZONTAL ''' def __init__(self, app): ''' リサイズするとボタンのほうだけ大きくなるサンプル ''' Gtk.ApplicationWindow.__init__(self, title='Title', application=app) # 縦型コンテナの作成 vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL) # ボタン button = Gtk.Button(label='ボタン', vexpand=True) button.connect('clicked', self.on_button_clicked) # 一行エディットは後で参照するので self にくっつける self.entry = Gtk.Entry(editable=False, text='ボタンを押せ') # pack_start pack_end は append に変わりました vbox.append(button) vbox.append(self.entry) self.set_child(vbox) def on_button_clicked(self, widget): self.entry.set_text('Hello World') 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)
枠無しウインドウ
枠無しウインドウは GTK3 同様に decorated プロパティを使います。ただしそれだけではなく CSS にて透過させる処理が必要になりました。
デスクトップマスコット等の背景透過画像を表示させるには以下のようにします。
又タイトルバーが無い状態でもマウスで動かせるようにする処理も入れています。
#!/usr/bin/env python3 import gi, sys gi.require_version('Gtk', '4.0') from gi.repository import Gtk, GdkPixbuf, Gdk, Gio # Picture File PNGFILE = 'test.png' # CSS APP_CSS = 'window { background-color: rgba(255, 255, 255, 0); }'.encode('utf-8') class Win(Gtk.ApplicationWindow): ''' GTK4: No Decorated Window ''' def __init__(self, app): try: Gtk.ApplicationWindow.__init__(self, application=app, decorated=False) # Transparent provider = Gtk.CssProvider() provider.load_from_data(APP_CSS) context = self.get_style_context() context.add_provider_for_display( self.get_display(), provider, Gtk.STYLE_PROVIDER_PRIORITY_USER) # Mouse Move Signal click = Gtk.GestureClick() click.connect('pressed', self.on_gesture_click_pressed) self.add_controller(click) # Draw self.pixbuf = GdkPixbuf.Pixbuf.new_from_file(PNGFILE) da = Gtk.DrawingArea() da.set_draw_func(self.da_draw_func) self.set_child(da) # Resize self.set_default_size(self.pixbuf.get_width(), self.pixbuf.get_height()) except Exception as e: print(e, file=sys.stderr) app.quit() def da_draw_func(self, da, cr, width, height): Gdk.cairo_set_source_pixbuf(cr, self.pixbuf, 0, 0) cr.paint() def on_gesture_click_pressed(self, click, n_press, x, y): ''' GTK4: gtk_window_begin_move_drag ''' button = click.get_button() toplevel = self.get_surface() # GdkToplevel display = self.get_display() seat = display.get_default_seat() device = seat.get_pointer() s, win_x, win_y = device.get_surface_at_position() #print(f'{win_x}, {win_y}') time = device.get_timestamp() toplevel.begin_move(device, button, win_x, win_y, time) class App(Gtk.Application): def __init__(self): Gtk.Application.__init__(self) def do_startup(self): Gtk.Application.do_startup(self) # set Ctrl+Q self.set_accels_for_action('app.quit_action', ['<Control>Q']) quit_action = Gio.SimpleAction(name='quit_action') self.add_action(quit_action) quit_action.connect('activate', lambda a, p: self.quit()) # 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 --- 2023.