成果物

概要
- テキストボックスに任意テキストを入力する
- Enterキーを押す
- 任意テキストが音声合成される
QML
import QtQuick 2.0
import QtQuick.Window 2.0
import QtQuick.Controls 2.0
ApplicationWindow {
    id: mainWindow
    width: 500
    height: 50
    title: qsTr("Qt + PySide2 + PyOpenJTalk")
    visible: true
    locale: locale
    Rectangle {
        color: "#FFCCDD"
        anchors.fill: parent
        TextInput {
            id: _talkText
            text: "発話したいテキストを入力してからEnterキーを押してください。"
            focus: true
            font.pixelSize: Math.max(16, parent.width / 40)
            anchors.fill: parent
            onAccepted: Connect.talk(_talkText.text)
        }
    }
}
ポイントはイベント処理。QMLのイベントをPythonで実装する。このとき、どのようにしてQMLとPythonを連携するか。
 QMLの以下がイベント処理する箇所である。Connectという名前はPython側でユーザが任意に定義している。詳細は後述。
onAccepted: Connect.talk(_talkText.text)
ソースコード
#!/usr/bin/env python3 # -*- coding: utf-8 -*- import os, sys, numpy, pyopenjtalk from PySide2.QtQml import QQmlApplicationEngine from PySide2.QtWidgets import QApplication from PySide2.QtQuick import QQuickView from PySide2.QtCore import QUrl, QObject, Slot import simpleaudio as sa class Connect(QObject): def __init__(self, parent=None): super().__init__(parent) @Slot(str) def talk(self, text): x, sr = pyopenjtalk.tts(text) ply = sa.play_buffer(x.astype(numpy.int16), 1, 2, sr) ply.wait_done() def Main(): app = QApplication(sys.argv) connect = Connect() engine = QQmlApplicationEngine() ctx = engine.rootContext() ctx.setContextProperty("Connect", connect) HERE = os.path.dirname(os.path.abspath(__file__)) UI = os.path.join(HERE, 'talker.qml') engine.load(UI) if not engine.rootObjects(): sys.exit(-1) sys.exit(app.exec_()) if __name__ == '__main__': Main()
 ポイントとなるイベント処理はQQmlApplicationEngineで設定する。以下でConnectという名前をQML内で使えるようにしている。
    ctx.setContextProperty("Connect", connect)
 Connectという名前に紐づけたオブジェクトconnectは、クラスConnectのインスタンスである。
class Connect(QObject): def __init__(self, parent=None): super().__init__(parent) @Slot(str) def talk(self, text): ...
 PySide2.QtCore.Slotでデコレートしたtalkメソッドがある。これがQMLで呼び出されている。
 もう一度QMLをみてみる。テキストボックスの入力値をtalkメソッドの引数に渡している。つまり、テキストボックスに入力したテキストを発話する。
onAccepted: Connect.talk(_talkText.text)
所感
環境構築からここまで、非常に大変だった。
おそらくOSが変わったらまた環境構築コードを書き直さねばならない。
対象環境
- Raspbierry pi 4 Model B
- Raspberry Pi OS buster 10.0 2020-08-20 ※
- bash 5.0.3(1)-release
$ uname -a Linux raspberrypi 5.4.83-v7l+ #1379 SMP Mon Dec 14 13:11:54 GMT 2020 armv7l GNU/Linux