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

GTK4 Python 001

そろそろ GTK4 を使ってみることにする。
筆者はもちろん Gir で PyGObject です。

The GTK Project – A free and open-source cross-platform widget toolkit

GTK 公式サイトに基本サンプルコードがある。
c js py vala は解るけど pl や rs なんて誰が使うんだろう?

GTK 4 Reference Manual: GTK 4 Reference Manual

ドキュメントは現状 Web で見る必要があり。
gtk4-devel-docs を dnf で探したけど見つからない。

Widget Gallery: GTK 4 Reference Manual

廃止されたのはツールバーくらいか。
ツリービューは無くなると思ったけど残したようで。
GtkVideo とか GtkPicture とか、まあいいんじゃない。

Stop using GtkWidget::draw

変更点でコレが一番気になる、まさか draw シグナルを無くすとは。
gtk_main が無くなったのは嬉しい、今でもコレ書くジジイは皆殺しだ。

説明を見ているだけじゃよくワカラン。
とっとと筆者がよく使うサンプルコードで確認。

#!/usr/bin/env python3

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

class TestWindow(Gtk.ApplicationWindow):
    def __init__(self, app):
        Gtk.ApplicationWindow.__init__(self, application=app)
        ''' widget '''
        btn = Gtk.Button(label='SUZUKI')
        btn.connect('clicked', self.on_button_clicked)
        self.label = Gtk.Label(label='Motor Cycle')
        ''' pack '''
        box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
        #box.pack_start(btn, False, False, 0) # v3
        box.append(btn)
        box.append(self.label)
        #
        #self.add(box) # v3
        self.set_child(box)
        #self.show_all() # v3
        self.present()

    def on_button_clicked(self, widget):
        self.label.set_text('かっこいい!' * 5)

class TestApplication(Gtk.Application):
    def __init__(self):
        Gtk.Application.__init__(self, application_id='org.suzuki.katana')

    def do_activate(self):
        TestWindow(self)

app = TestApplication()
app.run()

gtk4_1

GtkBox はパッキング情報をコンテナ側に指定ではなくなった。
引き伸ばすか等の情報は挿入する Widget 側のプロパティで指定する。

hexpand|vexpand はデフォルトで FALSE。
これは hexpand-set|vexpand-set を TRUE にしないと変更できない。
halign|valign はデフォルトで GTK_ARIGN_FILL。
margin-* はデフォルトで全部ゼロ、以上で調節する。

gtk_widget_add は GtkWidget の関数だったけど無くなった。
gtk_window_get_child と GtkWindow の関数になった。
普通に UI を作っている人は名前が変わっただけに見えるね。

present は show_all の代わりではない。
GtkWidget のプロパティ visible のデフォルトが TRUE になった。
ただし present しないとウインドウは表示されない。
でこうなったみたい、今までの非表示がデフォルトって変だったもん。

clicked 等のシグナルは変わっていない。
draw が気になるので早々に試してみる。

#!/usr/bin/env python3

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

class TestWindow(Gtk.ApplicationWindow):
    def __init__(self, app):
        Gtk.ApplicationWindow.__init__(self, application=app)
        da = Gtk.DrawingArea()
        da.set_draw_func(self.da_draw_func)
        self.set_child(da)
        #self.resize(400, 150) # v3
        self.set_default_size(400, 150)
        self.present()

    def da_draw_func(self, da, cr, width, height):
        cr.set_source_rgb(0.5, 0.5, 0.5)
        cr.set_line_width(5.0)
        cr.move_to(0, 0)
        cr.line_to(width, height)
        cr.stroke()

class TestApplication(Gtk.Application):
    def __init__(self):
        Gtk.Application.__init__(self, application_id='org.suzuki.hayabusa')

    def do_activate(self):
        TestWindow(self)

app = TestApplication()
app.run()

gtk4_2

