PySide(3)

Qt Designerで作成した .ui ファイルには、フォームとコントロール全ての情報がxml形式で含まれていて

loader = QUiLoader()
UI = loader.load(ファイルパス名)

のように読み込むと、配置したコントロールのオブジェクトが全て含まれたUIオブジェクトが作成される。たとえばLabelコントロールのオブジェクト名を”label”とした場合

UI.label.setText(‘Foo’)

とすればラベルコントロールのテキストを変更することができる。

それだけ分かっていれば、コードのみでPySideのGUIを作成したプログラムをQt Designer版に対応させるのは簡単に行える。

ボタンオブジェクト名 = QtGui.QPushButton(ボタンテキスト)
ボタンオブジェクト名.clicked.connect(関数名)

UI.ボタンオブジェクト名.clicked.connect(関数名)

のように置き換えてしまえば良い。

Quick2Wireを使ってカラーLEDを指定した色で光らせるPySideプログラムを .ui ファイルに対応させて、更に選択した色名を画面にカラー表示させたり、クリックしたボタンの色を変えたりできるように修正すると、最終的にプログラムは以下のようになった。

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

import sys
import os
sys.path.append('/usr/lib/python3/dist-packages')
from PySide import QtCore, QtGui
from PySide.QtCore import Qt
from PySide.QtUiTools import QUiLoader
from quick2wire.gpio import pins, Out

CURRENT_PATH = os.path.dirname(__file__)
COLOR_TBL = ((Qt.black,  'BLACK'), 
             (Qt.red,    'RED'),
             (Qt.green,  'GREEN'),
             (Qt.yellow, 'YELLOW'),
             (Qt.blue,   'BLUE'),
             (Qt.magenta,'MAGENTA'),
             (Qt.cyan,   'CYAN'),
             (Qt.white,  'WHITE'))
BUTTON_COLOR = (Qt.red, Qt.green, Qt.blue)

class ColorLED:
    led = (pins.pin(0, direction=Out),
           pins.pin(1, direction=Out),
           pins.pin(2, direction=Out))
    def __init__(self):
        for e in self.led:
            e.open();
    def __del__(self):
        for e in self.led:
            e.close();
    def set(self, color):
        self.led[0].value = color & 0x01
        self.led[1].value = color & 0x02
        self.led[2].value = color & 0x04

class Window(QtGui.QMainWindow):
    def __init__(self, parent=None):
        super(Window, self).__init__(parent)
        loader = QUiLoader()
        uiFilePath = os.path.join(CURRENT_PATH, 'QtColorLED.ui')
        self.UI = loader.load(uiFilePath)
        self.setCentralWidget(self.UI)
        self.btnRGB = (self.UI.pushButton_r,
                       self.UI.pushButton_g,
                       self.UI.pushButton_b)
        self.btnRGB[0].clicked.connect(lambda: self.click_btn(0))
        self.btnRGB[1].clicked.connect(lambda: self.click_btn(1))
        self.btnRGB[2].clicked.connect(lambda: self.click_btn(2))
        self.led = ColorLED()
        self.color = 0x01
        self.pal = self.UI.pushButton_r.palette()
        self.click_btn(0)
    def click_btn(self, n):
        self.color = self.color ^ (0x01 << n)
        self.led.set(self.color)
        pal = QtGui.QPalette()
        pal.setColor(QtGui.QPalette.Foreground, COLOR_TBL[self.color][0])
        self.UI.label.setPalette(pal)
        self.UI.label.setText(COLOR_TBL[self.color][1])
        if self.color & (0x01 << n) != 0:
            pal.setColor(self.btnRGB[n].backgroundRole(), BUTTON_COLOR[n])
            self.btnRGB[n].setPalette(pal)
        else:
            self.btnRGB[n].setPalette(self.pal)

if __name__ == '__main__':
    app = QtGui.QApplication(sys.argv)
    win = Window()
    win.show()
    sys.exit(app.exec_())

実行してボタンをクリックすると、ボタンの色が変わってボタンの組み合わせによる色でLEDが光り、同時に色の名前が表示される。

範囲を選択_0202014-11-13 09.33.45

範囲を選択_021

