pythonはマルチスレッドが苦手とのことなので
シリアル/シーケンシャルでしか考えられない、かえるのクーの助手の「井戸中 聖」です。
pythonがマルチスレッドが苦手な理由は、「Global Interpreter Lock(GIL)」による一貫性保証のしくみを使用しているからだそうです。マルチスレッドにしても、同時には実行しないようです。
先輩おしえてください!
なるほど、わかりました!
では、マルチプロセスでいきましょう
ところで、わたくしが使用しているマシンはWindows10です。linuxもありますが、メインでは(アンチにもかかわらず)Windows10です。
完全に別機能にしてメッセージパッシング(MPI)とかで情報通信すればいいのかもしれませんが、そこまで高速性は要求しないので、いちばん典型的な「キュー」による通知を考えます。
簡単なデザイン
(1)表示側のプロセスは表示だけでなく、何等かの入力をして、実験側のプロセスに通知できるようにしたい。(双方向のキュー)
(2)キューに入れる情報は(要求コマンド,データ)のタプルもしくは配列にして汎用性をもたせたい。
(3)通信の実験をやりやすくするため、表示側、実験側のプロセスをQT(キュート)画面で模擬させたい。
簡単な実装(準備)
まずはQTで画面をつくります。ボタンがいくつかあるだけの簡単なものにします。
画面はpythonコードに変換せず、.uiのままloadする方法にします。
画面は結構コーディング量があるので、マルチプロセスは次のぺージになりそうです。
QTに興味がない方はこのページを飛ばしてください。
QTデザイナでオブジェクトを張り付けます。保存されるuiファイルは大きいので、最後に貼っておきます。
とっても楽ちんです。
最終的にはこの画面を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_())
実行すると、下のようになります。(値を入れて横のボタンを押すとオブジェクトを作って、それを受信欄に表示します。まだ送受信は行っていません)
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>