mieki256's diary



2016/10/01() [n年前の日記]

#1 [nitijyou] 写真画像に線を引いて眺めてたり

ゲームに使えそうなキャラ画像を描きたい。横方向から見て走ってる感じのソレにしたいなと。しかし横顔がどうもうまく描けなくて。

Google画像検索で横顔写真っぽい画像をいくつか入手して、Inkscape を使って線を引きまくってじっと眺めて比率を確認してみたり、A・ルーミス著「やさしい人物イラスト」内で紹介されてる球と平面で構成される顔のソレと比較してみたりしてるところ。

この手のソレはあちこちで目にしてたけど、自分で比率を把握しようと思わない限り、あるいは実際に手を動かして確認しない限り、記憶にさっぱり残らないのだなと今更痛感。

#2 [anime] Thunderbolt Fantasy 東離劍遊紀、最終回を視聴

虚淵脚本と現代布袋劇(台湾の人形劇)の組み合わせ、の作品。

面白かった…。映像がとにかくド派手で、ただ見ているだけでもゲラゲラ笑ってしまうぐらいに凄まじかったけど。映像のみならず、脚本がイイ…。キャラもそれぞれ立ってたし、会話のやりとりも時々ニヤリとしてしまうしで。かっこいいとは、こういうことさ。みたいな。

終わり方も実に上手い。あの繋げ方、イイな…。なんだか作品としてビシッと決まった感じがする。素晴らしい。

2016/10/02() [n年前の日記]

#1 [ruby] RubyのGUI関係のアレコレをググってる

FxRubyてのが、ちょっと気になるのでググって関連情報を眺めてるところ。

2016/10/03(月) [n年前の日記]

#1 [prog] Tcl/Tkのteacupってなんじゃろ

Tcl/Tk関連ページを眺めてたら、teacup なる単語が目に入った。なんじゃろコレ。

_Teacup_TEApot と TEAcup:Rainy Day Codings:So-net blog を眺めると…。ActiveTclをインストールしてある環境なら使えるらしいパッケージマネージャ、ということでいいのかな。

以下はWindows10 x64環境での話。ActiveTcl を C:\Tcl にインストールしたものとする。

DOS窓で tclsh と打つと Tcl のコマンド入力(?)ができる状態になる。そこで teacup list と打てば、インストールできるパッケージの一覧が表示されるらしい。

例えば、ファイルのドラッグアンドドロップができるようになる tkdnd をインストールしたければ、teacup install tkdnd と打てばいい。

インストールされたアレコレは、C:\Tcl\lib\teapot\package\win32-ix86\lib\ 以下に入る。確認したところ、tkdnd2.8 というフォルダが増えていた。

teacup のバージョン確認。
% teacup version

        8.5.18.0.299124

ヘルプ表示。
% teacup help

    teacup.EXE
    is a tool to access package repositories

        teacup.EXE help cmds-by-group   Grouped list of commands provided by teacup
        teacup.EXE help commands        Alphabetical list of commands provided by teacup
        teacup.EXE help help            How to use help
        teacup.EXE help options         Describes the standard options
        teacup.EXE help queries         Describe the syntax of complex queries

ちなみに tclsh から抜けるなら exit と打てばいいらしい。

#2 [ruby] Ruby/GTK2をアップデートしようとしてハマった

Windows10 x64 + Ruby 2.2.5 p319 で、Ruby/GTK2 ( _gtk2 ) 3.0.9 をインストールしようとしてハマった。

本来であれば、gem install gtk2 と打てば必要なものをインストールしてくれるはずだけど、現時点では _gio2_gobject-introspection もインストールしないといけないらしい。

_gdk_pixbuf2 3.0.9 lacks dependency - Issue #862 - ruby-gnome2/ruby-gnome2
_Detection of libgirepository-1.0 broken - Issue #900 - ruby-gnome2/ruby-gnome2
@kou 3.0.9にgio2とgobject-introspectionがgdk_pixbuf2に必要とされているが、
gdk pixbuf に gobject-introspection と gio2 の gem が必要とされていません。
また、gobject-introspectionが必要とされていることで↑の様に
rsvg2などを使っている場合 heroku 度にビルドパックなしでデプロイが出来ない状況です。
今ビルドパックを作ろうとしていますが、
そもそもgdk_pixbuf2にgio2/gobject-introspectionが必要なければ除いた方が良いのでは?と思います。
もし必要であれば gdk_pixbuf2 の gemspec に gobject-introspection と gio2 を足した方が良さそうです。

_gdk_pixbuf2: Fix runtime dependency by cosmo0920 - Pull Request #906 - ruby-gnome2/ruby-gnome2

#3 [ruby] RubyのGUI関連について調べてる

Windows上でRubyを使ってGUIアプリを書きたい。できればエクスプローラからファイルをD&D(ドラッグアンドドロップ)するとウインドウ内にファイルを読み込める仕様にしたい。

RubyでGUIアプリを書けるアレコレ、かつ、今もメンテナンスされてる気配があるソレと言うと、Ruby/Tk、 _Ruby/GTK2(Ruby-GNOME2?)_FxRuby 等があるわけだけど。FxRuby がちょっと気になってググってみたものの、D&Dはサポートしてないぜ、てな情報を見かけた。

_FXRuby Users - Drag and drop from the filesystem

FxRuby が使っている FOX なるソレがそもそもサポートしてないから無理なんだぜ、と書いてあるように見える。そうか。FxRuby はダメか…。

Ruby/Tk でD&Dするなら、おそらく Tkdnd を使ってどうにかするのだろうと想像するけれど、ググってみても使用例がまったく出てこない。Ruby関連のドキュメントの中に Tkdnd という文字列はあるから、何かと絡めて実装されてるのかもしれんけど…。

Ruby/GTK2 は、ファイルのD&Dをサポートしてるっぽい。

_Ruby Window Drop: いち雑記

その他のGUIのアレコレについて。 :

_Rubyist Magazine - Ruby de GUI でいろいろ紹介されてるものの、2004年の古い記事だし、QTRuby、Ruby/FLTK、wxRuby、VisualuRuby は開発停止で死んでるし。

一時期 _Shoes なんてものも話題になったけど、 _Shoes - Wikipedia によると、これまた2009年に一度死んでいたらしい。有志がforkだかメンテナンスを続けて、Ruby/GTK2を利用した _green_shoes は2013年で止まってるし。

Qt を利用したソレは、 _qtbindings という形で使えるのかもしれない。もっとも、2015年の5月、つまり去年の5月から開発が止まってるようだけど。

_Ruby の GUI ライブラリ - メモ@wantora でも、2012年の時点での各フレームワークの死滅具合が列挙されてた。

Ruby、ボロボロやな。 *1

*1: いや、でも、Rubyに限った話じゃないよな。Go言語から使えるGUIフレームワークなどは、もっと酷い状況だし。

2016/10/04(火) [n年前の日記]

#1 [cg_tools] Asepriteを試用

普段自分がドット絵を作る時は _EDGE2 を使っているのだけど。最近は _Aseprite というドットエディタの評判がいいらしいので少し試用してみたり。

_Asepriteメモ - 茶ポーン
_EDGE と Aseprite と DXRuby と私 - あおたくノート
_asepriteというドット絵作成ソフトが結構良い感じです: ドット絵とアイデアと
_Aseprite - HaxeFlixel Wiki
_Blenderいいよたぶん: Aseprite
_Aseprite v1.1.3 - ドット絵ペイント&スプライトアニメーション制作が可能な高機能ピクセルアートツール!Win&Mac&Linux

Aseprite は有償ソフト。現行版のバージョンは 1.1.9。体験版(ファイル保存ができない)もあるので、試用だけならできる。また、昔のバージョン 0.9.5 ならフリーで使えるらしい。 _Aseprite - Download の下部の Older Versions を選べばフリー版を入手できる。

今現在は Steam でも購入できるようになってるそうで。Steamって使ったことないけど。

_Steam:Aseprite

1,480円だから、EDGE2 の4,000円よりも随分と安い。のかな。たぶん。

タイルモードは便利っぽい。 :

現行版で実装されてるタイルモードが便利そう。View → Tiled Mode で使える。これならシームレス画像を作りやすい。フリーで使える 0.9.5にはついてない機能だけど。

_Aseprite - How to make Seamless Tiles - YouTube

ちなみに、無料で使えるお絵かきソフトの _Krita にも同様の機能がある。表示 → ラップアラウンドモード、を有効にすれば使える。

_Seamless stone texture using Krita - YouTube

また、 _Pyxel Edit というドットエディタも、キャンバスに同じ番号のタイルを敷き詰めることで、似たようなことができる。

_Pyxel Edit Tutorial Part 2/3: Tiling - YouTube

一応 EDGE2も、範囲をキャプチャフレームしてから、アニメーションプレイヤー → 表示 → タイル表示、を有効にすることで、マップ状に敷き詰めたような表示ができるけど。あくまで表示確認ができるだけ、でしかないので、上下左右が繋がるようにドットを打っていくのは面倒というか、正直ちょっとした拷問レベルの作業だったり。できれば Aseprite や Krita のような機能が欲しいところだけど…。

日本語フォルダが一切表示されない。 :

Aseprite は英語圏のアプリだけあって、日本語フォルダが一切表示されないようで。マイピクチャ(Pictures)すら表示できないのは困った…。自分はえてして作業ファイルをそこに入れてしまってるので…。

ビルドしてみた。 :

関連情報をググってたら、Aseprite はオープンソースなので github からソースをゲットして自分でビルドすることもできるよ、という情報を見かけたり。

_Asepriteのマウスポインタを固定する | 日々創作
_カクカンジメチル:ASEPRITE v1.1.0-devをビルド - livedoor Blog(ブログ)

面白そうなので自分も試してみたり。環境は Windows10 x64。必要になるのは、 _Git for Windows_Visual Studio Community 2015_CMake

前述の記事に従って作業してみたら、警告がやたらと出てきたけど一応ビルドできて exe が出来上がった。実行してみたら動いてしまった…。

もっとも、一見ビルドが通ったように見えても何か不具合があるのかもしれず。それに、Visual Studio をインストールするとHDDの空き容量が数GBほど減るわけで。自分などは昔の Visual Studio ほにゃらら Express なども入れてるから 12GB ぐらい使ってたり。ということで、Aseprite を使いたいがためにHDDを数GB使うぐらいならフツーに購入したほうがいいですよね、という気もしたり。

#2 [cg_tools] ドットエディタの購入方法が分からない

_Aseprite は Steam でも売ってるらしいので、そこから買えばいいのかなと思うのだけど。 _Pyxel Edit の買い方が分からない…。英語が分からん…。

#3 [cg_tools] EDGE2への不満

前述の Aseprite 等のタイルモード・ラップアラウンドモードが欲しい、てのはともかく。

触ってて思ったけど、EDGE2は、マウスホイールでキャンバスの表示倍率を変更できないのが地味に不便だなと。Zキー、Xキーに一々手を伸ばすのも面倒だし。

一応、Ctrl + ホイールで倍率変更できる、ということになってるけど、ビミョーにツライ。いやまあ、他のOffice系アプリもえてしてそういう操作ではあるけど、CGツール関係はそこらへん違う仕様になってる場合が多いよなと。何故なら、文書作成作業と、画像作成作業では、表示倍率を変更する頻度が全然違うわけで…。そりゃ少しでもサクッとできるようにしておいたほうが作業効率は上がるよなと。

もっとも、ペンタブレットで作業してるときはホイールを使いようもないから…。マウスで作業してるときは欲しい機能だけど、利用入力デバイスにもよるんだよな…。

ちなみに、過去にも要望としては出ていたらしいけど。

_edge2/要望/111 - ホイールスクロールで拡大縮小 - Takabo Soft Wiki*

2012年に要望が出されたまま放置状態らしい。


そういえば、マウスの中ボタンドラッグでもスクロールができたら…と思ったら、コレは開発版で実装済みらしい。

_edge2/要望/163 - 中ドラッグでハンドスクロール - Takabo Soft Wiki*
_edge2/ベータ版 - Takabo Soft Wiki*

試してみたら、たしかに中ボタンドラッグでキャンバスのスクロールができた。素晴らしい。

#4 [digital] RD-BZ710がフリーズ

HDDレコーダ TOSHIBA RD-BZ710がまたフリーズ。

状況としては、予約録画が終了して、画面右上に準備中の例のクルクル回るアイコンが出てきているタイミングで、うっかりタイニー番組ナビゲータからレコーダの予約情報を取得しようとしたら画面が固まってしまって。TV映像もピタリと止まってるし、アイコンのアニメも止まってるしで。参った。どうしよう。

リモコン操作も一切受け付けなくなっていたので、仕方なく本体電源ボタン長押しで電源を切ってから入れ直してみたけど、録画したばかりの番組は消滅していた。

ということで、録画終了時の準備中アイコンが出ているタイミングで、ネット経由で何かをしようとしたら、フリーズするようだなと…。まあ、「○○をしているタイミングで△△をするとフリーズする」なんてのは、ありがちなバグだし、噂によるとHDDレコーダって3つのOSが同時に動いている面倒臭い機器らしいから、色々と仕方ないところもあるのだろう…。

ていうか今はもう東芝はHDDレコーダを作ってないので今更な話ではあるなと。でも、液晶TVのREGZAシリーズも同種のバグが残ってたりしないのだろうか、などと余計な心配を。

いや、さすがにこの手のバグは、チェックリストの類を作ってテストして、潰してあるよな…。あるんだろうな…。どうかな…。東芝だもんな…。

2016/10/05(水) [n年前の日記]

#1 [cg_tools] 基本パーツを組み合わせてドット絵を作成するドット絵バケーションを試用

ググってたら、ちょっと気になるソフトを見かけたわけで。

_ドット絵バケーション : Vector

基本パーツを自由に並べて、新たにドット絵を作っていくソフト、らしい。

なるほど、プラモ感覚でパーツを合体させて3Dモデルを作成する、 _DOGA-L1 のドット絵版、みたいなものかな…。あるいは、自分が以前、Ruby + DXRuby を使って書いた、 _8bit PC CG Editor に近いアプローチかもしれないなと。要は、パーツをひたすら“選んで”並べていけば誰でもそれっぽいモノを作れるだろう、みたいな。「『描く』のは難しいとしても、『選ぶ』ことならできるでしょ?」みたいな。…考えてみたら年賀状作成ソフトなどはどれもそういうアプローチなのだな。

2002年頃に作成されたソフトのようで、対応環境は Windows Me/2000/NT/98/95。かなり古い。が、幸いそのままでも Windows10 x64上で起動してくれた。ただし、下手に互換モードを有効にしてしまうと「ちゃんとしたexeじゃねえよ」と Windows10 に怒られて逆に動かなくなるので注意。

ウインドウの右側にプリセットなる基本パーツが並んでいるけど、クリックしても選択できなくて悩んだり。やはり古過ぎて Windows10 では動作しないのか…。と思ったが、何かの拍子にドラッグしたらキャンバス内に貼り付けられることに気づいたり。クリックしてブラシ選択、ではなくて、パーツをドラッグして並べる、てな操作仕様らしい。や、添付マニュアルでちゃんと説明されてたことに後から気づいたけど。

文章だけでは操作が分かりづらいだろうから、一応スクリーンショットも貼っておく。パーツをマウスでドラッグすると貼り付けられる様子がお分かりいただけるだろうか。

dotevake_ss01.gif


10年以上前のソフトなので、キャンバスが小さいのに拡大表示できなくて視認性が悪い上にキャンバスサイズも変更できない等、正直なところ使い勝手はよろしくないけれど。この発想は全然アリだよなと。開発終了してしまったのが惜しまれる。もっとも、2000年前後はドット絵がかなり迫害されて、この手のツールの需要がほとんどなかった時期のような気もするし、放置されてしまったのは仕方ないのかも、などと思ったりもする。

基本パーツを合成するソレ。 :

基本パーツを合成することでより複雑なドット絵を生成する、というアプローチは、色々なソフトやツールが取り入れてるようで。

例えば、ウディタ(WOLF RPGエディター)には、「グラフィック合成機」というソレがある、とググっていて今頃知った。

_WOLF RPGエディターのダウンロード
_グラフィック合成器を使ってみよう - はじめてのウディタ 挫折して再び Wiki*
_グラフィック合成器パーツ画像作成にPhotoshopCCの機能を活用! : ぴぽや
_ぴぽや32x32グラフィック合成器用パーツ 基本セット修正版 : ぴぽや

また、enchant.js にも、avatar.enchant.js なる、組み合わせでキャラ画像を作成できるソレがあるらしい。

_wise9 > avatar.enchant.jsが登場!2000万通りの中から君だけのキャラが作れる!?

どちらもキャラ画像の生成に特化してそうなあたりがちょっと気になるけど、2D RPG などはたくさんキャラ画像を作らないといかんだろうから自然とこうなったのかなと。

#2 [gimp][cg_tools] GIMPやPhotoshopのブラシ画像のフォーマットについてもやもや

ドット絵バケーションを試用しているうちに、もしかして、この基本パーツ画像を、GIMP や Photoshop のブラシファイルに変換して使えば、より使い勝手のいい作業環境が得られるのではと思えてきたわけで。

てなわけで、GIMPのブラシファイルのフォーマットについて調べていたのだけど。考えが甘かった。

まず、GIMP や Photoshop のブラシ画像は、グレースケール画像であることが前提で。黒い部分が不透明、白い部分が透明として扱われる。そしておそらく、アルファチャンネルは持てない。

この仕様では、思ったような描画結果を得られないはずで。

例えば、こういうブラシを作りたいと思って、画像モードをグレースケールにしてドットを打ったとして。

pixel_art_brush_ss01.png

GIMPのブラシファイル(.gbr)として保存してから、ブラシフォルダ(~/.gimp-2.x/brushes/)にコピーして使ってみると、以下のような結果になる。

pixel_art_brush_ss02.png

描画色はたしかに反映してくれたけど…。いやあ、そうじゃねえんだよ…。

ブラシファイル(.gbr)を画像として開いてみる。

pixel_art_brush_ss03.png

透明だった部分は真っ白になっていた。予想通り、アルファチャンネルは残ってない。これではいかんのだ。

要は、こういう状態のブラシを作りたいわけで…。

pixel_art_brush_ss04.png

そのためには、ブラシファイルがアルファチャンネルを持ってないと困るのだ。

調べた範囲では、Photoshop の場合、ブラシ画像 = グレースケール画像なので、ここで手詰まりらしい。

しかし、GIMP ならもう少し手段が残ってる。GIMP のブラシ画像には種類があって、グレースケール画像と、RGBAのフルカラー画像の2種類があるそうで。後者にすればアルファチャンネルを持てる。作り方は、画像をRGBモードにしてから .gbr として保存するだけ。

試してみたら、こうなった。

pixel_art_brush_ss05.png

ちゃんと、白・黒・透明が出てる。

が、しかし、この種類のブラシ画像 ―― RGBAフルカラーブラシは、グレースケールのブラシ画像と違って、描画色が一切反映されない仕様だったりする。

さて、聡明な方なら、ここで疑問が湧くはず。

問題: RGBAフルカラーブラシを使いながら、描画色も変えたい場合、どのようにすればよいのだろう?

答え: 色を変えたブラシ画像をたくさん用意して頑張れ。

pixel_art_brush_ss06.png

アホだ。いや、もう、どう考えてもアホとしか思えないのだけど。他に手はないのか。どうなんだ。

でもまあ、昔に比べたらまだマシで。昔のGIMPは、プログラム側でブラシサイズを変えることすらできなかったので、「ブラシサイズを変えたいなー」と思ったら、『異なるサイズのブラシ画像』を『たくさん用意して』頑張ってました。…そんな時代に比べたら、これでもまだ改善されたほうだったり。でも、やっぱりまだまだアホだ。

ということで、Photoshop だの GIMP だの、画像編集ソフトって結構完成度が高まって改良できる余地はそれほど無いような印象があったのだけど、こうして見るとブラシ機能一つとってもまだまだ発展できる余地がありそうだなと思い直した次第です。

と言っても、このあたり、ブラシ機能の範疇に入るのかな、別の機能として扱うべき話じゃないのか、などと疑問が湧いたりもするのですが。

打開策は無いのだろうか。 :