なんだシグナルではなく関数登録になっただけで同様に使えるヤン。
全 Widget で draw シグナルが使えるのを変えたかったのかな。
GtkSnapshot がどうとか書いているけど今はよく解っていない。

ついでに、set_size_request は適さないのでコッチ使えだそうです。
いや gtk_window_resize って関数があったやろと少し思うけど。

それより、困ったぞ。
Gedit でスクリプトを debug – Paepoi
コレをやると例外になった時に勝手に死んでくれなくなっている。
端末から ps ax で探して kill するしかなくなる。
でも端末から起動なら Gjs 同様に Ctrl+C が使えるようになった。
けどやはり不便だ、別の手段を考えなければ。

Gedit 40 Plugin

Gedit v40 は何も変わっていないように見えるが実は凄い進化をしていた。
ファイルをいくつ開いていても Ctrl+Q で終了できるようになっていた。
いや昔はそうだったような、記憶があいまいだけど。
とにかく最近は Ctrl+W で全部閉じた後に自身を Ctrl+W で閉じる必要があった。

地味に面倒くさかったのでこれは嬉しい。
Mac の Finder もこうしてくれないかなぁ、同じなんだが。

ところで。

筆者は自作 Gedit プラグインを公開していますけど。
repeat_line と a_href_picture の2つが v40 で動かなかった。
gir の指定を変更しなきゃなのかな。

gir3

3.0 のままじゃん。

gedit/meson.build ? master ? GNOME / gedit ? GitLab

本体は GTK3 のままで GtkSourceView4 になったってことみたい。
そりゃ IArg=3 指定のままプラグインは動いて当然だった。
よく考えたらプラグインで措定している Gtk も 3.0 のままだね。

repeat_line の不具合は簡単。
<Super> や Up|Down を set_accels_for_action 指定できなくなった。
困ったけどしかたがない、Ctrl+Alt+u Ctrl+Alt+d に変更しよう。

ついでにバグ修正、上記を実験している時に原因を発見したので。
行末でこのプラグインを動かしたら変な所にカーソルが飛ぶんだけど。
LF の後に流し込みをすると cursor_position も移動しちゃうのね。

# Check cursol position is LF?
is_lf = buf.get_iter_at_offset(buf.props.cursor_position).get_char() == '\n'

でカーソル位置が LF だった場合は自力で移動をしないことで回避。
最初に作った時はこんな処理いらなかったはずだけどなぁ。

a_href_picture のほうは結構困った。
「get_uri_for_display なんてメソッド無いよ?」
と怒られるんだけど公式サイトは以前のまま、解説が何も存在しない。
しかたがないので python の dir を使って自力で漁った。

doc = self.window.get_active_document()
#path = doc.get_uri_for_display() # under 3.38
#print(dir(doc)) # check method name
efile = doc.get_file()       # Gedit File Object
gfile = efile.get_location() # GFile
path = gfile.get_path()      # location string

無駄な処理をしている気がするけど今はこの手段しか見つからない。
そういえば GTK3 の最初期もこんなことをやったなぁ。
端末から gedit を立ち上げればこの stdout は出力できる。

修正はできたけどコレだけじゃ寂しいな。
そうだ html_escape にアンエスケープ機能の追加でも。
Python の html モジュールを使うだけ、2分で完成。
後は完全に GTK4 になった時に弄くるだろうしでそのまま。

しかし何故プラグインだけは古いバージョンの公開を続けているのか?
ページ内容が寂しかったという理由だけど今は公開数も増えたし。
最新版と一つ前だけ公開に変更しよう。

Gedit2 や Windows 版も今はもういいかなって。
Ubuntu は Gedit 3.12 にメニューバーを付けた前科を忘れないぞ。

Gedit 及び Eye of Gnome プラグイン – Paepoi

ということで Gedit version 40 版プラグイン公開しました。
関係ないけど EoG プラグインの画像が凄く古かったので差し替えた。

macOS Big Sur 11.2.1

