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

GdkPixbuf.Pixbuf.new_from_xpm_data

いつのまにか PyGI から

GdkPixbuf.Pixbuf.new_from_xpm_data(str)

が使えるようになっていた。

#! /usr/bin/python
# -*- encoding: utf-8 -*-

from gi.repository import Gtk, Gdk, GLib, GdkPixbuf

ICON = [
"32 32 3 1",
" 	c None",
".	c #000000",
"+	c #FFFFFF",
"                                ",
"   ............                 ",
"   .++++++++++.                 ",
"   .++++++++++. ......          ",
"   .++++++++++. .++++.          ",
"   .++++++++++. .++++.          ",
"   .++++...+++. .++++.          ",
"   .++++....... .++++.          ",
"   .++++.       .++++.          ",
"   .++++.       .++++.          ",
"   .++++.       .++++. ......   ",
"   .++++.       .++++. .++++.   ",
"   .++++.       .++++. .++++.   ",
"   .++++.       .++++. .++++.   ",
"   .++++.       .++++. ......   ",
"   .++++.       .++++.          ",
"   .++++.       .++++. ......   ",
"   .++++.       .++++. .++++.   ",
"   .++++.       .++++. .++++.   ",
"   .++++.       .++++. .++++.   ",
"   .++++.       .++++. .++++.   ",
"   .++++.       .++++. .++++.   ",
"   .++++.       .++++. .++++.   ",
"   .++++.       .++++. .++++.   ",
"   .++++....... .++++. .++++.   ",
"   .++++...+++. .++++. .++++.   ",
"   .++++++++++. .++++. .++++.   ",
"   .++++++++++. .++++. .++++.   ",
"   .++++++++++. .++++. .++++.   ",
"   .++++++++++. .++++. .++++.   ",
"   ............ ...... ......   ",
"                                "]


class XpmWin(Gtk.Window):
    """
        XPM Data Read
    """
    def __init__(self):
        Gtk.Window.__init__(self)
        self.connect("delete-event", Gtk.main_quit)
        # Icon as XPM data
        pixbuf = GdkPixbuf.Pixbuf.new_from_xpm_data(ICON)
        image = Gtk.Image.new_from_pixbuf(pixbuf)
        # ini
        self.add(image)
        self.show_all()

XpmWin()
Gtk.main()

xpmdata

Lubuntu 12.10 でも動くじゃないの。

ちなみに XPM 画像は中身はテキストデータです。
Gimp で画像を XPM にエクスポート(拡張子を xpm に指定する)してテキストエディタで開き上記のように Python の list 中に {} の中身をコピペすればどんな画像もイケる。
ただしテキスト化するのでサイズが巨大になります、アイコンサイズが限界かと。
eog や Shotwell では変換できないのね。

まあとにかく。
前回のクリップボードと合わせコレで clipolix を GTK3 化できる。
早速 GTK3 化してみると Fedora 18, Lubuntu 12.10 共問題なく動作した。

trayicon

のだが。。。

実は Fedora 18 にしてから clipolix はまったく使っていない。
メッセージトレイが GNOME 3.6 で改悪され恐ろしく使いにくくなったので。
マウスカーソルを下端に置いてもラグがあるし出てこない場合も多々ある。

ついでにこのアプリは Unity 初期状態では動かないと今頃知った。
即 Lubuntu に変更してしまったし、月刊 10 程度のアプリだしまあ。

【Ubuntu】 Unityをもっと使いやすく強化する方法 ? Libre Free Gratis! | Libre Free Gratis!

gsettings set com.canonical.Unity.Panel systray-whitelist "['all']"

でイケた。
つかマジで色々と糞だな Unity って、何故使っている人がいるのだ?

とにかく Fedora で使いにくいのはなんとかしたいのだが。
いっそ Gnome Shell エクステンションに変更とかも考えるが…
そうすると GNOME 3 でしか動かないし、Ubuntu には Gjs が入っていないし。

駄目だ、良い案が思い浮かばない。
次の Fedora と Ubuntu に期待して様子見という名の放置かな。

GtkClipboard in PyGI

GtkClipboard を PyGI で試したら PyGtk と少し違っていた。
違うというより C と同じになったというほうが正しいか。

PyGtk

#! /usr/bin/python
# -*- encoding: utf-8 -*-

import gtk