現状で打開策は無いのだろうかと考えてたら、ふと、フィルタを使って少しはどうにかなるかなと思えてきたり。

とりあえず、RGBAフルカラーブラシを使ってパーツ相当をペタペタと描いてから、色 → 着色、を呼び出して色を変えてみたり。

pixel_art_brush_ss07.png

意外とそれっぽくなったような気がする。色を変えたい部分をひたすらレイヤー分けしておくか、あるいは選択範囲をその都度作成してフィルタをかける、等々、ちょっと面倒なところもあるけれど、全部ドットを打っていくよりは楽かなと。

まあ、一つ描いて色を変えて、ソレを選択してコピーして貼り付けしていくやり方でもどうにかできるんじゃね、という気もしてきたけれど。

2016/10/06(木) [n年前の日記]

#1 [gimp][scheme] GIMPのScript-fuで文字列のゼロ埋め

GIMP の Script-fu というか TinyScheme で、数値から文字列に変換したものを桁揃え? ゼロ埋め? ゼロパディング? とにかくそういう感じの処理ができるか試したり。

GIMP起動後、フィルタ → Script-fu → Script-fuコンソール、を起動して、以下のように打ち込みながら動作確認。
> (define cnt 3)
cnt
> cnt
3
> (number->string cnt)
"3"
> (string-append "0000" (number->string cnt))
"00003"
> (define str (string-append "0000" (number->string cnt)))
str
> str
"00003"
> (substring str (- (string-length str) 3) (string-length str))
"003"
> (substring str (- (string-length str) 4) (string-length str))
"0003"
> (substring str (- (string-length str) 2) (string-length str))
"03"

要するに…。
ということで、できそうだなと。

一行で書くと、こうだろうか。
> (let* ((s (string-append "0000" (number->string cnt))) (n (string-length s))) (substring s (- n 2) n))
"03"

#2 [gimp] GIMPのブラシ画像ファイルを作成する手順をメモ

GIMP用のブラシファイルを作成する手順をメモ。

ブラシの種類。 :

まず前提として。GIMPのブラシ画像(一枚絵、.gbr)には2種類ある。
  • グレースケール画像。白が透明で、黒が不透明(描画色)。描画色が反映される。アルファチャンネルは持てない。
  • RGBA画像。アルファチャンネルを持てるが、描画色は反映されない。色は、画像の色がそのまま使われる。
GIMP上では、画像 → モード → RGB or グレースケールを選ぶことで画像の種類を切り替えられる。

他にも、複数の画像を登録したパイプブラシ(.gih)や、ブラシエディターだけで作れる媒介変数つきブラシ(.vbr)、なるものもあるらしいけど。そのあたり自分は作ったことが無かったり。

_7. ブラシの追加

ブラシを一つ作成。 :

GIMPで、ブラシにしたい画像を作成するなり開くなりして、ファイル → 名前を付けてエクスポート → 拡張子を「.gbr」にして保存すればブラシファイルになる。

ブラシを追加。 :

GIMPへブラシを追加する方法は、以下のページを見てもらったほうが分かりやすいかも。

_追加ブラシの使用方法
_GIMPにブラシを追加する方法 | GIMP2の使い方
_ゼロからのGIMP: ◆ブラシを追加する

ちなみに、brushes フォルダの中にサブフォルダを作って、その中にブラシファイルを追加すれば、それらのブラシにはサブフォルダ名でタグが付けられる。GIMPのブラシウインドウの上のほうに「タグで検索」てのがあるので、少しは有効に活用できるようになる、はず。

_2. GIMP
_3.6. タグづけ

たくさんブラシを作りたい。 :

ブラシを1つ2つ作るだけなら、前述したように、一つ一つを手作業で保存して作ればいいのだけど。場合によっては、十数ファイルぐらいを一気にたくさん作りたい時もあって。

そういう時は、 _GIMPでPhotoshopのブラシを使おう の下のほうで紹介されてる、save-all-layers-as-brushes というスクリプトを使うと楽。コレを使うと、全レイヤーを1つ1つブラシファイルとして自動で保存してくれる。尚、件のページにはスクリプトの追加の仕方も書いてある。

例えば、ブラシにしたいpng画像がたくさんあったら、ひとまずGIMPのレイヤーにどんどん追加していく。
  • エクスプローラその他から、GIMPのレイヤーウインドウ?にドラッグして追加。
  • または、GIMP上の、ファイル → レイヤーとして開く、でもOK。ファイル選択時に、Shift や Ctrlキーを押しながらクリックすれば複数のファイルを選んで一度にたくさん追加できる。
その後、ファイル → レイヤーをブラシとして保存、を選べば、一気に大量のブラシファイルとして保存してくれる。ありがたや。

ただ、このスクリプト、ちと問題があって。十数ファイルほど保存すると、番号が、hoge1、hoge10、hoge12、hoge2、…みたいなことになってしまう。ブラシの順番がグチャグチャになるというか。

ということで、hoge01、hoge02、... hoge10、hoge11、となるように、スクリプトを少し修正してみたり。

_ブラシ保存スクリプト(Save All Layers as Brushes)で保存されるブラシ名の連番部分をゼロでパディングするように修正。

元のスクリプトのライセンスはGPLだから、改造版もソースを出しとけば問題ないだろう…。

#3 [gimp] ドット絵用のGIMPブラシを作成

ドット絵バケーションの基本プリセット画像を参考にしながら、自分もドット絵用のパーツ画像を描いてみたり。

ちなみに、ライセンス的にドット絵バケーションのソレをコピペして使うわけにはいかないはずなので、全部自分でドットを打ち直しました。シルエットは同じでも、中のドットの打ち方やパレットの値は違うはず。

_pixelart_parts_a.png
pixelart_parts_a.png
各パーツは32x32ドット。画像のライセンスは CC0 / Public Domain ってことで。自由に使ってくださいな。

さて、この画像を、GIMPのブラシファイルに変換していきたいわけで。

画像を分割。 :

前述の画像を32x32ドット単位でバラバラに分割する。

今回は、Windows10 x64上で、 _PL_ImageConstructor というツールを使わせてもらって分割してみたり。このツール、透過png(アルファチャンネルを持ったpng)を対象にして色んな処理ができる。素晴らしい。

ちなみに、ImageMagick を使っても分割できるはず。 _Cutting and Bordering -- IM v6 Examples によると、-crop やら +repage やら +adjoin やらを使えばいいらしい。試してないけど。

さておき。これで、24ファイル、32x32ドットの透過pngができた。 → _split_images.zip

GIMPにレイヤーとして読み込む。 :

この大量の透過pngを、GIMPで読み込む。最初のファイルを開いたら、残りのファイルをレイヤーウインドウにドラッグ。これで全画像がレイヤーになってる画像を作れた。念のために一旦保存しておく。ファイル → 名前を付けて保存。.xcfファイルとして保存した。

ブラシファイルとして書き出す。 :

Save All Layers as Brushes.scm を導入しておいて呼び出す。ファイル → レイヤーをブラシとして保存。ブラシファイルができた。

完成品はコチラ。

_gimp_brushes_pixelart_parts_a_20161006.zip (25KB)

CC0 / Public Domain ってことで。自由に使ってください。

ブラシとして追加すると、ブラシウインドウ内では、こんな感じの見た目になる。

gimp_ss01.png

使い方。 :

ドット絵用のブラシなので、「鉛筆ツール」を使って、ブラシサイズをデフォルトにして使ったほうがいい、はず。

gimp_ss02.png


そのままだと白黒でしか描画できないので、色 → 着色、を使ってチマチマと色を変更したり。

とりあえず、こんな感じのソレがサクサク作れる。
sample_01.png
gimp_ss03.png

まあ、これだけではショボいけど。もっと使えそうなパーツを増やしていけばどうにかなる、のかな。どうかな。どうなんでしょう。

2016/10/07(金) [n年前の日記]

#1 [gimp] GIMPのブラシの謎

GIMP 2.8.18 Windows版を触ってて、ちょっと気になった点が。ブラシ名って変えられないのかな…。昨日作ったブラシの名前を変更しようとしても変更できなくて。謎。

_3.2. ブラシダイアログ によると、
一覧表に列挙して表示

基本的には「並べて表示」と使い方に大差ありませんが、 次の点が異なります。
ブラシの名前はダブルクリックすると編集ができます。
ただし名前を変更できるのは自作のブラシや自前でインストールしたものに限られ、 GIMP 同梱でインストールされたブラシは対象外です。
仮に同梱版のブラシの名前を変えてみれば確かに編集はできますが、 確定しようとして Enter キーを押したりどこか他のところをクリックするとたちまち名前は元に戻されてしまいます。
GIMP と同時にインストールされるブラシ、 パターン、 グラデーションなどの資源は原則として変更不可です。
変更できるのは自作のものや自前のものだけです。

3.2. ブラシダイアログ より

とあるのだけど…。 にも関わらず、何故に情報を書き換えられないのだろうと。謎。

ブラシファイル内の情報を書き換える専用ツールでもあれば…。しかしググってみてもそんなツールは存在しないようで。需要が無いのかな…。

ちなみに、.gbrのファイルフォーマット仕様は、gbr.txt なるファイルに書かれてるらしい。たぶんコレだろうけど。

_Gimp-Matting/gbr.txt at master - rggjan/Gimp-Matting

ブラシ名を変更するとブラシ名の文字列の長さが変わるので、その都度ヘッダサイズを変更しないといけない、ように見える。例えば事前に255文字まで登録できるように領域を固定で確保、とかそういうフォーマットではなかったようで。

#2 [gimp] GIMPでGIFアニメを作成する際のwait値を変更するスクリプトその2

GIMP でGIFアニメを作る際、レイヤー名に (100ms) 等の指定を描いておけば、そのwait値でそのレイヤー(というかフレーム)を表示してくれるわけだけど。再生速度を変更したい際は、全レイヤー名を一括変更できるスクリプトがあるとありがたいわけで。

一応、 _2015/11/28の日記 で一度探してはいたのだけど、今回探したら他にもヨサゲなスクリプトを見かけたので感謝しつつメモ。

framerate-change.scm :

_フレーム表示時間変更

UIが日本語で書かれてるので分かりやすい。また、Script-fu で書かれてるので、「スクリプトを再読み込み」ですぐに使えるようになる。ありがたや。

rename_layers_for_animation.py :

前回探した時に見かけたスクリプトも再掲。 :

_Animation Frame Delay - GIMP Scripts

ファイル名は、layer_names.py。

_Ofnuts' Gimp Tools - Browse /scripts at SourceForge.net

ファイル名は、retime-linked-layers-0.0.py。

#3 [pc] NVIDIA GeForce Experience 3.xが酷い

自分のメインPCに積んでいるビデオカードは、NVIDIA GeForce GTX 750 Ti モノなのだけど。NVIDIA製GPUを使ったビデオカードで色々なことができるようにしてくれる、NVIDIA GeForce Experience というユーティリティがあって。

その、NVIDIA GeForce Experience 3.0.7.34 が出たよ、てな通知が表示されたので、試しに入れてみたり。

うむ。酷い。これはちょっと酷い。

まず、前バージョンの GeForce Experience をアンインストールする段階で、いきなりランタイムエラーが出た。インストール処理でエラー出すような作りはダメ過ぎだろ…。結局、最新ドライバーをDLしてきて前バージョンの GeForce Experience まで入れ直す羽目に。

ググったところ、他のアプリが動いてる状態ではその手のエラーが出ることがあるらしい。ので、色々停止させた状態で再度インストールを試したら、今度はエラーが出なかった。何が原因なのやら。

更に、使うためには NVIDIAアカント or Googleアカウント or facebookアカウントが必要になるようで。…迂闊に個人情報なんか集めたら後々流出問題云々で大変なことになるだろうに、どうしてわざわざ集めるんだ。NVIDIA は馬鹿なんじゃないのか。

でもまあ、とりあえず、NVIDIAアカウントを取ってみようかと思ったら。入力内容が不適切だった際の警告表示のフォントが、小さくて細くて薄くて何が書いてあるのか読めない。コレ、たぶん、英語圏に合わせてフォントサイズを決めちゃったんじゃないのか…。そのフォントサイズ使って日本語・漢字で表示したら読めなくなるんだよ分かってねえなあ。仕方なくWindows10標準のルーペ機能で拡大して推測しながら読むことに。こんなUIデザインにしたヤツは一体どこのどいつだ。馬鹿なんじゃないのか。

生年月日の入力欄の、月の入力が英語。どうしてそこは素直に数字にしとかないのか…。こんなところも英語圏の人間だけを意識して作ってやがる。馬鹿なんじゃないのか。

そして、ShadowPlayの設定項目が見当たらないのだけど…。どこに行った…。まさか、機能削除されちゃったのかコレ…。

ということで、あちらこちらに馬鹿じゃないのかと思える箇所がチラホラと。まあ、一番馬鹿なのは、対応ゲームを1本も持ってないのにわざわざインストールした自分なんですけど。

とりあえずアンインストールして、前バージョンに戻したり。

しかしコレ、3.x が正式公開になったらしいから、そのうち差し替えられてしまうのだろうな…。せめてもうちょっと改善してほしいのだけど。

2016/10/08() [n年前の日記]

#1 [python] wxPyhonを勉強中

ちょっとやってみたい処理があって、GUIアプリを作れそうなフレームワークについてググって調べてたり。…やっぱり wxPython が楽かな、と思えてきたのでサンプルソースを眺めてるところ。

ググってみた感じでは…。CG業界は PyQt を使うことが多いらしい。まずは Python がCG業界のアレコレに浸透していて、更に Python 3.x でも使えるとしたら、となると PyQt になるようで。ライセンス的には PyQt より PySide のほうがいいけれど、PyQt は Qt5 にも対応してるけど、PySide は Qt4 までの対応、らしいので…。

#2 [neta] ラスボスは居なかった設定とヒロインは別人だった設定

寝ていたら、夢の中に名探偵コナンの映像が出てきたのだけど。そこで出てきた設定が、目が覚めてからもなんだか引っ掛かったので、なんとなくメモ。妄想メモ。

一つは、黒の組織にラスボスは居なかった、という設定。

一つは、蘭姉ちゃんは本物と偽物(FBIから派遣されたコナン君の身辺警護をする人物)が居て時々入れ替わってた、あるいは、物語の早い段階で蘭姉ちゃんは組織から殺されていてずっと別人が蘭姉ちゃんを演じてた、という設定。

目が覚めてから、なんじゃその設定、訳分からん、と思ったけど、もしかしてアレンジすれば何かに使えたりもするのかなと。

ラスボス居ない設定。 :

漫画やアニメに出てくる悪の組織にはえてしてラスボスが居るもんだけど。悪の組織のメンバー全員が、自分達にはボスが居るもんだろうと思い込んで行動してたけど、実はボスなんて居なかった、皆で居るような気がしていただけだった、というのは展開としてちょっとビックリ、しないかな。どうかな。どうなんだろう。なんだか、「ルパンなんて実は居なかった」をテーマにする予定だった押井ルパンをちょっと思い出したりもするけど。

考えてみたら、現実世界では時々そういう場面があるよなと。例えば、太平洋戦争中のアレコレは旧日本軍が行ったとか、軍の命令で泣く泣く実行したとかそういう話があったりするけど。よくよく調べてみると軍がそういう命令を出したという記録が無かったり、たまたま組織の長が精神論を振りかざして実行させたとかそういう事実が発掘されたりもするわけで。沖縄の自決強要のアレコレとか、上野動物園の大型動物の殺処分とか。上はそこまで命令出してないのに、どこかで誰かが話を捻じ曲げて、みたいな。

北朝鮮の将軍様だって、どこまでラスボス扱いが妥当なのかちょっと分からんところがあるよなと。太平洋戦争中の昭和天皇みたいなポジションだったりしないのかと。あの国ではなんでもかんでも将軍様の命令ってことにされてるけど、後になってから「俺そんなこと言ってねえ」てな話になる可能性だってあるよなと。戦時中の日本がそうだったのだし。

こういうのは、ラスボスなんて居なかったけど皆でなんとなくラスボスが居るような気になってた、てな状況に近いのかもなと。空気に支配されて行動してた、強いて言えば空気がラスボス相当だった、みたいな。ということで、現実世界ではあり得る話なのだから、娯楽作品内でもそういう設定があったら妙なところでリアルだな、という印象になりそうな予感も。まあ、空気云々はガッチャマンクラウズで描いてた記憶もあるけど。

もっとも、娯楽作品でそういう設定が出てきてしまうと読者視聴者はストレスが溜まりそうでもあるなと。ラスボス倒したぞ、コレでハッピーエンドだ、てなスッキリした話にならないわけで。皆が皆、アレは仕方なかったんだ、俺は与えられた仕事を忠実にこなしてただけだ、などと言い訳し始めるだろうし。戦犯は誰だよと問い詰めてみても口を揃えて俺じゃない俺じゃないの大合唱。これは見ていてかなりイライラするはず。A級戦犯のソレをちょっと連想しないでもない。てなわけで、ちと使いどころが難しい設定だったりするのかもしれず。

ヒロインは別人だった設定。 :

ヒロイン的ポジションと思ってたキャラが実は知らないところで別人とすり替わってた、てのはビックリ、しないかな。どうかな。どうなんだろう。

事前に「別人ですよ」と提示しておくなら、アリなのかな。例えば、∀ガンダムがソレだった気もするし。

実はヒロインが既に死んでいて、別人がそこに居た、というのは…。もしかするとEVAの綾波もその系統になったりするのだろうか。EVAの場合は、ヒロインがそうなる場面が分かりやすかったけど、もし、終盤で、「えっ? あそこでそうなってたの? …あー、言われてみれば」的な見せ方をしてたら更にビックリ、したのだろうか。したのかなあ。

名探偵コナンで、蘭姉ちゃんは既に死んでいた、とかだったらちょっとビックリ、どころじゃないだろうな。ソレを知った時のコナン君の気持ちを想像すると…。今まで散々、「俺が蘭を守る!」とか言ってたのに最初から全然守れてなかったとかどう考えてもキツイ。まあ、あの作品でそういう展開にするのは無理があるけど。

ヒロインじゃないけれど、俺は主役だ、主役級のキャラだ、と、主役が思っていたけど実はそうじゃなかった、てな感じの設定のRPGがあったような気も。 *1

読者視聴者は、このキャラは主役だろう、ヒロインだろう、などと思いながらその手の娯楽作品を楽しんでるはずだけど。そこを一旦何かしらでひっくり返すのは、なんだか面白くなりそうでもあるなと。

まあ、どれもこれも寝言です。ていうか夢の中で出てきた設定なので正真正銘の寝言ですが。

*1: や、自分は途中までプレイして放置しちゃったから本当にそういう設定だったのか分かりませんが、伝え聞くところによるとそういう感じの設定だったらしい。

2016/10/09() [n年前の日記]

#1 [cg_tools] JTrimが便利そう

プログラミングをする際の実験用画像を作成する際に、えてして画像をリサイズしてトリミングしたりするのだけど。たったそれだけの作業をしたいがために、GIMPを起動するのはシンドイなと。何せ自分の環境では、GIMPが起動するまで1分以上かかるわけで。ブラシ、フォント、スクリプト、プラグインを山ほど入れまくってるせいだと思うけど。

一応、 _PixBuilder Studio というフリーの画像編集ソフトも併用していて、コレなら結構早く起動してくれるのだけど。これはこれで、トリミング時の調整作業が難しくて。

起動時間が短くて、かつ、目的を果たせるツールはないものか…。と思ったところで、JTrim の存在を思い出して。試用してみたところ、圧倒的に起動が早かった。リサイズとトリミングぐらいなら、コレで十分だなと。

PhotoScapeも試してみたけど。 :

PhotoScapeというソフトの評判もいいみたいなので少し試用してみたけれど。リサイズ時のアルゴリズムが選べない上に結果がボケボケになるのが厳しいなと。機能豊富なあたりはヨサゲなのだけど。

#2 [prog] Visual Studio Codeを試用中

Visual Studio Code は、Microsoftが開発したエディタ。気になってきたので、インストールして少し触ってみたり。

_Visual Studio Code - Wikipedia
_Visual Studio Code - Code Editing. Redefined

Windows、Mac、Linux、どのOSでも動くらしい。

