Python」タグアーカイブ

GtkStack GtkStackSwitcher GtkHeaderBar

GNOME 3.10 で誰でもすぐ気が付くこと。
nautilus, gnome-system-monitor 等のタイトルバーがコンテナに。

gnome_system_monitor

今までどおりとタッチパネルの両立を考えると上手い手段だよね。
タッチ操作では常に最大化、なのでタイトルバーにボタンを配置。
双方で別々に UI を作る必要がなくなる、違和感は最初だけだし。

メニューバーと右クリックを排除、アクティブウインドウが一画面。
アプリケーションメニューがスマホやタブレットのメニューボタン。
という考え方で UI を構築していけば勝手にタッチ対応、みたいに。

で、このタイトルバーにするにはどうするのかな。

GtkHeaderBar を作って set_titlebar() だけでいいようだ。
set_custom_title() で中心、pack_start() で左寄せにできる。
同じく GTK+ 3.10 で追加された GtkStack で Notebook みたいに使える。
GtkStackTransitionType にてアニメーションの指定まで可能。

#!/usr/bin/env python3

from gi.repository import Gtk

class Win(Gtk.Window):
    """
        Python with GTK+ 3.10 new widget
        GtkStack, GtkStackSwitcher, GtkHeaderBar
    """
    def __init__(self):
        Gtk.Window.__init__(self)
        # GtkStack
        stack = Gtk.Stack()
        stack.set_transition_type(Gtk.StackTransitionType.SLIDE_LEFT_RIGHT)
        # GtkStackSwitcher
        sw = Gtk.StackSwitcher()
        sw.set_stack(stack)
        # page1
        tv = Gtk.TextView()
        buf = tv.get_buffer()
        buf.set_text("This is a\nGtkTextView")
        stack.add_titled(tv, "view", "view")
        # page2
        label = Gtk.Label("This is a\nGtkLabel")
        stack.add(label)
        stack.child_set_property(label, "title", "label")
        # GtkHeaderBar
        hbar = Gtk.HeaderBar()
        hbar.set_custom_title(sw)
        hbar.set_show_close_button(True)
        self.set_titlebar(hbar)
        self.add(stack)
        self.resize(400, 200)
        self.show_all()

    def do_delete_event(self, event):
        Gtk.main_quit()

Win()
Gtk.main()

gtk_headerbar

gtk_stack_add_titled() で add と title 指定が一発。
調べると単なる gtk_container_add_with_properties() のラッパーみたい。
なので child_set_property() にて title を変更することもできる。

予想より簡単だったし今後はコレでいこうと思う。
問題は GnomeShell 以外の環境ではどういう見た目になるか…

GtkApplication: delete-event and remove_window

destroy and delete-event Signal | PaePoi

の問題がやっと解決した。
正しい手段ではないだろうけどと前置きして。

delete-event を emit してもウインドウは破棄されない。
でも[閉じる]ボタンなら普通に破棄される。
destroy を投げる手段は単独ウインドウならイケるけど複数だと駄目だった。
GtkApplication から remove すれば当然強制破棄される。

だったらこうすりゃいいじゃないか。

#!/usr/bin/env python3

import sys
from gi.repository import Gtk, Gdk

class RemoveWin(Gtk.ApplicationWindow):
    """
        emit delete-event to remove_window
    """
    def __init__(self, app):
        Gtk.ApplicationWindow.__init__(self, application=app)
        button = Gtk.Button("remove_window")
        button.connect("clicked", self.on_button_clicked)
        self.add(button)
        self.resize(320, 240)
        self.show_all()

    def on_button_clicked(self, button, data=None):
        # emit the delete-event
        self.emit("delete-event", Gdk.Event(Gdk.EventType.DELETE))
        # remove
        self.props.application.remove_window(self)

    def do_delete_event(self, event):
        # check the window geometry
        x, y = self.get_size()
        print(x, y) #=> 320 240

class App(Gtk.Application):
    def __init__(self):
        Gtk.Application.__init__(self)
        
    def do_activate(self):
        RemoveWin(self)

    def do_window_removed(self, window):
        # check the window geometry
        x, y = window.get_size()
        print(x, y) #=> 320 240 or 123 32
        # call the default
        Gtk.Application.do_window_removed(self, window)

app = App()
app.run(sys.argv)

delete-event を投げて remove_window で強制破棄。
かなり強引な処理だと思うけどこの手段ならボタンやメニューから確実に終了できる。
かつボタンでも[閉じる]ボタンでも delete-event を通る。

ついでに delete_event と window_removed でジオメトリを確認。
window_removed では[閉じる]ボタンの場合はサイズがおかしい。
やっぱり強引すぎるかな…

つまり conf 等に終了時サイズを記録したい場合は delete_event ハンドラ時に。
いや、自アプリで必要だったので。