class ClipPyGtk(gtk.Window):
    """
        PyGtk(GTK2) Version
    """
    def __init__(self):
        """
            Copy the label of the button text
        """
        gtk.Window.__init__(self)
        self.connect("delete-event", gtk.main_quit)
        button = gtk.Button("Copy to the Clipboard in PyGtk")
        button.connect("clicked", self.on_button_clicked)
        self.add(button)
        self.show_all()

    def on_button_clicked(self, widget):
        """
            Very easy.
            gtk.Clipboard(display=gtk.gdk.display_get_default(),
                          selection="CLIPBOARD")
        """
        text = widget.get_label()
        clipboard = gtk.Clipboard()
        clipboard.set_text(text)

ClipPyGtk()
gtk.main()

PyGI

#! /usr/bin/python
# -*- encoding: utf-8 -*-

from gi.repository import Gtk, Gdk

class ClipPyGI(Gtk.Window):
    """
        PyGI(GTK3) Version
    """
    def __init__(self):
        """
            Copy the label of the button text
        """
        Gtk.Window.__init__(self)
        self.connect("delete-event", Gtk.main_quit)
        button = Gtk.Button.new_with_label("Copy to the Clipboard in PyGI")
        button.connect("clicked", self.on_button_clicked)
        self.add(button)
        self.show_all()

    def on_button_clicked(self, widget, data=None):
        """
            Like a C.
            gtk.Clipboard() to Gtk.Clipboard.get_for_display().
            There is no default arguments.
        """
        text = widget.get_label()
        display = self.get_display() # or Gdk.Display.get_default()
        clipboard = Gtk.Clipboard.get_for_display(display, Gdk.SELECTION_CLIPBOARD)
        clipboard.set_text(text, len(text)) # or set_text(text, -1)

ClipPyGI()
Gtk.main()

PyGtk では gtk_clipboard_get_for_display() を gtk.Clipboard() に割り当てて更にデフォルト引数を独自に用意していたってことみたい、本当に楽させてもらっていたんだなと。

PyGI は set_text に文字列のバイト長指定も必要、実は -1 でいいのだけど。
Python3 だと文字列が UCS-4 なのでエラいことになるから -1 にしよう。
うーん Python らしくないと思ったけど Vala も確認したら同じだった。
Gtk.Clipboard ? gtk+-3.0

Gtk.Clipboard.get() との違いがイマイチ解らない。
しかしキャストや破棄コードは不要とはいえ随分書くことが増えたものだ。

GtkMenuButton “gears” menu

何を今頃気になった。
Nautilus 3.6 からメニューがボタンになったけどアレは新規 Widget だった。

GtkMenuButton

gears_menu

ギアアイコン側の呼び方は gears menu でいいのかな。
GtkMenuButton を作成すると矢印アイコンにはなるけどギアにするには?
今後設定はこのギアアイコンのボタンを使えになるかもしれないし探さなきゃ。

しかしいくら devhelp を漁ってもそんな方法は見つからない。
海外を検索してもよく解らない、ええい面倒だ!
ということで素直に Nautilus のソースコードを落として直接調べてみる。

image = Gtk.Image()
image.set_from_icon_name("emblem-system-symbolic", Gtk.IconSize.MENU)
menubutton.set_image(image)

とやっていた、いや元は当然 C 言語なんだけど。
ということは /usr/share/icons 以下のどこかにギアの画像がありそうだ。

/usr/share/icons/gnome/scalable/emblems/emblem-system-symbolic.svg

svg ファイルを見つけた、コレを貼り付けるってことか。
gnome ディレクトリ以下なら Ubuntu でも問題なく使えそうなので試してみる。

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

from gi.repository import Gtk

GEARS_MENU = """<ui>
    <popup name="Popup">
        <menuitem action="open"/>
        <menuitem action="quit"/>
    </popup>
</ui>"""