Electron を使ってるとのことで、 _Atomエディタ と似たようなものだろうかと思ったら全然起動が早くて驚いた。かなりイイ感じ。

Visual Studio Code にPython用の拡張を入れて、Pythonスクリプトを書いてみたけど、そこそこ補完が効いてくれる、ような気がする。イイ感じ。

#3 [python] wxPythonを再勉強中

wxPythonで表示したウインドウの中でマウスを動かすと何かが描ける、ようなことをしてみたい。

ウインドウ内がちらつく版。 :

最初に書いたのはコレ。

_drawing_test.py
"""
描画テスト。
マウスボタンを押した時だけ画面に何かを描画する。
ただし、この書き方では画面がちらつく。

VerySimpleDrawing - wxPyWiki
https://wiki.wxpython.org/VerySimpleDrawing

wxPythonのPaintDCでアンチエイリアスや透過を使う :右京web
http://hujimi.seesaa.net/article/161079355.html
"""

import wx

bg_image = "images/bg.jpg"

class DrawPanel(wx.Frame):

    def __init__(self, *args, **kwargs):
        """初期化"""
        wx.Frame.__init__(self, *args, **kwargs)

        # ワーク確保
        self.mouseLeftFlag = False
        self.pos = wx.Point(0, 0)

        # イベント割り当て
        self.Bind(wx.EVT_PAINT, self.OnPaint)
        self.Bind(wx.EVT_LEFT_DOWN, self.OnMouseLeftDown)
        self.Bind(wx.EVT_LEFT_UP, self.OnMouseLeftUp)
        self.Bind(wx.EVT_MOTION, self.OnMouseMove)

        # 背景bitmap画像読み込み
        image = wx.Image(bg_image)
        self.bitmap = image.ConvertToBitmap()

        # 画像サイズでフレームサイズを設定し直し
        self.SetSize(image.GetSize())

    def OnMouseLeftDown(self, e):
        """ 左ボタンを押した時の処理 """
        pos = e.GetPosition()
        self.pos = pos
        self.mouseLeftFlag = True
        self.Refresh()

    def OnMouseMove(self, e):
        """ マウスカーソルを動かした時の処理 """
        if self.mouseLeftFlag:
            pos = e.GetPosition()
            self.pos = pos
            self.Refresh()

    def OnMouseLeftUp(self, e):
        """ 左ボタンを離した時の処理 """
        pos = e.GetPosition()
        self.pos = pos
        self.mouseLeftFlag = False
        self.Refresh()

    def OnPaint(self, event=None):
        """ 描画処理 """
        pdc = wx.PaintDC(self)

        try:
            # アンチエイリアスをかける
            dc = wx.GCDC(pdc)
        except:
            dc = pdc

        dc.Clear()
        dc.DrawBitmap(self.bitmap, 0, 0, True) # 背景ビットマップ画像を描画

        if self.mouseLeftFlag:
            # 円を描画
            dc.SetBrush(wx.Brush(wx.Colour(255, 0, 0, 64)))
            dc.SetPen(wx.Pen("RED", 2))
            x = self.pos.x - 50
            y = self.pos.y - 50
            dc.DrawEllipse(x, y, 100, 100)

if __name__ == '__main__':
    app = wx.App(False)
    frame = DrawPanel(parent=None, title=u"ウインドウ内でマウスボタンを押してみるべし")
    frame.Show()
    app.MainLoop()

しかしこのスクリプトは画面がちらつく。

wxpython_ss1.gif

ウインドウ内がちらつかない版。 :

画面がちらつかないようにするには、wx.BufferedDC() を使うといいらしい。

_drawing_test2.py
"""
描画テスト。
マウスボタンを押した時だけ画面に何かを描画する。
ちらつきを少なくする。

VerySimpleDrawing - wxPyWiki
https://wiki.wxpython.org/VerySimpleDrawing

wxPythonのPaintDCでアンチエイリアスや透過を使う :右京web
http://hujimi.seesaa.net/article/161079355.html

wxPython の画面に色々描く(+マウスのイベントを処理する) - 見切り発車
http://d.hatena.ne.jp/uyamae/20090305/1236261541
"""

import wx


class DrawPanel(wx.Frame):

    def __init__(self, *args, **kwargs):
        """初期化"""
        wx.Frame.__init__(self, *args, **kwargs)

        # ワーク確保
        self.mouseLeftFlag = False
        self.pos = wx.Point(0, 0)

        # イベント割り当て
        self.Bind(wx.EVT_PAINT, self.OnPaint)
        self.Bind(wx.EVT_LEFT_DOWN, self.OnMouseLeftDown)
        self.Bind(wx.EVT_LEFT_UP, self.OnMouseLeftUp)
        self.Bind(wx.EVT_MOTION, self.OnMouseMove)

        # 画像読み込み
        image = wx.Image("images/bg.jpg")
        self.bitmap = image.ConvertToBitmap()

        # 画像サイズでフレームサイズを設定し直し
        self.SetSize(image.GetSize())

    def OnMouseLeftDown(self, e):
        """ 左ボタンを押した時の処理 """
        pos = e.GetPosition()
        self.pos = pos
        self.mouseLeftFlag = True
        self.Refresh(False)

    def OnMouseMove(self, e):
        """ マウスカーソルを動かした時の処理 """
        if self.mouseLeftFlag:
            pos = e.GetPosition()
            self.pos = pos
            self.Refresh(False)

    def OnMouseLeftUp(self, e):
        """ 左ボタンを離した時の処理 """
        pos = e.GetPosition()
        self.pos = pos
        self.mouseLeftFlag = False
        self.Refresh(False)

    def OnPaint(self, event=None):
        """ 描画処理 """
        pdc = wx.BufferedDC(wx.PaintDC(self))

        try:
            # アンチエイリアスをかける
            dc = wx.GCDC(pdc)
        except:
            dc = pdc

        dc.Clear()
        dc.DrawBitmap(self.bitmap, 0, 0, True)  # ビットマップ画像を描画

        if self.mouseLeftFlag:
            # 円を描画
            dc.SetBrush(wx.Brush(wx.Colour(255, 0, 0, 64)))
            dc.SetPen(wx.Pen("RED", 2))
            x = self.pos.x - 50
            y = self.pos.y - 50
            dc.DrawEllipse(x, y, 100, 100)

if __name__ == '__main__':
    app = wx.App(False)
    frame = DrawPanel(parent=None, title=u"ウインドウ内でマウスボタンを押してみるべし")
    frame.Show()
    app.MainLoop()

たしかにちらつかなくなった。

wxpython_ss2.gif

スクロールできる版。 :

調子に乗って、ウインドウ内をスクロールできるソレにしてみた。ついでにブラシ相当をビットマップ画像にして描画。

_drawing_test3.py
"""
画面にマウスで円を描画する。
スクロールバーでウインドウをスクロールできる版。
"""
import wx

draw_bitmap_brsuh = True
bitmap_brush_image = "images/ball.png"

class DrawPanel(wx.ScrolledWindow):

    def __init__(self, parent):
        wx.ScrolledWindow.__init__(self, parent, -1)

        self.maxWidth = 1024
        self.maxHeight = 768
        self.rate = wx.Point(8, 8)

        self.brushBitmap = None
        if draw_bitmap_brsuh:
            image = wx.Image(bitmap_brush_image)
            self.brushBitmap = image.ConvertToBitmap()

        self.SetVirtualSize((self.maxWidth, self.maxHeight))
        self.SetScrollRate(self.rate.x, self.rate.y)

        # 空のビットマップを確保。以後はこれに描画していく
        self.buffer = wx.EmptyBitmap(self.maxWidth, self.maxHeight)
        self.buttonFlag = False

        self.DrawFirst()  # 初期描画

        # イベント割り当て
        self.Bind(wx.EVT_PAINT, self.OnPaint)
        self.Bind(wx.EVT_LEFT_DOWN, self.OnLeftButtonEvent)
        self.Bind(wx.EVT_LEFT_UP,   self.OnLeftButtonEvent)
        self.Bind(wx.EVT_MOTION,    self.OnLeftButtonEvent)

    def OnPaint(self, event=None):
        dc = wx.BufferedPaintDC(self, self.buffer, wx.BUFFER_VIRTUAL_AREA)

    def OnLeftButtonEvent(self, event):
        if self.IsAutoScrolling():
            self.StopAutoScrolling()

        pos = event.GetPosition()
        if event.LeftDown():
            # マウスの左ボタンを押した
            self.DrawMiniCircle(pos.x, pos.y)
            self.buttonFlag = True
        elif event.Dragging() and self.buttonFlag:
            # マウスカーソルを動かした
            if self.buttonFlag:
                self.DrawMiniCircle(pos.x, pos.y)
        elif event.LeftUp() and self.buttonFlag:
            # マウスの左ボタンを離した
            self.buttonFlag = False

    def DrawMiniCircle(self, x, y):
        """ 指定座標に小さい円を描画 """
        pdc = wx.BufferedDC(None, self.buffer)
        try:
            # アンチエイリアスをかける
            dc = wx.GCDC(pdc)
        except:
            dc = pdc

        # スクロールを考慮した座標値を取得
        nx, ny = self.CalcUnscrolledPosition(x, y)

        if draw_bitmap_brsuh:
            # ビットマップ画像をブラシにして描画
            nx -= self.brushBitmap.GetWidth() / 2
            ny -= self.brushBitmap.GetHeight() / 2
            dc.DrawBitmap(self.brushBitmap, nx, ny, True)
        else:
            # 円をブラシにして描画
            dc.SetBrush(wx.Brush(wx.Colour(192, 192, 255)))
            dc.SetPen(wx.Pen("BLACK", 0))
            dc.DrawCircle(nx, ny, 16)

        del dc
        self.Refresh(False)

    def DrawFirst(self):
        """ 初期描画 """
        pdc = wx.BufferedDC(None, self.buffer)
        try:
            dc = wx.GCDC(pdc)
        except:
            dc = pdc
        dc.SetBackground(wx.Brush(self.GetBackgroundColour()))
        dc.Clear()

        # 円を描画
        dc.SetBrush(wx.Brush(wx.Colour(128, 128, 255)))
        dc.SetPen(wx.Pen("GREEN", 1))
        dc.DrawCircle(100, 100, 100)

        dc.SetBrush(wx.Brush(wx.Colour(128, 255, 128)))
        dc.SetPen(wx.Pen("RED", 3))
        dc.DrawCircle(self.maxWidth - 100, 100, 100)

        del dc


class MyApp(wx.App):

    def OnInit(self):
        frame = wx.Frame(None, -1, "Draw Test 3", size=(640, 480))
        DrawPanel(frame)
        frame.Show()
        self.SetTopWindow(frame)
        return True

if __name__ == '__main__':
    app = MyApp()
    app.MainLoop()

wxpython_ss3.gif


実験に使った画像は以下。スクリプトと同じ場所に images というフォルダを作成して、その中に入れて使った。

_bg.jpg
_ball.png

#4 [pc] 画面をキャプチャするソフトの調子がよろしくない

今まで、Windows のデスクトップ画面をキャプチャしてアニメGIFで保存する際、GifCam 5.1 を使っていたのだけど。どうもここ最近、途中で緑一色のアニメGIFになってしまうことが多くて、ちと困ったなと。

代わりのソフトを探してみたり。とりあえず、ScreenToGif 2.2 を使ってみた。

これも一部の表示がおかしくなるな…。でも、GifCam より、マシな結果が得られそう。しばらくこちらを試用してみるか…。

しかし、どうして GifCam が正常動作しないのだろう。何か常駐ソフトの類が悪さをしてるんだろうか。

2016/10/10(月) [n年前の日記]

#1 [python] Pylintの設定ファイルについて

Windows10 x64上で、PySide という、Python と Qt4 を使ってGUIアプリを作れるフレームワークについて勉強しようとして Visual Studio Code でサンプルソースを開いてみたら、やたらめったら警告だかなんだかが表示されて。「こんな名前知らねえ」とかなんとか。

どうやら Visual Studio Code は、裏で Pylint というPythonのソースをチェックしてくれるツールが走るようで。つまり Pylint が文句を言いまくってる状態、だと思うのだけど。その Pylint に、「PySideってのを使うからよろしくな」と伝えてやらないといかんようだなと。で、ググった感じでは、
pylint --extension-pkg-whitelist=PySide hoge.py
みたいな指定をしてやると怒られなくなる、ということらしく。

さて、これをどこで指定してやれば…。

以下のページで解説されてた。

_Pythonの主要なLint(pep8, pylint, flake8)の設定方法まとめ - Qiita
_[Python]pep8とpylintの設定ファイルを作成して一部の警告を非表示にする - dackdive's blog
_Pythonの開発環境をプロジェクト単位にWindows環境で用意する(VSCode+virtualEnvWrapper+Pylint) - Qiita

例えば、カレントフォルダに pylintrc なるファイルを作成して、そこに色々指定してやれば設定ができるっぽい。

pylintrc の作り方は、
pylint --generate-rcfile > pylintrc
で雛形ができるから、後はソレを開いて修正を。今回は、以下の修正を加えてみた。
extension-pkg-whitelist=
↓
extension-pkg-whitelist=PySide

これで、DOS窓上で pylint hoge.py と打つ分にはそれほど怒られなくなった。また、Visual Studio Code 上でもソースが真っ赤にならなくなった。

$HOME/.config/pylintrc を置いとけば、各フォルダに一々作らなくてもいいのだろうか。一応自分の環境では環境変数 HOME を設定済みなので、そのフォルダの下に .config/pylintrc を置いてみた。…DOS窓上では、ちゃんと設定が読み込まれてるように見える。どうやら Windows上でも、該当ファイルが共通設定として使われるらしい。

Pylintのインストール方法。 :

Windows 10 x64 + Python 2.7.11 では、以下でインストールできるっぽい?
pip install pylint
アップデートは以下。
pip install -U pylint

バージョン確認。
> pylint --version

pylint 1.6.4,
astroid 1.4.8
Python 2.7.11 (v2.7.11:6d1b6a68f775, Dec  5 2015, 20:32:19) [MSC v.1500 32 bit (Intel)]

2016/10/11(火) [n年前の日記]

#1 [python] PySide勉強中

Python + PySide を使って、マウスボタンを押してドラッグするとウインドウ内に何かを描く、みたいな処理をしてみたい。

こんな感じだろうか…。

_drawing_pyside_3.py
"""
PySideでウインドウに描画。
空の QPixmap を作ってそこに描画する例。
マウス左ボタンを押して動かすとその位置に円を描画する。

参考ページ:

「プログラムでシダを描画する」をPythonで描画過程まで出力する - Qiita
http://qiita.com/tatuno/items/23d291dd3bf01693bde4

[Python][PySide] 簡単なメニュー付きウィンドウを表示する | unlinked log
http://log.noiretaya.com/?p=250

PyQtでのグラフィックス その1 (QWidgetに直接描画する) - bravo's blog
http://bravo.hatenablog.jp/entry/2016/02/07/084048
"""

import sys
from PySide.QtCore import *
from PySide.QtGui import *

width = 480
height = 360

brush_use = True
brush_filename = "ball.png"


class MyWidget(QWidget):

    """描画担当ウィジェット"""

    def __init__(self, parent):
        super(MyWidget, self).__init__(parent)
        self.mouseOnFlag = False

        # 空のQPixmapを生成。以後はここに描画していく
        self.pixmap = QPixmap(width, height)
        self.pixmap.fill()

        # ブラシ画像を読み込み
        self.brush_pixmap = QPixmap(brush_filename)

    def paintEvent(self, event):
        qp = QPainter()
        qp.begin(self)
        qp.drawPixmap(0, 0, self.pixmap)
        qp.end()

    def mousePressEvent(self, event):
        """ マウスボタンを押した """
        if event.button() == Qt.LeftButton:
            self.mouseOnFlag = True
            self.mydraw(event)

    def mouseMoveEvent(self, event):
        """ マウスを動かした """
        if self.mouseOnFlag:
            self.mydraw(event)

    def mouseReleaseEvent(self, event):
        """ マウスボタンを離した """
        if event.button() == Qt.LeftButton:
            self.mouseOnFlag = False

    def mydraw(self, event):
        """ 描画処理 """
        qp = QPainter()
        qp.begin(self.pixmap)

        # マウス座標取得
        x = event.pos().x()
        y = event.pos().y()

        if brush_use:
            # ブラシ画像で描画
            x -= self.brush_pixmap.width() / 2
            y -= self.brush_pixmap.height() / 2
            qp.drawPixmap(x, y, self.brush_pixmap)
        else:
            # 円を描画
            qp.setBrush(QColor(0, 0, 128))
            qp.setPen(QColor(0, 64, 255))
            qp.drawEllipse(event.pos(), 20, 20)

            # 点を描画する例
            # qp.drawPoint(x, y)

        qp.end()
        self.repaint()


class MainWindow(QMainWindow):

    """ メインウインドウ """

    def __init__(self, parent=None):
        super(MainWindow, self).__init__(parent)
        self.resize(width, height)
        # self.setFixedSize(width, height)
        # self.refresh_rate = 200

        # メニューバー作成
        menubar = QMenuBar()
        file_menu = QMenu("&File", self)
        exit_action = file_menu.addAction("&Exit")
        exit_action.setShortcut("Ctrl+Q")
        exit_action.triggered.connect(qApp.quit)
        menubar.addMenu(file_menu)
        self.setMenuBar(menubar)

        # ステータスバー作成
        self.statusBar()

        # 描画するウィジェットを作成して真ん中に配置
        widget = MyWidget(self)
        self.setCentralWidget(widget)


def main():
    app = QApplication(sys.argv)
    window = MainWindow()
    app.setActiveWindow(window)
    window.show()
    sys.exit(app.exec_())

if __name__ == '__main__':
    main()
使ったブラシ画像は以下。

_ball.png

pyside_draw_ss01.gif

#2 [nitijyou] 市の健康診断に行ってきた

AM09:30-AM10:40まで。それほど混雑してる感じではなかったけど、血圧測定と血液検査で待たされて、1時間以上かかった感じ。

毎年、北団地の集会所で受けてたけど、今年は期日を把握してなくてうっかり機会を逃してしまい、もう少し遠い、森宿区民会館で受けることに。場所も把握してなければ中に入ったこともなかったけど、結構綺麗な建物だなと感心(?)を。

親父さんの電動自転車を借りて行ってきたけど…。区民会館が面してる道路が、歩道が無いぐらいに細いのに何故か異様に車の通行量が多く、しかもどの車もビュンビュン飛ばすので、自転車で通るのも一苦労。あの調子だといつか事故が起きそうな気がする。かといってどう見ても道路を広げる等の工事ができそうにない状態だし。一般住宅に加えて工場までギリギリに建ってるし。どうするんだろ。どうしようもないか。

2016/10/12(水) [n年前の日記]

#1 [python] PySideをまだ勉強中

Python + PySide には、QGraphicsView、QGraphicsScene、QGraphicsItem 等の便利そうなソレがあるので、そのあたりを使ってマウスを動かすと何か描ける、みたいなソレを試しているところ。

考え方としては、空のビットマップ(QPixmap) を作って、QGraphicsPixmapItem として QGraphicsScene に追加登録してやればいいのではないか、後は空のビットマップの上に QPainter を使って色々描いていけばいいのでは、と思って試してるのだけど、描画後に画面というかウインドウ内にそのビットマップを再描画する方法が分からず。update() や refresh() じゃダメなのか…?

2016/10/13(木) [n年前の日記]

#1 [ruby] Windows10 + Ruby/Tk でエクスプローラからファイルをD&Dしてファイルパスを取得

Windows10 x64 + Ruby 2.2.5 p319 + Ruby/Tk + TkDND 2.8 で、エクスプローラからファイルをD&D(ドラッグアンドドロップ)して、ファイルパスを取得できるか実験。

参考ページは以下。

_[ruby-list:49771] TkDND での日本語名の扱い
_DebianでRubyからTcl/TkのTkDNDを利用してドラッグアンドドロップを実装する -- ぺけみさお

