読者です 読者をやめる 読者になる 読者になる

dothikoのカクカクワールド2D REBOOT

プログラミングとかLinux関連(特にOSSのグラフィックツール関連)とかレトロゲームとか色々。

PyGObject(PyGi)で「デフォルトのフォントサイズ」を取得する

PyGObject…でいいのでしょうか。PyGiなのか。未だによくわかりません。 とにかく新しい方のGtkpythonバインディングで、コンボボックスのカスタムレンダラーで使うデフォルトのフォントサイズを取得したくなりました。

これはMyPaintにOpenCVのgrabCut関数を使った新しいペイント方法(グラフカットを利用し穴の開きまくった線画を分離してペイントする)を実装した時、実験中は「真上のレイヤー」を線画レイヤーにしていたのですが、これは実際使いづらい。

そのため、コンボボックスで線画レイヤーを選べるようにしよう!とピコーンと思いついたんですが、カスタムレンダラーでフォントサイズを他のウィジェットと揃える方法が全く分からんのですね。

do_renderコールバックにおけるcairo contextでのtext_extents系関数でサイズを得ると、明らかに小さい文字が描画される…

しかしながら検索方法が悪いのか一向にそのものズバリにヒットしない…

そこでまぁ頑張っていろいろ調べ&試した結果がコレ

ただし、これが正しいのかどうかは不明、という…

from gi.repository import Gtk, Gdk, Pango

st = Gtk.ComboBox.get_default_style()
desc = st.font_desc
size_base = desc.get_size() / Pango.SCALE
if desc.get_size_is_absolute():
    # True == size is in device unit
    pass
else:
    # False == size is in Point.
    # pixel = Point * (DPI / 72.0)
    scr = Gdk.Screen.get_default()
    dpi = scr.get_resolution()
    if dpi < 0:
        dpi = 96.0
    size_base = math.floor(size_base * (dpi / 72.0))
  • スタイル構造体の中にfont_descがあるので、コンボボックスのデフォルトスタイルを得る。
  • font_descのget_size()でサイズを得られるが、これはPango.SCALEでスケーリングされた値であるため、割る。
  • それで得られた値はデバイス単位かポイント単位かに別れる。これはget_size_is_absolute()で区別できる。
  • ポイント単位であった場合、DPI設定 / 72.0 で得られた値を先ほどの値に掛けることでピクセル単位に変換できる。

これで変数size_baseにデフォルトのコンボボックスのフォントサイズ(ピクセル単位)が入る…はず

SWIGとnumpyについて(ImportErrorとセグメンテーションフォールト)

何か「ハリーポッターと魔法のナントカ」みたいなタイトル…

それはともかくSWIGとnumpyでハマってしまったので備忘録的にメモ。

現象としては

  • hppでNO_IMPORT_ARRAYを定義して#include <numpy/arrayobject.h>すると、.pyファイルでSWIGモジュールをインポートした時点でimporterror (undefined symbol: PyArray_API)が起きる。
  • NO_IMPORT_ARRAYを定義しないと、 cppでPyArray_ZEROS()を呼んだところでセグフォが起きる。

苦しんだのですが調べていてようやくわかりました。 基本的に

PY_ARRAY_UNIQUE_SYMBOL

が必要。

  • .iファイルのinit節でimport_array();呼び出し
%init %{
import_array();
%}
  • PY_ARRAY_UNIQUE_SYMBOLをdefineする共通のヘッダファイルを作成
#ifndef COMMON_HPP
#define COMMON_HPP

#define PY_ARRAY_UNIQUE_SYMBOL mytest_array_API

#endif

(↑基本的にmypaintのcommon.hppのパクリ)

  • このcommon.hppを、すべてのヘッダファイルで#include
  • ラッパー用ヘッダでのみNO_IMPORT_ARRAYを定義せず、その他の自作ヘッダではすべて#include <numpy/arrayobject.h>前にNO_IMPORT_ARRAYをdefineする。
  • NO_IMPORT_ARRAYを定義せずに#include “common.hpp"してあると、その都度シンボルが作られるらしくビルドエラーになる

これで、ImportErrorもビルドエラーもセグフォも起きなくなりました。

ところで統合開発環境のPyCharmを試してみたのですがこれは凄いですね。MyPaintも楽々走るしブレークポイントも普通に置けて(当然か…)これヤバイよ!と思いましたが、唯一悲しいのが起動速度の遅さ…まぁ、いいか。まだ、あんまり使ってないのですが。 ああ、あとちょっともっさり気味かもですが、これはまぁ機能の高さとおそらくpythonで書かれてる点からして仕方ないかも。

