モブ沢工房

プログラミングとかLinux関連(特にOSSのグラフィックツール関連)とかレトロゲームとか3Dプリンタやら日曜大工等、色々。

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周辺機器としてものすごいアピールポイントになると思うんですけどねぇ。世間的にはそうでもないのだろうか…

本当に駄目なマウスだろうか?自分の使い方が悪いのでは?と思って足掻く & 中ボタンの凝った使い方

実は買ったのはこのマウスなのですが

f:id:dothiko:20161016165838j:plain

このマウス、微妙である。

(糞WiiUさんのブログ「 俺的糞ゲー処刑ブログ 」様の、真似w)

握り具合自体は悪くないのと、特に変な事をしていないので (一時期、Win8対応!とかいってサイドボタンから迷惑至極なキーコードを吐くマウスがありましたが、そういうことはまったくない)、素直にLinuxでも全ての機能を挿すだけで使用できるのは実に良いのですけども。

なにが悪いってホイールの位置(高さ)なのですね。 実の所、ロジクール以外のサプライ各社のマウスでホイールの位置がマトモな奴を見たことがない気がするのですが。 多分、数ミリレベルの差だと思う。

指の付け根が接しないのでホイールを駆使したとき、指というか指を動かす腱が疲れるのだと思います。

そこで思いました。まずサイズが間違えた(Lサイズを買うべきだったのでしょう…)のと、もうひとつの可能性としては、使い方が根本的に間違っている可能性。

普通の人はそんなホイール動かさない、のではないかという。

ではホイールを動かしたいワタクシと普通の使い方を整合させるにはどうしたらいいのか、と考えていて思い当たりました。

そういえば、昔のLinuxはデフォで3ボタンマウスを挿すと中ボタンが押しているあいだホイールの代わりとなり、マウスを動かした分がスクロールとなる、フォトショなどの「手のひらスクロール」のような挙動を示していました。 当時Windowsユーザーだった自分は、コレ便利じゃん!コレwindowsに欲しいわ〜と思いましたが、まぁいわゆるオートスクロールで代用できるし…と通り過ぎたのでした。

これなら、大量に動かすときはホイール押し込みでマウスを回してスクロールさせ、少しだけ動かすときは指の腹で撫でればいい。

アレはまだ生きていないのだろうか?と思ったわけです。 ぐぐったら、生きてました! 今は、evdevの機能を使うらしい…

unix.stackexchange.com

自分なりの設定

とりあえず実験用として、次のようなスクリプトを書きました。

#!/bin/sh

id=$1

xinput --set-prop $id 'Evdev Wheel Emulation' 1
xinput --set-prop $id 'Evdev Wheel Emulation Button' 2
xinput --set-prop $id 'Evdev Wheel Emulation Axes' 6 7 4 5

xinput --set-prop $id 'Evdev Wheel Emulation Inertia' 3

xinput --set-prop $id 'Evdev Wheel Emulation Timeout' 16

Inertiaは、どれだけマウスを移動させるとイベントを発するかという設定です。高速スクロールを目指して3ピクセルという極短距離で発動するように設定。

Timeoutは、「そのミリ秒内にボタンを離せば、エミュレーションせず単体ボタンの発動とみなす」時間です。 中ボタンとしては使わないと思えばすぐに発動したほうがよいため、1フレームの16msに設定してみました。

やってみて感動、これはすばらしい。

マウスでもいいですが、M570のようなトラックボールでさらに大威力を発揮する設定でしょう…!! でもトラックボールは掃除がめんどい。誰かなんとかして。

さらに思いついた応用

やっているうちに思いついたことがあります。

「機能しないボタンにエミュレーションさせれば、xbindkeys/xvkbdと組み合わせてジェスチャ的にボタンを増やせる」

#!/bin/sh

id=$1

xinput --set-prop $id 'Evdev Wheel Emulation' 1
xinput --set-prop $id 'Evdev Wheel Emulation Button' 2
xinput --set-prop $id 'Evdev Wheel Emulation Axes' 8 9 10 11

xinput --set-prop $id 'Evdev Wheel Emulation Inertia' 64

xinput --set-prop $id 'Evdev Wheel Emulation Timeout' 200

このスクリプトだと今度は、中ボタン押してマウスを動かしても横で8,9ボタン、(進む・戻るボタン)縦では10,11ボタンという誰も使わない高次ボタンの機能を発して、そのままだと何の反応もありません。

そこでxbindkeysで次のように設定します

#mouse-to-top
"xvkbd -text "\[Control_L]\[Home]""
  m:0x10 + b:10