2014-11-13 09.33.58

範囲を選択_022

2014-11-13 09.34.07

範囲を選択_023

2014-11-13 09.34.18

ソースコードの説明は省くが、やっている事は一目瞭然なので必要ないだろう。
Qtを使うのはほとんど始めてなので、冗長な部分やもっとエレガントな使い方があるのだろうと思うが、Raspberry PiでGUIアプリを動かしてハードを制御するという目標は達したので良しとする。

Qtには色々と高度なコントロールが用意されているので、次はそのへんを使って何か作ってみる予定。

PySide(2)

PySide(Qt)を使用してGUIアプリを作成する時に、コントロールの数が少ない単純デザインであればすべてコードで記述する事は可能だが、複雑なレイアウトになると現実的では無くなる。頑張って書いたとしても、メンテナンス性が悪くなるので調整や追加を行うのはかなり大変だ。

多くのツールキットにはGUIデザイン用のツールが存在していて、QtにはQt Designer、wxPythonにはwxGladeというリソースエディタがある。wxGladeはまだ使った事が無いが、そちらはwxPythonを試してみる時まで置いておくとして、今回も引き続きツールキットとしてPySideを使用するのでツールは必然的にQt Designerとなる。

Qt Designerはかなり重量級のアプリなので、Raspberry Piでも動かせない事はないのだが実際に試してみると動作が遅くて実用的では無かった。なのでQt Designerは別なPCで動かし、デザインしたファイルだけをRaspberry Piに持って行って動かす事にする。

具体的にはWindows7の仮想PCとしてインストールしたUbuntu(Xubuntu)にQt Designerをインストールした。Ubuntuソフトウェアセンターで「Qtデザイナー」を検索すれば出てくるので、そこでインストールすれば良い。

Ubuntuソフトウェアセンター_002

Windowsの場合はQt Projectのダウンロードページから、Qt Online Installer for Windowsをダウンロードして実行すれば必要なツールはすべてインストールされる。Windows版ではQt DesignerはQt Creatorの一部になっているため、Qt Designerだけを使用したい場合でもすべてインストールしなくてはいけないようだ。

Qt Creatorに含まれるQt Designerを使用する場合、最初にプロジェクトを作成してからでないとデザインが行えないようなので、PySideのためだけにQt Designerを使いたい場合にはWindowsではなくLinuxで行うほうが今のところはやりやすい。Windows環境下でもVirtualBox等の仮想PCツールで手軽にLinux環境を構築可能なので、そちらをお勧めする。

XubuntuではインストールしたQt Designerはメニューの開発カテゴリの中に「Qt4デザイナ」という名前で追加されるので、それを選択すれば起動するのだが、起動する前にまずはどんなGUIを作るのかを考えておく。
Qtのレイアウトはコントロールが多層構造になるので、デザインツールを使用する場合でもGUIの構造は予め考えておいたほうが良い。大雑把に紙に書くのも有効。

前回作ったGUIと同じものでは面白く無いので、光らせたカラーLEDの色と同じ名前を同じ色で表示させる事にした。ついでに押したボタンの色も変えて分かりやすくする。

Form - [プレビュー]_018

プレビューだと分からないが、フォーム全体のレイアウトをVertical Layoutにしておき、そこにLabelとHorizontal Layoutを並べて、Horizontal Layoutの中にボタンを三個並べた構造になっている。これをQt Designerで作ってみる。

最初にQt Designerを起動すると、新しいフォームの作成ダイアログが出るのでWidgetを選択。

Qt Designer_003

フォームが表示されたら、左側のウィジェットボックスからLabelとHorizontal Layoutをドラッグしてフォームの上に貼り付ける。

Qt Designer_007

メニューの「フォーム→垂直に並べる」を選択してフォーム全体のレイアウトを指定すると、LabelとHorizontal Layoutが縦に並ぶ。

Qt Designer_008

この状態で左側のウィジェットボックスからHorizontal Layoutの中にPush Buttonをドラッグして三個貼り付ける。ボタンは自動的に横に並ぶ。

Qt Designer_009