_tkdnd_test.rb
# Ruby/Tk + tkDND でファイルのドロップを受け付けるかテスト
# Tk拡張を導入していないと動作しない。
#
# [ruby-list:49771] TkDND での日本語名の扱い
# http://ruby-list.ruby-lang.narkive.com/lx7VFWDo/ruby-list-49771-tkdnd
#
# DebianでRubyからTcl/TkのTkDNDを利用してドラッグアンドドロップを実装する -- ぺけみさお
# https://www.xmisao.com/2013/07/25/ruby-tkdnd-linux.html

require 'tk'
require "tkextlib/tkDND"

Tk::TkDND::DND

label = TkLabel.new {
  text "Please File Drop"
  pack
}

txt = TkText.new(nil){
  dnd_bindtarget('text/uri-list','<Drop>', "%D") { |d|
    p d
    insert('end',d[0])
    insert('end',"\n")
  }
  pack
}

TkButton.new {
  text "Exit"
  command "exit"
  pack
}

Tk.mainloop

スクリプトを実行して表示されたウインドウに、エクスプローラからファイルをD&Dしてみた。

tkdnd_test_rb_ss.png

できたっぽい。

が、日本語ファイル名は、たしかに化けてるな…。

_[ruby-list:49771] TkDND での日本語名の扱い で紹介されてる変換処理を入れてみた。ついでに、複数ファイルを渡した時に全部処理するようにしてみた。

_tkdnd_test2.rb
require 'tk'
require "tkextlib/tkDND"
require 'nkf'

Tk::TkDND::DND

label = TkLabel.new {
  text "Please File Drop"
  pack
}

txt = TkText.new(nil) {
  dnd_bindtarget('text/uri-list','<Drop>', "%D") { |d|
    d.each { |s|
      # 日本語ファイル名を渡した時におかしくなるので変換処理をする
      u8str = s.encode("iso-8859-1","utf-8").encode("iso-8859-1","utf-8")
      u8str.encode!('cp932', 'utf-8')
      
      puts u8str
      insert('end',u8str)
      insert('end',"\n")
    }
  }
  pack
}

TkButton.new {
  text "Exit"
  command "exit"
  pack
}

Tk.mainloop
tkdnd_test2_rb_ss.png

文字化けしてないように見える。

が。 _[ruby-list:50353] [再]Re: TkDND での日本語名の扱い によると、空白が入ったファイル名を渡したり、\が入ったファイル名を渡すとおかしくなるそうで。試してみたところ、たしかにエラーが表示された。このあたり、なかなか厳しいものがあるようで。

TkDNDの導入方法。 :

Windows10 x64 + Ruby 2.2.5 p319 + Ruby/Tk の環境で、TkDND も含めたTk拡張(?)を使うには…。

とりあえず、ActiveTcl が必要。 _Download Tcl | ActiveState から入手してインストールする。たしか、自分の環境では、Windows x86(32bit版)、8.6.4.1 をインストールしたような。 セットアップファイルは、ActiveTcl8.6.4.1.299124-win32-ix86-threaded.exe、だった気がする。

次に、ActiveTcl に、TkDND をインストールする。tclsh と打つとプロンプトが「%」になるので、ActiveTcl についてきた teacup なるパッケージマネージャを使ってネット経由でインストール。
% teacup install tkdnd
インストールできたら、exit と打って抜ける。

最後に、Ruby/Tk からTk拡張を使えるようにする。
ActiveTclインストールフォルダ\lib\teapot\package\win32-ix86\lib\
の中に入ってるファイルやフォルダを、
Ruby2.2インストールフォルダ\lib\tcltk\
の中にごっそりコピー。

このあたりは色々やり方があるらしいけど、自分の環境ではこの方法でやってたり。昔試した時は、この方法なら ocra を使ってRubyスクリプトをexe化する時も問題が出なかったはず。

_2016/10/03の日記 や、 _2015/12/10の日記 にもそのへんメモしてあった、とメモ。

#2 [python] PySideでマウスを動かしてお絵かき

Windows10 x64 + Python 2.7.11 + PySide 1.2.4 で、ウインドウ上でマウスを動かすと何かを描ける、てな処理を QGraphics* を使って試してみたり。

_drawing_pyside_4.py
_ball.png
"""
PySideでウインドウに描画。
マウスを動かしてお絵かきできる。
QGraphics* を使う。

ヲドリテヒヅル PyQtでイメージビューワ
http://melpystudio.blog82.fc2.com/blog-entry-138.html
"""


import sys
from PySide.QtCore import *
from PySide.QtGui import *

width = 640
height = 480
brushPath = 'ball.png'


class ImageViewScene(QGraphicsScene):

    """ メインビューと関連付けられる Scene """

    def __init__(self, *argv, **keywords):
        super(ImageViewScene, self).__init__(*argv, **keywords)

        self.buttonFlag = False
        self.pixmap = QPixmap(width, height)
        self.pixmap.fill()
        self.brushImage = QPixmap(brushPath)

        # Scene に Item を追加
        self.imgItem = QGraphicsPixmapItem(self.pixmap)
        self.addItem(self.imgItem)

    def mouseDoubleClickEvent(self, event):
        pass

    def mousePressEvent(self, event):
        """ マウスボタンが押された際の処理 """
        if event.button() == Qt.LeftButton:
            self.buttonFlag = True
            self.drawing(event)
        elif event.button() == Qt.RightButton:
            self.pixmap.fill()
            self.imgItem.setPixmap(self.pixmap)

    def mouseMoveEvent(self, event):
        """ マウスカーソル移動中の処理 """
        if self.buttonFlag:
            self.drawing(event)

    def mouseReleaseEvent(self, event):
        """ マウスボタンが離されたときの処理 """
        if event.button() == Qt.LeftButton:
            self.buttonFlag = False

    def drawing(self, event):
        """ ブラシで描画 """
        x = event.scenePos().x()
        y = event.scenePos().y()
        x -= self.brushImage.width() / 2
        y -= self.brushImage.height() / 2
        qp = QPainter()
        qp.begin(self.pixmap)
        qp.drawPixmap(x, y, self.brushImage)
        qp.end()
        del qp

        # 描画更新する方法が分からない…
        # 仕方なく、pixmap を再設定してるけど…これでいいの?
        self.imgItem.setPixmap(self.pixmap)
        # self.imgItem.update()
        # self.update()


class ImageViewer(QGraphicsView):

    """ メインとなるビュー """

    def __init__(self):
        super(ImageViewer, self).__init__()

        self.setCacheMode(QGraphicsView.CacheBackground)
        self.setRenderHints(QPainter.Antialiasing |
                            QPainter.SmoothPixmapTransform |
                            QPainter.TextAntialiasing
                            )

        scene = ImageViewScene(self)
        self.setScene(scene)
        scene.setSceneRect(QRectF(self.rect()))

    def resizeEvent(self, event):
        """ ビューをリサイズ時にシーンの矩形を更新 """
        super(ImageViewer, self).resizeEvent(event)
        self.scene().setSceneRect(QRectF(self.rect()))

if __name__ == '__main__':
    app = QApplication(sys.argv)
    viewer = ImageViewer()
    viewer.show()
    sys.exit(app.exec_())

drawing_pyside_4_ss.gif

これで一応できた、のかな。

Qtには、ビットマップデータ相当として使える QPixmap てのがあるわけだけど。その QPixmap に対しては、QPainter を使って何か描き込むことができるわけで。

しかし、その QPixmap を渡して生成した QGraphicsPixmapItem の見た目を更新する方法がどうにも分からず。

結局、生成時に作った QPixmap を更新するたびに、QGraphicsPixmapItem の setPixmap() で設定し直してみたら、見た目も更新されるようになったけど…。果たしてコレでいいのだろうか…?

もしかして、QGraphicsPixmapItem が持ってる QPixmap は、QGraphicsPixmapItem の生成時、あるいは setPixmap() を呼んだ時に渡された QPixmap の内容を丸々コピーして持ってたりするのだろうかと。仮にそうであれば、生成時に渡した QPixmap を後から更新したところで、見た目も即座に更新されるわけでは無い、てのも分かる気もする。が、実際はどういう仕組みになっているのやら。

それと、どうせ QGraphicsPixmapItem が QPixmap を持っているなら、その QPixmap に直接描き込んでしまえばいいのではないか、とも思ったのだけど。試してみたところ、Pythonスクリプトが強制終了してしまって。となると…。 何にせよ、よく分かりません。

よく分からないと言えば…。QGraphicsView を使うとスクロールバーがなんだか中途半端な長さで出てくるのだけど、コレって何をどうやって設定したり使用したりするのだろう…。今後の課題。

#3 [neta][anime] BS朝日とBSフジはいっそアニメを放送しないでくれないものか

BS朝日とBSフジは、どうしてアニメ番組の放送時間がガンガンずれるのか…。何かの嫌がらせですか。HDDレコーダの予約録画がグチャグチャだよ…。

多少ずれても問題を起こさない時間帯や曜日が他にあるのに、どうしてわざわざ、あの曜日・時間帯に…。都市圏の地上波放送と照らし合わせるとあそこの曜日・時間帯になっちゃう、みたいなことなんだろうか。

2016/10/14(金) [n年前の日記]

#1 [python] PySideのレイアウトについて勉強中

GUIアプリを作ろうとした際に、GUI部品(ウィジェット)を配置するやり方・考え方には、大別して2種類あって。 ということで、PySide で、後者のレイアウトについて勉強中。

以下を参考にして、スクリプトの動作確認をしていたり。

_PySideメモ-レイアウト方法

#2 [nitijyou] 自転車で買い物に

ダイソー、ホームセンターサンデー、ザ・ビッグに寄って、ストップウォッチ、保冷材、夜食等を購入。

ダイソーのストップウォッチとは何故か相性が悪い。 :

ダイソーでストップウォッチを買ってみたのだけど、帰宅後、使おうとしてみたら何度ボタンを押してもピクリとも動かない。電池を交換したりアレをソレしたりしてみたけど変化無し。初期不良品に当たってしまった、のかな。

以前もこういうことあったな…。ダイソーでストップウォッチを買ったらやっぱり使えなくて…。どうも自分、ダイソーのストップウォッチとは相性が悪い…。2回連続で不良品を引き当てるとは…。

#3 [anime] 「ルパン三世 カリオストロの城」を視聴

TV放送されてたので途中から見てみたり。やっぱり凄い。昔の宮崎駿監督は凄い。

それにしても…。前にも書いた記憶があるけど、子供の頃、街中に貼られてるカリ城のポスターを見て、「なんだこのパチモンのルパンは」と思ってしまったことが…。当時ルパンと言えば赤ジャケルパンしか知らなかったから、ジャケットが緑というだけで「これはニセモノに違いない」と思い込んでしまったという。おそらくスタッフの中では、一番最初にルパンを手掛けたのは俺達だ俺達が作るルパンが本物だ、てな自負もあっただろうし、緑ジャケは絶対外せないポイントだったのだろうけど。もし、ジャケットが赤かったら、それだけでも興行成績の類がもうちょっとマシに…。などと想像したりもするけど今更な話ではあるよなと。

今はもう、世間一般では、ルパンと言えばカリ城のルパンでしょう、みたいなイメージになってるところもあるような、ないような。

2016/10/15() [n年前の日記]

#1 [python] PySideのQMainWindow内のレイアウトについて悩んだり

QWidget 内で QHBoxLayout や QVBoxLayout を使ってレイアウトするところまでは動作確認できたのだけど。QMainWindow 内で、同じノリでレイアウトできないことに気が付いて悩んだり。

ググってみたら、以下のサンプルを見かけた。

_python - PySide (Qt) - Layout not working - Stack Overflow

どうやら QMainWindow は、事前にウインドウ内がいくつかの領域に分けられてるようで。上、下、左、右、真ん中、みたいな。で、一旦 QWidget を作ってその中に各ウィジェットをレイアウトしてやって、その QWidget を QMainWindow の真ん中に配置(setCentralWidget()を使って設定)、みたいなことをすれば配置できなくもない、ようだなと。

#2 [nitijyou] 自転車でダイソーまで

昨日購入したストップウォッチが不良品だったので、犬の散歩のついでにダイソーによって交換してもらってきた。

店頭に並んでる品が全部不良品だったら面白いことになりそうだなと少し期待しながら店員さんの振舞いを見守ったけど、ちゃんと動作するモノも陳列されていたようで。やっぱり自分はダイソーのストップウォッチと相性が悪い。どうして2回も、初期不良品を引き当てるのか…。

2016/10/16() [n年前の日記]

#1 [python] PySideのQMainWindow上に色々配置する例

こんな感じかな…。

_mainwdwlayout.py
"""
PySideを使ってQMainWindow上にレイアウトする事例
"""

import sys
from PySide.QtCore import *
from PySide.QtGui import *

color_info = None


class MyColorInfo(QPushButton):
    """ 色を表示するウィジェット """

    def __init__(self, parent=None):
        super(MyColorInfo, self).__init__(parent)
        self.color = QColor(128, 128, 128)
        self.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
        self.setMinimumSize(48, 48)

    def paintEvent(self, e):
        qp = QPainter()
        qp.begin(self)
        qp.setPen(QColor(0, 0, 0))
        qp.setBrush(self.color)
        qp.drawRect(2, 2, 44, 44)
        qp.end()

    def updateColor(self, r, g, b):
        """ 色を更新 """
        self.color.setRgb(r, g, b)
        self.update()


class MySlider(QWidget):
    """
    RGB値指定スライダー部分
    """

    def __init__(self, parent=None):
        super(MySlider, self).__init__(parent)

        gb = QGridLayout()  # 升目上に並べる
        gb.setContentsMargins(0, 0, 0, 0)  # 外のマージン(隙間)を極力無くす

        # 色表示部分
        global color_info
        color_info = MyColorInfo(self)
        gb.addWidget(color_info, 0, 0, 1, 3)

        # スライダーやスピンボックス部分
        self.value = []
        self.sld = []
        self.spb = []

        # RGBの3つ分、Slider や SpinBox を確保
        for (i, s) in enumerate(["R", "G", "B"]):
            lbl = QLabel(s)
            sld = QSlider(Qt.Orientation.Horizontal, self)
            spb = QSpinBox()

            # レイアウト
            gb.addWidget(lbl, i + 1, 0)
            gb.addWidget(sld, i + 1, 1)
            gb.addWidget(spb, i + 1, 2)

            # 値の範囲を 0-255 に設定
            sld.setRange(0, 255)
            spb.setRange(0, 255)

            # 現在値を初期化
            v = 128
            sld.setValue(v)
            spb.setValue(v)

            # 後で使うので記録しておく
            self.value.append(v)
            self.sld.append(sld)
            self.spb.append(spb)

        # 自身のレイアウトとして登録
        self.setLayout(gb)

        # Slider や SpinBox 値が変えられた時に呼ばれるメソッドを設定
        for i in range(3):
            self.sld[i].valueChanged[int].connect(self.sliderChangeValue)
            self.spb[i].valueChanged[int].connect(self.spinBoxChangeValue)

    def sliderChangeValue(self, value):
        """ Slider の値が変わった時に呼ばれる処理 """
        for (i, sld) in enumerate(self.sld):
            self.spb[i].setValue(sld.value())
        self.updateColor()

    def spinBoxChangeValue(self, value):
        """ SpinBox の値が変わった時に呼ばれる処理 """
        for (i, spb) in enumerate(self.spb):
            self.sld[i].setValue(spb.value())
        self.updateColor()

    def updateColor(self):
        """ 色表示部分を更新 """
        v = []
        for spb in self.spb:
            v.append(spb.value())
        global color_info
        color_info.updateColor(v[0], v[1], v[2])


class MyBrushWidget(QWidget):
    """ メインウインドウ左側に配置するウィジェット """

    def __init__(self, parent=None):
        super(MyBrushWidget, self).__init__(parent)

        layout = QVBoxLayout()  # 縦に並べる

        # ブラシ画像選択部分
        self.cb = QComboBox()
        self.cb.addItem("default.png")
        self.cb.addItem("extra.png")
        layout.addWidget(self.cb)

        # ブラシ画像表示部分
        self.scene_brush = QGraphicsScene()
        self.gview_brush = QGraphicsView(self.scene_brush, self)
        self.gview_brush.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
        self.gview_brush.setMinimumSize(256, 256)
        layout.addWidget(self.gview_brush)

        # 色選択部分
        self.rgb_area = MySlider(self)
        layout.addWidget(self.rgb_area)

        # スペーシング
        spc = QSpacerItem(16, 16, QSizePolicy.Expanding, QSizePolicy.Expanding)
        layout.addSpacerItem(spc)

        # 自身のレイアウトとして設定
        self.setLayout(layout)


class MyMainWindow(QMainWindow):
    """ メインウインドウ """

    def __init__(self, parent=None):
        super(MyMainWindow, self).__init__(parent)

        # メニューバー
        mb = QMenuBar()
        file_menu = QMenu("&File", self)
        exit_action = file_menu.addAction("&Close")
        exit_action.setShortcut('Ctrl+Q')
        exit_action.triggered.connect(qApp.quit)
        mb.addMenu(file_menu)
        self.setMenuBar(mb)

        # ステータスバー
        self.status = QStatusBar(self)
        self.status.showMessage("Status Bar")
        self.setStatusBar(self.status)

        # 左側のドックウィジェット
        self.leftDock = QDockWidget("Left Dock Widget", self)
        self.brushw = MyBrushWidget(self)
        self.leftDock.setWidget(self.brushw)

        # 移動とフローティングは有効にするが閉じるボタンは無効にする
        # 注意:
        # メインウインドウが十分大きくないと、フローティング後、元に戻せない
        self.leftDock.setAllowedAreas(Qt.LeftDockWidgetArea
                                      | Qt.RightDockWidgetArea)
        self.leftDock.setFeatures(QDockWidget.DockWidgetMovable
                                  | QDockWidget.DockWidgetFloatable \
                                  # | QDockWidget.DockWidgetVerticalTitleBar
                                  )

        self.addDockWidget(Qt.LeftDockWidgetArea, self.leftDock)

        # 中央ウィジェット
        self.scene_image = QGraphicsScene()
        self.gview_image = QGraphicsView(self.scene_image, self)
        self.setCentralWidget(self.gview_image)


def main():
    app = QApplication(sys.argv)
    # app.setStyle(QStyleFactory.create('Cleanlooks'))
    w = MyMainWindow()
    w.setWindowTitle("PySide Layout on QMainWindow")
    w.resize(640, 480)
    w.show()
    sys.exit(app.exec_())

if __name__ == '__main__':
    main()

mainwdwlayout_ss01.png


QMainWindowは、上、下、左、右に、ドックウィジェット(QDockWidget)なるものを配置することができるらしいので、そのあたりにRGB値を設定するアレコレを置いてみたり。

QDockWidget は、タイトルバーをドラッグすればメインウインドウから切り離したり、またくっつけたり、左じゃなくて右に再配置したりできる。しかし、デフォルト設定では閉じることも可能で…。閉じてしまうと、再復活させる方法が…。なので、今回は設定を変えて閉じるボタンを無効にしてみたり。

QDockWidget を一度メインウインドウから切り離して(フロート状態にして)、またメインウインドウに戻そうとしても戻ってくれなくて悩んだけれど。どうやらメインウインドウのサイズより、ドックウィジェットのほうがサイズが大きいとくっついてくれないようで。メインウインドウのサイズを大きくしてからドラッグしたらくっついてくれた。気づくまで時間がかかった…。

参考ページ。 :


2016/10/17(月) [n年前の日記]

#1 [zatta] 何がどう山なのかが分からない

「不気味の谷」に対する「人形劇の山」、なる言葉をどこかで見かけたのだけど。何がどう山なのか、分からない…。

_不気味の谷現象 - Wikipedia

グラフにしてみると落ち込むところが出現するから「谷」なんだけど…。その「人形劇の山」の「山」って、どこらへんが山に見えるという話なのか。モヤモヤする…。どういうグラフを想定してるのか…。そもそもグラフを想定していないのか…。

どうも「不気味の谷」がどういう現象なのか分かってない人が単に言葉のイメージだけで造語を、てな状況だったりしないかと邪推してしまったり。「なるほど、この形はたしかに山ですね」と納得してみたい…。

それはともかく。人形劇に見えるソレは何故か視聴者側の許容度が広くなる、という現象はたしかにありそうな気もするのだけど。カリ城の作画面と共通するところがあるような気もして。カリ城の映像は今のアニメ映像と比べると全然話にならないぐらいに線が少ない。影がついてるわけでもなく、服の皺を細かく描いてるわけでもないのに、魅力的な映像に見えてくるのは何故か。みたいな。

