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

Python with GTK+3.0 GtkPixbuf Saveing

昨日及び今朝の追記で GdkPixbuf の扱い方は解った。
今度は保存、無意味に勉強してもツマランので Gedit Plugin にする。

メニューで選択すると GtkFileChooserDialog を出す。
画像を選択して OK すると 300*300 以下の縮小画像を画像位置に自動生成する。
それをリンク画像にし元画像へのリンク文字列を相対パスでカーソル位置に流し込む。
つまり blog 機能のようなモンを作って自分が楽したいので。

仕様として JPEG, PNG 限定でいいだろう。
圧縮レベルは Gimp デフォルトに合わせ JPEG:85, PNG:9 固定。
ダイアログを出して縮小サイズ指定や圧縮率変更も可能だけどパス。
保存されていない場合は相対パスが得られないから弾く。
メニューの挿入位置は「ファイル(f)」メニューに入れたほうが迷わないと思う。
こんなもんかな。

File saving

gdk_pixbuf_save 関数があるはずだがドコにも見つからなかった。
しかたがないので Pixbuf オブジェクトの savev メソッドを使う、多分大丈夫。

scale_simple メソッドは GdkPixbuf.Pixbuf 内にあった。
GTK2 の時と使い方はあまり変わらないようだ。

savev メソッドの第三及び第四引数は何故かリストにする必要があった。
文字列にすると WARNING が出るし適用されないしで困ったよ。

os.path.relpath を初めて使ったけどコレって基準はディレクトリ名なんだね。

アスペクト比の計算は以前作ったというか動画プレイヤーで散々やったしコピペでいい。
何か作りつづけていれば後々で何かに流用できる。

後はとにかく GTK+3.0 の仕様に合わせてひたすら書く。
半分以上が dir() でメソッドや列挙体名を探しまくる時間だったのは秘密だよ。
結構外国からのアクセスがあるので相変わらず全部英語でコメント。

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

#    Gedit a_href_picture plugin version 3.0.0
#    Copyright © 2011 sasakima-nao <m6579ne998z@gmail.com>
#
#    This program is free software; you can redistribute it and/or modify
#    it under the terms of the GNU General Public License as published by
#    the Free Software Foundation; either version 2 of the License, or
#    (at your option) any later version.

from gi.repository import GObject, Gedit, Gtk, GdkPixbuf
import os

ui_str = """<ui>
    <menubar name="MenuBar">
        <menu name="FileMenu" action="File">
            <placeholder name="FileOps_1">
                <menuitem name="AHrefPicture" action="AHrefPicture"/>
            </placeholder>
        </menu>
    </menubar>
</ui>
"""

