Programming」カテゴリーアーカイブ

No frame GtkWindow

Gtk.Window は Gtk.WindowType.POPUP 指定で作成すると枠無しになる。
でもタスクバーには現れないしフォーカスも持てない。

結局アクセサリとしてしか使い道が無いのかなと思っていた。
そういえば google-chrome はどうやって枠を無くしているんだろう?
気になったので調べてみた。

GtkWindow のプロパティをよく見ると decorated がある。
False にすると枠無しにできるっぽい、ならば実験。

#!/usr/bin/env python
#-*- coding:utf-8 -*-

from gi.repository import Gtk, Gdk

class Win(Gtk.Window):
    def __init__(self):
        """
            Switch the frame
        """
        Gtk.Window.__init__(self)
        self.connect("delete-event", Gtk.main_quit)
        # Mouse Click
        self.set_events(Gdk.EventMask.BUTTON_PRESS_MASK)
        self.connect("button-press-event", self.on_button_press_event)
        self.show_all()

    def on_button_press_event(self, widget, event, data=None):
        """
            Double-click on each switch.
            Have the focus.
            Moving without the frame.
        """
        if event.type == Gdk.EventType.BUTTON_PRESS:
            self.begin_move_drag( event.button, event.x_root, event.y_root, event.time)
        elif event.type == Gdk.EventType.DOUBLE_BUTTON_PRESS:
            self.props.decorated = self.props.decorated == False

Win()
Gtk.main()

ダブルクリックで切り替わる。
ついでにクライアントエリアをマウスで掴んで移動できるようにしてみた。
よししっかり切り替わるしフォーカスも持てるし移動もできる。

これで google-chrome みたいなウインドウが作れるね。

ついでに気がついた、 GTK+ 3.6 からみたいだけど
GDK_DOUBLE_BUTTON_PRESS, GDK_TRIPLE_BUTTON_PRESS
というエイリアスが追加されていたのを知った。

Python のアトリビュートは数値で始めることができないからって
Gdk.EventType._2BUTTON_PRESS って何か変だったし。

ということで覚書ページも更新っと。
ウインドウを作る – L’Isola di Niente

Gtk+ Container

GTK+ コンテナの使い方をまとめるのに一週間掛かった。
つかコンテナってこんなにいっぱいあったのか。
絶対に必要になるので GtkScrolledWindow なんかも追加しているけど。

コンテナ – L’Isola di Niente
オーナメント – L’Isola di Niente

GtkLayout とかGtkAlignment とか何に使うかよくワカラン。
もっと面白いサンプルコードにしたいけど思いつかない。

GtkOverlay も面白そうなのに使い方サンプルが見当たらない。
Pastebin でなんとか見つけた。
[C] GtkOverlay test – Pastebin.com

overlay

add_overlay で指定した Widget が add した Widget に被さるのね。
DrawinArea じゃ長くなりすぎだからサンプルコードは Label にしたけど。

海外を探しても devhelp を見れば解るようなことしか見つからなくて困る。
コンテナは大抵は GtkBox のみで事足りるわけなんですけど。

「おぉこんなことができたんだ!」
となるのがアプリを作る最大の楽しみなのにと思う私である。

以上チップスページ追加のお知らせでした。

GtkTable to GtkGrid

GtkTable は GTK+3.2 で廃止になっていた。
当面は使えるみたい、だけどいつ無くなるか解らないって怖い。

seemex は GTK+3.0 の時に作ったし公開終了したからからセフセフ。
GtkVBox や GtkHBox という非推奨も使っていたりするがもうシラネ。

しかし GtkGrid を代替で使えということだけど全然引数が違っている。

まず GtkTable は左上が頂点で絶対値にて四角形を指定だった。
GtkGrid は幅と高さを指定、これでは全置換できないので全書き換えになる。

それより attach(…) の引数から Fill や padding を指定することができない。
それらしき関数も見当たらない、ヘルプもチト解りにくい。
どうやらそれらは GtkWidget 側のプロパティで指定するようだ。
こんなプロパティがあったことすら知らなかったが。

GtkWidget.html#GtkWidget–expand

リサイズ時に引き伸ばすなら expand Property を True にする。
padding Property は無いけど margin Property でイケそうだ。

Gtk+ はコンテナ側にこの指定があるのが少し不便だった。
どう考えても子ウイジェット側にあったほうがウイジェットの入れ替えやパレントの変更がスムース、実際 Windows の WPF はそうなっている。
やはり代えたいのだろうと憶測。
GtkBox なんかも今後そうなっていくかもしれない。
reparent した後に set_child_packing 必須って意味わかんなかったし。

さて、これが解ったところで。

いつものように画像検索から面白そうなことをやっていそうな人を探す。
日本人には最初から期待していない、おかげでリンク先が海外ばかりなこのブログ。
しかし今回はイマイチなものしか見当たらない、
教科書的というか、実用的な解説をしている人が見当たらないというか。

しかたがないので自力でやって確認してみる。
seemex の編集部分に使っていた GtkTable を抜いて GtkGrid に書き換えてみた。
非推奨関数とテキトーだったところは書き換え、外国人が解るようにヘタクソな英語で。