と思ったけど、ソレたぶん違うな…。カリ城の場合は動きによるところが大きいような。人形劇は動きどうこうじゃないんだよな…。人形劇は、一体どの部分で、何を許されてしまうのか…。分からん…。

関係ないけど、「不気味の谷」でググってたら気になる記事が。

_ソフトとハードの交差点:『不気味の谷』についての批判と提案 - livedoor Blog(ブログ)

2016/10/18(火) [n年前の日記]

#1 [python] Atomエディタ、落ちまくり

Visual Studio Code より、Atomエディタのほうが、Pythonスクリプトの補完をガンガンしてくれることに気が付いて、今現在 Atom 1.11.2 でPythonスクリプトのソースを書いてるのだけど。

Atomエディタ、落ちるな…。ガンガン落ちる…。何かの拍子にフッと落ちる。何が原因なのやら。

Windowsのイベントビューアで確認したけど、ログが残ってるようには見えなかった。

2016/10/19(水) [n年前の日記]

#1 [python] Python + PySideでお絵かきウインドウを作成

Python + PySide + Pillow(PIL) を使って、お絵かきウインドウモドキを作成。動作確認環境は、Windows10 x64 + Python 2.7.11 + PySide 1.2.4 + Pillow 3.4.2。

今のところこんな感じに。左側でブラシを選んで、右側でマウスをカチカチすれば描ける。

drawing_pyside_5_ss01.png


ソースが長くなってきたので、ちょっとココに貼りづらいな…。とりあえず、ファイルだけ置いておこう…。

_drawing_pyside_5.py

スクリプトと同じフォルダに、brushes というフォルダを作って、中に、 _default.png_extra.png を置いておくと、ブラシ画像を読み込んでくれる。
C:.
|   drawing_pyside_5.py
|
\---brushes
        default.png
        extra.png

ひとまず、ここまでできれば、Python + PySide (あるいは PyQt)を使ってお絵かきソフトやドットエディタの類を作れそうではあるなと。

スクリプトソースとブラシ画像は CC0 / Public Domain ってことで。

ライセンス問題についてメモ。 :

上記のソレは、スクリプトソースと画像を公開してるだけなので、ライセンスは自由に決められるだろうと思っているのだけど。これがもし、バイナリ化(exe化)して配布するという話になると、ライセンスがちと面倒臭くなる予感。

仮に、 _PyQt を使って実装した場合は、同梱するPyQtのライセンスがGPLと商用ライセンスしか許されてないので、個人が趣味で作るソレはGPLにせざるを得ない。GPLが嫌いな人、GPL汚染を嫌がる人達は、ちと手を出しにくい。

ただ、 _PySide を使えば、 PySide はLGPLで使えるので、自分で書いた部分についてはGPL以外のライセンスにもできるんじゃないかと。たぶん。

しかし、PySide は Qt4 までの対応で Qt5 に対応してなくて。PyQt は Qt5 にも対応してるらしい。「ワシは Qt5 を Python から使いたいんじゃ」という人は、PyQt を選択するしか無いんだろうなと。

PySide のインストール方法。 :

ネットに繋がってるWindows環境で、Python がインストール済みなら、DOS窓を開いて以下を打てば PySide がインストールできる。
pip install pyside

既に PySide がインストールされていて、アップデートしたい場合は、以下。
pip install -U pyside

アンインストールしたい場合は、以下。
pip uninstall pyside

しかし、Pillow (画像処理ライブラリ) のインストールは、pip を使ってネット経由でやるとエラーが出ちゃったので…。 _Python Extension Packages for Windows - Christoph Gohlke から、自分の環境にあった .whl を一旦ローカルにダウンロードして、以下のように指定してインストールしてやったらエラーが出なかった、とメモ。
pip install Pillow-3.4.2-cp27-cp27m-win32.whl
ちなみに上記の例では、cp27 = Python2.7用、win32 = 32bit版、というファイル名のルールになってる。

上記のサイトでは PySide の whlファイルも公開されてるけど、バージョンが 1.2.2 なのでちょっと古いみたい。PySide の現行版は 1.2.4 なので。

色を反映させる処理で悩んでたり。 :

ブラシ画像がグレースケール画像の場合は、色を簡単に反映させられることが分かったのだけど。処理としては、元画像の明度をアルファチャンネルとして使って、RGB値は選択色をそのまま使えばいいわけで。

ただ、RGBAフルカラー画像のブラシ画像に、色を反映させる処理で悩んでたり。元画像のRGB値が128の時に、現在の色がそのまま出るように、と考えて、今のところ2つの方法を試してるけど…。

about_color_change.png

ガンマ補正っぽい処理をしたほうがいいのか、それとも、RGBではなくて _HLS(色相、輝度、彩度) にして計算するべきか…。何にせよ今の処理では、結果が望んでるものとはビミョーに違う感じが。

#2 [cg_tools] JTrimはアルファチャンネルを扱えなかったのか

Inkscape でエクスポートしたpng画像をJTrimで読み込んでみたらなんだか妙な表示に。

ググってみたら…。

_JTrimは予めアルファチャンネルの設定された透過画像はきちんと読み込めないらしい(「透過色設定」はできるが) | さまようけんばん

なるほど。JTrim はアルファチャンネルを持った画像を開けないのか。起動が早くて便利そうと思ってたけど、ちと残念。

#3 [prog] マルチカーソルって便利そう

Windows10上で、Atomエディタを使ってPythonスクリプトを書いてるのだけど。マルチカーソルなる機能がなんだか便利そうだなと。

atom_multicursor_ss01.gif


文字列を選択すると「他にもこのあたりに同じ文字列があるよ」と点線で枠が表示されるので、Ctrl+D を押していくとどんどん選択されてマルチカーソルモードに入る。その状態で編集すると、複数の場所を一気に編集できる。ESCキーを押すとマルチカーソルモードから抜ける。

Ctrl+マウス操作でもマルチカーソルモードになるらしい。

2016/10/20(木) [n年前の日記]

#1 [python] PySideのWidgetに入ったり出たりする時のイベント

enterEvent() や leaveEvent() を書けば、Widget内にマウスカーソルが入ったり出たりした時に処理ができるらしい。

_enterevent_test1.py
"""
PySideのWidget内にマウスカーソルが出たり入ったりした時のイベント動作を確認

Windows10 x64 + Python 2.7.11 + PySide
"""

import sys
from PySide.QtCore import *
from PySide.QtGui import *


class MyLabel(QLabel):

    def __init__(self, *argv, **keywords):
        super(MyLabel, self).__init__(*argv, **keywords)
        self.setFrameStyle(QFrame.Box | QFrame.Raised)
        self.setFixedSize(240, 64)
        self.enterCount = 0
        self.leaveCount = 0

    def enterEvent(self, event):
        self.enterCount += 1
        self.setText("enterEvent %d" % self.enterCount)

    def leaveEvent(self, event):
        self.leaveCount += 1
        self.setText("leaveEvent %d" % self.leaveCount)


class MyWidget(QWidget):

    def __init__(self, *argv, **keywords):
        super(MyWidget, self).__init__(*argv, **keywords)
        layout = QVBoxLayout(self)
        layout.addWidget(MyLabel("Please move mouse cursor"))
        layout.addWidget(MyLabel("Please move mouse cursor"))
        layout.addWidget(MyLabel("Please move mouse cursor"))
        self.setLayout(layout)


def main():
    app = QApplication(sys.argv)
    w = MyWidget()
    w.show()
    sys.exit(app.exec_())

if __name__ == '__main__':
    main()

enterevent_test1_ss01.png

たしかに処理されてる。ように見える。

アレ? このスクリーンショットだと反映されてるように見えないな…。ちょっと変なタイミングでキャプチャしてしまったようで。まあいいや。こういう感じの画面が出るスクリプトだよ、ぐらいは伝わるだろう…。

#2 [cg_tools] PhotoFiltreを試用

JTrimが、アルファチャンネルを持った画像を開けないことに気が付いて、ちょっとガックリ来たわけで。圧倒的に起動が早いのに、惜しい…。

似たようなことができるツールは無いのかなとググっていたら、PhotoFiltre というフリーソフトがあることに気が付いて。PhotoFiltre Studio という有償版の機能削減版、という位置づけらしいけど。

_PhotoFiltre 7 - k本的に無料ソフト・フリーソフト
_Photofiltreダウンロード&日本語化(ver7.x以降)

試しにインストールして試用してみたところ、JTrim ほどではないけれど起動は比較的早くて、かつ、トリミングその他も似たような感じでできたので、しばらくはコレを使ってみようかなと。

PixBuilder Studioでもトリミングはできた。 :

_PixBuilder Studio ではトリミング作業がちょっとアレだな、矩形の選択範囲を作ってもサイズを後から調整できないし、と思っていたのだけど。改めて確認してみたら、別途 Crop ツールが用意されてることに気が付いて。

Cropツールを使えば後からサイズ調整もできたじゃないか、何故見落としたのか…。自分の注意力の無さにガックリ。

2016/10/28追記。 :

やっぱり PixBuilder Studio は、ちょっと使いづらかった。Cropツールで縦横比を指定して矩形選択しても、何かの拍子に選択範囲がヒョコッと勝手に小さくなったり大きくなったりしてしまう。そこからサイズ調整してトリミングして画像を縮小しても、望みの画像サイズにならない…。何かしら計算誤差でもあるんだろうか。

2016/10/21(金) [n年前の日記]

#1 [cg_tools] StylePixを試用

StylePix なる、Windows上で動作する画像編集ソフトがあることを知ったので試用してみたり。1.14.5.0 が Stable版(安定板)だけど、2.0.0.5 beta版もあるらしい。開発した Hornil社は、公式サイトを眺めた感じでは韓国にあるソフトウェア会社っぽい。

_Hornil - StylePix - Free Photo Editor and Image Editor
_Hornil - StylePix - Download Free Lightweight and Powerful Image Editor.
_高機能で軽量な画像編集ソフト「Hornil StylePix」
_クールなデザインで高機能な画像編集フリーソフト 『StylePix』 軽量軽快でミニチュア化など面白い | PCあれこれ探索
_無料で利用できるグラフィックソフト

そこそこ機能豊富なのに、起動が比較的早くて、なんだかイイ感じ。言語設定で Japanese を選べばメニュー等も日本語化される。

しかし、メニューの日本語があちこち怪しい…。「出口」って何だ? ああ、「Exit」か…。「○○の地域」は…「region」かな。regionって日本語ではどう書くべきなのか。「作物の選択」は…作物って何だろ…。ひょっとしてGoogle翻訳そのままだったりするのだろうか。なかなかイイ感じのソフトなのに、ちと惜しい。

日本語メニューをキャプチャしてみた。 :

CGソフトの類ではメニューの各単語をどのように翻訳すべきなのか、なんだか疑問が湧いてきた。試しに、画面をキャプチャして、一つ一つ自分なりに検討してみたり。

0_0_main_menu.png

1_0_menu_file.png

2_0_menu_edit.png

3_0_menu_image.png

4_0_menu_object.png

5_0_menu_filter.png

6_0_menu_view.png

7_0_menu_window.png

8_0_menu_help.png

9_0_tooloption_brush.png

9_1_tooloption_select.png

9_2_batch_process.png

こういう感じに修正すれば、まだ伝わるだろうか…。どうなんだろ。

「整列」「配置」のあたりはどう書くべきか悩んでしまうなと。そのあたり、以前自分もソレ系のツールを作った時に項目名で悩んだ記憶が。

言語ファイルが別途存在することに気が付いた。 :

キャプチャ画像をせっせと撮ってアレコレ作業した後で気が付いたけど。Languageフォルダの中に Japanese.txt というファイルがあって、どうやらコレが言語ファイルだった模様。キャプチャ画像を作る意味は無かった…。Japanese.txt を勝手に書き換えれば済む話だった…。

テキスト形式でずらずらと記述してあるので、各自勝手に修正すれば怪しい日本語を減らせそう。エンコードは、xyzzy で開いたところ UTF-16LE BOM付き、と表示されてる。

#2 [cg_tools] PictBearやAzPainter2を試用

起動が早い画像編集ソフトを探して、 _PictBear_AzPainter2 を試用。どちらも開発終了してしまったソフトだけど、その分軽いはず。

PictBear は一瞬で起動して驚いた。しかし、選択範囲のサイズを後から調整することができない。トリミングをするのも一苦労。

AzPainter2 も圧倒的に起動が早く。かつ、選択範囲のサイズを後から調整することも可能なので、これならトリミング作業もフツーにできそうだなと。

ちょっとした作業なら、あえて昔の、動作が軽いソフトを使う、てのも全然アリだなと思えてきたり。

#3 [prog] GIMPの「着色」ってどんな処理をしてるのだろう

GIMPの、色 → 着色、ってどんな処理をしているのか気になって、GIMPのソースをDLして眺めていたり。しかし、分からん…。

以下のような処理をしてるのかなと想像するのだけど、どうなんだろう。
  1. 元画像のRGB値から輝度を算出。
  2. 着色ダイアログで指定された、色相、彩度、輝度、のうち、輝度を元画像から求めた輝度にも反映。
  3. HLS(色相、輝度、彩度)からRGBに変換。
_gimp/gimpoperationcolorize.c at master - GNOME/gimp を眺めた感じでは、そのように見えるのだけど。しかし、ググって見つけたこのソース(gimp 2.9と書いてある)と、DLしてきた GIMP 2.8.18 のソースでは、内容もファイル名も異なっていて。バージョンが違うから当たり前なんだろうけど。

RGBからグレースケールに変換。 :

RGBからグレースケールへの変換は、GIMPの場合は3種類の式があって。以下のページが分かりやすいような気がするのでメモ。

_Converting color to grayscale
_More on colors and grayscale
_明るさとコントラストの調整 < 色の調整 < 知っておきたい機能 | GIMP入門(2.8版)

もっとも、今回は輝度を求めたいのだから、0.21 * R + 0.72 * G + 0.07 * B の式でいいのだろうけど。

ただ、ググってみたら掛ける値は色々あるようで。

_osakana.factory - HSB 値と輝度の求め方 によると、0.298912 * R + 0.586611 * G + 0.114478 * B となっていた。また、 _画像のモノクロ化は輝度で変換しないとヒドイことになる - 知らなきゃ絶対損するPCマル秘ワザ では、0.299 * R + 0.587 * G + 0.114 * B となってた。掛ける値を足せば 1.0 になるのは同じだけど、それぞれ違う値だな…。

_YUV - Wikipedia によると、GIMPのソレは ITU-R BT.709 で、他は ITU-R BT.601、ということだったらしい。

_色空間の変換 (4) によると、
  • BT.709 : HDTV(ハイビジョンテレビ)の規格。Y = 0.2126 * R + 0.7152 * G + 0.0722 * B
  • BT.601 : SDTV(標準テレビ)の規格。Y = 0.299 * R + 0.587 * G + 0.114 * B
なのだとか。

_第36回:カラーマネジメント Rec.709は3種類ある? | PERCH長尾の知っ得!デザインビズ必読ポイント! | AREA JAPAN によると、BT.709 にもいくつか種類があるようで。

#4 [python] PySideでQMainWindowのレイアウト方法を再確認

PySide(PyQt)のQMainWindowは、レイアウトの仕方がちょっと違うのでメモ。

_dockwidgetlayout.py
"""
PySideでQMainWindow上にDockWidgetその他をレイアウト
動作確認環境 : Windows10 x64 + Python 2.7.11 + PySide 1.2.4
"""

import sys
from PySide.QtCore import *
from PySide.QtGui import *


class MyVWidget(QWidget):
    """ メインウインドウ周辺に配置するWidget。縦長タイプ """

    def __init__(self, parent=None):
        super(MyVWidget, self).__init__(parent)
        l = QVBoxLayout()  # 縦に並べる
        l.addWidget(QLabel("Dummy"))
        l.addWidget(QLabel("Dummy"))
        self.setLayout(l)


class MyHWidget(QWidget):
    """ メインウインドウ周辺に配置するWidget。横長タイプ """

    def __init__(self, parent=None):
        super(MyHWidget, self).__init__(parent)
        l = QHBoxLayout()  # 横に並べる
        l.addWidget(QLabel("Dummy"))
        l.addWidget(QLabel("Dummy"))
        self.setLayout(l)


class DrawAreaScene(QGraphicsScene):
    """ 描画ウインドウ用Scene """

    def __init__(self, *argv, **keywords):
        super(DrawAreaScene, self).__init__(*argv, **keywords)
        self.buttonFlag = False
        self.pixmap = QPixmap(800, 600)
        self.pixmap.fill(QColor(0, 0, 0, 0))

        # Scene に Item を追加
        self.imgItem = QGraphicsPixmapItem(self.pixmap)
        self.addItem(self.imgItem)


class DrawAreaView(QGraphicsView):
    """ メインになるQGraphicsView """

    def __init__(self, *argv, **keywords):
        super(DrawAreaView, self).__init__(*argv, **keywords)

        self.setCacheMode(QGraphicsView.CacheBackground)
        self.setRenderHints(QPainter.Antialiasing |
                            QPainter.SmoothPixmapTransform |
                            QPainter.TextAntialiasing)
        self.setBackgroundBrush(Qt.darkGray)  # 背景色を設定

        # Sceneを登録
        scene = DrawAreaScene(self)
        self.setScene(scene)
        scene.setSceneRect(QRectF(self.rect()))

    def resizeEvent(self, event):
        """ リサイズ時に呼ばれる処理 """
        super(DrawAreaView, self).resizeEvent(event)
        self.scene().setSceneRect(QRectF(self.rect()))  # Sceneの矩形も更新


class MyMainWindow(QMainWindow):
    """ メインウインドウ """

    def __init__(self, parent=None):
        super(MyMainWindow, self).__init__(parent)

        # メニューバー
        mb = QMenuBar()
        file_menu = QMenu("&File", self)
        exit_action = file_menu.addAction("&Close")
        exit_action.setShortcut('Ctrl+Q')
        exit_action.triggered.connect(qApp.quit)
        mb.addMenu(file_menu)
        self.setMenuBar(mb)

        # ステータスバー
        status = QStatusBar(self)
        self.setStatusBar(status)
        status.showMessage("Status Bar")

        # 上のDockWidget
        self.topDock = QDockWidget("Top Dock", self)
        self.topDock.setWidget(MyHWidget(self))
        self.addDockWidget(Qt.TopDockWidgetArea, self.topDock)

        # 下
        self.bottomDock = QDockWidget("Bottom Dock", self)
        self.bottomDock.setWidget(MyHWidget(self))
        self.addDockWidget(Qt.BottomDockWidgetArea, self.bottomDock)

        # 左
        self.leftDock = QDockWidget("Left Dock", self)
        self.leftDock.setWidget(MyVWidget(self))
        self.addDockWidget(Qt.LeftDockWidgetArea, self.leftDock)

        # 右
        self.rightDock = QDockWidget("Right Dock", self)
        self.rightDock.setWidget(MyVWidget(self))
        self.addDockWidget(Qt.RightDockWidgetArea, self.rightDock)

        # 中央Widget
        self.gview_image = DrawAreaView(self)
        self.setCentralWidget(self.gview_image)


def main():
    app = QApplication(sys.argv)
    w = MyMainWindow()
    w.setWindowTitle("DockWidget Layout")
    w.resize(800, 600)
    w.show()
    sys.exit(app.exec_())

if __name__ == '__main__':
    main()

dockwidgetlayout_ss01.png

上下左右にドックウィジェットを配置して、真ん中にメインとなるウィジェットを配置できる、のが QMainWindow のレイアウト、なのかなと。

ドックウィジェットは、タイトルバー?部分をマウスでドラッグしてやれば、切り離して任意の場所に置くことも可能。例えば以下のような配置にもできるので、その手のツールを作成する時は便利かもしれない。

dockwidgetlayout_ss02.png

2016/10/22() [n年前の日記]

#1 [cg_tools] StylePixの日本語化ファイルを修正していたり

Windows上で動作する軽量画像編集ソフト、 _Hornil StylePix 。日本語化できるものの、なんだか怪しい日本語ばかりなので、言語ファイルの修正作業を試してみたり。

