VirtualBox の今後

Ubuntu 9.04 上の VirtualBox ose にとりあえず Windows XP を再投入。
それは普通に終わり、さて GuestAdditions だ。

メニューから普通に実効したが GuestAdditions がマウントできない!
何か変だ、落ちてきた iso をよく見るとたったの 32 bytes だった。
んなわきゃネェ!中身を見るとやっぱり HTML だ、つまりダウンロード失敗。

http://dlc.sun.com/virtualbox/2.1.4/
ココから無理やり落と…せない。

wget http://download.virtualbox.org/virtualbox/2.1.4/VBoxGuestAdditions_2.1.4.iso
も駄目だ、あきらめよう。

追記 @ 2009.05.03 17:45
駄目元で今日 wget したら落ちてきた!昨日がたまたまなのか修正なのかは解らない。
本体からは試していないけど駄目だったら上のコマンドやってちょ。

Sun の配布している通常版 VirtualBox でも落とすか。
…って何?いつからユーザー登録が必要になったの。
多分これが原因で弾かれているんだなと、これじゃ今後も不具合出まくると思う。
そのうち修正されると思うけど今欲しいんだが。

Windows 版なら窓の杜に…いつのまにか Sun から落とせになっている。
それでもこっちと違い GuestAdditions が同梱なだけマシ、最近のは知らないが。
通常版なら Linux 用にも同梱されているのかな?無かったら終わりなんですけど。

やる気をなくした、このままでも遅くて少し不便ながら使えるからいいや。

次は Ubuntu 8.04 だ。
それは普通に終わり、やっぱり画面が 800×600 なので xorg.conf の書き換え…
ってビデオドライバーは…そうかこのまま使うしかないのか。

最後の手段、コイツをダウンロード。
VirtualBox用仮想マシン | Ubuntu Japanese Team

アップデートしたらマウス統合が無効になっちゃった。
まあいいや、Windows XP も同じだし逆に間違えないだろう。

弄くっていると X が落ちまくる、3D で使っているのが悪いのだろうか?
Windows と違って再起動するのは X だけで済むけどやはり ATI はまだ相性が悪いっぽい。
とにかく半日以上使ってやっと仮想環境構築が終わったので前日のコードを試す。

desk_all

おぉ動く動く。
PyGtk のサイトで使えるバージョンを確認しても実際に試さないと怖いのよ。

しかし GuestAdditions がどうにかならないと他の OS が入れられないぞと。
Linux は 800×600 以上にならない、VMware なら Ubuntu はドライバー標準装備なんけど。
別の仮想環境を試…ビデオドライバーがありそうなのはこの2つしか無いので選ぶ余地は無いか。
なんか他にイイ方法は無いものか、パソコンの台数を増やしてももったいないしなぁ。

それよりあーあ、仮想の Windows から UnitMovie をリモートして動画結合ができないや。

GtkUIManager と GtkActionGroup

さーて、今日も Python だ。
ところで先月のウチへの検索ワードを晒す。

acc1

Python や PyGtk 関係はいずこ、、、、、orz

SEO もクソもあったもんじゃねぇ、でも続ける。

つーことで gEdit も内部で使っている GtkUIManager とやらを使ってみたい。
内部ではどう使われているかなんか知らないよ、当然実装方法なんか解らない。
日本語で PyGtk を探しても見つかるはずがないので最初から米国の google で検索!

私自身がこうだもの、そりゃ SEO にならない、、、、orz

PyGTK 2.0 Tutorial

公式のの「16.7. The UIManager」が普通に一番解りやすい。
つまり gtk.UIManager のインスタンスに読み込ませるとオブジェクトが作られる。
ソレを get_widget メソッドで取り出してパッキングする。
という流れでいいみたい。

ついでに GtkActionGroup の使い方も解りやすく書いてくれている。
XML で action 属性を同じ str にしたものは同じ動作をするわけだ。

