mieki256's diary



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

以上、10 日分です。

過去ログ表示

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