class GearsMenuWin(Gtk.Window):
    """
        Gears Menu Sample
    """
    def __init__(self):
        Gtk.Window.__init__(self)
        self.set_title("Nautilus 3.6 Menu")
        self.connect("delete-event", self.on_quit)
        # GtkUIManager
        uimanager = Gtk.UIManager()
        self.add_accel_group(uimanager.get_accel_group())
        actiongroup = Gtk.ActionGroup("NautilusMenu")
        actiongroup.add_actions([
            ("open", Gtk.STOCK_OPEN, None, None, "Null", self.on_open),
            ("quit", Gtk.STOCK_QUIT, None, None, "Quit", self.on_quit) ])
        uimanager.insert_action_group(actiongroup, 0)
        uimanager.add_ui_from_string(GEARS_MENU)
        # PopupMenu
        popup = uimanager.get_widget("/Popup")
        popup.props.halign = Gtk.Align.CENTER
        # GtkMenuButton
        menubutton = Gtk.MenuButton.new()
        menubutton.set_popup(popup)
        # Arrow Direction
        #menubutton.set_direction(Gtk.ArrowType.UP)
        # Gear Image
        image = Gtk.Image()
        image.set_from_icon_name("emblem-system-symbolic", Gtk.IconSize.MENU)
        menubutton.set_image(image)
        # Widget etc...
        label = Gtk.Label("Nautilus 3.6 Menu")
        drawingarea = Gtk.DrawingArea()
        # Pack
        hbox = Gtk.Box.new(Gtk.Orientation.HORIZONTAL, 0)
        hbox.pack_start(label, True, True, 0)
        hbox.pack_start(menubutton, False, False, 0)
        vbox = Gtk.Box.new(Gtk.Orientation.VERTICAL, 0)
        vbox.pack_start(hbox, False, False, 0)
        vbox.pack_start(drawingarea, True, True, 0)
        self.add(vbox)
        #
        self.resize(320, 240)
        self.show_all()

    def on_open(self, action, data=None):
        pass

    def on_quit(self, action, data=None):
        Gtk.main_quit()

GearsMenuWin()
Gtk.main()

gears_menu_for_ubuntu

GNOME3 環境だけでなく Ubuntu 12.10 でもしっかり gears-menu になるようだ。
多分 GTK+ 3.6 以降が使える環境ならどのディストリでもいいと思うが保証はしない。
ストックメニューなので日本語化もアクセラレーターもバッチリ。

コレって右クリックメニューとも共用なんかもできるのかな。
GNOME は右クリックメニューを無くしたいのだろうけど当面は無理だよね。

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

from gi.repository import Gtk, Gdk

class GearsMenuWin(Gtk.Window):
    """
        This code accelerator keys are displayed
    """
    def __init__(self):
        Gtk.Window.__init__(self)
        self.set_title("Nautilus 3.6 Menu")
        self.connect("delete-event", self.on_quit)
        # Add Accelerator
        accelgroup = Gtk.AccelGroup.new()
        self.add_accel_group(accelgroup)
        # MenuIten
        item_open = Gtk.ImageMenuItem.new_from_stock(Gtk.STOCK_OPEN, accelgroup)
        item_open.connect("activate", self.on_open)
        item_quit = Gtk.ImageMenuItem.new_from_stock(Gtk.STOCK_QUIT, accelgroup)
        item_quit.connect("activate", self.on_quit)
        # Show
        item_open.show()
        item_quit.show()
        # Menu
        popup = Gtk.Menu.new()
        popup.append(item_open)
        popup.append(item_quit)
        popup.props.halign = Gtk.Align.CENTER
        # GtkMenuButton
        menubutton = Gtk.MenuButton.new()
        menubutton.set_popup(popup)
        # Gear Image
        image = Gtk.Image()
        image.set_from_icon_name("emblem-system-symbolic", Gtk.IconSize.MENU)
        menubutton.set_image(image)
        # Widget etc...
        label = Gtk.Label("Nautilus 3.6 Menu")
        drawingarea = Gtk.DrawingArea()
        # Pack
        hbox = Gtk.Box.new(Gtk.Orientation.HORIZONTAL, 0)
        hbox.pack_start(label, True, True, 0)
        hbox.pack_start(menubutton, False, False, 0)
        vbox = Gtk.Box.new(Gtk.Orientation.VERTICAL, 0)
        vbox.pack_start(hbox, False, False, 0)
        vbox.pack_start(drawingarea, True, True, 0)
        self.add(vbox)
        # Mouse Right Button Clicked
        self.set_events(Gdk.EventMask.BUTTON_PRESS_MASK | Gdk.EventMask.STRUCTURE_MASK )
        self.connect("button_press_event", self.on_button_press, popup)
        #
        self.resize(320, 240)
        self.show_all()

    def on_open(self, action, data=None):
        pass

    def on_quit(self, action, data=None):
        Gtk.main_quit()

    def on_button_press(self, widget, event, popup):
        if event.button == 3:
            popup.popup(None, None, None, None, 0, event.time)

GearsMenuWin()
Gtk.main()

gears_menu_into_accelerator

ものは試しで GtkUIManager を使わずコードで書いてみたんだが…
コッチだとアクセラレーターキーが表示されているじゃないの、何故だ?