class AHrefPicturePlugin(GObject.Object, Gedit.WindowActivatable):
    """
        Create small image and link insert
        (e.g. <a href="img/pic1.jpg"><img src="img/pic1-300x225.jpg" alt="img/pic1.jpg" /></a>)
        This Plugin Gedit 3 only
    """
    __gtype_name__ = "AHrefPicturePlugin"
    window = GObject.property(type=Gedit.Window)
    def __init__(self):
        GObject.Object.__init__(self)

    def do_activate(self):
        manager = self.window.get_ui_manager()
        self._action_group = Gtk.ActionGroup("AHrefPicturePluginActions")
        # GtkActionEntry
        # name, stock_id, label, accelerator, tooltip, callback
        actions = [("AHrefPicture", None, "a href Picture", None, "a href Picture", self.on_a_href_activate)]
        self._action_group.add_actions(actions)
        manager.insert_action_group(self._action_group, -1)
        self._ui_id = manager.add_ui_from_string(ui_str)
        
    def do_deactivate(self):
        manager = self.window.get_ui_manager()
        manager.remove_ui(self._ui_id)
        manager.remove_action_group(self._action_group)
        manager.ensure_update()

    def do_update_state(self):
        self._action_group.set_sensitive(self.window.get_active_document() != None)

    def on_a_href_activate(self, action, data=None):
        doc = self.window.get_active_document()
        viewpath = doc.get_uri_for_display()
        if viewpath[0] != "/":
            self.messagebox("Please save the file")
            return
        dlg = Gtk.FileChooserDialog(
                "Select Picture",
                self.window,
                Gtk.FileChooserAction.OPEN,
                (Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL, Gtk.STOCK_OPEN, Gtk.ResponseType.OK) )
        dlg.set_current_folder(os.path.dirname(viewpath))
        r = dlg.run()
        if r == Gtk.ResponseType.OK:
            try:
                filepath = dlg.get_filename()
                pixbuf = GdkPixbuf.Pixbuf.new_from_file(filepath)
            except:
                dlg.destroy()
                return
            # create under 300*300 px image
            d_width = 300
            d_height = 300
            p_width = pixbuf.get_width()
            p_height = pixbuf.get_height()
            width = 0
            height = 0
            if (d_width * p_height) > (d_height * p_width):
                width = p_width * d_height / p_height
                height = d_height
            else:
                width = d_width
                height = p_height * d_width / p_width
            smallpix = GdkPixbuf.Pixbuf.scale_simple(pixbuf, width, height, GdkPixbuf.InterpType.BILINEAR)
            # jpeg or png
            name, ext = os.path.splitext(filepath)
            ext = ext.lower()
            if ext == ".jpg" or ext == ".jpeg":
                smallpath = "{0}-{1}x{2}.jpg".format(name, width, height)
                smallpix.savev(smallpath, "jpeg", ["quality"], ["85"])
            elif ext == ".png":
                smallpath = "{0}-{1}x{2}.png".format(name, width, height)
                smallpix.savev(smallpath, "png", ["compression"], ["9"])
            else:
                self.messagebox("Extension not found")
                return
            # create image link text
            dpath = os.path.dirname(viewpath)
            path1 = os.path.relpath(filepath, dpath)
            path2 = os.path.relpath(smallpath, dpath)
            result = '<a href="{0}"><img src="{1}" alt="{0}" /></a>'.format(path1, path2)
            # insert text
            view = self.window.get_active_view()
            buf = view.get_buffer()
            buf.insert_at_cursor(result)
        dlg.destroy()

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

という感じに仕上がった。
Plugin は本サイトで公開しているのでコッチはコードの参考に。

Python with GTK+3.0 Create GtkPixbuf

Python with GTK+3.0 で GtkPixbuf をファイルから作成する方法。

GDK 3 Reference Manual

こっちでは見つからなくて困っていた、GDK じゃなくなったのかな?
でも色々探していたら同じ GNOME 内でこんなのを見つけた。

API Reference
File Loading

なんだ、こっちに定義されていたのか。
Python ではどう書くか解らないからとにかく dir() を使って自力で調べる。

見つからない、もっと下の階層なのかな。
#include <gdk-pixbuf/gdk-pixbuf.h>
なんだからもしかして

やっと見つかった、コツがなんとなく解ってきた。
つまり gi.repository はヘッダの階層位置どおりに指定する必要があるのね。

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

from gi.repository import Gtk, GdkPixbuf

class PixbufTest(Gtk.Window):
    def __init__(self):
        Gtk.Window.__init__(self)
        # pixbuf
        self.pixbuf = GdkPixbuf.Pixbuf.new_from_file("god_madoka.png")
        self.set_icon(self.pixbuf)
        self.connect("delete-event", Gtk.main_quit)
        self.show_all()

if __name__ == '__main__':
    w = PixbufTest()
    Gtk.main()

コレでとりあえずファイルからアイコンを作る方法は解ったぞと。

次は GtkDrawingArea を使ってサーフェスに描写…
expose-event シグナル名が draw に変わっているなぁ…
cairo_t がなんだかよく解らない、まだまだ調べなきゃ。

追記

Migrating from GTK+ 2.x to GTK+ 3

上記を見ると、とにかく GTK+ 3.0 の描写は cairo_t を使えになっているな。
pixbuf でページ内検索して見つかった cairo_set_source_pixbuf を利用かな。