そういえば Delphi にも TActionList なんてのがあった。
というか Palepoli v2 で使っていた、便利なんだよね。
メニューと右クリックメニューとボタンの動作指定やハンドラが共用できて。

倍率指定のラジオボタンをメインメニューと右クリックメニューで同じにする必要がある。
ハンドラは同じ名前を指定すればいいだけだがラジオの位置は別々に変更を迫られる。
だけどこれを使えば解決だ、位置も XML で指定できるから同じにする必要はない。

早速書いてみようと思ったけどココ何か説明がたりない。

gtk.ActionGroup

add_radio_actions には GtkRadioActionEntry を入れなきゃいけないんだが。
詳しくは Devhelp を見てね、つーことで昨日のコードに書きたし。

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

ui_str = """<ui>
    <menubar name="MenuBar">
        <menu action="File">
            <menuitem action="quit"/>
        </menu>
        <menu action="View">
            <menuitem action="x00"/>
            <menuitem action="x10"/>
            <menuitem action="x20"/>
            <menuitem action="x30"/>
            <menuitem action="x05"/>
            <menuitem action="x15"/>
            <menuitem action="x25"/>
            <menuitem action="xs1"/>
            <menuitem action="xs2"/>
            <menuitem action="xs3"/>
        </menu>
    </menubar>
    <popup>
        <menuitem action="x00"/>
        <menuitem action="x10"/>
        <menuitem action="x20"/>
        <menuitem action="x30"/>
        <menuitem action="x05"/>
        <menuitem action="x15"/>
        <menuitem action="x25"/>
        <menuitem action="xs1"/>
        <menuitem action="xs2"/>
        <menuitem action="xs3"/>
        <separator/>
        <menuitem action="quit"/>
    </popup>
</ui>"""

import gtk
import common

class TestWindow(gtk.Window):
    def __init__(self):
        # 継承のお約束
        gtk.Window.__init__(self)
        #
        # メニューを作る
        #
        # まず GtkUIManager 作成
        uimanager = gtk.UIManager()
        # GtkAccelGroup を得る
        accelgroup = uimanager.get_accel_group()
        self.add_accel_group(accelgroup)
        # GtkActionGroup 作成
        actiongroup = gtk.ActionGroup("sasakimamenu")
        # GtkActionEntry を作成して突っ込む
        # name, stock_id, label, accelerator, tooltip, callback
        actions0 = [("quit", gtk.STOCK_QUIT, "終了(_Q)", "<Control>Q", "さいなら", self.on_quit),
                    ("File", None, "ファイル(_F)"),
                    ("View", None, "表示(_V)")]
                    #("Popup", None, "")]
        actiongroup.add_actions(actions0)
        # GtkRadioActionEntry の List を作成
        # name, stock_id, label, accelerator, tooltip, value
        actions1 = [("x00", None, "ウインドサイズ", "0", "ウインドサイズ", 0),
                    ("x10", None, "x1.0", "1", "x1.0", 1),
                    ("x20", None, "x2.0", "2", "x2.0", 2),
                    ("x30", None, "x3.0", "3", "x3.0", 3),
                    ("x05", None, "x0.5", "4", "x0.5", 4),
                    ("x15", None, "x1.5", "5", "x1.5", 5),
                    ("x25", None, "x2.5", "6", "x2.5", 6),
                    ("xs1", None, "set1", "7", "set1", 7),
                    ("xs2", None, "set2", "8", "set1", 8),
                    ("xs3", None, "set3", "9", "set1", 9) ]
        # GtkRadioAction を突っ込む
        # entries, value=0, on_change=None, user_data=None
        actiongroup.add_radio_actions(actions1, 0, self.on_size_change)
        #
        uimanager.insert_action_group(actiongroup, 0)
        uimanager.add_ui_from_string(ui_str)
        # Popup Menu を得る
        self.popup_menu = uimanager.get_widget('/popup')
        # セレクタの List を作っておく
        self.size_str = ["win","x1.0","x2.0","x3.0","x0.5","x1.5","x2.5","set1","set2","set3"]
        # おしまい
        #
        # ステータスバーを作る
        self.sb = common.CStatusBar(2, self)
        self.sb.label[0].set_text("こんどは")
        self.sb.label[1].set_text("menu を作ったよ")
        vb = gtk.VBox()
        # 最初にメニューを突っ込もうね
        menubar = uimanager.get_widget("/MenuBar")
        vb.pack_start(menubar, False)
        da = gtk.DrawingArea()
        vb.pack_start(da)
        vb.pack_start(self.sb, False, False, 0)
        self.add(vb)
        self.connect("delete-event", self.on_quit)
        # GDK イベントの有効化を行っておく
        da.set_events(gtk.gdk.BUTTON_PRESS_MASK)
        da.connect("button_press_event", self.on_button_down)
        self.resize(320, 240)
        self.show_all()
    
    def on_size_change(self, action, current):
        # ステータスバーにサイズの表示
        self.sb.label[1].set_text(self.size_str[action.get_current_value()])
    
    def on_quit(self, widget, event=None):
        # bye
        gtk.main_quit()
        return True
    
    def on_button_down(self, widget, event):
        # 右クリで PopupMenu
        if event.button == 3:
            self.popup_menu.popup(None, None, None, event.button, event.time)