M1 Mac の Big Sur を 11.2.1 にアップデート。
やっぱりか、また Command Line Tools が削除されてしまった。

Command Line Tools reinstall | Paepoi Blog

dmg を残しておいて良かった。
多分 Xcode のほうに入っている奴しか見ていないんだろうな。
JXA の NSMakeRect バグについてはもうあきらめているよ。

Clang を再びインストールした所で、前回の続き。

起動すると林檎メニューにもアクセスできないじゃん。
多分 activateIgnoringOtherApps がずっと働いているっぽい。
PyObjC はインタプリタのおかげでメソッドを抜けているのかな。
main 関数内じゃだめだ、デリゲートのハンドラ内に移動する。

#import <Cocoa/Cocoa.h>

// clang app.m -framework Cocoa

@interface AppMenu : NSMenu
@end

@interface AppDelegate : NSObject<NSApplicationDelegate>
@end

@implementation AppDelegate
- (id) init {
    [super init];
    return self;
}
- (void) applicationDidFinishLaunching:(NSNotification *)aNotification{
    NSLog(@"applicationDidFinishLaunching");
    id window = [[[NSWindow alloc] initWithContentRect:NSMakeRect(0, 0, 200, 200)
        styleMask:NSWindowStyleMaskTitled
        backing:NSBackingStoreBuffered
        defer:NO] autorelease];
    [window cascadeTopLeftFromPoint:NSMakePoint(20,20)];
    [window setTitle:@"日本語"];
    [window makeKeyAndOrderFront:nil];
    //
    // App Active
    //
    [NSApp activateIgnoringOtherApps:YES];
}
@end

@implementation AppMenu
- (id) init {
    [super init];
    id item_app = [[NSMenuItem new] autorelease];
    [self addItem:item_app];
    id menu_app = [[NSMenu new] autorelease];
    [item_app setSubmenu:menu_app];
    id item_quit = [[NSMenuItem new] autorelease];
    [item_quit initWithTitle:@"Quit App" action:@selector(terminate:) keyEquivalent:@"q"];
    [menu_app addItem:item_quit];
    return self;
}
@end

int main(int argc, char *argv[]) {
    // ガベージコレクションではないのでコレを利用
    [NSAutoreleasePool new];
    // NSApp を作る
    [NSApplication sharedApplication];
    // C ではコレが必須だった
    [NSApp setActivationPolicy:NSApplicationActivationPolicyRegular];
    // command+Q で終了するメニューを入れる
    id main_menu = [[AppMenu new] autorelease];
    [NSApp setMainMenu:main_menu];
    // Delegate に activateIgnoringOtherApps を移動
    id delegate = [[AppDelegate new] autorelease];
    [NSApp setDelegate:delegate];
    // No!!!
    //[NSApp activateIgnoringOtherApps:YES];
    // メインループを回す
    [NSApp run];
    //
    return 0;
}

app

上手くいったのでウインドウも追加。
前回みたいなコードは世界中で見つかるけど誰もこのこと書いていない。

それにしても。
PyObjC や PyGObject って本当に簡単だなって。

NSApp

Objective-c のコードだけで NSApp を作るソースを作成。
って以前 PyObjC で作ったんだから Objective-c に書き戻すだけだ。

NSApplication – Paepoi

と思ったんだけど動かない、何故だ?
検索しまくって以下のページを見つけた。

Minimalist Cocoa programming

setActivationPolicy を追加したらなんとか動くようになった。
逆に PyObjC は何故いらないんだ?
それとガベージコレクションではないので破棄が必要なんだなこれが。
NSAutoreleasePool という便利なものはこう使うみたい。

#import <Cocoa/Cocoa.h>

// clang app.m -framework Cocoa

@interface AppMenu : NSMenu
//- (id) init; // いらない
@end