ついでにコードで作ると show() しないと GtkMenuItem が表示されないと今頃知った。
これが関係あるのかもと思って以下をやってみたけど関係ないようだ。

uimanager.get_widget("/Popup/open").show() # No...

どちらでも Ctrl+Q で終了できるからアクセラレーターは問題ないのだが。
とにかく表示する手段があるなら次期 Nautilus はキーを表示してほしいです。

とにかくストック svg を利用する方法もオマケで解ったし得した気分。

Drag a string from the GTK+ Application

GTK+ Drag and Drop #(以下 DnD)
で検索すると自アプリ内で DnD が完結するサンプルコードしか見当たらない。
そんな使い道がゼロに等しいサンプルコードではアプリを作る面白さが伝わらない。

実際の話 GUI アプリで DnD って text/uri-list のドロップ以外の使い道がほとんど無い。
text/uri-list は Content Type のことです。

GTK+ や Qt はプロセス間で選択文字列の OLE DnD がデフォルトで可能だったりするし。
Gedit で文字列選択して Firefox の検索バーに DnD するとあら不思議みたいな。
なので Drag 関連はいままで無視してきたけど勉強せねば。

ということで。

GtkTreeView にある文字列を別アプリにドロップなんてどうだろう。
文字列なら Content Type は text/plain ですね。

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

from gi.repository import Gtk, Gdk

class TextDragWin(Gtk.Window):
    def __init__(self):
        """
            TreeView Text "text/plain" Drag
        """
        Gtk.Window.__init__(self)
        self.connect("delete-event", Gtk.main_quit)
        # TreeView
        liststore = Gtk.ListStore.new([str])
        for s in ("CBR1000RR", "YZF-R1", "GSX-R1000", "ZX-10R"):
            liststore.append([s])
        cell = Gtk.CellRendererText.new()
        column = Gtk.TreeViewColumn("Super Sports", cell, text=0)
        treeview = Gtk.TreeView.new_with_model(liststore)
        treeview.append_column(column)
        # Drag Source
        #dnd_list = Gtk.TargetEntry.new("text/plain", 0, 0) # Error
        dnd_list = ("text/plain", 0, 0)
        treeview.enable_model_drag_source(Gdk.ModifierType.BUTTON1_MASK, [dnd_list], Gdk.DragAction.COPY)
        treeview.connect("drag-data-get", self.on_drag_data_get)
        #
        self.add(treeview)
        self.show_all()

    def on_drag_data_get(self, widget, context, data, info, time):
        """
            data @ GtkSelectionData
        """
        selection = widget.get_selection()
        model, it = selection.get_selected()
        text = model.get_value(it, 0)
        data.set_text(text, -1)

TextDragWin()
Gtk.main()

listview_dnd

GtkTargetEntry に text/plain を指定。
Drop 時と違ってタプルのリストという PyGtk と同じ指定でないとエラーになる。
PyGI は互換との整合性がまだまだみたい。

gtk_tree_view_enable_model_drag_source はこんな感じで指定。
GDK_BUTTON1_MASK はマウス左ボタンのこと。

drag-data-get シグナルで GtkTreeView で選択状態の文字列を得る。
それを GtkSelectionData に突っ込むという処理を入れただけ。

ドロップターゲット側が text/plain を受け入れる gtk_drag_dest_set を行っているなら受け入れるエフェクトが入る。
ドロップすると GtkSelectionData から文字列を取得し相応の処理を行ってくれる。
と、これだけで別プロセスのアプリでも文字列を普通にドロップできるようだ。

こんなに簡単だったのか、今まで何故知らなかったのだw
そういうことなら Drag の使い道はある、かな…

Transparent GtkWindow

前回 GtkWindow の枠を消した、これで Google Chrome みたいなウインドウ…
とか書いたけどウインドウ自体を透過させなきゃ四角いウインドウしか作れない。
透明にできれば角を丸くなんかは DrawingArea でなんとかなるはず。

python – Making Gtk.Window Transparent? – Ask Ubuntu

アッサリ見つかった。
ついでに自分のストックから PyGtk 版も出てきた。

せっかくなので PyGtk との違いを見るため両方で書いてみる。

透明なだけでは面白くないので透過付き画像を表示させる処理も。
私は絵心が壊滅的なので Text という文字を Gimp で書いただけですが…
アーカイブも置いておきますので自作して試してくださると嬉しい。
visual.tar.gz

