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

GLib stdin

時は 2015 年

@ サイト管理者やプログラマー
今後は更にスマートフォンの時代になる、タッチパネル向けに作らなければ!

@ 絵師
ソシャゲ用イラストの需要が凄い、ソッチ向けを考慮しなきゃ!!

@ 一般的な人
モンストと Line で忙しい、おっと便利なサイト発見etc…

@ ネクラでキモチワルイ人達
スマホで充分という人はパソコンでもたいしたこと(以下略)ドャァ!!!

こうなることは解っていたがスピードが速すぎな気がする。
実際の話、仕事に必要という理由以外でパソコンを買う人は減る一方なのは確実。
筆者は Fedora 愛用だが実は他人には Mac かタブレットを勧めている。
Linux なんてサイトを自力で作っている人以外にはメリットが無いので。

確実なのは Windows は既にデファクトスタンダードでは無いということ。
モバイルでの Web に関してはもはや Safari がスタンダードに近い。

大型バイク乗りのオッサン、もういいかげんにビクスクを認めろよ。
確かに大型バイクはカッチョイイけどそんなものオイラ達には不要なんですよ。
PCX150 がバカ売れしている時点で余程のバカでなければ気が付くはずなのだが。
その「余程のバカ」が多すぎる、スマホ普及率をガン無視するネラー共と同じく。

絵に関しては Mac 使いにまかせる、その理由で Mac にしたはずだし。
こんな時代にパソコンを使うならプログラミングは必須。
でないとネクラでキモチワルイ人達の一人だと思われてしまう。

そんなこんなで GLib Tips のページを作ろうと思ったわけですが。
GLib って scanf, fgets, fgetc という標準入力のラッパーが無いんですね。

scanf は色々アレなので fgets を今迄使っていたのだが。
コレも色々ある、バッファオーバーランに無力だしスタック領域が無駄だし。
何より改行コード付随というのが気に入らない。

#include <stdio.h>
#include <string.h>
 
int
main (int argc, char *argv[]) {
    char line[256];
    printf("Hi!\nPlease enter your name: ");
    fgets(line, sizeof(line), stdin);
    /* Remove '\n' */
    line[strlen(line)-1] = '\0';
    printf("Welcome, %s!\n", line);
    return 0;
}

ライブラリ関数で取得したものを自力で再加工なんて無駄にもほどがある。
ならば最初から自力で取得し加工したほうが無駄がない。

/* valac -C src.vala */
stdout.printf ("Hi!\nPlease enter your name: ");
var name = stdin.read_line ();
stdout.printf ("Welcome, %s!\n", name);

Vala には stdin.read_line() という便利な関数がある。
コレをどう C 言語にジェネレートしているか -C オプションで確認。
なんと自前で関数を作って適用するという恐るべし手段を使っていた。
相変わらず最適化依存のナニコレなコードを出力するが省略するとこういうこと。

#include <glib.h>
#include <stdio.h>

/* gcc src.c `pkg-config --cflags --libs glib-2.0` */

static gchar*
iostream_read_line (FILE* p) {

	GString* gstr;
	gchar* result;
    gint n;

	gstr = g_string_new("");
	while (TRUE) {
	    n = fgetc(p);
        if (n == ((gint)'\n') || n == EOF) {
			break;
		}
	    g_string_append_c(gstr, (gchar)n);
    }
    result = g_strdup(gstr->str);
    g_string_free(gstr, TRUE);
    return result;
}

int
main (int argc, char *argv[]) {

    gchar* line;

    g_printf("Hi!\nPlease enter your name: ");
    line = iostream_read_line(stdin);
    g_printf("Welcome, %s!\n", line);
    g_free(line);

    return 0;
}

glib_iostream

fgetc で 1 byte ずつチェックしヒープ領域に積み立てて最後にポインタを戻す。
なるほど、これならスタック領域の無駄も最小限だしバッファオーバーランの心配も皆無。
日本語入力も半角スペースも何一つ問題なく処理できてしまう恐るべき関数であった。
Vala を作っている人達は想像以上に頭がいいようです。

ライブラリ関数に徹底的なほど拘るのも悪くないと思うんですけど。
強引に利用するより自分で関数を作ったほうが建設的だなと。
つか、ライブラリ関数に拘る人ってモレなく何もアプリを作っていない現実がね。

GtkFlowBox

開発者、システム管理者、ディストリビューター向けの新規事項