#!/usr/bin/env python
#-*- coding:utf-8 -*-

from gi.repository import Gtk

EDITOR_LABELS = ("Name", "Key", "URL", "Query")
EDITOR_CHECK = "Is POST"
ENTER_BUTTON = "_Edit"

class TableWin(Gtk.Window):
    def __init__(self):
        """
            GtkTable has been deprecated. Use GtkGrid instead.
            table.attach (  child,
                            left_attach,
                            right_attach,
                            top_attach,
                            bottom_attach,
                            xoptions=Gtk.AttachOptions.EXPAND,
                            yoptions=Gtk.AttachOptions.EXPAND,
                            xpadding=0,
                            ypadding=0 )
        """
        Gtk.Window.__init__(self)
        self.set_title("Table")
        self.connect("delete-event", Gtk.main_quit)
        # GtkTable
        table = Gtk.Table(5, 3)
        # Edit Widget
        self.edit_name = Gtk.Entry()
        table.attach(self.edit_name, 1, 2, 0, 1)
        self.edit_key = Gtk.Entry()
        table.attach(self.edit_key, 3, 4, 0, 1, Gtk.AttachOptions.FILL)
        self.edit_url = Gtk.Entry()
        table.attach(self.edit_url, 1, 5, 1, 2)
        self.edit_query = Gtk.Entry()
        table.attach(self.edit_query, 1, 4, 2, 3)
        self.check_post = Gtk.CheckButton.new_with_label(EDITOR_CHECK)
        table.attach(self.check_post, 4, 5, 2, 3, Gtk.AttachOptions.FILL)
        # Label
        labels = []
        for label in EDITOR_LABELS:
            labels.append(Gtk.Label(label))
        table.attach(labels[0], 0, 1, 0, 1, Gtk.AttachOptions.FILL)
        table.attach(labels[1], 2, 3, 0, 1, Gtk.AttachOptions.FILL, xpadding=10)
        table.attach(labels[2], 0, 1, 1, 2, Gtk.AttachOptions.FILL)
        table.attach(labels[3], 0, 1, 2, 3, Gtk.AttachOptions.FILL)
        # Button
        self.button = Gtk.Button.new_with_mnemonic(ENTER_BUTTON)
        table.attach(self.button, 4, 5, 0, 1, Gtk.AttachOptions.FILL)
        #
        self.add(table)
        self.show_all()

TableWin()
Gtk.main()
#!/usr/bin/env python
#-*- coding:utf-8 -*-

from gi.repository import Gtk

EDITOR_LABELS = ("Name", "Key", "URL", "Query")
EDITOR_CHECK = "Is POST"
ENTER_BUTTON = "_Edit"

class GridWin(Gtk.Window):
    def __init__(self):
        """
            GtkGrid
            grid.attach(child, left, top, width, height)
            or
            grid.attach_next_to(child, sibling, GtkPositionType, width, height)
            
            GtkAttachOptions to GtkWidget 'expand' Property
            Padding to GtkWidget 'margin' Property
        """
        Gtk.Window.__init__(self)
        self.set_title("Grid")
        self.connect("delete-event", Gtk.main_quit)
        # GtkGrid
        grid = Gtk.Grid.new()
        # Edit Widget
        self.edit_name = Gtk.Entry()
        # Substitute Gtk.AttachOptions.EXPAND
        self.edit_name.props.expand = True
        grid.attach(self.edit_name, 1, 0, 1, 1)
        self.edit_key = Gtk.Entry()
        grid.attach(self.edit_key, 3, 0, 1, 1)
        self.edit_url = Gtk.Entry()
        self.edit_url.props.expand = True
        grid.attach(self.edit_url, 1, 1, 5, 1)
        self.edit_query = Gtk.Entry()
        self.edit_query.props.expand = True
        grid.attach(self.edit_query, 1, 2, 3, 1)
        self.check_post = Gtk.CheckButton.new_with_label(EDITOR_CHECK)
        grid.attach(self.check_post, 4, 2, 1, 1)
        # Label
        labels = []
        for label in EDITOR_LABELS:
            labels.append(Gtk.Label(label))
        grid.attach(labels[0], 0, 0, 1, 1)
        grid.attach(labels[1], 2, 0, 1, 1)
        grid.attach(labels[2], 0, 1, 1, 1)
        grid.attach(labels[3], 0, 2, 1, 1)
        # Substitute xpadding
        labels[1].props.margin_left = 10
        labels[1].props.margin_right = 10
        # Button
        self.button = Gtk.Button.new_with_mnemonic(ENTER_BUTTON)
        grid.attach(self.button, 4, 0, 1, 1)
        self.add(grid)
        self.show_all()

GridWin()
Gtk.main()

gtkgrid

よしリサイズでの引き伸ばされかたも完全に同じだ。
GtkGrid はデフォルトが Fill なので引き伸ばしたい Widget の expand を True にすればいいということなのね。

つか、やっぱりコードは似ているようで全然違うわw
子ウイジェット側で引き延ばし指定を行うほうがやはり理解しやすいなと思う。
たとえ記述量が多くなっても理解しやすいほうがいいなと。