右側のオブジェクトインスペクタでボタンを選択して、オブジェクト名の部分をダブルクリックしてボタンに名前を付ける。ここでは pushButton_r、pushButton_g、pushButton_b とした。ボタンのtextプロパティでそれぞれに「RED」「GREEN」「BLUE」のボタンテキストも設定する。

Qt Designer_010

オブジェクトインスペクタでFormを選択して、プロパティのminimumSizeを400×100に設定。フォームをリサイズして指定したサイズよりも小さくならない事を確認する。

Qt Designer_011

オブジェクトインスペクタでLabelを選択して、プロパティのfontを適当なサイズに変更。ここではフォントサイズを28に設定した。

Qt Designer_012

最後に三個のボタン全ての autoFillBackground プロパティをON(チェックマークを付ける)にして完了。適当なファイル名を付けて保存する。ここでは QtColorLED.ui という名前で保存した。

保存したファイルは以下のような内容になっている

<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
 <class>Form</class>
 <widget class="QWidget" name="Form">
  <property name="geometry">
   <rect>
    <x>0</x>
    <y>0</y>
    <width>400</width>
    <height>100</height>
   </rect>
  </property>
  <property name="minimumSize">
   <size>
    <width>400</width>
    <height>100</height>
   </size>
  </property>
  <property name="windowTitle">
   <string>Form</string>
  </property>
  <layout class="QVBoxLayout" name="verticalLayout">
   <item>
    <widget class="QLabel" name="label">
     <property name="font">
      <font>
       <pointsize>36</pointsize>
      </font>
     </property>
     <property name="text">
      <string>TextLabel</string>
     </property>
     <property name="alignment">
      <set>Qt::AlignCenter</set>
     </property>
    </widget>
   </item>
   <item>
    <layout class="QHBoxLayout" name="horizontalLayout">
     <item>
      <widget class="QPushButton" name="pushButton_r">
       <property name="autoFillBackground">
        <bool>true</bool>
       </property>
       <property name="text">
        <string>RED</string>
       </property>
      </widget>
     </item>
     <item>
      <widget class="QPushButton" name="pushButton_g">
       <property name="autoFillBackground">
        <bool>true</bool>
       </property>
       <property name="text">
        <string>GREEN</string>
       </property>
      </widget>
     </item>
     <item>
      <widget class="QPushButton" name="pushButton_b">
       <property name="autoFillBackground">
        <bool>true</bool>
       </property>
       <property name="text">
        <string>BLUE</string>
       </property>
      </widget>
     </item>
    </layout>
   </item>
  </layout>
 </widget>
 <resources/>
 <connections/>
</ui>

このuiファイルをRaspberry Piに転送して、とりあえず「表示するだけ」のコードを書いて表示を確認。

Raspberry Pi_019

表示するだけのコードは以下の通り

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

import sys
import os
sys.path.append('/usr/lib/python3/dist-packages')
from PySide import QtCore, QtGui
from PySide.QtUiTools import QUiLoader

CURRENT_PATH = os.path.dirname(__file__)

class Window(QtGui.QMainWindow):
    def __init__(self, parent=None):
        super(Window, self).__init__(parent)
        loader = QUiLoader()
        uiFilePath = os.path.join(CURRENT_PATH, 'QtColorLED.ui')
        self.UI = loader.load(uiFilePath)
        self.setCentralWidget(self.UI)

if __name__ == '__main__':
    app = QtGui.QApplication(sys.argv)
    win = Window()
    win.show()
    sys.exit(app.exec_())        

もちろんボタンをクリックしても何も動作しないが、ウィンドウをドラッグしたり、サイズを変えたりすることはできる。次回はこのコードに肉付けして、実際に動作するアプリにしていく予定。

PySide

pythonでGUIを構築するためのライブラリ(ツールキット)はTkinter、wxPython、PyQt、PySide、PyGTK、PyGame等色々と存在しているが、種類が多くなると何を使うべきなのか悩むことになるし、ネット上での情報が分散してしまうので選択肢が多いのは良いことばかりとは限らない。