#mouse-to-bottom
"xvkbd -text "\[Control_L]\[End]""
  m:0x10 + b:11

これにより、中ボタン押して左右で「戻る/進む」、上下でページ先頭・ページ末尾というジェスチャ的機能に代わります。

平凡な3ボタンマウスにこういうのを設定すれば、平凡な安マウスが多機能マウスにクラスチェンジです。

なお、Inertiaが64なのは、このタイプではあまりイベントを発動しまくってもらっては困るからです。64でも小さすぎるぐらいで、200とかでも良いかも知れません。

また、Timeoutを活かせば中ボタンよりむしろ右クリックでもよいかもしれない…?右クリックって、右ドラッグにはまず使わないです…よね?(オイラだけ?)

最終的なスクリプト

なんか、id周りが上手く処理できなかった(ダブルクォートすると、デバイス名をスペースで区切ってしまって?正常に認識してくれない)ので、必死こいてナントカ動くようにしましたが…無駄でダサい処理をしているのでしょう…。

とにかくユニバーサルな処理を心がけました。

#!/bin/bash
timeout=16
button=2
inertia=3
mapping="6 7 4 5"

#====================#
while [ "$1" != "" ];do

    case $1 in
    -t | --timeout)
        timeout=$2
        shift 1
        ;;
    -b | --button)
        button=$2
        shift 1
        ;;
    -i | --inertia | --length | --pixel)
        inertia=$2
        shift 1
        ;;
    --id)
        id=$2
        shift 1
        ;;
    --name)
        id=\'$2\'
        shift 1
        ;;
    -m | --mapping):
        mapping=$2
        shift 1
        ;;
    -h | --help)
        linenum_start=`cat $0 | grep -n -e '^#==*#' | sed -e 's/:.*//g'`
        linenum_end=`cat $0 | grep -n -e '^#--*#' | sed -e 's/:.*//g'`
        linenum_end=$((linenum_end - 1))
        tail_cnt=$((linenum_end - linenum_start))
        echo "help document of $0"
        echo "-------------------"
        cat $0 | head -n $linenum_end | tail -n $tail_cnt 
        exit 0
        ;;
    * )
        expr "$1" + 1 >/dev/null 2>&1
        if [ $? -le 1 ];then
            id=$1
        else
            id=\'$1\'
        fi
        ;;
    esac

shift 1
done
#--------------------#

function set_xinput {

call="xinput --set-prop $1 '$2' $3"
#echo $call
eval $call

}

if [ "$id" = "" ];then
    echo "ERROR: you must specify id or device name"
    exit 1
fi

set_xinput "${id}" 'Evdev Wheel Emulation' 1
set_xinput "${id}" 'Evdev Wheel Emulation Button' $button
set_xinput "${id}" 'Evdev Wheel Emulation Axes' "$mapping"
set_xinput "${id}" 'Evdev Wheel Emulation Inertia' $inertia
set_xinput "${id}" 'Evdev Wheel Emulation Timeout' $timeout

バッファローのマウスで、ホイールではなく右クリックをエミュレーションスクロールに割り当ててみました。

上記のスクリプト「middle_button_setup.sh」で以下のように設定しますと (全然ミドルボタンじゃないというツッコミはさておき)

./middle_button_setup.sh "SONiX Full-Speed Mouse" -b 3 -t 180

な、なんだこの使いやすさは!!!

大規模スクロールは辛くなく…しかも右クリとも一応共存出来ています。 今の所、右ドラッグを行うソフトの記憶が無いのでこれで十分では…という…

ていうか…だとするとえっ…マウスってもしかして3ボタンどころか2ボタンで十分…(汗

まぁ、右ドラッグ要るぞ!という状況が出てくるかもなので、なんとも言えないですけどね。まぁキーボードショートカットでこの機能をトグルすることも原理的には可能なので、とりあえずは大丈夫ではないかと…

あっ、ちなみに先日書いたサイド化M100rは結局、サイドボタン、それも小さいタクトスイッチでのホールドには無理があると判断。シングルワンショットの戻るボタンとして機能させ、右ボタンで上記のスクロール機能を割り当てたところ、これまた結構使いやすいです。(進む機能は、Firefoxのアドイン「Firegestures」のロッカージェスチャ機能で行っています。戻るより頻度が少ないのでこれでok)

どっちも結構良くなってしまった。 うーむどっちを使うべきか悩むなぁ…

問題はコレはWindowsでどう再現できるのか?という点なのですが、まぁ、自分はあんまりWin使わないので良いのですが ますますLinuxから抜け出せなくなりそうです…