cgdbを用いてpython拡張モジュール(てか、mypaint)をデバッグする方法

これまた備忘録的に…

  • 当然ながらgccとかg++はデバッグオプション(-g -O0) でビルド
  • mypaintの場合はsconsを使っているため、SConstructに書いてあるようにdebugオプションを使う
  • つまり、scons debug=true
  • デバッグビルドができたらcgdb pythonとする
  • b 関数名でブレークポイントを先行して設置
  • run mypaint
  • この後、関数が呼ばれたところで停止する…のだけど、もしかしてmypaintがX11をgrabしてしまっている場合はX11にアクセスできなくなり操作不能状況になるかもなので注意する。
  • あとは普通にcgdbを動かす

動かしているのがxfce4-terminalだとF10などのcgdbなどとデフォルトではバッティングするので、設定の上級者でF10を(xfce4-terminalにとって)無効化する。これはF10を「xfce4-terminalが」使わなくするので、アプリ側に制御が渡ると言う意味。多分キーバインドが変えられるはずなんだけども…

あとマストな設定がハイライトですかね。

cgdbは「現在の行」にブレークポイントを置くのだけど、ソースウィンドウのどこが現在の行なのかデフォルトでは全然わからない(もしかして、こっちのターミナルの設定が悪いのかもですが)

そこで、~/.cgdb/cgdbrcに

:hi SelectedLineNr cterm=none ctermfg=Cyan ctermbg=Black term=underline

(行頭のセミコロンが必要な模様)

として色を水色にしてしまうとかなりわかりやすい感じがします。とはいえ、ブレークポイントとかちあうとブレークポイントが優先されて見えなくなるのですが、それはそれでまぁ構わないというか…

こうやってやるのか、と勉強になる設定でした…っていうかもしかしてこれなら、netbeansとかのIDEから出来そうな気も?

Python+SWIGでがんばってみた for mypaint (備忘録的)

MyPaintに塗りつぶし拡大ルーチンを設置すべく、Python+SWIGで頑張ってみますた!(`・ω・´)

結果は… 何とか動くようになった!?!?

それで悩んだところを備忘録的に書いておこうと思います。

手こずったところ

まずPyDict_SetItem()。これがキツかった。

こいつのせいで何度セグフォを連発したことか… 原因は、既に存在するアイテムにはPy_INCREFしないという点です。(よくよく考えると当たり前か)

このせいでPy_DECREFを余計にしてしまい開放されてセグフォになってしまいました。

あとなんだろう…何か、手こずった所があったはずなのだけど…PyDictの扱いがインパクト強すぎてすっかり忘れてしまいましたな(^^;

あ、そうそう!PyDict_GetItem()でアイテムが辞書内に存在しない場合、これまた当然ですがCのNULLを返してくるわけですが、何故かPy_Noneを返してくるはずだと思い込んでいてそこでしばらく詰まってましたな〜

デバッグ

デバッグは、gdb pythongdbを起動して break hogefuncで前もって設置、そして run test.pyみたいにするとできます。 cgdbを使うとソースレベル感が増して実に心地よかったですね。まぁこんなんで十分でしょう、別に統合開発環境はいらない…

まとめ

  • ドキュメント少なし
  • でもなんとかなる
  • 慣れればどうということはない

しかしこれでC言語の中からPythonオブジェクトに(比較的)自由自在にアクセスできるようになってきました。 こうなるとPythonの弱点だった実行速度もあんまり関係ない感じになってきますね。

そのうち、ゲームを作りたいところ(´・ω・`) 昔作ってきたところでは作り方も悪かったのでしょう、遅すぎてビックリしました(特に配列周り) 測ってみたらnumpyでもなんでも遅くて、普通のリストが一番早かったという… Cの中に配列を置いて管理させれば良いんじゃないかと妄想中です。てか、javascriptでブラウザゲーのほうがいいか。

PyGiで謎のワーニングに対処する

今試作中のMypaint向けスタンプツールでポップアップメニューをgladeで作るように変更しました (以前はコードから作ってたけど、見通しが悪いので)

それでいろいろ試行錯誤した結果、Gtk.ActionGroupでアクションを作って、このアクションからcreate_menu_itemでメニューアイテムを作ってしまうのが手っ取り早いという結論に達しました。 (pygiからはget_actionが無いとか色々ありまして…)

        actgroup = builder.get_object("actiongroup1")
        for i, ca in enumerate(actgroup.list_actions()):
            nm = ca.create_menu_item()
            nm.show()
            self.menu2.insert(nm, i)

こんなような感じのコードで複数のpopup menu間の共有メニューをinsertしていくような感じですね。

しかし、そうしているとこのような謎のワーニングが大量に出るはめに。