GTK+ 3.14 はマルチタッチをサポートしているのようです。
GtkGesture という名前か、マウスジェスチャはもう過去の遺物だな。

よし早速、ってタッチパネルのパソコンなんて持っていないよ。

後二年もすれば五万円台で ASER か ASUS が普通に出してくるのだろう。
コイツはそれからでいいや。

Gtk 3.12 のほうを見ると GtkActionBar, GtkPopover, GtkFlowBox
全部コンテナか、そんなに沢山の種類が必要なのかな?

GtkFlowBox はコンテナサイズで複数 Widget の位置を調節してくれる。
という解釈でいいのかな、GtkIconView とは違うのかな。
ちと試してみよう。

2015.03.05 修正

#!/usr/bin/env python3

from gi.repository import Gtk, Gio

class FBox(Gtk.Window):
    """
        Minimum width is standard ?
    """
    def __init__(self):
        Gtk.Window.__init__(self)
        # 
        self.flowbox = Gtk.FlowBox.new()
        button = Gtk.Button.new_with_label("Insert Label")
        button.connect("clicked", self.on_insert_button_clicked)
        # pack
        vbox = Gtk.Box.new(Gtk.Orientation.VERTICAL, 0)
        vbox.pack_start(button, False, False, 0)
        vbox.pack_start(self.flowbox, True, True, 0)
        # self
        self.index = 0
        self.add(vbox)
        self.connect("delete-event", Gtk.main_quit)
        self.resize(400, 1)
        self.show_all()

    def on_insert_button_clicked(self, widget):
        label = Gtk.Label("Label")
        label.show()
        self.flowbox.insert(label, self.index)
        self.index += 1

FBox()
Gtk.main()

gtkflowbox

3 クリック目から横に余裕があるのに縦に広がってしまう。
どうも横幅を最小値にした場合を常に計算しているようで。
ついでに、GtkListBox 同様に選択もできます。

どんな場面に便利か思いつかないのは筆者の想像力が足りないのだろう。
昨今の Web ページではよく見かけるのですけどね。

Gedit 3.14 Plugin

そろそろ新しい Fedora が出るので準備をしなければ。
ベータが出ているようなので試す、面倒だから久々に仮想マシン。

boxes

Boxes(qemu-kvm) はやはり超簡単というか全自動です。
Fedora on Fedora なら 3D デスクトップもストレスが無いのが嬉しい。
細かい指定はできないけどテスト目的には十分すぎる。

ただ Live 中はフル HD なのを認識したのにインストール後は SXGA に。
相変わらずよく解らない認識をする、フル HD 固定だとそれも困るわけですが。
筆者はウインドウモードで使うので 1440×900 に固定。

インストール直後に生 XML から iso を排除する必要はなくなったみたい。
Boxes の設定画面[デバイス]の CD/DVD の所で取り外しできる。

cd_dvd

Alt+Space がウインドウメニューに割り当てされているや。
US キーボードなのでコレで入力切り替えできないと不便なんだよ。
まあ設定で簡単に変えられるのでちゃっちゃと変更。

header_bar

いやぁ、見事に GtkHeaderBar 化されて…
っておい Eye of GNOME さん、あんたが一番メニューバー邪魔でしょ。
これ以上の変更点レビューは正式版の時に。

とにかく Gedit はメニューバーが無いので以前の自作プラグイン達は使えない。

Apps/Gedit/PythonPluginHowTo – GNOME Wiki!
残念ながら 2014.11.16 現在チュートリアルは以前のままだ。

Gedit 3.12 をスルーしたおかげで日本語の先人を見つけた。

うぇーん、GNOME 3.12 にしたら、自家製Linespacing が動かないよー | (まだ無題 ; そのうち変更するかも)

メニュー項目は GMenu 化、GtkApplication 側に登録。
ハンドラは GAction として GtkWindow 側に登録するみたい。
GtkUIManager 関連を消すのを忘れているみたいですけど。

なんか上手くいかないので QuickOpen のソースを参考に少し作り替え。
*.plugin ファイルは以前と同じでいいようです。

#-*- coding:utf-8 -*-

from gi.repository import GObject, Gedit, Gtk, Gio

