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

Gedit C lang

プログラミングへの意欲がすっかり無くなってきた君達(俺のこと…
ここいらで基本の基本をやり直ししたいところだ。

ということで gcc による C 言語コンパイルの基本を。
コンパイラやヘッダ類は導入済みという前提で。

Gedit を利用します。
プラグインの「コードスニペット」を有効にします。
プラグインの「外部ツール」を有効にします。

Manage External Tools… を開き以下を登録します。

#!/bin/sh
gcc $GEDIT_CURRENT_DOCUMENT_NAME

準備はコレだけです。

test.c という名前で空のファイルを作成し Gedit で開きます。
Inc と打ち込んでキーボードの Tab キーを叩きます、i だけ大文字。

上記のようなテキストが流し込まれたはず。
inc と i を小文字にすると自作ヘッダ用のダブルコーテーションになる。
Delphi(pascal) 屋の人は「それインクリメントじゃないの?」と勘違いしない。

後はお約束の stdio と打ち込み Tab キーで下方にカーソルが移動する。
スタンダード In/Out ヘッダと通ぶらずとも「スタジオえっち」で誰にでも通…(以下略
これで printf 関数が使えるようになる。

Enter キーで一段下げて main と打ち込み再び Tab キー。
main 関数が流し込まれ関数内にカーソルが移動するので printf() を書く。

コンパイルは最初に作成した外部ツールを実行するだけ。
Gedit は実はこんなに凄い!

、、、、、、、、、、

では Gedit の宣伝なので分割コンパイル方法も少し。
extern 宣言を使う方法もあるけど事実上ヘッダを利用する人しかいない。

cbr.h cbr.c という2つのファイルを同一ディレクトリに作成。

cbr.h

#include <stdio.h>

void
print_cbr(void);

stdio.h をインクルードし、関数プロトタイプのみを書く。

cbr.c

#include "cbr.h"

void
print_cbr(void)
{
    printf("でも CBR250R 買ってしまったし...\n");
}

cbr.h をインクルードし、プロトタイプの実体を作成する。

test.c も書き換え。

#include "cbr.h"

int
main (int argc, char *argv[])
{
    printf("新型 Ninja250 カッケェ!\n");
    print_cbr();
    return 0;
}

stdio.h は cbr.h で宣言されているので書く必要は無い。
プロトタイプがヘッダで宣言されているので print_cbr 関数が使える。

先ほどの外部ツールは残念ながら使えない。
$GEDIT_DOCUMENTS_PATH を使えば複数ファイルのリンクもできるけど *.c のみを全部開いている状態をビルド時に作る必要があるので逆に面倒くさい。
普通に Makefile を作って Ctrl+F8 のほうがいい。

Makefile

ninja: test.c cbr.c
	gcc -o ninja test.c cbr.c

cbr.tar.gz

以上 Gedit はこんなに凄い、ってだからぁ…
すっかり月一更新臭くなっているこのブログ、なんとかせねば。

ApplicationMenu

プログラミング関係は次回!
と書いてから一ヶ月も放置してしまった…

とはいえ何もやっていなかったわけではなく
GNOME 3.4 リリースノート
の「アプリケーションメニュー」を PyGI でやろうと四苦八苦していた。

なんか Linux も Windows もメニューバーを無くそうと試行錯誤しているね。
そりゃまあキーボードとマウスからタッチパネルに絶賛変革中だからだろうけど。
Windows がリボンを作った時はナンジャコリャだったけど先を見ていたと今なら解る。

C のサンプルコードは公式の以下にあるのだが
GtkApplication
GtkApplicationWindow
GActionMap

GtkActionEntry はシーケンス(つまりリスト)でよかったが
GActionEntry は上手く行かなかった。
こんな感じでイケると思ったけどシーケンスでは Gio.ActionEntry ではないと例外になる。

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

from gi.repository import Gtk, Gio

menu_xml = """
<interface>
    <menu id='app-menu'>
        <section>
            <item>
                <attribute name='label' translatable='yes'>_MessageBox</attribute>
                <attribute name='action'>app.messagebox</attribute>
                <attribute name='accel'>&lt;Primary&gt;m</attribute>
            </item>
            <item>
                <attribute name='label' translatable='yes'>_Quit</attribute>
                <attribute name='action'>app.quit</attribute>
                <attribute name='accel'>&lt;Primary&gt;q</attribute>
            </item>
        </section>
    </menu>
</interface>"""

class Win(Gtk.ApplicationWindow):
    def __init__(self, app):
        Gtk.Window.__init__(self)
        self.set_application(app)
        self.show_all()

def on_messagebox(action, parameter, user_data):
    dlg = Gtk.MessageDialog(
            None,
            Gtk.DialogFlags.MODAL,
            Gtk.MessageType.INFO,
            Gtk.ButtonsType.OK,
            "text")
    r = dlg.run()  
    dlg.destroy()
    return r

def on_quit(action, parameter, user_data):
    user_data.quit()

class App(Gtk.Application):
    def __init__(self):
        Gtk.Application.__init__(self,
            application_id="apps.test.appmenu",
            flags=Gio.ApplicationFlags.FLAGS_NONE)
        self.connect("startup", self.on_startup)
        self.connect("activate", self.on_activate)

    def on_startup(self, data=None):
        try:
            # GActionEntry [] a Gio.ActionEntry
            aes = [ ("messagebox", on_messagebox, None, None, None),
                    ("quit", on_quit, None, None, None) ]
            ###
            self.add_action_entries(aes, len(aes), self)
            #=> Expected Gio.ActionEntry, but got StructMeta
            ###
            builder = Gtk.Builder()
            builder.add_from_string(menu_xml)
            menubar = builder.get_object("app-menu")
            self.set_app_menu(menubar)
        except Exception, e:
            print e
            self.quit()

    def on_activate(self, data=None):
        w = Win(app)

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

全部 const 指定だしスタック上のデータでないと駄目なのかな?
それだとヒープ上にしか変数を作れない動的言語はお手上げになるんだけど。

海外を探しても PyGI のコードはほとんど見つからない。
現状ではみんな試行錯誤して様子見しているかブン投げているかだろうな。
はたしてこの動的バインディングは熟成して普及するのであろうか?

ええい、今回もとりあえず C 言語でコンパイルだけやってみるか。

Fedora は初期状態では gcc すら入っていない。
gcc, gtk3-devel, gtk3-devel-docs でとりあえず環境は揃う。
ついでに anjuta を導入、vala なんかもオマケで入る。
これらについては気が向いたら何か書く。

c_appmenu.tar.gz

うん、C ならやはり普通にアプリケーションメニューは使える。
それなら DLL にすれば、って動的言語な意味ネェ…
C が一部でも必要ならソースコードのみで配れる C で全部作ったほうが楽。
Python と DLL では最低 x86, x86_64 用の2つを配るはめになるわけで。

ということで当面は C 言語とお付き合いになりそうです。
難しくはないんだけど、ただただ面倒くさいのが難点。

WebKit Seed imports

GNOME の JavaScript 実装は seed と gjs がある。

JavaScript – GNOME Live!

seed は WebKit の Javascript エンジン。
gjs は Spidermonkey(Firefox) の Javascript エンジン。

であるようだ。
一応書くと Google Chrome のエンジンは V8 なので違います。
ようするに普通に WebKit を GNOME で使うと Seed を利用するということかな。

Python with GTK+3 WebKit Browser 2 | PaePoi
試しに以前作った上記と Google Chrome で V8 ベンチをやってみる。
V8 Benchmark Suite

V8 はやはり早い、圧倒的な差があるのね。
まあそれはよくて、Seed なら imports が使えるはず。

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>GtkWindow</title>

<script>
function test() {
    try {
        var Gtk = imports.gi.Gtk;
    }
    catch(e) {
        alert(e);
    }
}
</script>

</head>
<body>
<input type="button" onclick="test()" value="Test">
</body>
</html>

ダメじゃん、ということで。

Blogging in the wind: WebKit: Extending Javascript – Seed (V)

こんなページを見つけた。
これは面白そうだ、ヘッダを揃えてビルドしてみよう。
webkitgtk-devel, seed-devel を入れればいいかな。

環境が揃ったのでビルド、問題なく実行ファイルが作成された。
ということで、GtkWindow を作成する HTML5 ファイルを読み込ませてみる。

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>GtkWindow</title>

<script>
var on_clicked = function() {
    //
};
function create() {
    try {
        var Gtk = imports.gi.Gtk;
        Gtk.init (null, null);
        var gtkwin = new Gtk.Window();
        gtkwin.signal.hide.connect(Gtk.main_quit);
        var label = new Gtk.Label({label:"Button Click -> WARNING"});
        var button = new Gtk.Button({label:"Button Clicked"});
        button.signal.clicked.connect(on_clicked);
        var vbox = new Gtk.VBox();
        vbox.pack_start(label);
        vbox.pack_start(button);
        gtkwin.add(vbox);
        gtkwin.show_all();
        Gtk.main()
    }
    catch(e) {
        alert(e);
    }
}
</script>

</head>

<body>
<div>First time an error</div>
<input type="button" onclick="create()" value="Do GtkWindow">
</body>
</html>

ボタンの初回クリックで例外を吐く。
けど2回めからは問題なく GtkWindow が作成された。
body.onLoad() で imports なんかも試してみたが同じだった。

それよりシグナルのハンドラがうまくいかない。
RangeError って意味がよく解らないんですけど…
Seed の例外は何が悪いのかが PyGI より理解し辛いのが難点だよな。

うーイマイチだ。
でもとにかくコレで WebKit から imports が使えることは解った。
利用場面があるかどうかは別の話として。

Trigraph

先日書いたように半月前に Fedora マシンを英語キーボードに変えた。
VirtualBox 仮想マシンの Windows Vista, XP も英語配列に変更。
「英語キーボード Vista」と検索すればワラワラ見つかるレジストリ書き換えで。
XP は簡単、どちらも仮想マシン状態でも方法は同じで変更できるようだ。

IME も問題ない、Windows では Alt+~ で切り替えなのね。
ホストの GNOME3 複数ウインドウ切り替え機能より優先されるようだ。
てゆーか間違えるわ、あんまり使わないからいいけど。

ついでなのでキーボードネタでも少し。

有名かもしれないけど C 言語では Trigraph という三文字表記が使える。
Digraphs and trigraphs – Wikipedia, the free encyclopedia

??=include <stdio.h>

main() ??<
    printf("%s??/n", "はろーわーるど");
??>

スッゲエ読みにくい、なんだこりゃですが使えるんです。
ただ gcc では無効になっているので -trigraphs オプションが必要。

gcc -trigraphs test.c

日本語をリテラルに使っても何も問題無い。
ついでに試したら VC++ ならオプション無しで普通にビルドできる。
もしかして C# でも使えるかな?と思ったけどダメだった。

どうでもいいが wprintf ってどんな時に使うのだ?
内部が UTF-16 で local が CP932 で .NET 読み書きが UTF-8 ってアホか。
結局コンソールを使う場合はマルチバイトビルドしか選べないわけで…
それは本当にどうでもよくて。

問題があるようで代わりに Digraph というのが C99 で既定されたようだ。
表現できるものが減っているけどコレで全世界でも問題ないと判明したのだろう。

%:include <stdio.h>

main() <%
    printf("%s\n", "はろーわーるど");
%>

gcc ならオプション無しでイケる、Visual Studio 2008 ではダメだった。
Visual Studio 2010 ならイケるのかはシラネ。
そして C#, Python は関係無かった。

つーか何故こんなもんがあるかというと。
フランス語やドイツ語キーボードにはシャープや角括弧や中括弧が無いから。
キー配列 – Wikipedia

でも実は右 Alt キーを利用して打つことはできるようです。
京都産業大学 外国語学部 フランス語学科/Windowsでのフランス語入力
ドイツ語キーボードの配列

ということで C 言語以外は三文字表記なんか気にしなかったようだ。
いや UTF-8 が普及したってのが大きいんだろうけど…
よく考えたら C# なんて # が使えないと言語名さえ変えなくてはいけないもん。
ぶっちゃけ誰もこんな表記方法は使わないだろう、と思うんだが。

ついでに。
「Gedit キーカスタマイズ」とかのワードで検索している人がいるんだな…
それ Windows の文化だから。

Gedit で検索は Ctrl+F で次を検索は Ctrl+G ですよね。
これ、たとえば devhelp もまったく同じなんです。
一つ覚えてしまえば他でも同じキーが使える環境のほうがいいと思うのだが。
そういうところが気に入って GNOME を選んでいる人って少ないのかな…

GtkSourceView Insert spaces instead of tabs

タブ幅とスペース切り替えで上手く行かなかった理由が解った。
indent-width を弄くっただけではなんか変な動作になるのね。

indent-width Property が -1 なら tab-width Property に従う。
デフォルトが -1 なので最初から tab-width のみで指定する、つまり
gtk_source_view_set_indent_width ではなく
gtk_source_view_set_tab_width で指定する。

ただ tab-width は困ったことに guint (unsignde int のマクロ) である。
ちなみに WindowsSDK でのマクロは UINT、なら最初から uint でいいのに…
C 言語を作った人達は何故こんな長い型名にしてしまったのやら。

Python ってつまり guint が無い。
型として記憶するようだし、旧文字列フォーマッタにも %u が何故かあるが。
しかし試してみると PyGI は guint を long として扱うようだ。
gint, guint の違いを long と扱って吸収するようだ。

int で型チェックを行っているモジュールに渡す場合は当然弾かれるのだが。
その場合は abs(int_value), int(uint_value) キャストでイケるみたい。
つまり我が自作の inifile8.py とかを使う場合、ココでしばし悩んだ。

gtk_source_view_set_insert_spaces_instead_of_tabs
の真偽値でタブかスペースかを切り替え。

gtk_source_view_set_indent_on_tab
の真偽値では Shift+Tab での動作。
False にすると Shift+Tab でもインデントしたり BackSpace でインデントが先頭まで削除されたりでワケが解らないよ。
思えばそんな動作をするエディタもある、私的に Shift+Tab は逆インデントが直感的だと思うのだが逆にそうなっているエディタのほうが少ないという現実。

更に GtkSpinButton を使うと値が float だったりする。
しかし面白いことに guint のまま値を突っ込むことができる。
そうなるようにバインドされているだけだが Python って簡単だね。
C 言語で同じことやっちゃダメだよ。

実際にアプリを作っている人でないと気がつきもしないだろうな。
こんなに簡単だとメモリ内でどうなっているかとかが勉強できないよ。

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

from gi.repository import Gtk, Gio, GtkSource, Pango

class TabTest(Gtk.Window):
    def __init__(self):
        Gtk.Window.__init__(self)
        # GtkSourceView
        self.view = GtkSource.View()
        desc = Pango.font_description_from_string("Monospace 11")
        self.view.override_font(desc)
        self.view.set_show_line_numbers(True)
        self.view.set_wrap_mode(Gtk.WrapMode.CHAR)
        self.view.set_draw_spaces(GtkSource.DrawSpacesFlags.ALL)
        # Tab Width
        spin = Gtk.SpinButton.new_with_range(0.0, 100.0, 1.0)
        spin.set_value(self.view.get_tab_width()) # uint to float ?
        spin.connect("value-changed", self.on_spin_changed)
        # Indent of Tab or Space
        check = Gtk.CheckButton.new_with_mnemonic("Insert _spaces instead of tabs")
        check.set_active(self.view.get_insert_spaces_instead_of_tabs())
        check.connect("toggled", self.on_toggled)
        # Test Button
        button = Gtk.Button.new_with_label("Type is Integer ?")
        button.connect("clicked", self.on_clicked)
        # pack
        vbox = Gtk.VBox()
        vbox.pack_start(self.view, True, True, 0)
        vbox.pack_start(spin, False, True, 0)
        vbox.pack_start(check, False, True, 0)
        vbox.pack_start(button, False, False, 0)
        # self
        self.add(vbox)
        self.resize(300, 300)
        self.connect("delete-event", Gtk.main_quit)
        self.show_all()
        self.set_focus(self.view)

    def on_spin_changed(self, widget, data=None):
        """
            There is no need to UINT
            self.view.set_tab_width( abs(widget.get_value_as_int()) )
        """
        self.view.set_tab_width( widget.get_value_as_int() )

    def on_toggled(self, widget, data=None):
        self.view.set_insert_spaces_instead_of_tabs(widget.get_active())

    def on_clicked(self, widget, data=None):
        """
            tab-width Type Check
        """
        value = self.view.get_tab_width()
        widget.set_label("Type is {0}".format(type(value)))

if __name__ == "__main__":
    TabTest()
    Gtk.main()

てなわけで、なんとかなった。
memopoli 0.1.1 をめでたく公開、多分自分しか使わないと思うけど。