draw callback 引数の cairo_t から得られるメソッドにそんなもの無い…
Gdk 以下で見つかった、static メソッドは分離しているようだ。

set して cr.paint() だけで画像は表示できるようだ。
GtkDrawingArea
上記の fill() は何だろう、まあ実際にやってみるのが一番早い。

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

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

class PixbufTest(Gtk.Window):
    def __init__(self):
        Gtk.Window.__init__(self)
        self.da = Gtk.DrawingArea()
        self.da.connect("draw", self.on_draw)
        self.add(self.da)
        # pixbuf
        self.pixbuf = GdkPixbuf.Pixbuf.new_from_file("god_madoka.png")
        #self.set_icon(self.pixbuf)
        self.connect("delete-event", Gtk.main_quit)
        self.show_all()

    def on_draw(self, widget, cr):
        """
            # Gtk+ 2.* (PyGtk) draw picture
            gc = widget.style.fg_gc[Gtk.StateType.NORMAL]
            widget.window.draw_pixbuf(gc, self.pixbuf, 0, 0, 0, 0, -1, -1)
        """
        # set picture
        Gdk.cairo_set_source_pixbuf(cr, self.pixbuf, 0, 0)
        #cr.paint()
        # draw arc
        width = widget.get_allocated_width()
        height = widget.get_allocated_height()
        cr.arc( width / 2.0,
                height / 2.0,
                min(width, height) / 2.0,
                0,
                2 * GLib.PI)
        cr.fill()

if __name__ == '__main__':
    w = PixbufTest()
    Gtk.main()

そういうことみたい、リージョン指定なんだね。
てゆーかたったこれだけなのに import 多いな。

iter_previous @ GTK+ 3 only

seemex を Ubuntu 11.04 で動かすテスト。
あらら色々問題が。
まず前回書いた StyleContext は GTK+ 2 では使えない。

# Toolbar Style
toolbar = self.uimanager.get_widget('/ToolBar')
toolbar.set_style(gtk.ToolbarStyle.ICONS)
try:
    style = toolbar.get_style_context()
    gtk.StyleContext.add_class(style, gtk.STYLE_CLASS_PRIMARY_TOOLBAR)
except Exception, s:
    pass # GTK+ 2

コレでいいや、GNOME 3 に合わせたいだけの処理なのだし。

AboutDialog の get_major_version() メソッドも使えなかった。
MAJOR_VERSION とかの定数はどちらでも使えるからコッチに変更しよう。

更に GtkTreeView 順番を上に移動する iter_previous もダメか。
GtkTreePath には確か prev メソッドがあったよな。

def on_item_up(self, widget, event=None):
    selection = self.custome_treeview.get_selection()
    model, it = selection.get_selected()
    p = model.get_path(it)
    if it:
        path = model.get_path(it)
        if path.prev():
            itprev = model.get_iter(path)
            model.swap(it, itprev)
            self.change()
        """ GTK+ 3
        itprev = it.copy()
        if model.iter_previous(itprev):
            model.swap(it, itprev)
            self.change()"""
    else:
        self.messagebox(MESSAGE_NON_SELECT)

せっかく簡単になったけど GTK+ 2 のままの Ubuntu を考慮しなきゃとは…

しかし Fedora の VirtualBox ose アップデートはまだか。
右 Ctrl+F でもそのまんま XGA サイズだし \ は打てないし辛い。

Fedora 15 Trash icon on your desktop

Fedora 15 x86_64 を二週間も使って何を今頃気がついた。
dconf-editor で

/org/gnome/desktop/background/show-desktop-icon
にチェックすればデスクトップに物が置けるようになるこうしておけば

/org/gnome/nautilus/desktop
以下で GNOME2 同様にデスクトップアイコンの表示切り替えができる、ついでに

/org/gnome/desktop/interface
以下でツールバー文字を消したりメニューやボタンにアイコンを付けたり。

