クーの自由研究

マスターのかえるのクーは、弟子達の召喚術により新たな依り代を得てⅡ世として復活しました。

QTでマルチプロセス実験用の画面をつくろう

pythonはマルチスレッドが苦手とのことなので

シリアル/シーケンシャルでしか考えられない、かえるのクーの助手の「井戸中 聖」です。

f:id:AssistantOfKoo:20200704215742p:plain

pythonがマルチスレッドが苦手な理由は、「Global Interpreter Lock(GIL)」による一貫性保証のしくみを使用しているからだそうです。マルチスレッドにしても、同時には実行しないようです。

先輩おしえてください!

 なるほど、わかりました!

では、マルチプロセスでいきましょう

ところで、わたくしが使用しているマシンはWindows10です。linuxもありますが、メインでは(アンチにもかかわらず)Windows10です。

完全に別機能にしてメッセージパッシング(MPI)とかで情報通信すればいいのかもしれませんが、そこまで高速性は要求しないので、いちばん典型的な「キュー」による通知を考えます。

簡単なデザイン

(1)表示側のプロセスは表示だけでなく、何等かの入力をして、実験側のプロセスに通知できるようにしたい。(双方向のキュー)

(2)キューに入れる情報は(要求コマンド,データ)のタプルもしくは配列にして汎用性をもたせたい。

(3)通信の実験をやりやすくするため、表示側、実験側のプロセスをQT(キュート)画面で模擬させたい。

簡単な実装(準備)

まずはQTで画面をつくります。ボタンがいくつかあるだけの簡単なものにします。

画面はpythonコードに変換せず、.uiのままloadする方法にします。

画面は結構コーディング量があるので、マルチプロセスは次のぺージになりそうです。

QTに興味がない方はこのページを飛ばしてください。

QTデザイナでオブジェクトを張り付けます。保存されるuiファイルは大きいので、最後に貼っておきます。

f:id:AssistantOfKoo:20200705111514p:plain

とっても楽ちんです。

最終的にはこの画面を2つ「別プロセス」で起動して「キュー」で通信するようにします。最初は「同一プロセス」で子画面は「モーダル」で起動するようにします。

QTデザイナのTIPSはそんなにネタがなく、それでも書き始めたらすごい量になりそうなのでパスします。最近はpythonでもQTを使う人がとても増えているようで、興味ある方は説明ページをいろいろ検索してみてください。

では、ベースとなるソースを張ります。画面系なので無駄に長いです。

#!python3
# -*- coding: utf-8 -*-

from PyQt5 import uic
import sys
from PyQt5.QtWidgets import QApplication
import numpy as np

# load ui file
uifile_main = './controlForm.ui'
form_main, base_main = uic.loadUiType(uifile_main)

uifile_child = './controlForm.ui'
form_child, base_child = uic.loadUiType(uifile_child)

#メインダイアログ
class MainDialog(base_main, form_main):
def __init__(self):
super(base_main, self).__init__()
global g_main_form #諸悪の根源、global を使いますが、テストプログラムだし、大目にみてください。
g_main_form = self #子画面から直接アクセスできるようにします
self.setupUi(self)
self.setWindowTitle('main dialog')
self.lineEdit_title.setText('Main Form')
#イベントの定義をします
self.btn_child_form.clicked.connect(self.open_child_form)
self.btn_sendtext.clicked.connect(self.send)
self.btn_sendpos.clicked.connect(self.send)
self.btn_sendarray.clicked.connect(self.send)
self.btn_sendclose.clicked.connect(self.send)
self.btn_recv.clicked.connect(self.recv)

#子ダイアログを開きます
def open_child_form(self):
self.child = ChildDialog()
self.child.show()

#送信用の仮スケルトン
#仮に自分のページの受信表示欄に内容を表示してます
def send(self):
request = self.sender()

if request == self.btn_sendtext:
self.textBrowser_Display.setText('text:' + self.lineEdit_sendtext.text())
elif request == self.btn_sendpos:
item = self.create_pos()
self.textBrowser_Display.setText('pos:{}'.format(item))
elif request == self.btn_sendarray:
item = self.create_array()
self.textBrowser_Display.setText('array:{0}'.format(item))
elif request == self.btn_sendclose:
self.textBrowser_Display.setText('SendClose button selected!')

#テスト用のタプル作成
def create_pos(self):
return (self.ds_y.value(), self.ds_x.value())

#テスト用の配列作成
def create_array(self):
return np.ones((self.sp_xcount.value(), self.sp_ycount.value())) * self.ds_val.value()

#送信用の仮スケルトン
def recv(self):
self.textBrowser_Display.setText('Receive button selected!')

#子ダイアログ
class ChildDialog(base_child, form_child):
def __init__(self):
super(base_child, self).__init__()
self.setupUi(self)
self.btn_child_form.setEnabled(False)
self.setWindowTitle('child dialog')
self.lineEdit_title.setText('Child Form')
self.setGeometry(g_main_form.pos().x() + self.rect().width() + 11,
g_main_form.pos().y() + 31,
self.rect().width(),
self.rect().height()) #てきとーに重ならないように表示
#子ダイアログにはまだ何も実装してません

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

 実行すると、下のようになります。(値を入れて横のボタンを押すとオブジェクトを作って、それを受信欄に表示します。まだ送受信は行っていません)

f:id:AssistantOfKoo:20200705112716p:plain

 childFormボタンを押すと、同じような画面を横に表示します。(内容は未実装)

 

これをもとにして、マルチプロセスのコーディング方法を確認します。

(本編へつづく)

今日の物欲

3Dプリンターが欲しい。欲しい。欲しい。