言語ファイルは、StylePixインストールフォルダ\Language に入ってる。Japanese.txt が日本語化ファイル。別名コピーしてそちらを修正していけば、新しい言語ファイルが作れて、StylePix のオプションで選ぶことができる。

言語ファイルの文字エンコードは UTF-16LE +BOM。とりあえず xyzzy で開いて修正できることを確認。

英語化ファイル(English.txt)その他と比較してみたところ、どうも Japanese.txt はID種類が一部不足してるようで。比較用に、全ファイルを読んでIDをまとめてCSV出力するRubyスクリプトを書き始めたのだけど、UTF-16LEのファイルを読んで正規表現を適用する方法がよく分からなくて、ググってアレコレ試していたり。

2016/10/23() [n年前の日記]

#1 [cg_tools] StylePixの日本語言語ファイルをそこそこ修正

少なくとも、メニュー項目の中に「出口」「作物」「地域」等の怪しい訳は無くなった…と思うので、一応置いておくのです。まだ怪しいところが残ってるけど、目立つところは直せたはず。 *1

_Jp_m256_20161023_txt.zip (StylePix 1.14.5.0 用)

Languageフォルダに入れて StylePix を起動すれば、オプションから言語を切り替えて使える。ファイル名が言語名として扱われるようで。

ちなみに、StylePix 2.0.0.5 の言語ファイルは、1.14.5.0 より微妙に項目数が増えていて、その増えた分が Japanese.txt に反映されてないように見えた。

チェック作業に使ったRubyスクリプト。 :

StylePix の言語ファイルを読み込んで、CSVファイルとして出力するRubyスクリプトを書いた。出力された CSV を、Excel や LibreOffice Calc 等で開けば視認性が良くなるので、抜けがないかチェックしやすくなる。というか抜けてるところだけリストアップするスクリプトにしたほうがいいのかもしれないけど。

動作確認環境は、Windows10 x64 + Ruby 2.2.5 p319。

使い方は、Languageフォルダの中に id_dump.rb を置いて、ruby id_dump.rb と打って実行。__tmp.csv が出力される。出力内容には、英語、日本語(オリジナル)、日本語(修正版)だけを並べている。

ちなみに、言語化ファイル群の中にはハングル文字で書かれたファイルがあるけど、そのままだとアクセスすらできないだろうから、そのファイルだけkorean.txt にリネームしてから作業した。

_id_dump.rb
# StylePix Language file check.
# output __tmp.csv (UTF-16LE + BOM)

output_file = "__tmp.csv"

# StylePix Language files
filelist = [
  'English.txt',
  'Japanese.txt',
  'Jp_m256_20161023.txt',
  'Arabic.txt',
  'ChineseSimplified.txt',
  'ChineseTraditional.txt',
  'French.txt',
  'German.txt',
  'Italian.txt',
  'Welsh.txt',
  'korean.txt', # please rename
]

# pickup language files
pickuplist = [
  'English.txt',
  'Japanese.txt',
  'Jp_m256_20161023.txt',
]

# UTF-16LE string
re_comment = Regexp.new( '^\/\/'.to_s.encode( "UTF-16LE" ))
re_blankline = Regexp.new( '^\s+$'.to_s.encode( "UTF-16LE" ))
re_word_tab_word = Regexp.new( '^(\S+)\s+\"(.+)\"$'.to_s.encode( "UTF-16LE" ))
re_start_quot = Regexp.new( '^"'.to_s.encode( "UTF-16LE" ))
re_end_quot = Regexp.new( '\"$'.to_s.encode( "UTF-16LE" ))
nullstr = "".encode("UTF-16LE")
sep = "\t".encode("UTF-16LE")

# File read
data = {}
id_lst = {}
filelist.each do |fn|
  dt = {}
  f = File.open(fn, 'rb:BOM|UTF-16LE')
  while l = f.gets
    l.chomp!
    next if l =~ re_comment # skip comment
    next if l =~ re_blankline # skip blank line
    if l =~ re_word_tab_word
      id, value = $1, $2
      dt[id] = value
      if id_lst.has_key?(id)
        id_lst[id] += 1
      else
        id_lst[id] = 1
      end
    end
  end
  f.close
  data[fn] = dt
end

# make csv
res = []
header = []
header.push("ID".encode("UTF-16LE"))
pickuplist.each do |fn|
  header.push(fn.encode("UTF-16LE"))
end
res.push(header)

id_lst.keys.each do |id|
  dt = []
  dt.push(id)
  pickuplist.each do |fn|
    v = data[fn]
    dt.push((v.has_key?(id))? v[id] : nullstr)
  end
  res.push(dt)
end

# output csv (UTF-16LE + BOM)
f = open(output_file, 'wb:UTF-16LE')
f.write "\uFEFF"  # BOM
res.each do |l|
  f.puts l.join(sep)
end
f.close

Excel や LibreOffice Calc に渡す CSV は、UTF-16LE + BOM じゃないといかんらしいので、スクリプト内でやたらと encode("UTF-16LE") を呼んでいたり。

一応、出力されたCSVも置いてみたりして。

___tmp_csv.zip

余談。StylePixの動作がちょっと怪しい気がする。 :

StylePix起動時の動作が、なんだかちょっと怪しい感じがする。OS起動直後に起動する分には素早く起動してくれるけど、何度か StylePix の終了と起動を繰り返しているうちに、デスクトップ画面全体を巻き込んで、しばらく操作できない場面が出てくる…。一体何が起きてるんだろう…。ちなみに環境は Windows10 x64。

余談その2。StylePix 2.xの起動は遅い。 :

StylePix 1.x は起動が比較的早いけど、2.x はめっきり起動が遅い感じで。

ただ、起動と終了を何度も繰り返した状況で確認したので、インストールした直後なら起動が早かったりするのかもしれないけど、そこまで確認していない。

2016/10/28追記。 :

最初は txt ファイルを置いていたのだけど、サーバにアップロードしてみたら途中で切れてた…。zipファイルにして置き直したり。

*1: 公式のソレに反映してもらえたらいいのだけど、自分、英語が全く分からんから交渉しようもなく。

#2 [ruby] Ruby でUTF-16LEのテキストファイルにアクセス

せっかくテストしたのでメモ。動作確認環境は、Windows10 x64 + Ruby 2.2.5 p319。

テストに使った UTF-16LE のテキストは以下。

_text_utf16le_bom.txt

読み込み。 :

まずは読み込んでみる。

_open_utf16le.rb
# UTF-16LE + BOM のテキストファイルにアクセス

infile = "text_utf16le_bom.txt"

File.open(infile, 'rb:BOM|UTF-16LE') {|f|
  while l = f.gets
    l.chomp!
    puts l.encoding # => UTF-16LE
  end
}
  • File.open() に 'rb:BOM|UTF-16LE' を指定すれば UTF-16LE + BOM として開ける。
  • さらに、gets で読み込めば、 string が UTF-16LE として扱われる。
  • フツーは、ファイルを開いたら close で閉じないといけないが、File.open() にブロック({ } とか do end とか)をつけた場合は close を省略できる。

結果。
> open_utf16le.rb
UTF-16LE
UTF-16LE
UTF-16LE
UTF-16LE
UTF-16LE
UTF-16LE
UTF-16LE
UTF-16LE
「文字列(String)のタイプは UTF-16LEだよ」と表示された。

正規表現。 :

UTF-16LE + BOM の文字列に対して正規表現を使う例。

_open_utf16le_regexp.rb
# UTF-16LE + BOM の文字列に対して正規表現を使う

infile = "text_utf16le_bom.txt"

re0 = Regexp.new( '^\/\/'.to_s.encode("UTF-16LE") )
re1 = Regexp.new( '^(.+)\t(.+)$'.to_s.encode("UTF-16LE") )

f = File.open(infile, 'rb:BOM|UTF-16LE')
while l = f.gets
  if l =~ re0
    puts "comment"
  elsif l =~ re1
    puts "data"
  end
end
f.close
ソースコードの文字コード(この場合はUTF-8)、とは異なっている文字コードの文字列(UTF-16LE)に対して正規表現を使いたい場合、Regexp.new() を使う。いつもの調子で l =~ /^(.+)/ とか書いてしまうと UTF-16LE と UTF-8 が混在してるので「おいコラ、違う文字コードが混ざってるぞ」と怒られる

結果。
> open_utf16le_regexp.rb
comment
comment
data
data
comment
comment
data
data
UTF-16LEの文字列に対して正規表現を使って判別処理ができてる。

書き込み。 :

UTF-16LE + BOM でテキストファイルを出力してみる。

_output_utf16le.rb
# UTF-16LE + BOM の文字列をテキストファイルとして出力

outfile = "_tmp.txt"

File.open(outfile, 'wb:UTF-16LE') { |f|
  f.write "\uFEFF"  # BOMを出力
  DATA.each do |l|
    f.puts l.encode("UTF-16LE")
  end
}

__END__
ABCDEFG
0123456
日本語を記述
  • f = open(outfile, 'wb:UTF-16LE') で、UTF-16LE で出力できる。
  • f.write "\uFEFF" で、BOMを出力。
  • DATA.each {|l| puts l } を使えば、__END__ 以降に書かれた文字列を1行ずつ読み込める。

参考ページ。 :

#3 [windows] やっぱりWindows10がスリープから復帰できない

NVIDIA GeForecドライバが何度か更新されたので、実は GTX 750 Ti のドライバも少しは中身が変わっているのではと期待してしまって。Windows10 をスリープさせて復帰させようと試してみたところ、やっぱりスリープ復帰に失敗してブルースクリーンに。DPC_WATCHDOG_VIOLATION と表示されてる…。

GTX 750 Ti は、スリープ関連については、もうダメかもしれん。省電力化を進めたことで不具合を起こすようになってしまったんだろう…。もっとも、Windows7時代の古いドライバなら一応スリープ復帰できていたので、ドライバ側で行える回避策があるのだろうけど…。

#4 [anime] ウルトラマンオーブ、べリアルさんの回

怪獣を倒すためとは言え、 街をガンガン破壊するウルトラマン…。イイな…。 ビルを引き抜いて怪獣をタコ殴りしたり、 八つ裂き光輪で怪獣の尻尾を切断した上にその 尻尾を凶器代わりにしてタコ殴りしたり。ウルトラマンは正義のヒーロー、という思い込みがあるので、乱暴なソレは見ていてクラクラしてきて、なんだかイイ感じ。もっともこのへん進めていくと、平成ライダーのようなギスギスシリーズになって差別化が難しい気もするけど。たまにはこういうのもいいよなと。

まあ、劇場版その他ではべリアルさんがそういう役として活躍してる印象もあるのでアレですが。

2016/10/24(月) [n年前の日記]

#1 [ruby] WindowsとRubyとRsenseのgem

Windows10上に Ruby 2.3 をインストールしたついでに、gem install rsense で、Rsense をインストールしてみたのだけど。

_rsense | RubyGems.org | your community gem host
_rsense/rsense: Rsense Can See All

Rsenseというのは、エディタ上でRubyのアレコレを補完してくれるツール。昔、 Javaで実装されてた時期があるんだけど、そこから放置されちゃって、数年前におそらくは別の方が再実装し始めて、また放置、みたいな。

_RSense - EmacsやVimなどに特化したRubyのための優れた開発援助ツール
_m2ym/rsense

さておき、インストール後に rsense start でRseneサーバが起動するはずなのだけど。
> rsense start
D:/Ruby/Ruby23/lib/ruby/gems/2.3.0/gems/rsense-0.5.18/lib/rsense/client/runner.rb:112:in `start': uninitialized constant Spoon::FileActions (NameError)
Did you mean?  FileUtils
        from C:/Ruby/Ruby23/lib/ruby/gems/2.3.0/gems/rsense-0.5.18/bin/rsense:12:in `<top (required)>'
        from C:/Ruby/Ruby23/bin/rsense:22:in `load'
        from C:/Ruby/Ruby23/bin/rsense:22:in `<main>'
予想はしてたけど、Windows上ではエラーが出て動かない。 _Spoon というライブラリが FileActions という名前の何かを持ってるはずだからソレを使おうと思ったけど見つからねえよ、と怒られてる。

_headius/spoon: A fork/exec replacement for FFI-capable implementations を眺めると…。 _spoon/spoon.rb の中で、Windows と UNIX で使うファイルを分けてて。 _spoon/unix.rb には FileActionsクラスどころか他にも色んなクラスやメソッドがあるんだけど、 _spoon/windows.rb はガラガラで。そもそも Spoon って何をしてくれるライブラリなのかそこからして分からんけど、おそらくこれじゃ動かないよなと。

なんとなく、以下の記事を思い出したりもして。

_依存関係をなくそう : Rubyアプリ・Gemの開発者への提言 | プログラミング | POSTD
_NPMとleft-pad : 私たちはプログラミングのやり方を忘れてしまったのか? | プログラミング | POSTD
_本の虫: npmからkikとその他諸々が消されたまとめ
_npm パッケージの unpublish に関するゴタゴタの大まかなまとめ - ヤルキデナイズド

ちなみに、Ruby 2.2 + Rsense 0.3(オリジナル版)は、一応 Windows10x 64上でも動いていて。NTEmacs 24.5.1 を使いながら Rubyスクリプトを書く際には補完も効いてはいるのですけれど。

#2 [ruby] UbuntuとRubyとRsenseのgem

VMware上でUbuntu 16.04 を動かして、その上で Rsense をgemを使ってインストールできるのか試してみたり。

まず、Ruby 2.3 をインストール。
sudo apt-get install ruby2.3 ruby2.3-dev
ruby2.3-dev を入れておかないと、ネイティブ(?)ライブラリがビルドできないので入れておく。

ググったところ、PPAだかリポジトリだかを追加して Ruby 2.3 をインストールする事例ばかりが出てきたけど。Ubuntu 16.04 はひょっとすると標準リポジトリに Ruby2.3 が既に入ってるようで。少なくとも /etc/apt/sources.list.d/ を眺めた感じでは、昔の自分はPPAを追加してなかった。が、インストールできた。たぶん。

RSense は、sudo gem install rsense でインストール。その後 rsense start と打ってみたらサーバが動いてくれたようで、接続すべき port が表示された。rsense stop でサーバを停止。

EmacsやVimでは使えない。 :

ググったところ、Emacs や Vim から RSense を使おうとした場合、オリジナル版の 0.3 を使った事例ばかりが出てきてしまって。gem経由でインストールできる版は、Atom や Sublime Text等の拡張しか用意されてないっぽい。

ということは、Emacs や Vim で使いたいならオリジナル版を、Atom や Sublime Text 等で使いたい場合は gem経由版を、ということになるのだろうかと。

Vim側でも問題が。 _Rsense(rsense/rsense)を利用してVimでRubyの補完を実現する - Qiita によると、Vim + gem経由版 Rsense で使えなくもないようだけど。
補完プラグインであるneocomplecacheは、すでに後継のShougo/neocomplete.vimに開発が移行して久しいですが、rsenseと組み合わせて使うプラグインは、neocompleteだと現在無く、neocomplecacheだとあるためです。

Rsense(rsense/rsense)を利用してVimでRubyの補完を実現する - Qiita より

という話も。自分の ~/.vimrc を眺めたら neocomplete を使ってたので、neocomplecache と競合しそうな気もする。

てなわけで、*NIXだったらすんなり使える、というわけでもなさそうだなと…。上手くいきそうな組み合わせを考えないといけないようで。ちと面倒。

Atomで試してみる。 :

とりあえず Atom をインストールして試してみる。

_atomをUbuntuにPPA経由でインストールする - Qiita
sudo add-apt-repository ppa:webupd8team/atom
sudo apt-get update
sudo apt-get install atom
インストールできた。ターミナルから atom と打ったら起動した。…関係ないけど、Atom は *NIX上でも、やっぱりちょっと起動が遅い。

Ctrl+, を叩いて、設定画面を表示。 _japanese-menu_autocomplete-ruby をインストール。autocomplete-ruby の設定で、rsense のパスを指定。ターミナルで which rsense と打てば、rsense のパスが分かる。

ここまでやってから、AtomでRubyスクリプトを書いてみたけど、補完が効いてるように見えた。Atom を使う分には、gem経由版 Rsense も使える、と捉えてもよさそう。

ついでに、Rubyスクリプトのソース整形もできるように、rubocop を gem install rubocop でインストールしたり、 _atom-beautify をインストールしてみたけど。Ubuntu の場合、atom-beautify のデフォルトキー割り当て、Ctrl + Alt + B が、スクリーンキーボード(仮想キーボード)の表示で既に使われているようで。

Ubuntu側の設定を変えて、仮想キーボード表示のショートカットキーを無効に設定。Ubuntuのデスクトップの上のほうにあるキーボードのアイコンをクリックして、設定 → 全体の設定 → 拡張オプションの表示 → 仮想キーボード、の割り当てを空に(ボタンをクリックしてからESCキーを押す)、みたいな。

#3 [ruby] Rubyのgemが使えない

Windows10 x64 + Ruby 2.2 / 2.3 で gem を使おうとしたら、SSL証明書が古くなったから使えないよ、みたいなメッセージが表示されて。ググった感じでは、AddTrustExternalCARoot-2048.pem を更新しないといけないらしいが…。rubygemsを更新することで対策する手段が現段階では用意されているようで。

_AddTrustExternalCARoot-2048.pem が not found 。また証明書が更新されたらしい --rails - ミスターFのいろいろプログラミング
_SSL Certificate Update - RubyGems Guides

rubygems-update-2.6.7.gem をDLして、以下を実行。
gem install --local rubygems-update-2.6.7.gem
update_rubygems --no-ri --no-rdoc
gem --version
gem uninstall rubygems-update -x
gem が使えるようになった。

たしか以前も、こういうことがあったな…。その時は .pem とやらを更新して対応したような…。どうだったかな…。

#4 [anime] 放映に間に合わないTVアニメが少し増えてきたような

ロボットアニメの「レガリア」、自転車アニメの「ろんぐらいだぁす」、フィギュアスケートアニメの「ユーリ」、魔法飛行少女アニメの「ブレイブウィッチーズ」等、制作が間に合わなくて放映が延期されるアニメがチラホラ増えてきたような。

そもそも本数が多過ぎるのだろうけど…。総集編を何度か入れたあげく最終回も間に合わなかった戦車アニメの「ガルパン」が大ヒットしたので、ある意味勇気づけられてるところもありそうだなと想像したりもして。

放映にはちょくちょく間に合わないけれど、しっかり作られてるアニメと。放映にはどうにか無理矢理かろうじて間に合わせたけど、ネット上ではやれ作画崩壊と騒がれてしまうアニメと。どちらがヒットする確率が高くなりそうかと考えたら…。そりゃ、再放送でも総集編でもとにかく入れて、一発でちゃんとした映像を作ったほうがまだマシかもと思えてきたりもするわけで。ということで個人的には、無理矢理間に合わせるより違う策を取れるなら取ったほうが、と思わないでもないですが、そこはそれぞれ色々な事情があるんだろうと…。

今はもう、TVアニメと呼ばれているOVAを作ってるようなもんだし…。映像のクオリティがとんでもないことになってるし…。玩具を売るために放映してるわけでもないし…。本数は無茶苦茶なことになってるし…。昔とは色々と違うのだから、昔のように、TVアニメは毎週新作が放送されるもの、などと思い込むのもおかしな話。どこぞのスタジオでは制作進行さんが過労死してるけど、リアルに人を殺してまで作るものでもないだろう、とも思うし。裾野が広くないと山は高くならないものだけど、制作リソースが限られている状況で裾野を無闇に広げたら逆に低くなっちゃうよな、とも思うし。

てなことをもやもやと思ってしまったのでメモ。思考メモです。

2016/10/25(火) [n年前の日記]

#1 [python] PySideでマウス座標を常に取得

PySideを使って、QGraphicsScene の中でマウス座標を常に取得することができるか実験。要は、マウスカーソルを動かすとその位置に追従して画像も表示される、みたいなことをしたい。お絵かきソフトではブラシ枠がマウスカーソル位置に表示されてたりするけど、おおよそあんな感じで。

_gview_mousecursor.py
"""
PySide + QGraphicsView上でマウスカーソルに何かを追従させる
動作確認環境 : Windows10 x64 + Python 2.7.11 + PySide 1.2.4
"""