PyGtk

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

import gtk, cairo

class ColormapWin(gtk.Window):
    """
        PyGtk(GTK2) Version
    """
    def __init__(self):
        """
            gtk.main_quit is Double Click
        """
        gtk.Window.__init__(self)
        self.connect("delete-event", gtk.main_quit)
        self.connect("expose-event", self.on_expose)
        # GdkColormap
        screen = self.get_screen()
        colormap = screen.get_rgba_colormap()
        if colormap != None and screen.is_composited():
            self.set_colormap(colormap)
        else:
            print "no Composited..."
        self.set_app_paintable(True)
        # Transparent background image
        self.pixbuf = gtk.gdk.pixbuf_new_from_file("test.xpm")
        # Mouse Click
        self.set_events(gtk.gdk.BUTTON_PRESS_MASK)
        self.connect("button-press-event", self.on_button_press_event)
        #
        self.set_decorated(False)
        self.resize(320, 240)
        self.show_all()

    def on_expose(self, widget, event=None):
        """
            cr is gtk.gdk.CairoContext
        """
        cr = widget.window.cairo_create()
        cr.set_source_rgba(1.0, 1.0, 1.0, 0.0)
        cr.set_operator(cairo.OPERATOR_SOURCE)
        cr.set_source_pixbuf(self.pixbuf, 0, 0)
        cr.paint()

    def on_button_press_event(self, widget, event=None):
        """
            Mouse Drag and Quit
        """
        if event.type == gtk.gdk.BUTTON_PRESS:
            self.begin_move_drag(event.button, int(event.x_root), int(event.y_root), event.time)
        elif event.type == gtk.gdk._2BUTTON_PRESS:
            gtk.main_quit()

ColormapWin()
gtk.main()

pygi

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

from gi.repository import Gtk, Gdk, GdkPixbuf
import cairo

class VisualWin(Gtk.Window):
    """
        PyGI(GTK3) Version
    """
    def __init__(self):
        """
            Gtk.main_quit is Double Click
        """
        Gtk.Window.__init__(self)
        self.connect("delete-event", Gtk.main_quit)
        # 'expose-event' to 'draw'
        self.connect("draw", self.on_draw)
        # GdkColormap to GdkVisual
        screen = self.get_screen()
        visual = screen.get_rgba_visual()
        if visual != None and screen.is_composited():
            self.set_visual(visual)
        else:
            print "no Composited..."
        self.set_app_paintable(True)
        # Transparent background image
        self.pixbuf = GdkPixbuf.Pixbuf.new_from_file("test.xpm")
        # Mouse Click
        self.set_events(Gdk.EventMask.BUTTON_PRESS_MASK)
        self.connect("button-press-event", self.on_button_press_event)
        #
        self.set_decorated(False)
        self.resize(320, 240)
        self.show_all()

    def on_draw(self, widget, cr):
        """
            cr is cairo.Context
        """
        cr.set_source_rgba(1.0, 1.0, 1.0, 0.0)
        cr.set_operator(cairo.OPERATOR_SOURCE)
        Gdk.cairo_set_source_pixbuf(cr, self.pixbuf, 0, 0)
        cr.paint()

    def on_button_press_event(self, widget, event, data=None):
        """
            Mouse Drag and Quit
        """
        if event.type == Gdk.EventType.BUTTON_PRESS:
            self.begin_move_drag(event.button, event.x_root, event.y_root, event.time)
        elif event.type == Gdk.EventType.DOUBLE_BUTTON_PRESS:
            Gtk.main_quit()

VisualWin()
Gtk.main()

Gnome Shell 3D
gnome3_3d

Unity 3D
ubuntu_unity

Fedora でも Ubuntu でも問題ない。
枠も無くウインドウは透過しているけど画像は綺麗に表示できている。

PyGtk と PyGI もあまり違いは無いようだ。
GdkColormap が GdkVisual に変わったくらいで後はもうお馴染みの違いだけ。
デスクトップアクセサリを作る程度ならこれで簡単に作れるかな。

と思ったけど。
GNOME 3 を強制フォールバックにしたら真っ黒に、2D では合成できないようだ。

gnome_2d

Lubuntu でもダメだった。

lubuntu

これじゃ「透過するアプリですヨン」って配れないじゃん。
デスクトップ Linux は仮想マシンな人がほとんどという現実だもの。

つまり Google Chrome みたいなウインドウはこの方法では作れないよ。
うーん、まだまだ勉強しなければ。