欲しい。

 

 

蛇足の controlForm.ui ファイル内容(gitには恥ずかしいのであげませんので,ここに張り付け)

<?xml version="1.0" encoding="ISO-8859-1"?>
<ui version="4.0">
<class>Dialog</class>
<widget class="QDialog" name="Dialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>455</width>
<height>323</height>
</rect>
</property>
<property name="windowTitle">
<string>Dialog</string>
</property>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="geometry">
<rect>
<x>90</x>
<y>290</y>
<width>341</width>
<height>32</height>
</rect>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Close</set>
</property>
</widget>
<widget class="QPushButton" name="btn_sendtext">
<property name="geometry">
<rect>
<x>20</x>
<y>40</y>
<width>75</width>
<height>23</height>
</rect>
</property>
<property name="text">
<string>SendText</string>
</property>
</widget>
<widget class="QPushButton" name="btn_sendpos">
<property name="geometry">
<rect>
<x>20</x>
<y>70</y>
<width>75</width>
<height>23</height>
</rect>
</property>
<property name="text">
<string>SendPos</string>
</property>
</widget>
<widget class="QPushButton" name="btn_sendarray">
<property name="geometry">
<rect>
<x>20</x>
<y>100</y>
<width>75</width>
<height>23</height>
</rect>
</property>
<property name="text">
<string>SendArray</string>
</property>
</widget>
<widget class="QDoubleSpinBox" name="ds_x">
<property name="geometry">
<rect>
<x>150</x>
<y>70</y>
<width>62</width>
<height>22</height>
</rect>
</property>
</widget>
<widget class="QDoubleSpinBox" name="ds_y">
<property name="geometry">
<rect>
<x>270</x>
<y>70</y>
<width>62</width>
<height>22</height>
</rect>
</property>
</widget>
<widget class="QLabel" name="label">
<property name="geometry">
<rect>
<x>110</x>
<y>40</y>
<width>50</width>
<height>12</height>
</rect>
</property>
<property name="text">
<string>Text</string>
</property>
</widget>
<widget class="QLabel" name="label_2">
<property name="geometry">
<rect>
<x>110</x>
<y>70</y>
<width>21</width>
<height>16</height>
</rect>
</property>
<property name="text">
<string>X</string>
</property>
</widget>
<widget class="QLabel" name="label_3">
<property name="geometry">
<rect>
<x>240</x>
<y>70</y>
<width>21</width>
<height>16</height>
</rect>
</property>
<property name="text">
<string>Y</string>
</property>
</widget>
<widget class="QSpinBox" name="sp_xcount">
<property name="geometry">
<rect>
<x>290</x>
<y>100</y>
<width>42</width>
<height>22</height>
</rect>
</property>
</widget>
<widget class="QLabel" name="label_4">
<property name="geometry">
<rect>
<x>240</x>
<y>100</y>
<width>41</width>
<height>16</height>
</rect>
</property>
<property name="text">
<string>XCount</string>
</property>
</widget>
<widget class="QDoubleSpinBox" name="ds_val">
<property name="geometry">
<rect>
<x>150</x>
<y>100</y>
<width>62</width>
<height>22</height>
</rect>
</property>
</widget>
<widget class="QLabel" name="label_5">
<property name="geometry">
<rect>
<x>110</x>
<y>100</y>
<width>41</width>
<height>16</height>
</rect>
</property>
<property name="text">
<string>Value</string>
</property>
</widget>
<widget class="QLabel" name="label_6">
<property name="geometry">
<rect>
<x>340</x>
<y>100</y>
<width>41</width>
<height>16</height>
</rect>
</property>
<property name="text">
<string>YCount</string>
</property>
</widget>
<widget class="QSpinBox" name="sp_ycount">
<property name="geometry">
<rect>
<x>390</x>
<y>100</y>
<width>42</width>
<height>22</height>
</rect>
</property>
</widget>
<widget class="QTextBrowser" name="textBrowser_Display">
<property name="geometry">
<rect>
<x>100</x>
<y>170</y>
<width>331</width>
<height>121</height>
</rect>
</property>
</widget>
<widget class="QPushButton" name="btn_recv">
<property name="geometry">
<rect>
<x>20</x>
<y>170</y>
<width>75</width>
<height>23</height>
</rect>
</property>
<property name="text">
<string>Receive</string>
</property>
</widget>
<widget class="QPushButton" name="btn_child_form">
<property name="geometry">
<rect>
<x>360</x>
<y>10</y>
<width>75</width>
<height>23</height>
</rect>
</property>
<property name="text">
<string>childForm</string>
</property>
</widget>
<widget class="QPushButton" name="btn_sendclose">
<property name="geometry">
<rect>
<x>20</x>
<y>130</y>
<width>75</width>
<height>23</height>
</rect>
</property>
<property name="text">
<string>SendClose</string>
</property>
</widget>
<widget class="QLineEdit" name="lineEdit_title">
<property name="geometry">
<rect>
<x>20</x>
<y>10</y>
<width>311</width>
<height>20</height>
</rect>
</property>
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
<widget class="QLineEdit" name="lineEdit_sendtext">
<property name="geometry">
<rect>
<x>140</x>
<y>40</y>
<width>191</width>
<height>20</height>
</rect>
</property>
</widget>
</widget>
<resources/>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>Dialog</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>248</x>
<y>254</y>
</hint>
<hint type="destinationlabel">
<x>157</x>
<y>274</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>Dialog</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>316</x>
<y>260</y>
</hint>
<hint type="destinationlabel">
<x>286</x>
<y>274</y>
</hint>
</hints>
</connection>
</connections>
</ui>