class TestTestAppActivatable(GObject.Object, Gedit.AppActivatable):
    """
        Set GMenu and Accelerator
    """
    app = GObject.property(type=Gedit.App)

    def __init__(self):
        GObject.Object.__init__(self)

    def do_activate(self):
        # Set Accelerator
        # (key, action name[win.xxxxx], parameter or None)
        self.app.add_accelerator("<Control>B", "win.action_name", None)
        # Append Menu
        self.menu_ext = self.extend_menu("tools-section")
        item = Gio.MenuItem.new("imouto",  "win.action_name")
        self.menu_ext.append_menu_item(item)

    def do_deactivate(self):
        # Remove Accelerator
        self.app.remove_accelerator("win.action_name", None)

class TestTest(GObject.Object, Gedit.WindowActivatable):
    """
        Set GAction
    """
    __gtype_name__ = "TestTest"
    window = GObject.property(type=Gedit.Window)
    def __init__(self):
        GObject.Object.__init__(self)

    def do_activate(self):
        # Set Action
        # (action name, parameter or None)
        action = Gio.SimpleAction.new("action_name", None)
        action.connect('activate', self.on_activate)
        self.window.add_action(action)

    def do_deactivate(self):
        # Remove Action
        self.window.remove_action("action_name")

    def do_update_state(self):
        pass

    def on_activate(self, action, data=None):
        self.messagebox("Yamete Oni-chan")

    def messagebox(self, text):
        dlg = Gtk.MessageDialog(
                self.window,
                Gtk.DialogFlags.MODAL,
                Gtk.MessageType.WARNING,
                Gtk.ButtonsType.OK,
                text)
        r = dlg.run()
        dlg.destroy()

gedit314_plugin

こんな感じで。

とりあえずアクション名を決める。
GtkApplication 側で使う場合は win.xxxxx と接頭子を付ける。
こうしないとメニューがアクティブにならなかった。

次はアクション名にアクセラレータキーに紐付ける
そしてメニューに表示する文字列を決め GMenu を作成。
それをメニューのどこかに突っ込む。
file-section, tools-section, view-section-2 等々。

本体側は先程のアクション名で GAction を作る。
シグナルハンドラをセットし本体に登録、コッチは簡単だね。
後は今迄どおりでいいみたい。

Eye of GNOME プラグインは変更しなくてもいいかな?と思ったけどダメだった。
と思ったら *.plugin の Loader=python3 書き換えだけでイケた。

eog314plugin

Python3 なので unicode 等の処理をお忘れなく。

GtkMenuButton F10

Y901x 1.1.2 公開、日は変わってしまったけど。

y901x112

メニューバーを取り払ったよ。
ラジオメニューじゃ解りづらい再生速度アスペクト比切り替えもステータスバーに移した。

GtkHeaderBar and GStreamer | PaePoi
Mini GtkButton | PaePoi

つーか、今頃になってやっと上記を実装した、半年も前だったのか。
使い道の例だと思ってください、多分作った本人しか使っていないアプリなので。

所で F10 キーでメインメニューがドロップするのは知っているよね。
GtkMenuButton ではこれを自分で実装しないといけないみたい。
こんな感じでいいみたい、F10 ってシステム管理じゃなかったんだ。
ちょっと具体的に追記

#!/usr/bin/env python3

class Win(Gtk.ApplicationWindow):
    def __init__(self, app):
        Gtk.ApplicationWindow.__init__(self, application=app)
        # GtkAccelGroup
        accelgroup = Gtk.AccelGroup.new()
        self.add_accel_group(accelgroup)
        # Preference button
        preferencebutton = Gtk.MenuButton.new()
        image_gear = Gtk.Image.new_from_icon_name("emblem-system-symbolic", Gtk.IconSize.MENU)
        preferencebutton.set_image(image_gear)
        # Set F10 Accel
        preferencebutton.add_accelerator("clicked", accelgroup, Gdk.KEY_F10, 0, Gtk.AccelFlags.VISIBLE)
        # HeaderBar
        headerbar = Gtk.HeaderBar()
        headerbar.pack_end(preferencebutton)

ついでに今頃気が付いた。
Alt+F10 で最大化切り替えできる、これは便利だ。
まあシステム設定で変更できるんだけど。

後 Y901 伝統の Z キーでフルスクリーンは実装できなかった。
一応強引な手段もあるけど GNOME デフォルトの F11 のみでいいかなと。

次は Alt+Enter でプロパティ表示を実装したいな。
一度弄り始めると急に色々思い付くんだよね。

