モブ沢工房

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

Kritaのten brushesの設定ダイアログが動かなくなったので改造

なんか原因はよく分かりませんが、Krita4のten brushesの設定ダイアログがRuntimeError吐いて動作しない現象が再発。 前も起きてたんですがPPA版にしたとき直った気がしたんですがね…

で、まぁ重い腰を上げて色々やってみるべぇ…ってやってみたんですがどうもこれ、何かうちのブラシ設定におかしい所があるか何かで、アクションのショートカットが削除されているが故の現象っぽい?

ともかく、まぁ滅茶苦茶いい加減な修正方法でやりました

/usr/share/krita/pykrita/tenbrushes/uitenbrushes.pyのloadButtonsメソッド、 93行あたりの

            label = QLabel(
                action.shortcut().toString())

これを、超適当な全例外捕捉でラップしただけ(^^;

            try:
                label = QLabel(
                    action.shortcut().toString())
            except:
                label = QLabel('unknown')

なんだかなー

なんか知らんけど突然Xubuntuのサスペンドが壊れた

Xubuntu Linux 18.04.3を使っているのですがアップデート直後からだと思いますが、少なくとも3週間程前までは普通に動作していた。 実家に来て実家メインマシンをアップデートしたらこのようになりました。

  • パネルのアクションボタンからサスペンド指定、もしくはターミナルからsystemctl suspendを実行
  • サスペンドに突入、しかしGPUファンが全開になってそのまま。当然電源遮断とリセット以外、何も受け付けない。

なお、マシン構成はなかなか古めかしいものではありますが

  • MSI P67A-GD55
  • i5-3470, DDR3 32GB
  • クロシコのRX550
  • ADATAの256GB SSD(ブート)
  • WD BLUEな1TB SSD(ホーム)
  • 雑多な書き込み用&SSD寿命延長用のWDの512GB SSD

といった感じでございます。

なお、自宅のRyzen 2600 + RX460では同じXubuntu 18.04.3において、同様の問題は発生していない(…筈)

そこで解決方法を探っていたところ、

「一回でも sudo pm-suspendを実行した後であれば、 sudoの不要なsystemctl suspendでサスペンドが出来る」

事に気づきました。

そんなわけで、pkexecを利用した、pm-suspendとsystemctl suspendの切り替えスクリプトを作成。 これをパネルアイテムとして登録してアクションボタンを代用することとしました。

パスワード入力が面倒なので一度だけしかやりたくない、というね。

/tmpにフラグファイルが存在すればsystemctl suspendを実行するという荒い作りですが、まぁ治るまでの辛抱ですからなぁ

#!/bin/sh

FLAGFILE=/tmp/suspend-workaroud.flag

if [ ! -e "$FLAGFILE" ];then
    pkexec pm-suspend
    if [ "$?" = "0" ];then
        touch $FLAGFILE
    fi
else
    systemctl suspend
fi

実際には、最初のpm-suspendはそのまま何も起こさず抜けてしまう場合がありましたので、初回サスペンド時のみ、二度クリックする必要があるかもです。

ではでは

Kritaのpythonプラグインについてメモ・Palette編

さてkritaのpythonプラグインですが、Paletteオブジェクトも癖があります。

最も不可思議なのは

Palette.numberOfEntries() メソッドが、必ず 実エントリの倍の数 を返してくるところ

です。

例えば320エントリあるパレットであれば、numberOfEntriesは640を返してきます (Krita ver 4.2.7.1の時点では)

実際にエントリを列挙させて名前を見てみると、不思議なことに確かに、実エントリ数を超えた「辺りから」先頭と同じ名前と中身が繰り返されるのです。が…「辺りから」というのがミソでして。

実エントリ数を超えたら直ぐ先頭から繰り返されるのなら、まだわかるのです。

実エントリ数以降の数エントリは空(黒)のようなのですよ。

何かやり方が悪いのか…それとももしかしてバグなのか…全く分かりませんが、とりあえず//2して使ってます(^^;

ではではぁ〜

Kritaのpythonプラグインについてメモ・ManagedColor編 (追記あり)

最近PythonによるKritaのプラグインづくりにハマっているのですが、 これが中々面白い。

Scriptorという即スクリプトを試せる環境もあって、これが実に心強いのです。

だがしかしドキュメント的にはあまり整備されていない点が多いようです そこで一応ネットの片隅にこうして放出しておこうかと…

そんなわけで今回はManagedColor編です。 これには本当に苦しめられました。

  • ManagedColorとはKrita内での色を表現し、やり取りするためのオブジェクト。
  • Kritaの使用しているフレームワークのQtには色を示すQColorがありますが、それとは完全に別物。
  • カラープロファイルなどでの変化等にも対応できる(らしい)
  • components()で色成分を取得でき、setComponents()で色成分を設定できる。
  • ViewオブジェクトのsetForeGroundColor()に与えるManagedColorオブジェクトでブラシ色を設定、foregroundColor()で得られるManagedColorで色を取得

…と、基本的にはこうなのですが問題はcomponents() / setComponents()なのであります。

ぶっちゃけこのcomponentsはRGBAの各成分を0.0〜1.0に正規化した、4要素のシーケンス*1なのですが…

r,g,b,a の並びの時もあれば、b,g,r,aの並びの時もある

のですな。これが。調べた結果、ManagedColorのcolorDepth()メソッドで文字列として取得できる色要素の精度が

8ビット符号なし整数の'U8' (←これがKritaデフォルトらしい) もしくは 'U16' の場合は、 b, g, r, a

32ビット浮動小数点の'F32' もしくは 'F16' の場合は、 r, g, b, a

となるようです。 このため、色を取得して設定しようとすると全然違う色になったりして、????という感じだったのですが

将来変わったりしないことを願っております(^^; ってまぁ自作プラグインというのはそういう時にイジれるのも醍醐味だったりして…

ではでは!


追記

ManagedColorを簡単にかつ正しくQColorに変換する方法がありました(^^;

まずViewからcanvas()でキャンバスを取得、そしてManagedColorオブジェクトでcolorForCanvas()を使ってQColorオブジェクトを取得します。これならば簡単確実!

ki = Krita.instance()
win = ki.activeWindow()
view = win.activeView()
cvs = view.canvas()

mcol = view.foregroundColor() # ManagedColorオブジェクト取得
qcol = mcol.colorForCanvas(cvs) # QColorオブジェクト取得!

ただし、残念ながら、QColorをManagedColorに安全に変換する方法が見つかりません。

とりあえず、ManagedColor("RGBA", "U8", "") で作られたオブジェクトであれば、setComponents( (qcol.blueF(), qcol.greenF(), qcol.redF(), 1.0) )で良いようです。今の所ですが…

これの重要な所はcolorModelが'U8'であることです。U16その他であると、setComponents() で設定された色成分は、setForeGroundColor() した時に微妙に色褪せていたり、逆に蛍光っぽかったり、違う色となって反映されます。

ManagedColor.fromXML() にXML文字列を突っ込めば出来ないかな?と思いましたが、これが何故かtoXML()メソッドで得たXMLでも通用しません。

mcol.setComponents((0.0, 1.0, 0.0, 1.0)) # colorModel()が何れにせよ、緑色的な何かになっているはず
xml = mcol.toXML() # -> xmlは<Color>タグ、<RGB>タグの入った文字列として正しく出力されている
print(xml) 

newcol = ManagedColor('RGBA', mcol.colorDepth(), mcol.colorProfile())
newcol.fromXML(xml) # <- 通用しない、RGB = 0,0,0のまま
print(newcol.toXML()) # <-ご覧の有様だよ!

といった感じです。

*1:colorModelが'RGBA'の場合であり、他の場合はcomponentsは当然RGBAではないのだが、割愛

Radeon RX550導入 & ファン制御で黙らせてみた

玄人志向Radeon RX550搭載「RD-RX550-E2GB/OC」が7500円程度だったので、まぁRX550レベルの適正価格と判断して購入しました 今年の最初に3980円ぐらいで売ってたらしいですけどね…全然知らんかったわ(涙

Xubuntu 18.04.3マシンに載せたら、最初は何故かopenglのrendererがllvmpipeにフォールバックして全然amdgpuを使ってくれなかったので/usr/share/X11/xorg.conf.dに極普通のconfファイルを書いたら動いてくれました

最初、llvmpipeであることに気づかず、なんかちょっともっさいなぁ…RX550なんてこんなもんかと思ってしまったのは内緒。逆にいうとllvmpipeやっぱりすげぇ… *1

さて問題は冷却ファンであります。 これがねぇ…linuxの場合だからか知りませんが、デフォで全開なんですよ。 以前買ったRX460は何もせずとも極めて静音なので、やっぱ廉価版と差があるのだなぁと

いよいよとなったら付属のファンを撤去、12cmファンを塩ビアングルでの自作マウンタで設置し、atmega328かlpc1124でpwm制御して温度センサーと連動で…と思いましたが、正直面倒なので取り敢えずlinuxからファンコントロールできんかな?と思ってやってみたら、簡単に出来ました(^^) *2

当然ながら、全部sudo権限が必要ですが まず、pwm制御をenableします

echo 1 > /sys/class/drm/card0/device/hwmon/hwmon2/pwm1_enable

次にpwmレベル数値を設定します

echo 66 >  /sys/class/drm/card0/device/hwmon/hwmon2/pwm1

都会よりはずっとマシな温度のド田舎のうちでは、66ぐらいで1600rpmぐらいとなり、静かさ・温度ともにちょうどいいぐらいかな…という感じです。まぁ高負荷な処理もほぼしませんので取り敢えず。

daemonにして十分おきぐらいでアツアツになってたら全開とかやっても良いのですが、それこそもう自作マイコンでやったほうが良いんじゃないかなって気はします

なお、サスペンドから復帰した際にはファンが全開に戻りますが systemdだと/etc/pm/sleep.dではなくsystemdのスリープフックでの設定が必要です

Archwikiを参考に次のようなものを書いてmy_on_resume.serviceとして、使っています

[Unit]
Description=User resume actions
After=suspend.target

[Service]
Type=simple
User=root
ExecStart=/usr/local/bin/my_on_resume.sh

[Install]
WantedBy=suspend.target

RX460+PentiumGの環境では、何故かgvimでのみ、ふとした瞬間に唐突にスローダウンする(てか、amdgpuだからか元々XorgのCPU占有率が高めで、しかも2コアしか無い)という意味不明の悲しい現象が起きましたが…RX460+i5-3470では平気だったし、RX550+i5-3470でもRX460+Ryzenでも平気なのですよな

まぁRyzenで平気なのは当然としても。すっかり我が家も多コア時代となってしまいました。つぅーか気づけばすっかりAMD派に…(^^;

ではでは

*1:なお、購入直後の動作チェックのためにi5-3450S搭載のサブマシンで試したときは、これは何の設定も使わずに挿しただけでamdgpuになったのですよね。実に不思議

*2:なお、llvmpipe状態でも、以下のファンコントロールは問題なく動作します。このせいでllvmpipeになってることに気づくのが遅れたともいえる

mypaint 2.0.0-alphaへの追従で四苦八苦

Mypaintの自作ブランチにupstreamのmergeを長期間怠っていたら、大変な作業量が待っていました…備忘録的に

目に付いた主な変更点

  • ビルドシステムがSConsからsetup.pyへ移行している
    • scons debug=trueに相当するのは、python setup.py build --debug
    • この結果、build/以下にlib.linux-x86_64-3.6 等のディレクトリが出来て、何かカッコいい。
  • scons cleanしておく必要がある(重要)
  • mypaintbrush 2.0が必要らしいので、gitから持ってきてmake installした。これが多分正しい。
    • エラーがでたので手当り次第に処置したけど、実はよくわかっていない…
  • python2.7でビルドするとlib/gettext.pyのfrom gettext import gettextでImportErrorが起きるように見える。
    • git cloneしたバニラなmypaint 2.0でも起きた
      • しかしpython3で一旦ビルド成功後は、python2.7でビルドし動かしても動いたりして二度と再現されない。要追跡。
    • いずれにせよ、そろそろpython3に本格的に乗り換えないとなぁ…という感じ。他の自作スクリプトも随時書き換えて行かねば。
  • update-alternativesで3をデフォにしてもいいけど、python-moinmoinが3非対応だったりして別の部分でつらい

_mypaintlib.soが見つからないエラーが出る

前述の通り、cleanしておかないとSConsで作られた古いmypaintlib.pyが lib/に残りこのエラーが起きる。削除すること。また、当然ながら以前のlib/_mypaintlib.so自体も削除しておくのが良い。

ビルドスクリプトの追加修正

独自のcppモジュールを追加するには、lib/mypaintlib.iのみならず、 setup.pyの576行目近辺

    mypaintlib = Extension(
        'lib._mypaintlib',
        [
            'lib/mypaintlib.i',
            'lib/fill.cpp',
            'lib/gdkpixbuf2numpy.cpp',
            'lib/pixops.cpp',
            'lib/fastpng.cpp',
            'lib/brushsettings.cpp',
            'lib/pyramidfill.cpp', # XXX for pyramid-fill
            'lib/opencv_util.cpp', # XXX for adjust tool
        ],

のように改変。(以前、SConstructにやっていたのと同じ)

Python3対応

  • SWIG(cpp)においてPyInt_AsLongはpython3のAPIでは存在しないので、PyLong_AsLongに変更する。(lib/pyramidfill.cpp)
  • from _future_ import division, print_functionはスクリプト先頭に置かねばならない。
    • git mergeでの自作部分とのコンフリクト検出時に不用意に済ませるとpython3ではこれがエラーとなる

importの厳密化

例えばgui/drawwindow.py でimport sizechangepopupとしてもエラーになるようになっている。 これは、

from . import sizechangepopup 
# もしくは
import gui.sizechangepopup as sizechangepopup # 若干美しくない気がする

と書かなければならない。これが物凄い多いので、普段からディレクトリを明示する等して徹底しておくべきだったorz

with_wait_cursor

このデコレータはgui.drawwindowから、gui.widgetsに移管されている。

python2のpropertyとタプルの分解で苦労した話

検索ワードが思いつかず苦労しました。アンパックというのですね。

foo, bar = (100, 200)

みたいなやつ。

例えばこんなクラスがあったとします

class _Node:
       
    def __init__(self, x=0.0, y=0.0):
        self._v = np.array( (x, y, 1) )

    @property
    def x(self):
        return self._v[0]

    @x.setter
    def x(self, v):
        self._v[0] = v

    @property
    def y(self):
        return self._v[1]

    @y.setter
    def y(self, v):
        self._v[1] = v

numpy.arrayを隠蔽して、簡単に行列とかかけられつつ 普段は感覚的に.x, .yとかで位置を変更できるという感じの。

こいつにこれをやっていました

n = Node()
n.x, n.y = (100, 101)

これをやるとダメ。

python2ではインスタンスnのプロパティにアンパックではなく、同名のxやyという属性が作成されてしまい、内部値は二重に存在している状態で、二度とプロパティでアクセスできなくなるのでございました。

なおpython3では普通に動くのでありました

ってこの話、遠い昔に見たような気がしなくもなく…(^^;

また一つpython3に乗り換える理由が増えた気がします