最も手軽に利用できるのは標準でPythonに含まれているTkinterで、何もインストールせずに使えるし単純なGUIならコードも短く書ける。その反面複雑なGUIになると思ったようにデザインするのが難しいという印象。これについては以前使ってみたPyGameでも似たような感じだったので、VisualStudioのようなインタラクティブにコントロールを配置できるリソースエディタが用意されているものという基準で選択すると、wxPythonとQt(PyQt、PySide)が良さそうに思える。サンプルコードの量などもその二つが豊富なようだ。

今回はその中からPySideを使ってみる事にした。同じQtのPythonバインディングであるPyQtではなくPySideを選んだのはライセンスの違いによるものだが、個人的に使うにはあまり気にしなくても良いかもしれない。機能的にはPyQtのほうが先行していて、コミュニティベースのPySideがその後を追うというのが現状らしいが、wxPythonの開発者がPySideの開発に加わったりと目まぐるしく状況が変化しているので、この先どうなるかは分からない。

QtにはQt DesignerというGUIコントロールを配置するためのリソースエディタが存在していて、PySideを使ってみようと思ったのはそれも理由の一つなのだが、最初はそれを使わずにコードだけでGUIアプリを作成してみた。

まず最初にPySideのインストールだが、Pyhton3では

$ sudo apt-get install python3-pyside

で簡単に行える。しかし、virtualenvでPythonの仮想環境を構築してある場合、このインストール方法では仮想環境下に追加されない。apt-getではなくpipでインストールすれば仮想環境下に追加されるはずなのだが、pipでのインストールは上手く行かなかった。ソースからのビルドも試してみたがやはりダメ。

import sys
sys.path.append('/usr/lib/python3/dist-packages')

と記述しておけば元のパッケージを参照できるようになるので、あまりスマートな方法ではないがとりあえずはこれで動かしておく。

Raspberry Piでやるからには何らかのハードを動かさないと面白く無いので、aitendoの100円コーナーで買った極小フルカラーLEDモジュールをGPIOに接続した。GPIOの制御にはQuick2Wireを使う。
画像1: 極小フルカラーLEDモジュール

RGBの三個のLEDが内蔵されていて、3ビットで7色の表示ができる。PWMを使えばもっと微妙な色の表現も可能だが、今回はそこまではやらない。

ウィンドウにボタンを三個表示させて、クリックするとRGBそれぞれをON/OFFできるようにした。接続はGPIO0(ピン番号17)をR、GPIO1(ピン番号18)をG、GPIO2(ピン番号27)をBに割り当てる。プログラムは以下の通り

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

import sys
sys.path.append('/usr/lib/python3/dist-packages')
from PySide import QtCore, QtGui
from quick2wire.gpio import pins, Out

class ColorLED:
    led = [pins.pin(0, direction=Out),
           pins.pin(1, direction=Out),
           pins.pin(2, direction=Out)]
    def __init__(self):
        for e in self.led:
            e.open();
    def __del__(self):
        for e in self.led:
            e.close();
    def set(self, color):
        self.led[0].value = color & 0x01
        self.led[1].value = color & 0x02
        self.led[2].value = color & 0x04

class Window(QtGui.QWidget):
    def __init__(self):
        QtGui.QWidget.__init__(self)    
        self.btn_r = QtGui.QPushButton('RED')
        self.btn_g = QtGui.QPushButton('GREEN')
        self.btn_b = QtGui.QPushButton('BLUE')
        layout = QtGui.QHBoxLayout()
        layout.addWidget(self.btn_r)
        layout.addWidget(self.btn_g)
        layout.addWidget(self.btn_b)
        self.setLayout(layout)
        self.setMinimumSize(400, 100)
        self.btn_r.clicked.connect(lambda: self.click_btn(0x01))
        self.btn_g.clicked.connect(lambda: self.click_btn(0x02))
        self.btn_b.clicked.connect(lambda: self.click_btn(0x04))
        self.led = ColorLED()
        self.color = 0
    def click_btn(self, n):
        self.color = self.color ^ n
        self.led.set(self.color)

if __name__ == '__main__':
    app = QtGui.QApplication(sys.argv)
    win = Window()
    win.show()
    sys.exit(app.exec_())

実行するとこのように表示される。

Raspberry Pi_001

次回はQt Designerを使ってもう少し複雑なGUIを作ってみる予定。