GtkButtonBox

GtkButtonBox って何に使うか解らなかった。
検索したら面白そうなのを見つけた。

PHP-GTK по-русски: Группирование кнопок

ドメインは jp だけど思いっきりロシア語なのは何故だろう。
モチロン読めないけど PHP-GTK コードはアルファベットなので解る。
PHP-GTK って使っている人がいるんだな…

Standard Enumerations

GTK_BUTTONBOX_DEFAULT_STYLE って定義は無いんだけど。
デフォルトを調べたら EDGE だった、EDGE とたしかに見た目は同じだね。

edge

つかこのコードだと hbox_main っていらなくね?
それと GtkHButtonBox も GtkHBox 同様に非推奨なのね。
Gtk.ButtonBox.new(Gtk.Orientation.HORIZONTAL) でいいみたい。

その辺りを考慮して PyGI で書き換えるとこんな感じか。

#!/usr/bin/env python
#-*- coding:utf-8 -*-

from gi.repository import Gtk

window = Gtk.Window()
window.set_position(Gtk.WindowPosition.CENTER)
window.set_size_request(450, -1)
window.connect('destroy', Gtk.main_quit)
 
vbox = Gtk.Box.new(Gtk.Orientation.VERTICAL, 0)
 
dic = {
    'Spread': Gtk.ButtonBoxStyle.SPREAD,
    'Edge(Default)': Gtk.ButtonBoxStyle.EDGE,
    'Start': Gtk.ButtonBoxStyle.START,
    'End': Gtk.ButtonBoxStyle.END,
    'Center': Gtk.ButtonBoxStyle.CENTER }
 
for key, value in dic.iteritems():
    box = Gtk.ButtonBox.new(Gtk.Orientation.HORIZONTAL)
    box.set_layout(value)
    box.add(Gtk.Button.new_from_stock(Gtk.STOCK_YES))
    box.add(Gtk.Button.new_from_stock(Gtk.STOCK_CANCEL))
    box.add(Gtk.Button.new_from_stock(Gtk.STOCK_NO))
    frame = Gtk.Frame.new(key)
    frame.set_shadow_type(Gtk.ShadowType.IN)
    frame.add(box)
    vbox.pack_start(frame, False, False, 5)
 
window.add(vbox)
window.show_all()
Gtk.main()

gtk_button_box

右寄せとか完全等間隔とか色々指定できるんだね。
なるほど、使い道は微妙だけどこんなコンテナもあるということで。

DynamicXamlReader

もう一つ IronPython ネタ。

ユーザーインターフェース付きのスクリプトをXAML×IronPythonを使って作る(2): 品質向上と効率改善の組み木パズル

え。。。。。

dynamic.xaml

<Window
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="DynamicXamlReader" Height="200" Width="300" Loaded="on_loaded">
    <DockPanel>
		<Menu DockPanel.Dock="Top">
		    <MenuItem Header="_File">
		        <MenuItem Header="_Open" Click="on_open" InputGestureText="Ctrl+O" />
		        <Separator />
		        <MenuItem Header="_Close" Click="on_close" InputGestureText="Ctrl+Q" />
		    </MenuItem>
		</Menu>
		<TextBox Name="textbox" />
	</DockPanel>
</Window>

dynamic.py

# -*- coding: UTF-8 -*-

import wpf

from System import *
from System.IO import *
from System.Windows import *
from Microsoft.Win32 import OpenFileDialog

class DynamicXamlReaderTest(Window):
    def __init__(self):
        # DynamicXamlReader Set
        wpf.LoadComponent(self, "dynamic.xaml")

    def on_loaded(self, sender, e):
        self.textbox.Focus()

    def on_open(self, sender, e):
        dlg = OpenFileDialog()
        if dlg.ShowDialog():
            # C# using(var sw = new StreamReader(dlg.FileName)){}
            with StreamReader(dlg.FileName) as sw:
                self.textbox.Text = sw.ReadToEnd()

    def on_close(self, sender, e):
        self.Close()

if __name__ == "__main__":
    Application().Run(DynamicXamlReaderTest())

dynamic

動的に XAML を合体できるんだ、知らなかったよ。
exe を作って一つにまとめるわけじゃないけどコレは便利。

と思ったけど…
LoadComponent は第一引数のオブジェクトと XAML を合体するので私の大好きなメニューだけ XAML みたいな方法が使えないよ。
ヒアドキュメントにもできないみたいだし、少し残念。
大半の人はそんなこと気にしないんだろうけど。

Microsoft.Scripting.Runtime.DynamicXamlReader
というのを使っているらしい。
IronPython ディレクトリにある Microsoft.Dynamic.dll リファレンスにあるようだ。

C# からも使えるかもと思って少し四苦八苦してみたけど無駄だった。
そりゃどう考えてもコンパイルで弾かれるわ。
「textbox は定義されていません」みたいな感じで。
動的割り当てなスクリプト言語だから可能な技だろうね。

MSBuild による WPF プログラムのビルド – WPF 入門
C# は素直にコレやるか VisualStudio を使えだろうね。