Gtk-CRITICAL **: gtk_accel_label_set_accel_closure: assertion 'gtk_accel_group_from_accel_closure (accel_closure) != NULL' failed

なんのこっちゃ?といろいろ調べてもよくわからない。

最終的にgladeでActionGroupに対して空のアクセラレーターグループを作って付けて置けば起きないということがわかりました。

f:id:dothiko:20161218214821j:plain

こんなふうですね。実は、アクセラレーターグループって一体何なのかよく分かっていません(^^;

無駄な時間を過ごしてしまった…(汗

xubuntu 16.04でvim-jediをまともに入れました

昔使っていたvim-jedi…当時、ちょっと遅いこともあって今ひとつ使いづらいときもあったりして、次第に離れておりました。

しかしながら最近、ちょっとpycharmというIDEを入れてみたらあまりにも使いやすいため、これはvimも整備しておかねば…ということで再度、まともにvim-jediを入れてみようと思い立ったら

なんとUbuntuでは、vim-jediはaptにパッケージがあるのですね、今は。

これがしかし、インストールしてポン付けで動いたりはしない…というもどかしさ。

  • vim-python-jediを入れる
  • 依存関係で入ってくれるかと思ったら入ってくれないのでpython3-jediも入れる
  • vim-addons install python-jediでremoved状態からinstalled状態に (vim-addons statusで確認)

恥ずかしながらvim-addonsなんてコマンド、今の今まで知りませんでしたな…(恥

それでもって速度は、というと超爆速なんですね。たぶんSSDだからだとは思いますが。

ただ、まだものすごい大きなプロジェクトで試したわけではないので、今の所だけかもですが… というか、そーいえばこのSSD買ってしまいました。

いままでのmx100と比べてどうか…?といえば、あまり差は体感できませんね(^^; まるごと1パーティションext4にし、/homeにマウントしています。OS起動ディスクは別の128GBのSSDです。まぁTBW 220TBの安心を買ったというところが大きいでしょうか。ってか他のところで壊れたりしたら意味なしですけど〜

Xubuntu(というか、Thunar)でエプソンEP-802Aのネットワークドライブに簡単アクセス

長いタイトルに短い記事といきたかったのですが、なんか長くなってしまいました…

実家用の複合機エプソン EP-802AにはLinuxドライバもありますが、出来る限りドライバレスで生きていきたいワタクシですので、単体スキャナ機能を活かしたいわけです。

以前は実家のEP-802A, 自宅のブラザーDCP-J940Nに東芝FlashAirを組み合わせれば無敵のドライバレスWIFIスキャナ…と思っていた時期が僕にもありました。

しかし、実はEP-802AにもDCP-J940NにもWIFIも有線LAN端子もあれば、ネットワークドライブ機能も存在する、という。

そしてDCP-J940Nはなんとネットワークドライブ機能がftpであるため、超簡単に、thunarでもwebブラウザでもスキャンした直後にファイルにアクセスできてしまいます。これは便利。 (むぅ、FlashAirを買って喜んでいた意味が一つ減ってしまった…)

一方、今回のテーマ、EP-802Aではsambaなのです…そのため、thunarで smb://EPSON****** (は伏せ字、EP-802A側で設定可能な識別子でランダムっぽい数字です) とすれば接続はできるのです…が。

MEMORYCARDフォルダを開こうとするとユーザーとパスワードを聞かれるダイアログが出るのですが、何を入れても同じダイアログが繰り返し表示されるだけなのですね。

もちろん匿名アクセスも同様にダイアログが繰り返されるだけ。

調べた結果、これは/etc/samba/smb.confに次のように書けばいいことがわかりました。

client use spnego = no

sambaパッケージをインストールしていない場合は/etc/samba自体が存在しませんが、sambaパッケージを入れずとも/etcにディレクトリとsmb.confを新規作成して上の一行を書けば、thunarがこれに従ってくれます。

ダイアログは匿名では駄目ですが、適当なユーザー名(anonymousでもなんでもよい)と、これまた適当なありもしないパスワードを適当に入れるとフォルダを開くことができます。

うーむ、FlashAirが余ってしまったけど貴重な面白いガジェットのFlashAirなので何か有効な遊び方を考えたいですな〜

っていうか、最近、安い複合機から単体スキャナ機能とかネットワークドライブ機能がどんどん消えているのですが…(汗 正直、これはLinuxのみならずWindowsでも便利な機能だと思うだけに継続してもらいたい所。

っていうか、ドライバレスってのは、PC周辺機器としてものすごいアピールポイントになると思うんですけどねぇ。世間的にはそうでもないのだろうか…