Qt Quick(QML)によるPython3とPySide2でQt5を料理する。GUI-第1章

まえがき

できればパスワードマネージャを作りたいわけだが、一通り調べた結果かなり時間がかかることがわかった。そこで当初の設計から大きく変わってしまうが、次の2点だけに絞ってやっていこうと思う。

  1. Python3とPySide2とQMLを使うGUIにする。
  2. パスワード生成機能にする。

変わりすぎだろというご批判もおありかと思うが、それよりも私の潔さを褒めていただきたい。 まぁそんなわけでやっていく。バージョン0.1としてはこれくらいで良いことにして、いつか時間があったら機能を追加していく。

変更履歴

新規作成:2020/03/07

この記事の開発環境

  • バージョン: Python 3.8.1
  • IDE: Spacemacs & QtCreator
  • ライブラリ: PySide2 (5.14.1)、Qt5(Desktop Qt 5.12.7 clang 64bit)、Qt Quick/QML
  • 環境構築: pyenv、pyenv-virtualenv
  • OS: macOS

PySide2による開発に必要なもの

  1. Python 3.5以降
  2. Qt5
  3. PySide2
  4. Qt creator
  5. Spacemacs (推奨)

Qtの公式サイトから「open source」のQt creatorをダウンロードする。 インストール時にQt5とQtの各種キットがインストールされる。 インストール出来る物がたくさん有るので、よく選んだ方がよい。全部だと数十ギガある。

ロジック部分とGUIのデザイン部分を分離する

基本的に制作は自分の使いやすいIDEなりエディタなりでやれば良いが、QMLについてはQt Creatorがドラッグ&ドロップで作れるのでGUIのデザイン部分のためだけにQt Creatorを使うことにする。 

  • ロジック部分は.pyへ記述していく。
  • GUIのデザイン部分は.qmlへ記述していく。

簡単に言えば上記のようにやっていく。あとはロジックとデザインを結びつけるようにすれば良い。

QtQuick/QMLでGUIをデザインする

Qt Creatorで制作していく。新しいプロジェクトを作成する時に、テンプレートの選択画面で「他のプロジェクト」→「Qt Quick UI Prototype」を選択する。そうして出来上がるのが、QMLファイルになる。

Qt Creatorの「デザイン」の機能はこのQMLファイルを開いていないと使えない。作成されると最低限必要なものがimportされた状態になる。

ロジック側の初期状態

先にロジック部分を載せておく。このファイルは自分で作成する。

import sys
import os
from PySide2.QtCore import QUrl
from PySide2 import QtCore, QtWidgets, QtQml


class Connect(QtCore.QObject):
    def __init__(self, parent=None):
        super(Connect, self).__init__(parent)

    @QtCore.Slot()
    def button_clicked(self):
        print("button clicked")


if __name__ == '__main__':
    os.environ["QT_QUICK_CONTROLS_STYLE"] = "Material"
    app = QtWidgets.QApplication(sys.argv)
    myconnect = Connect()
    engine = QtQml.QQmlApplicationEngine()
    ctx = engine.rootContext()
    ctx.setContextProperty("Connect", myconnect)
    engine.load(QUrl("pw-manager.qml"))

    if not engine.rootObjects():
        sys.exit(-1)

    sys.exit(app.exec_())

参考サイト1:がれすたさんのDI Y日記:PySide2でQtQuick(qml)使うメモ1

参考サイト2:Qt.io:Qt for Python Documentation

QMLファイルの初期状態

import QtQuick 2.12
import QtQuick.Window 2.12

Window {
    visible: true
    width: 640
    height: 480
    title: qsTr("Hello World")
}

CSSのようなJsonのような記法であることがわかる。分かりやすい。

Qt for Python(PySide2)のモジュール

パスワードマネージャを制作するにあたって必要な最低限のモジュールになると思われる。当然モジュールはここに挙げるよりもずっと多い。バージョン1.0としては最小限の機能の実装としたいので、むしろ引き算の目で選びたい。

基本モジュール

Qt Core

シグナルとスロット、プロパティ、アイテムモデルの基本クラス、シリアル化など、GUI以外のコア機能を提供する。

Qt GUI

QtCoreをGUI機能で拡張:イベント、ウィンドウとスクリーン、OpenGLとラスターベースの2Dペイント、画像を扱う。

Qt Widgets

UIのグラフィカル要素を含む、すぐに使用できるウィジェットをアプリケーションに提供します。

QMLとQt Quick