import sys
from PySide.QtCore import *
from PySide.QtGui import *

brushFile = "brush.png"
canvasSize = (640, 480)
padding = 48
status = None


class DrawAreaScene(QGraphicsScene):
    """ 描画ウインドウ用Scene """

    def __init__(self, *argv, **keywords):
        super(DrawAreaScene, self).__init__(*argv, **keywords)

        global brushFile
        global canvasSize
        global padding

        # Scene に 空QPixmap を追加
        w, h = canvasSize
        self.pixmap = QPixmap(w, h)
        self.pixmap.fill(QColor(192, 192, 192, 255))
        self.imgItem = QGraphicsPixmapItem(self.pixmap)
        self.addItem(self.imgItem)
        self.imgItem.setX(padding)
        self.imgItem.setY(padding)

        # Scene にブラシ画像を追加
        self.brushPixmap = QPixmap(brushFile)
        self.brushImgItem = QGraphicsPixmapItem(self.brushPixmap)
        self.addItem(self.brushImgItem)

    def mousePressEvent(self, event):
        """ マウスボタンを押した """
        x, y = self.getMousePos(event, "Click")

    def mouseReleaseEvent(self, event):
        """ マウスボタンを離した """
        x, y = self.getMousePos(event, "Release")

    def mouseMoveEvent(self, event):
        """
        マウスを動かしてる時に呼ばれる処理。
        デフォルトではマウスボタンを押してる間(ドラッグ中)しか呼ばれないが、
        親のQGraphicsView で viewport() の mouseTracking を True にすれば
        マウスを動かした際に常時呼ばれるようになる。
        """
        x, y = self.getMousePos(event, "Move")

        # ブラシが非表示なら表示を有効化
        if not self.brushImgItem.isVisible():
            self.setVisibleBrush(True)

        # ブラシの表示位置を変更
        pm = self.brushImgItem.pixmap()
        xd = pm.width() / 2
        yd = pm.height() / 2
        self.brushImgItem.setOffset(int(x - xd), int(y - yd))

    def getMousePos(self, event, msg):
        """ マウス座標を取得 """
        x = event.scenePos().x()
        y = event.scenePos().y()
        global status
        status.showMessage("(%d , %d) %s" % (x, y, msg))
        return (x, y)

    def setVisibleBrush(self, flag):
        """ ブラシ表示の有効無効切り替え """
        self.brushImgItem.setVisible(flag)


class DrawAreaView(QGraphicsView):
    """ メインになるQGraphicsView """

    def __init__(self, *argv, **keywords):
        super(DrawAreaView, self).__init__(*argv, **keywords)
        self.setBackgroundBrush(QColor(64, 64, 64, 255))  # 背景色を設定

        self.setCacheMode(QGraphicsView.CacheBackground)

        # self.setRenderHints(QPainter.Antialiasing |
        #                     QPainter.SmoothPixmapTransform |
        #                     QPainter.TextAntialiasing)

        # Sceneを登録
        scene = DrawAreaScene(self)
        self.setScene(scene)
        self.setSceneNewRect()

        # 子のSceneに対してマウストラッキングを有効に
        vp = self.viewport().setMouseTracking(True)

    def resizeEvent(self, event):
        """ リサイズ時に呼ばれる処理 """
        super(DrawAreaView, self).resizeEvent(event)
        self.setSceneNewRect()

    def scrollContentsBy(self, dx, dy):
        """ スクロールバー操作時に呼ばれる処理 """
        # スクロール中、Scene内にブラシがあると
        # 何故かゴミが残るので、ブラシを非表示にしている
        self.scene().setVisibleBrush(False)
        super(DrawAreaView, self).scrollContentsBy(dx, dy)

    def setSceneNewRect(self):
        """ Sceneの矩形を更新 """
        # 以下は、Sceneのアイテム群境界ボックスを取得する例
        # rect = self.scene().itemsBoundingRect()

        # 以下は、viewport のサイズを指定する例
        # rect = QRectf(self.viewport().rect())

        global canvasSize
        global padding
        w, h = canvasSize

        # キャンバス周辺に余白を設けたサイズを求める
        w += padding * 2
        h += padding * 2
        rect = QRectF(0, 0, w, h)

        # Sceneの矩形を更新。自動でスクロールバーの長さも変わってくれる
        self.scene().setSceneRect(rect)


class MyVWidget(QWidget):
    """ メインウインドウ周辺に配置するWidget """

    def __init__(self, parent=None):
        super(MyVWidget, self).__init__(parent)
        l = QVBoxLayout()  # 縦に並べる
        l.addWidget(QLabel("Dummy"))
        self.setLayout(l)


class MyMainWindow(QMainWindow):
    """ メインウインドウ """

    def __init__(self, *argv, **keywords):
        super(MyMainWindow, self).__init__(*argv, **keywords)
        self.setWindowTitle("Mouse Tracking Test")
        self.resize(640, 480)

        # メニューバー
        mb = QMenuBar()
        file_menu = QMenu("&File", self)
        exit_action = file_menu.addAction("&Close")
        exit_action.setShortcut('Ctrl+Q')
        exit_action.triggered.connect(qApp.quit)
        mb.addMenu(file_menu)
        self.setMenuBar(mb)

        # ステータスバー
        global status
        status = QStatusBar(self)
        self.setStatusBar(status)
        status.showMessage("Status Bar")

        # 左ドック
        self.leftDock = QDockWidget("Left Dock", self)
        self.leftDock.setWidget(MyVWidget(self))
        self.addDockWidget(Qt.LeftDockWidgetArea, self.leftDock)

        # 中央Widget
        self.gview_image = DrawAreaView(self)
        self.setCentralWidget(self.gview_image)


def main():
    """ メイン処理 """

    # このあたりを指定すると描画が速くなるという話を見かけたが、
    # "native"、"raster"、"opengl" を指定しても結果は変わらなかった…
    QApplication.setGraphicsSystem("raster")

    app = QApplication(sys.argv)
    w = MyMainWindow()
    w.show()
    sys.exit(app.exec_())

if __name__ == '__main__':
    main()

gview_mousecursor_ss01.gif

一応できたっぽい。

ブラシ画像は、 _brush.png を使った。

mouseTracking が肝らしい。 :

QGraphicsScene には、mouseMoveEvent() という、マウスカーソルを動かした時に呼ばれるメソッドがあらかじめ用意されているのだけど。この mouseMoveEvent() は、デフォルトでは「マウスボタンを押してる間」しか呼ばれない。要は、ドラッグ操作を前提としているわけで。

しかし今回は、マウスボタンの状態に関係なく、マウスカーソルが動いたらとにかく座標を取得したい。

その場合は、QGraphicsScene の親に相当する、QGraphicsView の viewport() に対して、setMouseTracking(True) をしてやるといいらしい。

_シーンでマウストラッキング - TB-code

試してみたところ、常にマウスカーソル座標が取得できるようになった。

他にも、全てのイベントを一旦受け付けて、イベント種類でフィルタリングして、マウスカーソルが動いたかどうかを検出する、というやり方もあるらしい。

スクロールバーの長さ。 :

今まで QGraphicsView を使った際に、スクロールバーの長さが妙な感じになっていて悩んでたけど。self.scene().setSceneRect() で、ちゃんとした QRectf を渡してやればそれっぽいスクロールバーの長さになってくれることが分かった。

例えば、Sceneのアイテム群境界ボックスを取得してやれば、それらしくなるし。
rect = self.scene().itemsBoundingRect()
self.scene().setSceneRect(rect)

あるいは、QGraphicsView の viewport のサイズを指定してやれば、QGraphicsView のスクロール領域サイズ = Sceneのサイズになるのでスクロールバーが消えてくれるし。
rect = QRectf(self.viewport().rect())
self.scene().setSceneRect(rect)

今回は、Scene が持ってる空のQPixmap(キャンバス相当)サイズ+余白を指定して、キャンバスの端の部分もある程度表示できるようにしてみたり。
w += padding * 2
h += padding * 2
rect = QRectF(0, 0, w, h)
self.scene().setSceneRect(rect)
ただし、この場合は、空のQPixmap の offset を余白分ずらしておく必要有。

描画速度が遅い。 :

一応処理はできたけど、別の問題が出てきた。マウスを素早く動かすと、追従してる画像がちらついてしまう。どうも描画が遅いというか、パフォーマンスが出ないというか。

アプリの開始時に QApplication.setGraphicsSystem("opengl") を呼ぶことで描画速度が変わってくるという話も見かけたけど、自分の手元の環境では特に変化は見られなかった。まあ、仮に変化があったとしても、環境によって、opengl を指定するとむしろ遅くなるとか、native と raster の速度が変わらんとかあるようで。改善してくれることを期待しちゃダメ、ってことだろうなと。

また、「QGraphics* は複数のアイテムを登録してアレコレできる分、色々と処理が遅い。処理速度を求めるなら使うべきではない」という主張も見かけたし、あるいは、「最低限の再描画領域をその都度求めてクリッピングしてやれば速度が稼げる」という話も見かけた。

もっとも、考えてみれば…。マウスを素早く動かしてる時に正確な画像描画を目にしないと作業ができないというわけでもないだろうし。このくらいは目を瞑るのもアリかもしれないなと。

あるいは、お絵かきソフトの類は大量のアイテムを登録して描画したいわけでもないだろうから、別のWidgetを使ってどうにかできないか検討するのもアリかもしれず。レイヤーに相当する QPixmap を何枚か上書きしていって、最後にブラシ枠画像を描画すればいいのだろうし。であれば、QGraphics* を持ち出すほどの処理ではない、かもしれない。

#2 [prog][windows] AtomのScriptがやっとWindowsに対応してくれた

Atomエディタの拡張として、 _Script という、PythonスクリプトだのRubyスクリプトだのをその場で実行できる拡張があるのだけど。

ふと気付いたら、ようやく Windows上でShebangを無視してくれるようになったようで。

_Ignore firstline check on Windows - rgbkrk/atom-script@4374623

修正箇所は、たったの1行っぽいけど。

長かった…。今まで、アップデートでファイルが差し変わるたびに、 _毎回自分で修正 してましたよ。実にアホらしかった…。

ちょっと解説。 :

Python や Ruby等のスクリプトは、えてして、ソースの1行目に、 _Shebang(シバン行) と呼ばれる、以下のような記述があって。
#!/usr/bin/env python

これは *NIX用の記述で…。

*NIX は Windows と違って、ファイルの拡張子と、そのファイルを利用するプログラムを関連付けてない。だから、「オイ、*NIXよ。このファイルを実行しろや」と指示しても、何を使って実行すればいいのか分からない。なので、ファイルの1行目を読んで、「ふむ。要は、この /usr/bin/env python なる者に、このファイルをそのまま渡してやればよろしいのですな」と判断して実行するわけで。

ただ、*NIX の世界は、環境によって、python だの ruby だのをどの場所に置いているか違っていたりする。だから、python等の場所(ファイルパス)を直接書くと問題が発生する。他所からスクリプトを持ってくるたびに、1行目を自分の環境に合わせて修正してやる羽目になる。

それはマズいよね、面倒臭いよね、ってことで、*NIX 世界の人達は、/usr/bin/ に _env ってツールを入れといて、env を経由してやれば python だの ruby だのは探して呼んでくれるよ、○○がどこに置いてあるかなんて気にしなくて済むよ、ということにした。これで、どの環境に持っていっても動いてくれるスクリプトが書けるようになったわけで。

ところが、Windows には /usr/bin/ なんてフォルダ自体が無いし、もちろん env も入ってない。そもそも Windows は、ファイルの拡張子で何のプログラムを呼び出すか覚えているから、Shebang を見る必要が無い。

ということで、Windows用の Python や Ruby は、1行目の Shebang を無視するようになっていて。せいぜい見るとしても、そこに書かれてるオプション文字列ぐらいしかチェックしない。「#!/usr/bin/env」の部分は無視しちゃう。

こうすることで、*NIX でも Windows でも、同じスクリプトソースを動かせるようにしていたわけですよ。

ところが、Atom拡張の Script は、1行目の Shebang をキッチリ見てしまう。*NIX でも Mac でも Windows でも、「/usr/bin/env python を呼べばええのやな」と処理していて。結果、Windows上で動かした時だけ、「/usr/bin/env なんて知らねえよボケ」とエラーが出ていたという。

更に、「コレ、エラー出るんだけど」とバグ報告があっても、「は? Linux や Mac では動くんですけど?」「Shebang に #!C:\〜\python って書けば動くだろ? 1行目には正確なパスを書くもんだぞ」とか返されちゃって。コイツラ、「#!/usr/bin/env 〜」の意味が分かってねえ…。

せっかく Pythonその他が、どのOS上でも動くようにと気配りして実装されてるのに、Atom拡張の Script はその努力を台無しにしていたわけですよ。…どうやら作者様が Windows を持ってないらしいので仕方ないところもありますが。

てなわけで、そのあたりの問題が解決したのはありがたいことだなと。こうして長々と誰も読まないであろう解説をテンション上がって書いてしまうぐらいに、個人的には大変喜んでおります。ありがたや。修正してくれてマジthx。

2016/10/26(水) [n年前の日記]

#1 [pc] メインPCのHDDが遅い気がする

どうもメインPCのHDDが遅いような気がする。GIMP や Inkscape を起動する際に数分かかるのはどうもおかしいというか。以前も数分かかってたけど、もうちょっと速かったような。

環境は…。 もしかすると、 _チップセットが H67 だからSATAが少しずつ壊れていく不具合が発生している のでは…。でも、SATAポート0〜1番(SATA 3.0)は問題が起きない、問題が起きるのは2番以降(STAT 2.0)という話じゃなかったか。それに、H67 でも、B3ステップは不具合修正版のはず…。

CrystalDiskMark で測定してみた。
-----------------------------------------------------------------------
CrystalDiskMark 5.1.2 x64 (C) 2007-2016 hiyohiyo
                           Crystal Dew World : http://crystalmark.info/
-----------------------------------------------------------------------
* MB/s = 1,000,000 bytes/s [SATA/600 = 600,000,000 bytes/s]
* KB = 1000 bytes, KiB = 1024 bytes

   Sequential Read (Q= 32,T= 1) :   125.153 MB/s
  Sequential Write (Q= 32,T= 1) :   113.850 MB/s
  Random Read 4KiB (Q= 32,T= 1) :     1.734 MB/s [   423.3 IOPS]
 Random Write 4KiB (Q= 32,T= 1) :     1.602 MB/s [   391.1 IOPS]
         Sequential Read (T= 1) :   120.791 MB/s
        Sequential Write (T= 1) :   110.307 MB/s
   Random Read 4KiB (Q= 1,T= 1) :     0.585 MB/s [   142.8 IOPS]
  Random Write 4KiB (Q= 1,T= 1) :     1.140 MB/s [   278.3 IOPS]

  Test : 100 MiB [C: 88.7% (203.1/229.0 GiB)] (x3)  [Interval=5 sec]
  Date : 2016/10/26 21:17:19
    OS : Windows 10 Professional [10.0 Build 10586] (x64)
    HDD WDC WD10EALX-009BA0 (SATA 6Gb/s)

アレ? HDD自体は、そんなに遅くないな。ということは…どゆこと?

#2 [nitijyou] 自転車で買い物に

近所のコンビニ(?)で煙草を、ザ・ビッグで夜食等を購入。

ゴールデンバットがフィルタ付きになってた。 :

煙管を使って吸ってる関係で、フィルタのついてないゴールデンバットを買ってたのだけど。今回買ったソレを開けてみてビックリ。なんでフィルタが付いてるんだ…。今までフィルム包装されてなかったのに、フィルム包装されてたから、何か妙だなとは思ったけれど…。

ググってみたら、2016年6月からフィルタ付きで売り始めたらしい。知らなかった。余計なことをしやがって…。

_ゴールデンバットにフィルター?狂気の沙汰だろ… - Togetterまとめ
_ゴールデンバットがフィルター付きに変更されていた... - 雨降ればブログ

それにしても、今まで買ってた品は、6月前に店が仕入れてた品だったのだな。自分は何も知らないまま、運良く買えてたのか…。逆に考えると、つまりはそれだけこの銘柄を買う人が少なかったということでもありそうな。市内中を探したけど、売ってる店は一軒しか見つからなかったし。どの店でも、そもそも売ってないという。

さて、困った。フィルタは要らないんだけど、どうしたもんか…。いやまあ、「そもそも吸うなよ」「禁煙しろよ」としか言われない話ではあるのだけど。

2016/10/27(木) [n年前の日記]

#1 [python] PySideでズーム表示を試しているところ

PySide の QGraphics* で、ズーム表示を試しているところ。

拡大縮小表示自体の処理。 :

とりあえず拡大したり縮小したりの表示はできるようになったのだけど…。要は、QGraphicsScene に登録された Item に、setTransform() で「拡大縮小しろや」と設定してやればいいようで。
    def changeScale(self, scale):
        """ ズーム変更 """
        global padding
        self.zoomv = scale

        t = QTransform()
        t.scale(self.zoomv, self.zoomv)  # スケールだけ反映
        self.bgItem.setTransform(t)

        bt = QTransform()
        bt.scale(self.zoomv, self.zoomv)
        self.brushItem.setTransform(bt)


しかし、欲を出して、中ボタンドラッグ(ホイールボタンドラッグ)でキャンバス相当をスクロールさせようとしたあたりでハマり始めて。

中ボタンドラッグでスクロール。 :

中ボタンドラッグでスクロールする方法は、以下が参考になった。

_python - How to scrolling QGraphicsScene in QGraphicsView by middle mouse? - Stack Overflow

要するに、マウス座標の変化分を offset としたとして、
self.horizontalScrollBar().setValue(self.horizontalScrollBar().value() + offset.x())
self.verticalScrollBar().setValue(self.verticalScrollBar().value() + offset.y())
みたいなことをしてやればいいらしい。

しかし、上手くいかない…。

今までは、QGraphicsScene 内でマウスイベントを取得してたけど。それだと、中ボタンドラッグ開始と同時にスクロールがガガガガガガガって感じになって、ずっと処理が返ってこなくなった。何が起きてるのだ…。

おそらくだけど、中ボタンドラッグを検知してスクロールバーの位置を変更 → スクロールバーが動いたから中のWidgetの位置も動く → マウスカーソルが動いたものとして扱われる → 中ボタンドラッグ処理がそのまま再度呼び出され、またスクロールバーの位置が変更されて、みたいなことが延々と起きて無限ループに陥ってる、ような気がする。

仕方ないので、QGraphicsScene の中でマウスイベントを受け取るのは諦めて、マウスイベント関係は全て QGraphicsView 側で処理するようにしてみたり。これで、前述のような不具合は起きなくなった。

ところが、コレはコレでマウスカーソル座標の扱いに関して別の問題が。

マウスカーソル座標取得の問題。 :

QGraphicsScene でマウスイベントを取る分には、「シーン内では、この辺にマウスカーソルがありますよ」的なイイ感じの座標取得ができたけど。QGraphicsView側で取ると、「シーンのオフセットその他モロモロなんてワシは知らんわい。とにかく QGraphicsView のガワに対してのマウスカーソル座標しか返さんぞ。つーかソレしか持ってないし」的な座標取得しかできない。

そこで、QGraphicsView 内のマウスカーソル座標+スクロールバーの値、みたいな座標値を求めて QGraphicsScene内でのマウスカーソル座標値を推測してやることに。

これで一見上手くいったように見えたものの、拡大表示をしてスクロールバーが存在しているうちはいいけれど、縮小表示をしてスクロールバーが無くなった際に正しいマウスカーソル座標値が得られなくなった。スクロールバーが無くなると、スクロールバーの値はずっと0を返すけど、実際には補正をしないといかんわけで…。

ということで泥臭いけど、おそらくは以下のようになってるのだろうと考えて、スクロールバーが有る時と無いときで座標値の求め方を変えることに。
qgview_viewport_about.jpg
  • self.viewport().rect().width() < self.scene().sceneRect().width() なら、スクロールバーが出ている(はず)。
  • self.viewport().rect().width() >= self.scene().sceneRect().width() なら、スクロールバーは出ていない(はず)。