if __name__ == "__main__":
    w = TestWindow()
    gtk.main()

おぉメインメニューも右クリも完全に同じ動作だ、コイツは楽ちん。
実は右クリックメニューをどうするかで悩んでいたけどコレで決まりだ。
ちなみに Y901 はメインと右クリックで同じメニューを出していたので必要無かった。

とりあえず Ubuntu 9.04 ならこのコードで動く。
以前のバージョンは…確認用にそろそろ旧バージョンを VirtualBox に入れなきゃ。

class 分け

Y901x は Glade を使って GtkBuilder 形式に変更する。
と書いたけど何か気に入らない、何かを忘れているような感じ。

Glade 等の GUI 作成ツールを使うと全部 GTK+ 部品のベタ打ちになる。

自分で都合がいいよう個別パーツに分けて作ったほうが良くないか。
現状では表示非表示切り替えやリストのリサイズ等で無駄を感じるんだが。
それよりも Glade のバージョンにまで左右されるのでは今後更に困るかも。

Delphi 屋だった頃も徐々に自作でクラスやコンポーネントを作るようになっていった。
今はまだいいがもう少しサイズが大きくなると破綻しそうだし、よしやってみるか。

ステータスバーを自作する

とりあえずこの部分をまとめて class にしてみよう。

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

import os
import gtk

class CStatusBar(gtk.VBox):
    """
        Instead GtkStatusbar
    """
    def __init__(self, num, window):
        """
            @param num: Total of Label
              statusbar.label[0].set_text("aiueo")
            @param window: Toplevel Window
              Use "begin_resize_drag" Event
        """
        self._win = window
        # public menber
        self.label = []
        # init Original inheritance
        gtk.VBox.__init__(self)
        # GtkLabel and GtkVSeparator
        hb = gtk.HBox()
        for i in range(num):
            l1 = gtk.Label()
            s1 = gtk.VSeparator()
            # HBox insert param (child, expand=True, fill=True, padding=0)
            hb.pack_start(l1, False, False, 10)
            hb.pack_start(s1, False, False, 0)
            self.label.append(l1)
        
        # Dummy
        ld = gtk.Label()
        hb.pack_start(ld)
        # Grip
        grip = gtk.EventBox()
        self._im = gtk.Image()
        path = os.path.dirname( __file__ ) + "/img/grip.xpm"
        self._im.set_from_file(path)
        grip.add(self._im)
        hb.pack_end(grip, False, False, 0)
        # Grip connect
        self._first_show = False
        grip.connect("button_press_event", self.on_resize)
        grip.connect("expose-event", self.on_expose)
        # pack
        s0 = gtk.HSeparator()
        self.pack_start(s0, False, False, 0)
        self.pack_start(hb, False, False, 0)
        self.show_all()
    
    def on_resize(self, widget, event):
        # begin resize @ Toplevel Window
        self._win.begin_resize_drag(gtk.gdk.WINDOW_EDGE_SOUTH_EAST,
                                    event.button,
                                    int(event.x_root),
                                    int(event.y_root),
                                    event.time)
    
    def on_expose(self, widget, event):
        # Cursor Change
        if not self._first_show:
            cur_grip = gtk.gdk.Cursor(gtk.gdk.BOTTOM_RIGHT_CORNER)
            self._im.window.set_cursor(cur_grip)
            self._first_show = True