やっぱりデスクトップには物が置けたほうが便利だ、ゴミ箱も表示できるし。
特にスクリーンショットの整理が今まで面倒だったのよね。

**********

ところでこの Gedit ツールバーのように seemex はできないのだろうか?
GNOME アプリは全部この色付きスタイルになっているので違うと気になる。

そんな時はソースコードを見れば解るはず、Eye of GNOME のサイトから FTP だ。
Eye of GNOME の「情報(A)」から普通にアクセスしてとっとと落とす。

gtk_style_context_add_class
という関数を使うようだ。
そうか Style がどうとかドキュメントにあるのはこういうことか。
今まで知らなかった、とにかく Python から利用するにはこう書けばいいようだ。

# Toolbar Style
toolbar = self.uimanager.get_widget('/ToolBar')
toolbar.set_style(gtk.ToolbarStyle.ICONS)
style = toolbar.get_style_context()
gtk.StyleContext.add_class(style, gtk.STYLE_CLASS_PRIMARY_TOOLBAR)

おぉ同じになった、こうすればいいようだ。
しかしこんなページを見つけたけどこうしなきゃダメなのかな?

“Hello World” in Python with GTK+ 3

というか PyGtk ではないので Python with GTK+ 3 と書くべきなんだろうなと。

**********

ところでアップデートしたら何故か gcc が入っていた。
/usr/include にヘッダとかも、自分で入れた覚えは無いがいつのまに。

何かにくっついてきたのだと思うけど、まあいいや。
Python のほうが便利なので気が向いたらでしか使わないと思うけどさ。

pygtk3 Variable Parameter

いきなり本題だが、GtkTreeView の並び順を変更するコードを書いていた。
以前の PyGtk では GtkTreePath が単なるタプルだったけどそうではないようだ。

GtkTreeModel
とにかく C 言語と同じように書けばいいだろう。
やってみると凄く簡単にできた、しかし…

''' old
def on_item_up(self, widget, event=None):
    selection = self.custome_treeview.get_selection()
    model, it = selection.get_selected()
    if it:
        path = model.get_path(it)
        if path[0] > 0:
            path2 = (path[0]-1, )
            itprev = model.get_iter(path2)
            if itprev:
                model.swap(it, itprev)
                self.change()
    else:
        self.messagebox(MESSAGE_NON_SELECT)
'''
# new
def on_item_up(self, widget, event=None):
    selection = self.custome_treeview.get_selection()
    model, it = selection.get_selected()
    if it:
        itprev = it.copy()
        if model.iter_previous(itprev):
            model.swap(it, itprev)
            self.change()
    else:
        self.messagebox(MESSAGE_NON_SELECT)

Python なのに戻り値で代入ではなく引数の値が直接変更されている。
model.iter_previous(itprev) が bool 値しか戻さないのでしばし悩んだ。
もしかして、とやってみたらこの結果。

こんなの ctypes の byref 以外で見たことが無い。
IronPython でさえ out だったら戻り値とまとめてタプルで戻すというのに。

てゆーか可能だったんだ。
明示的な型宣言ができない Python では問題がでないだろうか心配。

**********

それより Opera 11.11 の search.ini はどうなっているんだ?
Default Search キーの UUID を手書きで書き換えても本体に適用できない。
書き換えるとたとえ非表示にしていても Google と Yahoo が復活するんだが。
並べ替えは何も問題なく行われるのですが。
わけがわからないよ。

コンボボックスで簡単に変更できるようにしたけどまったく無意味になった。
こんなの酷いよ、あんまりだよ…

しかも Deleted を 1 にして Opera 本体機能で編集するとそのセクションが消滅する。
こんなの絶対おかしいよ。

とにかく現状では Default Search を弄くらず deleted を利用しなければ多分問題ない。
需要ゼロに等しいアプリだからこんなんでいいよね、もう少し改造と Debug をするけど。

GNOME3 の雰囲気に合わせて激シンプルへと向かっている。
バックアップ
seeme-1.9.1.tar.gz