Qt QML

モジュールとやり取りするためのベースPython API。

Qt Quick

QtアプリケーションにQt Quickを埋め込むためのクラスを提供します。

Qt QuickWidgets

Qt Quickをウィジェットベースのアプリケーションに埋め込むためのQQuickWidgetクラスを提供します。

QMLのパーツを配置する

必須のパーツは、

  • 決定(実行する)ボタン 1個
  • 条件を選べるようにするチェックボックスが 6個
  • 結果を表示する、表示領域 1個

想像以上に時間がかかった。。。これは全くの見た目だけ。

QMLのソースコード

import QtQuick 2.12
import QtQuick.Window 2.12
import QtQuick.Controls 2.3
import QtQuick.Controls.Material 2.0

Window {
    visible: true
    width: 640
    height: 480
    title: qsTr("文字列ジェネレータ")
    color: "darkgrey"

        Button {
            id: button
            x: 113
            y: 35
            text: qsTr("生成")
        }
        Button {
            id: button1
            x: 450
            y: 35
            text: qsTr("クリア")
        }

    GroupBox {
        id: groupBox1
        x: 70
        y: 104
        width: 500
        height: 64
        title: qsTr("文字数指定")

        Row{
            width: 476
            height: 40
            anchors.centerIn: parent
            spacing: 70

            RadioButton {
                id: radioButton
                x: 9
                y: 3
                text: qsTr("8文字")
                checked: true
            }
            RadioButton {
                id: radioButton1
                x: 162
                y: 3
                text: qsTr("16文字")
            }
            RadioButton {
                id: radioButton2
                x: 334
                y: 3
                text: qsTr("24文字")
            }
        }
    }

    GroupBox {
        id: groupBox
        x: 70
        y: 186
        width: 500
        height: 100
        title: qsTr("条件指定")

        Row{
            width: 476
            height: 40
            anchors.verticalCenterOffset: -15
            anchors.horizontalCenterOffset: 0
            anchors.centerIn: parent
            spacing: 70

            CheckBox {
                id: checkBox
                x: 0
                y: 0
                text: qsTr("大文字")
            }
            CheckBox {
                id: checkBox1
                x: 369
                y: 0
                text: qsTr("記号")
            }
            CheckBox {
                id: checkBox3
                x: 189
                y: 0
                text: qsTr("数字")
            }
        }

        Row{
            width: 476
            height: 40
            anchors.verticalCenterOffset: 21
            anchors.horizontalCenterOffset: 0
            anchors.centerIn: parent
            spacing: 50

            RadioButton {
                id: radioButton3
                x: 189
                y: 0
                text: qsTr("文字始まり")
                checked: true
            }
            RadioButton {
                id: radioButton4
                x: 369
                y: 0
                text: qsTr("記号始まり")
            }
            RadioButton {
                id: radioButton5
                x: 0
                y: 0
                text: qsTr("数字始まり")
            }
        }
    }


    GroupBox {
        id: groupBox3
        x: 70
        y: 302
        width: 500
        height: 57
        title: qsTr("形式指定")

        Row{
            width: 476
            height: 40
            anchors.centerIn: parent
            spacing: 70

            RadioButton {
                id: radioButton6
                x: 0
                y: 5
                text: qsTr("なし")
                checked: true
            }
            RadioButton {
                id: radioButton7
                x: 192
                y: 5
                text: qsTr("ー形式")
            }
            RadioButton {
                id: radioButton8
                x: 373
                y: 5
                text: qsTr(".形式")
            }
        }
    }

    GroupBox {
        id: groupBox2
        x: 70
        y: 373
        width: 500
        height: 88
        title: qsTr("出力文字")

        Text {
            id: element
            x: 0
            y: 15
            width: 476
            height: 36
            text: qsTr("")
            font.pixelSize: 12
        }
    }
}

指定できるのは大きさや位置、色などだが、これらは細かく指定できるし、指定の仕方もいくつかある。ドラッグ&ドロップで微調整はできるが、ある程度詳しく調べてxとyだけでなくしっかりとレイアウトできる属性を指定した方が良い。

RowやGroupBoxなどのパーツを纏めてくれるものを使った方がきれいに並べられる。纏められたものは親と子の関係が出来上がるらしく、親を基準に並べたりできるので本格的にやるならそこまでやった方がいいと思う。

むすび

とりあえずGUI部分はこんな感じでよしとする。次はロジックの部分を作ってこのGUIとつなげていこう。

コメントする