DocString を英語にしているのは単にカッチョイイと思ったから。
間違えている可能性大だからあんまり気にしないでチョ。
これを common.py という名前で保存して以下のコードを。

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

import gtk
import common

class TestWindow(gtk.Window):
    def __init__(self):
        # 継承のお約束
        gtk.Window.__init__(self)
        # 使ってみる
        sb = common.CStatusBar(2, self)
        sb.label[0].set_text("自作")
        sb.label[1].set_text("statusbar クラスを作ったよ")
        # pack
        tv = gtk.TextView()
        vb = gtk.VBox()
        vb.pack_start(tv)
        vb.pack_start(sb, False, False, 0)
        self.add(vb)
        self.connect("delete-event", gtk.main_quit)
        self.resize(320, 240)
        self.show_all()

if __name__ == "__main__":
    w = TestWindow()
    gtk.main()

statusbar

んーこれでまったく同じように使えるようになりました。
GtkLabel のリストである label メンバ以外にアクセスしないようにした。
結果的に本体でやることは作成とラベルへの文字列挿入だけになる。
コードも XML で作るよりスッゲェ短くなる、他への使い回しもこれなら簡単。

なんだよ、コードで作るって良いことだらけだ。
いや、Glade があったおかげで私は GTK+ が使えるようになった。
それは間違いない、だけどそろそろ継承をやる時期なのかなと思ったので。

しかしコードで部品のパッキングをやるのは久々でやりかたを忘れて困ったわ。
expand や fill は親から使うのだった、ちょっと紛らわしいよねココ。

Y901x 0.0.6

Y901x v0.0.6 に更新

* フルスクリーンをとりあえず可能にした
   Z キーと W クリックで切り替え
   現在フルスクリーンでシークバーは出ませんのであしからず
* 多重起動防止でしばらくプロセスが残ってしまうのをなんとかした
* シークバーからマウスを離すとフォーカスを他へ移すようにした
* has_tooltip, tooltip_markup プロパティの削除
* 諸事情でメニューからアクセラレータキーの表示が消えています、ごめん
* 次回より GtkBuilder 形式及び Ubuntu 9.04 以降専用になる予定

はぁ、メニューからアクセラレータのキー文字を消すはめになった。
操作性が少々特殊な部類のアプリなのに自殺行為だ。

ぶっちゃけ Totem とかに操作性を合わせるほうが圧倒的に楽なんだが…
しかしこのポリシーを曲げたら私のアプリではないし作る意味すら無い…
とにかく出すと書いたからムリムリにつじつまだけ合わせた。

GtkBuilder 形式に変更すればもうちょっとマシな方法があるかな?
色々と面倒くさいのでさっさと GtkBuilder 形式に書き換えようと思っていたんだけど。

menu216

9.04 の Gnome は 2.26 だけど Gtk+ のバージョンは 2.16 なので勘違いしないでね。
Ubuntu 8.10 の Gtk+ は 2.15 だ、何も考えずに利用するわけにはいかない。
自分が使うことだけを考えるならどうでもいいことなんですけど、どうしよう…

Fedora 11 はまだ出ていないし事実上現在は Ubuntu 9.04 専用になってしまう。
なんて言っていてもこの業界ではすぐに陳腐化してしまうのですけど。
「最新環境で作っているコード」のほうがイイかもしれない、ネタも作りやすい。