ところで do_* はハンドラではなく関数のオーバーライドみたい。
ハンドラからこの関数が呼ばれているという解釈でいいのかな。
do_window_removed は多分こうだろうと適当に書いたら普通に通った。
全部のハンドラがこの仕組みではないようだけど覚えておいたほうがいいね。

Python with GTK3 Auto-connected

今まで PyGI で散々ウソを書いていた。

#!/usr/bin/env python3

from gi.repository import Gtk, Gdk

class Win(Gtk.Window):
    """
        Auto-connected by the prefix of do_
    """
    def __init__(self):
        Gtk.Window.__init__(self)
        self.set_events(Gdk.EventMask.BUTTON_PRESS_MASK)
        #self.connect("button-press-event", self.on_button_press_event)
        #self.connect("delete-event", Gtk.main_quit)
        self.show_all()

    def do_delete_event(self, event):
        Gtk.main_quit()

    def do_button_press_event(self, event):
        self.begin_move_drag( event.button, event.x_root, event.y_root, event.time)

Win()
Gtk.main()

まさかこんな方法があったなんて、、、、、

ハイフンをアンダーバーに置換して do_ のプリフィクスだけで自動コネクト。
パッキングされた Widget は無理なので素直に自前コネクトを…
いや、コレも手段を知らないだけかも。

自前コネクトでも動くのでお好みで、ということで。

#!/usr/bin/env python3

import sys
from gi.repository import Gtk, Gio

"""
    ApplicationMenu does not apply in GtkWindow
"""

#class Win(Gtk.ApplicationWindow):
class Win(Gtk.Window):
    def __init__(self, app):
        #Gtk.ApplicationWindow.__init__(self, application=app)
        Gtk.Window.__init__(self, application=app)
        self.show_all()

class App(Gtk.Application):
    def __init__(self):
        Gtk.Application.__init__(self)

    def do_activate(self):
        self.window = Win(self)
        self.window.present()

    def do_startup(self):
        Gtk.Application.do_startup(self)
        # AppMenu
        menu = Gio.Menu()
        menu.append("New", "app.new")
        menu.append("Quit", "app.quit")
        self.set_app_menu(menu)
        # option "new"
        new_action = Gio.SimpleAction.new("new", None)
        new_action.connect("activate", self.new_cb)
        self.add_action(new_action)
        # option "quit"
        quit_action = Gio.SimpleAction.new("quit", None)
        quit_action.connect("activate", self.quit_cb)
        self.add_action(quit_action)

    def new_cb(self, action, parameter):
        print("New")

    def quit_cb(self, action, parameter):
        self.quit()

if __name__ == "__main__":
    app = App()
    app.run(sys.argv)

更に覚書ページでは GtkWindow を add_window していたけど
GtkApplicationWindow にしないとアプリケーションメニューが出せない。
自アプリに使おうとして適用されなくて初めて気が付いた。
application property は GtkWindow にあるのにさ。

実際に何か作らないと気が付かないことって多いよなぁ。
それにしても。

appmenu

アプリケーションメニューは Lubuntu 等ではタイトルバーの下になる。
LXDE が Qt に移行する最大の原因はコレなのかな。
どうせ Qt もタッチパネル向け UI に変わると思うけど。

Here Document

ヒアドキュメントが便利なのでもう少し調べる。
Y901x のインストールスクリプトで使っているから経験はあるのだが。

ヒアドキュメント – Wikipedia

シェルスクリプトはバッククォーテーションでコマンドも使えるのか。

#!/bin/sh

func() {
    echo バッククオートで関数も使える
}
shstr=シェルスクリプト
hdstr=ヒアドキュメント

cat << __EOS__
${shstr}で$hdstrは今更だけど
最初の改行は無視される、最後は echo や cat が改行する
エスケープ文字 \\ は有効
記号は\$とバッククオートを除いて <'"{[+=~ と普通に使える
`func`
__EOS__

自作関数でもいいようだ。

# output
シェルスクリプトでヒアドキュメントは今更だけど
最初の改行は無視される、最後は echo や cat が改行する
エスケープ文字 \ は有効
記号は$とバッククオートを除いて <'"{[+=~ と普通に使える
バッククオートで関数も使える

これはもしかして PHP でも関数が使えるかも。

<?php

function func() { return "関数"; }
$plstr = "Perl";
$shstr = "シェルスクリプト";

echo <<< __EOS__
PHP は${shstr}や $plstr 同様に利用できる
最初と最後の改行は無視されるので最後は一行開けるとよし
エスケープ文字 \\ は有効
記号は\$を除いて <'"{[+=~` と普通に使える
`func` って PHP にこんな機能はもともと無いよ!

__EOS__;
/* output
PHP はシェルスクリプトや Perl 同様に利用できる
最初と最後の改行は無視されるので最後は一行開けるとよし
エスケープ文字 \ は有効
記号は$を除いて <'"{[+=~` と普通に使える
`func` って PHP にこんな機能はもともと無いよ!
*/
?>