@implementation AppMenu
- (id) init {
    [super init];
    id item_app = [[NSMenuItem new] autorelease];
    [self addItem:item_app];
    id menu_app = [[NSMenu new] autorelease];
    [item_app setSubmenu:menu_app];
    id item_quit = [[NSMenuItem new] autorelease];
    [item_quit initWithTitle:@"Quit App" action:@selector(terminate:) keyEquivalent:@"q"];
    [menu_app addItem:item_quit];
    return self;
}
@end

int main(int argc, char *argv[]) {
    // ガベージコレクションではないのでコレを利用
    [NSAutoreleasePool new];
    // NSApp を作る
    [NSApplication sharedApplication];
    // C ではコレが必須だった
    [NSApp setActivationPolicy:NSApplicationActivationPolicyRegular];
    // command+Q で終了するメニューを入れる
    id main_menu = [[AppMenu new] autorelease];
    [NSApp setMainMenu:main_menu];
    // コレをしないと最前面に出てこない
    [NSApp activateIgnoringOtherApps:YES];
    // メインループを回す
    [NSApp run];
    //
    return 0;
}

a.out

動いたんだけど。。。。。

command+Q は使えるんだけどメニューが選択できない。
一度非アクティブにして再度アクティブ化すればイケるんだけど。
てかリンク元のコードも同じジャン。
PyObjC 版はそんな必要なんて無いのに何故だろう?

どうでもいかもしれないけれど、M1 Mac でビルドしたんだけど。
Silicon Info で確認したらこの a.out は arm64 で動いていた。

ついでに。
SyntaxHighlighter は objective-c ハイライトが無いのね。

BloggerのSyntaxHighlighterでObjective-Cを表示する方法 – menXpon

作っている人がいるってのが有名アプリのいいところ。
筆者の WordPress は自前設置なので scripts に放り込むだけ。
後は Add Code to Head プラグインの設定で以下を追加。

<script type="text/javascript" src="/sh2/scripts/shBrushObjectiveC.js"></script>

プレビュー、よしハイライトできた。
ただ Objective-c なんて今後そんなに使うか疑問なんだけど。

C++14

unary_function は C++11 から非推奨だったのね。
functional – cpprefjp C++日本語リファレンス

std::cout を覚えただけで C++ を簡単みたく書く初心者ブログが多いよね。
筆者の Tips ページはそいつらが見たら絶望するように STL を使っていたり。
なのでワケワカメの代表 unary_function が使えないのは困ったなぁ。
と思ったけどコメントアウトしただけで動くじゃん。

それと auto で便利になったとかも見かける。
いや、class で this を戻すのに auto 指定できるレベルでないとそれほど。
…できるジャン!

// stl.cc
#include <iostream>
#include <vector>
#include <algorithm>

/**
 * Fedora 33
 * g++ stl.cc
 *
 * macOS Big Sur
 * clang++ -std=c++14 stl.cc
 */

/*
 * みんな大好き C++ のテンプレート
 * C++11 より unary_function は非推奨
 * なんだけど、継承する必要が無くなっただけだったり
 */
template <class T>
struct outputfunc { // : public std::unary_function<T, void> {
    void operator()(T obj) {
        std::cout << obj << " ♪♪♪ ";
    }
};

class App {
public:
    App() {
        std::vector<std::string> vec = {"SUZUKIの", "バイクは", "カッコイイ"};
        // 関数オブジェクトにしたけど今は範囲ベース for のほうがいい
        std::for_each(vec.begin(), vec.end(), outputfunc<std::string>());
    }
    ~App() {
        std::cout << std::endl << "今日も元気でね!" << std::endl;
    }
    static auto init() {
        // auto で this を戻せる
        return new App();
    }
};

int
main(int argc, char* argv[]) {
    // イヂワルな初期化
    auto app = App::init();
    delete app;
    return 0;
}

cpp14

macOS ではオプションが必要なのね、c++17 指定でもいい。
for も Python と同じようにできるし、使わない間に随分進化していた。
後は :: と -> が全部ドットになれば、って今更無理だろうな。
たまにしか C++ を触らないとどうしてもソコがね。