こうなったら Mono で、そのほうがウケは悪いが Python よりアクセスは稼げる。
って、標準の中に GStreamer# が見当たらないや、ヤメとこう。

ところで気分を変えるために TomBoy を使ってみようと考えた。
Windows や Mac 版もあるみたいだし便利かも、と思ったんですけど…

Tomboy/Installing/Windows – GNOME Live!

Tomboy/Installing/Mac – GNOME Live!

Windows に GtkSharp を入れてくれかよぉ!
Mac に至っては当然 Mono 自体のインストールが必要になる。
VB ランタイムの頃を思い出した、だから依存はイヤなんだ。

Ubuntu 9.04 そのよん

さて Ubuntu 9.04 をインストールいたしました、4回め。

こんなブログを運営している人はスナップショットをよく使うと思います。
それで私はこんな覚書を作ったのですが

スクリーンショットを取る

9.04 の標準「スクリーンショットの取得」は神レベル。

screen

あると便利なマウスドラッグでの指定範囲取得が可能になりました。
それだけなら Windows Vista 付属の Snipping Tool と変わらないように思えます。
しかしコレはボタンを押した時点で起動前にアクティブだったウインドウが再アクティブになる。

Snipping Tool 最大の欠点はウインドウが非アクティブ状態でしかショットできなかったこと。
つまりタイトルバーが薄い状態です、濃い状態で画像化したい場合って結構多い。
しかたがないので全画面をスナップしてペイントに張り付けて保存してソレを開いてスナップ…
なんて冗談みたいな苦労をやっていたのは私だけじゃないと思いますけど。

コレはアクティブ状態で選択範囲が普通に取得できる。
あぁこれでもう枠付けや加工をする時意外は Gimp を使わなくてもいいんだ、本気で嬉しい。

toolbar

こうしておいて Snipping Tool のように使おうと思ったけどオプションが無かった…
それだけが残念、まぁちょっぴり手順が増えるだけだしこれでイイかなと。

で、私は Y901x という需要の無さそうな Python 製動画プレイヤーを作っているのだが。

…この Glade で以前の glade ファイルを編集できねぇ!!!!!

上書き保存したら XML が少々違うタグ付けになって読み込めない。
手順が悪いのかな?と思って色々試したけどダメ、上手くいった人いたら教えて。
しかたがないから手書きで XML を編集するはめに、簡便してよ。

とりあえず明日あたりに次版を出した時点で GtkBuilder 形式に変更しよう。
もう LibGlade 形式は消えゆく運命なのかな。

以下 Python 覚書。

PyGTK FAQ Entry @ How can I focus an external window?

これを使って URI を送ったらウインドをアクティブにしようと思っていたんだけど…
gtk.get_current_event_time() がどうしてもゼロになってしまい上手くいかない。
Gtk アプリは URI を渡されてもアクティブ化しないのが普通なのでこのままいくか。
どうもそういう細かい違いが気になるんだよなぁ、WIndows で長くやっていると。

とりあえずフルスクリーンが上手くいきそうだから付けてみる。
ところで GtkMenubar は非表示にするとアクセラレータが効かなくなっちゃうのね…
全部 GtkAccelGroup 利用に書き換えだ、覚書はどうしよう…

ところで今まで多重起動防止でしばらく次タスクが出てしまう件は回避方法を見つけた。

gtk.gdk.notify_startup_complete()

を DBus 経由で URI を渡した後に呼び出せば綺麗に死んでくれる。
コレを呼び出す前にキュー(でいいのかな?)を空にしておいて。

while gtk.events_pending():
    gtk.main_iteration()

つまり簡単に言えば Delphi でいう Application.ProcessMessage() である。
余計に解り憎いわ!いや、私にはこれが一番解りやすいんですけど。
ちょっと Windows が長かったので Linux らしくない説明が多いのはご了承。