んなわけないか。
ところで PHP のヒアドキュメントは最後を改行しなくて最初戸惑った。
よく考えたら echo, cat, print() が改行していただけだった。
PHP の echo は改行しないもんね。

Perl や Lua って Fedora に最初から入っているけど使ったことが無いな。
余程のことがないかぎり今後も使うことは無いと思うけど。
GNOME は以前 JavaScript を押していたけど今はどうなんだろう?

// Gjs
// imports /usr/share/gjs-1.0/format.js

const Format = imports.format;

String.prototype.format = Format.format;

let jstr = "\
JavaScript はエスケープで強引な改行しか手段が無い\n\
更に%s機能は無い\n\
しかし %s ならこんなことができる\n\
%%s, %%d, %%x, %%f のみ";

print(jstr.format("文字列フォーマット", "Gjs"));
/*
JavaScript はエスケープで強引な改行しか手段が無い
更に文字列フォーマット機能は無い
しかし Gjs ならこんなことができる
%s, %d, %x, %f のみ
*/

これだものな。
面倒臭くなってブン投げたのは筆者だけではないと思う。

#include <stdio.h>

#define CSTR "\
%s は\n\
説明不要だよね\n"

int
main (int argc, char ** argv) {
	printf (CSTR, "C 言語");
	return 0;
}
/*
C 言語 は
説明不要だよね
*/

C のほうが簡単なんて洒落にもならん。
プラス記号で成形するのって最初は分かり易いのでいいと思うが。

#!/usr/bin/env python3

DOCSTR = """{0} はお馴染 {1}
最初と最後の改行も有効だけど print が最後を改行する
{2}
{1} は \\ エスケープが有効"""

def func():
    return "関数は format で実行すれば文字列さ"

print(DOCSTR.format("Python", "docstring", func()))

''' output
Python はお馴染 docstring
最初と最後の改行も有効だけど print が最後を改行する
関数は format で実行すれば文字列さ
docstring は \ エスケープが有効
'''

あぁ楽チン、やっぱりコレだよコレ!

しかしやっぱりヒアドキュメントに変数を直書きできたほうが便利。
それには $ 記号を変数に利用する言語しか無理なんだろうな。
と思っていました。

// Vala

const string DOCSTR = """Python と同じ
%s も使える、でも ''' は使えない
何故か \\ エスケープ\nは使えない
""";

string func() { return "無理"; }

int main (string[] args) {
    string one = "Vala";
    string tow = "ヒアドキュメント";
    stdout.printf(@"$one は実は@\"\"を使って$towもどきが利用できる
ですが$${one}みたいなブレース表記はダメみたい
最初と最後の改行も有効、エスケープ文字 \\ は有効
記号は$$と\"を除いて <'{[+=~` と普通に使える
`func` は当然不可能\n\n");
    //
    stdout.printf(DOCSTR, "docstring");
    return 0;
}
/* output
Vala は実は@""を使ってヒアドキュメントもどきが利用できる
ですが${one}みたいなブレース表記はダメみたい
最初と最後の改行も有効、エスケープ文字 \ は有効
記号は$と"を除いて <'{[+=~` と普通に使える
`func` は当然不可能

Python と同じ
docstring も使える、でも ''' は使えない
何故か \\ エスケープ\nは使えない
*/

Vala はやってくれました。
おかげで解った、滅茶苦茶使い辛いということを。
これなら docstring 方式のほうがいいや。

最後に、Gedit の色分けってスゴすぎる!

gedit

まさかこの Vala の $ 変数を見分けて色分けするとは思わなかった。
PHP 部分と HTML 部分をしっかり見分けるとかは知っていたが。
どの言語でもヒアドキュメント内でマズい記号を打つと即座に色が変わる。
これがデフォルトエディタって GNOME 恐るべしだよ。

GNOME Developer Platform Demos

GNOME Developer Platform Demos

ナイスなページ発見。
GNOME プロジェクトは何時の間かにこんなのを作っていた。

JavaScript はもう Gjs にしなさいみたい。
C, C++, Vala は Anjuta を使うのを前提にしているんだなぁ。

しかし Python だけ妙に詳しくて笑う。

インストールはこんな方法があるのか、autoreconf なんて知らなかった。
しかし print に括弧が無い、Python2 でまだいいよってことですか。
試したら本当にインストールできたけど…
先頭に #!/usr/bin/env python が無いので動かないwwwww
わざと?

Tutorial for beginners (Python)

アプリケーションメニュー(GMenu)はこうやればよかったのか。
GtkSwitch の notify::active なんて devhelp に載っていないんですけど…
とにかく参考になるわ、筆者はビギナー達の一人だったのか。