Ubuntu での動作確認は明日やろう(ぉい!
個人的には超有名な某ゲームエンジンと同じ名前であるあの糞デスクトップ環境はガン無視したいんだけどユーザー数がなぁ…

GtkToggleButton and keyboard

GtkToggleButton のシグナルには activate と clicked がある。

activate は On 時のみ、clicked は On/Off 両方シグナルが発生する。
マウス等でクリック時だけでなく gtk_button_clicked() 関数にも反応する。

なので GtkToggleButton を Off させる場合に困る場合がある。
トグルなので二度押しで上るわけだが複数ボタンで排他制御を行う場合に。
更にキーボードでも同様に動作するようにとなると…

activate では二度押しで上るのを検知できない。
clicked では排他で上るボタンからもシグナルが飛んでくる。

activate は使えそうもないので clicked にするしかない。
Off 検知を逃すのとキーボードでつじつまを合わせるのに一苦労。
そのためアクロバチックなコードを今まで書いていた。

class Y901window(Gtk.ApplicationWindow):
    def __init__(self, app):
        #
        # etc...
        #
        self.toolbox.one.connect("clicked", self.on_rep_btn, 1)
        self.toolbox.all.connect("clicked", self.on_rep_btn, 2)
        self.toolbox.rdm.connect("clicked", self.on_rep_btn, 3)
        #
        # etc...
        #

    def on_keydown(self, accelGroup, window, keyval, modifier):
        if keyval == Gdk.KEY_F7:
            self.toolbox.one.clicked()
        elif keyval == Gdk.KEY_F8:
            self.toolbox.all.clicked()
        elif keyval == Gdk.KEY_F9:
            self.toolbox.rdm.clicked()
        ''' old
        if keyval == Gdk.KEY_F7:
            self.on_rep_btn(None, 1)
        elif keyval == Gdk.KEY_F8:
            self.on_rep_btn(None, 2)
        elif keyval == Gdk.KEY_F9:
            self.on_rep_btn(None, 3)'''

    def on_rep_btn(self, widget, num):
        if widget.get_active():
            if self.settingwin.repeat != num:
                self.toolbox.one.set_active(num == 1)
                self.toolbox.all.set_active(num == 2)
                self.toolbox.rdm.set_active(num == 3)
                self.settingwin.repeat = num
        else:
            if not self.toolbox.one.get_active() and not self.toolbox.all.get_active() and not self.toolbox.rdm.get_active():
                self.settingwin.repeat = 0
        ''' old
        if widget == None:
            if num == 0:
                self.toolbox.one.set_active(False)
                self.toolbox.all.set_active(False)
                self.toolbox.rdm.set_active(False)
            elif num == 3:
                self.toolbox.one.set_active(False)
                self.toolbox.all.set_active(False)
                self.toolbox.rdm.set_active(True)
            elif num == 2:
                self.toolbox.one.set_active(False)
                self.toolbox.all.set_active(True)
                self.toolbox.rdm.set_active(False)
            elif num == 1:
                self.toolbox.one.set_active(True)
                self.toolbox.all.set_active(False)
                self.toolbox.rdm.set_active(False)
        else:
            if widget.get_active():
                if self.settingwin.repeat != num:
                    action = self.actiongroup.get_action(ac2_str[num])
                    action.set_current_value(num)
                    if num != 3:
                        self.toolbox.rdm.set_active(False)
                    if num != 2:
                        self.toolbox.all.set_active(False)
                    if num != 1:
                        self.toolbox.one.set_active(False)
            else:
                if self.settingwin.repeat == num:
                    action = self.actiongroup.get_action(ac2_str[0])
                    action.set_current_value(0)
                    self.toolbox.one.set_active(False)
                    self.toolbox.all.set_active(False)
                    self.toolbox.rdm.set_active(False)'''

最初に書いたとおり gtk_button_clicked() 関数にも反応だからコッチを使う。
これでマウスクリックと直接呼び出しとを振り分けする必要はなくなった。

排他を if 文で振り分けしていた部分はこの手があった。
今まで何をやってきたのかと情け無くなった。

排他トグルで All Off は All Off の場合のみ処理すればいい。
それ以外だったら必ずどこかが On になっているということだから。
散々悩んだことなのに解ってしまえばこんなにアッサリ。

ということで、こんなにコードが短くなった。
強引に短くするドヤ顔コードは吐き気がするけど正攻法で短くするのは気持ちいい。

GtkActionGroup の処理が取り除かれているけど気にしないで。
メニューバーを GNOME の意向に合わせ取っ払っているだけなので。
iPhone を使うようになってから更にメニューバーが邪魔に思えてきた。
何故あんなのが普及したのか謎と考えるくらいに。