後者の場合は、シーンが viewport内の中央に表示されてるはずだから…。得られたマウスカーソル座標が、viewportの中央からどれだけ離れてるかを計算して、シーン上でも、中央からどれだけ離れているかで座標を求める、みたいな処理に。

ズーム表示の基準位置問題。 :

ズームをする時は、今まで見えてた範囲の左上の位置を基準にして拡大縮小するようにしていたのだけど。どういう処理をしているかといえば、ズーム変更前に今までのスクロールバーの値を取得しておいて、ズーム変更後、さっき取得したスクロールバーの値に新しいズーム率を掛けて、その値でスクロールバーの位置を設定、みたいな。

が、ここで欲を出して、見えてる範囲の中心を基準にしてズームを変えようとしてハマり始めたり。色々試したけど上手くいかない…。どうもスクロールバーの値とシーン内の座標値との関係が自分はさっぱり把握できてない気がする。

#2 [anime] 「ViVid Strike!」4話を視聴

変身魔法少女が魔法格闘技をするアニメ、という説明でいいのだろうか…。

中盤、色々キツイ展開だな、見るの止めようかなと思ってたけど、ラストで「うわ」と声を出してしまった。そう来るか…。いやはや、これはよくやった。…何が「よくやった」なのかちょっと分かんないけど。

2016/10/28(金) [n年前の日記]

#1 [python] PySideでズーム変更処理をまだ書いてたり

_QGraphicsView - PySide v1.0.7 documentation を眺めてたら、 _scale というメソッドがあることに気が付いて。これは…もしかして…。

今までは、QGraphicsScene 内の各Itemを拡大縮小表示させることでズーム変更をしていたけど。もしかして QGraphicsView の scale を使えば、まとめてズーム変更できるんじゃないか、と思えてきたので試したり。

scaleの使い方は、以下のやり取りが参考になった。というか、そのものズバリの PyQt版のサンプルも投稿されていて助かった。

_c++ - QGraphicsView Zooming in and out under mouse position using mouse wheel - Stack Overflow

こんな感じになった。 :

自分で書いてみたら、こんな感じになった。…ソースが長くなってしまったので、Gistに貼ってみたり。

_gview_zoom2.py - Gist



  • マウスホイールで拡大縮小。
  • ステータスバー上のボタンを押しても拡大縮小。
  • 中ボタンドラッグ(ホイールボタンドラッグ)でスクロール。
マウスカーソル位置を基準にして拡大縮小するようにもなったし、マウスカーソル位置に常時画像を表示することもできてる。これならイイ感じ。

ダメな例も貼っておく。 :

もったいないから、昨日までごちゃごちゃやってた、ダメな例のソースも貼っておく。

_gview_zoom.py - Gist

前述の通り、こちらの例では QGraphicsScene内のItemを拡大縮小することでズーム変更してるのだけど。このやり方では、マウスカーソル位置を基準にして拡大縮小することが最後までできなかったという…。

mapToScene()は便利。 :

前述のページを見ていて知ったのだけど。どうやら、mapToScene() や mapFromScene() を使うと、マウスカーソル座標の扱いも楽になるっぽい。

おそらくだけど…。
  • mapToScene() は、QGraphicsView 用の座標値を、QGraphicsScene 内での座標値に変換するメソッド。
  • mapFromScene() は、QGraphicsScene用の座標値を、QGraphicsView用の座標値に変換するメソッド。
なのではないか。たぶん。違うのかな。どうなんだろ。英語分かりません。

今まで、シーン内の座標値を得るために、スクロールバーの値を加味して、倍率を掛けて、とか面倒なことをしてたけど。mapToScene() を使ったらそのへんの処理をごっそり削れた。ありがたや。

浮動小数点の誤差で悩んだり。 :

ズーム変更を何度も繰り返していたら、倍率が100%に戻らず、99%になってしまって悩んだり。print で変数の値を表示しても100%(1.0)なのに、アプリ上の表示は99%になる。なんでや。Qtのバグなのかコレ。

浮動小数点の誤差っぽい。

_Python の小数 - Qiita

0.1を10回足しても1にならないソレ。超基本的なことなのに、うっかり忘れてた。

とりあえず、倍率は整数値で持つことに。実際に使う際に 100.0 で割ってから使えばええやろ…。結局最後に割ってるから誤差は残ってるけど、見た目ではどうせ分からんし。

ただ、倍率を増減させる際に 1.2 とか 0.8333 を掛けてるので、結局はやっぱり誤差が溜まってきて、どこかでおかしくなるという…。実際のソレを作る際は、100%、200%、300%等、キリがいい倍率をテーブルで持っておいて、順々に変化させていくことになるかなと。

qAppてのがあったのか。 :

マウスカーソル形状を変える際には、 _QtGui.QApplication.setOverrideCursor() を使うのだけど。

今までは、
global myApp
myApp = QApplication(sys.argv)
とかやっておいて、後から、
global myApp
myApp.setOverrideCursor(Qt.ClosedHandCursor)
として、カーソル形状を変えてたわけで。

で、今頃になって「qApp というグローバル変数があるぜ」「QApplication.〜 とか書かなくて済むぜ」と知り。コレを使うと以下のように書けるらしい。
qApp.setOverrideCursor(Qt.ClosedHandCursor)
少し楽になった。

ちなみに、カーソル種類は、 _QCursor Class | Qt 4.8 を眺めればなんとなく分かるかなと。ページの真ん中あたりに、画像一覧が。

#2 [prog][neta] ある範囲に数値を収めるソレ

変数が持ってる数値を、ある範囲内に収めたい時、今までこういう書き方をしてたのですけど。
if a < 0:
    a = 0
if a > 100:
    a = 100

分かりやすいからコレはコレで、とは思うものの、なんだかダサいよなと。もっと短く書けないものか。

と思ってたら、以下のような書き方を見かけて。
a = max(0, min(a, 3200))

なるほど…。4行が1行で済むのだな…。

でも、分かりやすさがちょっと落ちるし、関数だかメソッドだかを2回呼ぶのもどうなの、という気も。ソースの見た目は短くなっても、内部処理的にはむしろ長くなってないか。

まあ、どっちでもいいか…。

#3 [nitijyou] 日記をアップロード

気付いたら、約2ヶ月ほど日記をアップロードしてなかった…。たしか、2016/09/03にアップしたまま忘れてたようで。ということで、今頃まとめてアップロード。

変な文章を書いてないか、読み返して修正するのが面倒臭くて…。いや、まあ、「読み返してコレかよ」と言われそうな気もするけど。

2016/10/29() [n年前の日記]

#1 [python] Atomエディタにlinter-pylintをインストールしてみたり

Pythonスクリプトの文法チェックをしてくれる pylint というツールがあるのだけど。その pylint をAtomエディタから常時呼び出して結果を表示してくれる、Atom拡張の linter-pylint をインストールしてみたり。環境は Windows10 x64 + Atom 1.11.2。

_linter-pylint
_steelbrain/linter: A Base Linter with Cow Powers

動作には、python、pylint の他に、linter なるAtom拡張も必要らしい。

ちなみに、pylint のインストール方法は、 _2016/10/10の日記 にメモってある。手元のバージョンは以下。
> pylint --version
pylint 1.6.4,
astroid 1.4.8
Python 2.7.11 (v2.7.11:6d1b6a68f775, Dec  5 2015, 20:32:19) [MSC v.1500 32 bit (Intel)]

linter と linter-pylint は、Atom の設定ウインドウ?からインストールできる。
  1. Ctrl+, を押すか、あるいは Ctrl+Shift+P を押してから「settings view: open」を選べば設定ウインドウ(settings)が開く。
  2. 左のほうで「Install」を選んで、「linter pylint」とか打ち込めば色々出てくるので、「linter」と「linter-pylint」をインストール。
後は設定を弄ったり。とりあえず Python Path には python とだけ書いてみたり。

これで、何か作業をするたび、下のほうに「この行の書き方、Python文化的にはおかしいぜ」と表示されるようになった。

ちょっと厳し過ぎる。 :

これで、書き方がよろしくなさそうなところを教えてもらえるようになったけど。

自作スクリプトを開いてみたら、膨大な警告数で。「なんだこの書き方は!」と何百行も文句を言われてゲンナリしてきた。スクリプトの行数より、pylintが出力した警告のほうが、行数が多い。

例えば…。

_C0103 で、「1文字の変数名はダメだ!」と怒られる。「x = event.pos().x()」と書くだけで怒られちゃうのはキツイ。しかも pylint はグローバル変数相当を認識してくれないようで、そのへん全部「コレ、定数だろ? 定数は全部大文字で書かないとダメだろ!」と怒られる。いや、それ、定数じゃねえよ…。

W0401で、「ワイルドカード(*)で import しちゃダメだ!」と怒られる。「from PySide.QtCore import *」と書くだけで怒られちゃう。

W0602で、「global hoge って書かれてるけど hoge なんて用意されてねえよ!」と怒られる。そもそもグローバル変数相当が認識できてなくて全部定数扱いされてるものだから、global を指定しても全部無いものとして扱われるようで。

W0603で、「そもそも global を使うんじゃねえ!」と怒られる。いや…限界有るだろ…。何が何でもグローバル変数を使うな、と言うなら、各事例はこう書き直せ、てなサンプルでも提示してよ…。

_W0613 で、「使ってない引数があるぞ!」と怒られる。…PySideのWidgetの、事前に用意された、イベントを取る各メソッドの中で、与えられた引数を使わずに処理する場面もたまに出てくるわけで。どうしろと。

W0614で、「お前、ワイルドカード(*)で import したよな? コレとコレとコレとコレと…使ってねえだろ!」と怒られる。…そんなこと言われても。 _PySide.QtCore_PySide.QtGui だけでもコレだけクラスがあるわけで、コレを全部人力で選別・列挙していけというのかと。どう考えても人力ではなくツール作って自動化すべきだけど、たぶん実際に記述していったらそこだけで長々とした記述になるだろうに。お前、Java になるつもりなの?

E1101が、ちょっとよく分からない。
zoomfit_btn.clicked.connect(self.zoom_fit)
てな行で、「E1101 Instance of 'Signal' has no 'connect' member」と怒られてるけど…。 _All codes - PyLint Messages にも説明が無いし。何を怒ってるんだろう…?

ある程度無視するように設定。 :

ということで、こういった警告だらけでは、かえって分かりにくくなりそうなので、無視することにする。

ソースの最初のほうに以下を追加。
# pylint: disable-msg=C0103,W0401,W0602,W0603,W0613,W0614,E1101
あるいは以下。
# pylint: disable=C0103,W0401,W0602,W0603,W0613,W0614,E1101

このように書いておけば、指定した種類の警告を無視する状態になる。

でも、W0613 あたりは残しておきたいのだけど…。しかし常時表示されていては、警告を出す意味が無いというか、肝心なところを見逃すだろうし…。

そもそも PySide が「addItem()」みたいなメソッド名をつけてるのに、自分が書く部分では「add_item()」みたいな命名規則を強要されるのは…なんだかなあ…。

#2 [nitijyou] 自転車で買い物に

コンビニとサンドラッグによって夜食や非常食を購入。天気が怪しかったのですぐに帰ってきた。

2016/10/30() [n年前の日記]

#1 [prog] Atomエディタ上でpep8やflake8を試したり

昨日、Atomエディタ上でPythonの文法チェックだか命名規則チェックだかができる linter-pylint を使ったのだけど。どうも色々と警告内容が厳し過ぎるなと。もうちょっと優しいツールは無いものか。

ググってみたら、Pythonの文法チェックをしてくれるツールは、pylint の他にも pep8、pyflakes、flake8 等があるらしく。

_Pythonのスタイルガイドとそれを守るための各種Lint・解析ツール5種まとめ! - SideCI Blog
_Python の Lint (文法チェッカ) まとめ - flake8 + hacking を使う - - Qiita
_Pythonの主要なLint(pep8, pylint, flake8)の設定方法まとめ - Qiita

更に、Atomエディタ上でもそれらを利用できる拡張があるようで。

_linter-pep8
_linter-flake8

試しに、linter-pylint を無効にして、linter-pep8 を入れてみたら、それほどゲンナリする警告は言ってこなくて。コレならイイ感じだなと。

調子に乗って linter-flake8 も試用してみたところ、こちらはもうちょっと厳しく言ってくる模様。とりあえず、linter-flake8 をしばらく利用してみようかなと。

デフォルトでは、タイピングが止まると自動的に flake8 が動くようだけど。まだソースをガシガシ打ってる最中にも動いてしまうとガンガンエラー表示が出て鬱陶しいので、Ctrl + Shift + P → linter : toggle を選んで停止/再開を切り替えたほうがいい時も。

flake8のインストール。 :

flake8 のインストール方法は以下。
pip install flake8
pip install flake8-pep257

使い方は以下。
flake8 スクリプトソース名

なのだけど、手元の環境(Windows10 x64 + Pyhon 2.7.11)で試したら、実行時にエラーが出て。

flake8 を含んでいるという hacking なるツールをインストールしてみたところ、そちらであれば flake8 の実行時にエラーが出なかった。
pip install hacking

flake8-docstrings もインストールすべき、と説明してるページも見かけたけど。flake8-docstrings-1.0.2 は標準入力関係でバグがある、みたいな話も見かけて。代わりに、flake8-pep257 を入れるといいらしいけど…。

_Missing docstring in public module error D100 - Issue #170 - AtomLinter/linter-flake8
_Value I/O Error - Issue #86 - AtomLinter/linter-flake8
_Linter does not recognize same docstring issues as flake8 on command line - Issue #116 - AtomLinter/linter-flake8

とりあえず、flake8 のバージョンを確認。
> flake8 --version
2.5.5 (pep8: 1.5.7, mccabe: 0.2.1, hacking.core: 0.0.1, ProxyChecker: 0.0.1, flake8-pep257: 1.0.5, pyflakes: 0.8.1) CPython 2.7.11 on Windows

flake8の一部の警告を無効にする方法。 :

flake8 は、一部の警告内容が矛盾するそうで。「関数宣言と docstring の間には空行が必要だぞ!」と「関数宣言と docstring の間には空行入れたらダメだぞ!」がぶつかるとかなんとか。ということで、やはりいくつかは常時警告を無視する設定が必要になるようで。

方法は、スクリプトと同じフォルダに、.flake8 というファイルを作って、例えば以下のような内容を書く。
[flake8]
ignore = D211
max-line-length = 79
  • ignore = Dxxx,Dxxx 等を書くことで、該当種類の警告が出なくなる。
  • max-line-length = 79 と書くことで、1行は79文字まで、と伝える。

あるいは、スクリプトソース内の警告が出てる行で、
from PySide.QtCore import *     # NOQA
といった感じで行の最後のあたりに「# NOQA」を書く。ちなみに、このコメントの前には2つ以上のスペースが必要。

_Configuring Flake8 - flake8 3.1.0.dev0 documentation には「~/.flake8 を書けば全体設定として使われる」と書いてあるように見えるのだけど。手元の環境で試したら、環境変数HOMEで設定してあるディレクトリに置いても、C:\Users\ユーザアカウント名\ 以下に置いても、設定が反映されなかった。一体どこに置けば反映されるんだ…。

むむ。どうやら、マイドキュメント\.flake8 なら反映されるっぽい。何故にどうしてそんな場所に…。

#2 [python] PySideでファイルオープンダイアログを表示したり等

PySideをまだ勉強中。

ファイルオープンダイアログとファイル保存ダイアログ。 :

こんな感じで書けば、ファイルオープンダイアログ、ファイル保存ダイアログが開くらしい。
    def show_open_dialog(self):
        u"""ファイルオープンダイアログを表示."""
        filter = "Image Files (*.png *.bmp *.jpg *.jpeg)"
        fpath, _ = QFileDialog.getOpenFileName(self, "Open File", self.cur_dir, filter)
        if fpath:
            self.gview.load_canvas_image(fpath)
        else:
            # cancel
            pass

    def show_save_dialog(self):
        u"""ファイル保存ダイアログを表示."""
        filter = "Image Files (*.png)"
        fpath, _ = QFileDialog.getSaveFileName(self, "Save File", self.cur_dir, filter)
        if fpath:
            self.gview.scene().save_canvas(fpath)
        else:
            # cancel
            pass
キャンセルボタンが押された時は何が返ってくるのか分からなかったけど、ひょっとすると空文字列("") が返ってくるのかもしれない。

返り値を、fpath, _ で受け取ってるサンプルがあって、「『_』って何だ?」と思ったけど。タプルの返り値を受け取る際に、使わない値は「_」で受け取って捨ててしまう、みたいな何かがあるらしい。

QPixmapのファイル保存。 :

PySideで、画像の描画その他に使う QPixmap を、画像ファイルとして保存するのは、save() を使えばいいらしい。
    def save_canvas(self, fpath):
        u"""キャンバスを画像ファイルとして保存."""
        self.canvas_pixmap.save(fpath, "PNG")
引数として、ファイルパス、画像フォーマット、圧縮率を渡す模様。圧縮率の指定を省略するとデフォルトの圧縮率で保存される、と書いてあるように見えた。

_QPixmap - PySide v1.0.7 documentation

QPixmapの指定座標の色情報を取得。 :

QPixmap の指定座標からRGBA値を取得したいと思ったのだけど、QPixmap からは取得できないようで。ただし、toImage() を使って、一旦 QImage に変換してやれば、long値として取得できる模様。
    def get_rgb_from_canvas(self, pos):
        u"""キャンバスの指定座標からRGBA値 (r,g,b,a) を取得."""
        x = int(pos.x())
        y = int(pos.y())

        pm = self.canvas_pixmap
        w, h = pm.width(), pm.height()
        if x < 0 or x >= w or y < 0 or y >= h:
            return None

        argb_long = pm.toImage().pixel(x, y)

        argb = QColor()
        argb.setRgba(argb_long)
        r = argb.red()
        g = argb.green()
        b = argb.blue()
        a = argb.alpha()
        return (r, g, b, a)

取得したlong値をRGBAに変換するなら、QColor が使える。QColor.setRgba( long値 ) で long値を渡して設定してやれば、後は red()、green()、blue()、alpha() で、R,G,B,Aの値を取得できる。

long値はARGBの順で並んでるようにも見えたから、シフトとマスクで取り出せるんじゃないのとも思ったけど、仕様変更された時の対処や、OSによって並びが異なる可能性も考えると、面倒でも(?)、QColor を使ったほうがいいのかもしれない。

2016/10/31(月) [n年前の日記]

#1 [python] PySideのアレコレをメモ

PySide関係のアレコレをメモ。

ボタンに画像を割り当てたい。 :

QPushButton に画像を割り当てて表示したい。
    btn_pixmap = QPixmap(24, 24)
    btn_pixmap.fill(QColor(r, g, b))

    btn.setIcon(QIcon(btn_pixmap))
    btn.setIconSize(btn_pixmap.rect().size())

    btn.update()
setIcon() で QIcon(QPixmap) を渡してやれば画像を設定できるようで。一応その次に setIconSize() を呼んでアイコンサイズも設定してるけど、必要なのかどうか…。

_user interface - Python QPushButton setIcon: put icon on button - Stack Overflow
_QPushButtonクラス - yu00’s blog

色選択ダイアログを表示。 :

色を選択したい。
    color = QColorDialog.getColor(Qt.green, self)
    if color.isValid():
        r = color.red()
        g = color.green()
        b = color.blue()
        self.set_rgb((r, g, b))
QColorDialog.getColor(初期色, 親) を呼べば、色選択だかカラーピッカーだか、そういう感じの例のダイアログが表示されるらしい。返り値は QColor。QColor.isValid() を呼んで False だったらキャンセルされたと分かる。

_QColorDialog - PySide v1.0.7 documentation
_QColor - PySide v1.0.7 documentation
_Pyside を使ったカラーピッカー(色選択ツール)のサンプル - Qiita
_Dialogs in PySide

以上、31 日分です。

過去ログ表示

Prev - 2016/10 - Next
1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31

カテゴリで表示

検索機能は Namazu for hns で提供されています。(詳細指定/ヘルプ


注意: 現在使用の日記自動生成システムは Version 2.19.6 です。
公開されている日記自動生成システムは Version 2.19.5 です。

Powered by hns-2.19.6, HyperNikkiSystem Project