

今、下のプログラムを実行させて、処理終了するときに、作成したウィンドウのwin1,win2を閉じた時に、適切に終わる方法が分かりません。  
いわゆるイベントハンドラのようなコードをpyqtgraphで記述したいのですが、どのようになるか教えていただきたいです。

[code]

    from numpy import *
    from pyqtgraph.Qt import QtGui, QtCore
    import pyqtgraph as pg
    import serial
    
    # Create object serial port
    portName = "COM6"                     
    baudrate = 115200
    ser = serial.Serial(portName,baudrate)
    
    ### START QtApp #####
    app = QtGui.QApplication([])            # you MUST do this once (initialize things)
    ####################
    
    win1 = pg.GraphicsWindow(title="Signal from serial port") # creates a window
    win2 = pg.GraphicsWindow(title = "signal calculation")
    p1 = win1.addPlot(title="Realtime plot")  # creates empty space for the plot in the window
    p2 = win2.addPlot(title="inference")
    curve1 = p1.plot()                        # create an empty "plot" (a curve to plot)
    curve2 = p2.plot()
    
    windowWidth = 500                       # width of the window displaying the curve
    #0を500個作成（Xm）
    Xm = linspace(0,0,windowWidth)          # create array that will contain the relevant time series 
    Ym = linspace(0,0,windowWidth)
    Zm = linspace(0,0,windowWidth)
    ptr = -windowWidth                      # set first x position
    
    # Realtime data plot. Each time this function is called, the data display is updated
    def update():
        global curve, ptr, Xm ,Ym
            
        Xm[:-1] = Xm[1:]                      # shift data in the temporal mean 1 sample left
        Ym[:-1] = Ym[1:]
        Zm[:-1] = Zm[1:]
        volt = ser.readline().rstrip()   # シリアル通信で受け取った情報（文字列）を改行コードがくるまで代入します。byte型で得る
        volt = volt.decode()
        volt = float(volt)
        Xm[-1] = volt                # vector containing the instantaneous values  
        Ym[-1] = volt
        ptr += 1                              # update x position for displaying the curve
        curve1.setData(Xm)                     # set the curve with this data
        curve1.setPos(ptr,0)                   # set x position in the graph to 0
        x = Ym[len(Ym)-131:len(Ym)-1]       #適当な関数で変換
        k = cal(x)
        Zm[-1] = k
        curve2.setData(Zm)
        curve2.setPos(ptr,0)
        QtGui.QApplication.processEvents()    # you MUST process the plot now
    
    ### MAIN PROGRAM #####    
    # this is a brutal infinite loop calling your realtime data plot
    while True: update()
    
    ### END QtApp ####
    pg.QtGui.QApplication.exec_() # you MUST put this at the end
    ##################
    
[/code]


CC BY-SA 4.0
2020-11-19T07:30:55.837
72059
2020-11-20T02:48:27.977
37633
question
python windows jupyter-notebook pyqt
pyqtgraph でイベント処理を実行したい


ウインドウ表示やデータ更新の方法が普通のやり方に沿っていないだけなので、どちらかのウインドウをクローズボックス(右上の×)クリックでクローズしたら、両方同時にクローズしたいとかでない限り、特にイベントを意識する必要は無さそうです。

それから使われている`GraphicsWindow()`メソッドですが、最新の仕様では`DEPRECATED`(非推奨)になっているので、見直した方が良いでしょう。  
[Deprecated Window
Classes](https://pyqtgraph.readthedocs.io/en/latest/graphicswindow.html)

* * *

質問のソースの問題は、以下2点になるでしょう。

  1. `MAIN PROGRAM`とコメントされている`while True: update()`による無限ループは、正しい使われ方では無い。
  2. `END QtApp`とコメントされている部分がPyQt本来のメッセージループだが、上記無限ループのために実行されていない。また実行されたとしても呼び方がおかしい。

* * *

修正の参考になるのはこちらの記事です。  
[Python/pyqtgraphのインストールとサンプル起動方法](https://watlab-
blog.com/2019/10/20/pyqtgraph-install/)  
[Pythonのpyqtgraphを使う](https://qiita.com/kai0706/items/612d21122bb15789144c)

修正するポイントは以下3点です。(3つ目はどちらでも良い)

  1. シリアルポート読み取りおよびグラフ更新処理はインターバルタイマを設定して、そこから呼び出す。
  2. PyQt通常のイベントループ処理を正しく呼び出す。
  3. `update()`の最後の`processEvents()`は有っても動くが意味は無いので外しておく。

これにより、表示されているウインドウを両方ともクローズすれば、プログラムも終了してコマンドプロンプトに戻ってきます。(確認はJupyterNotebookでは無く、素のPythonでやっています)

* * *

以下の`##■`でコメントした所が変更点です。

[code]

    from numpy import *
    from pyqtgraph.Qt import QtGui, QtCore
    import pyqtgraph as pg
    import serial
    import random
    
    # Create object serial port
    portName = "COM6"                     
    baudrate = 115200
    ser = serial.Serial(portName,baudrate)
    
    ### START QtApp #####
    app = QtGui.QApplication([])            # you MUST do this once (initialize things)
    ####################
    
    win1 = pg.GraphicsWindow(title="Signal from serial port") # creates a window
    win2 = pg.GraphicsWindow(title = "signal calculation")
    p1 = win1.addPlot(title="Realtime plot")  # creates empty space for the plot in the window
    p2 = win2.addPlot(title="inference")
    curve1 = p1.plot()                        # create an empty "plot" (a curve to plot)
    curve2 = p2.plot()
    
    windowWidth = 500                       # width of the window displaying the curve
    #0を500個作成（Xm）
    Xm = linspace(0,0,windowWidth)          # create array that will contain the relevant time series 
    Ym = linspace(0,0,windowWidth)
    Zm = linspace(0,0,windowWidth)
    ptr = -windowWidth                      # set first x position
    
    # Realtime data plot. Each time this function is called, the data display is updated
    def update():
        global curve, ptr, Xm ,Ym
            
        Xm[:-1] = Xm[1:]                      # shift data in the temporal mean 1 sample left
        Ym[:-1] = Ym[1:]
        Zm[:-1] = Zm[1:]
        volt = ser.readline().rstrip()   # シリアル通信で受け取った情報（文字列）を改行コードがくるまで代入します。byte型で得る
        volt = volt.decode()
        volt = float(volt)
        Xm[-1] = volt                # vector containing the instantaneous values  
        Ym[-1] = volt
        ptr += 1                              # update x position for displaying the curve
        curve1.setData(Xm)                     # set the curve with this data
        curve1.setPos(ptr,0)                   # set x position in the graph to 0
        x = Ym[len(Ym)-131:len(Ym)-1]       #適当な関数で変換
        k = cal(x)
        Zm[-1] = k
        curve2.setData(Zm)
        curve2.setPos(ptr,0)
        ##■ コメントアウト QtGui.QApplication.processEvents()    # you MUST process the plot now
    
    ### MAIN PROGRAM #####    
    # this is a brutal infinite loop calling your realtime data plot
    ##■ while True: update() #### 無限ループは削除
    
    ### END QtApp ####
    ##■ pg.QtGui.QApplication.exec_() # you MUST put this at the end #### 以下へ移動
    ##################
    
    ##■ PyQt 通常の呼び出し方法
    if __name__ == '__main__':
        import sys
        
        ##■ シリアルポート読み取りおよびグラフ更新処理のインターバルタイマー設定
        timer = QtCore.QTimer()
        timer.timeout.connect(update)
        timer.start(30) ##■ インターバルはシリアルポートの読み取り間隔に合わせて調整する
        
        ##■ PyQt イベントループ実行
        if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'):
            QtGui.QApplication.instance().exec_()
    
[/code]

* * *

ちょうど良さそうな記事があったので、融合させて新しいAPIを使うのと、1つのウインドウに2つのグラフを入れて、終了イベントは気にせずとも良い形にしてみました。  
[Pyqt5 with pyqtgraph building two
graphs](https://stackoverflow.com/q/50070752/9014308)

前のプログラムも確認ではそうしていましたが、シリアルポートの入力等は乱数でシミュレートしています。  
そのため、本当にシリアルポートからの入力で動作しているかの確認はしていません。

[code]

    import sys
    from PyQt5.QtWidgets import (QWidget, QVBoxLayout, QApplication)
    from pyqtgraph.Qt import QtGui, QtCore
    import pyqtgraph as pg
    import numpy as np
    import serial
    import random
    
    class CustomPlot(pg.PlotWidget):
        def __init__(self, title=''):
            pg.PlotWidget.__init__(self, title=title)
            self.curve = self.plot()
    
    # The top container/widget for the graphs
    class Window(QWidget):
        def __init__(self):
            super().__init__()
            
            self.windowWidth = 500                      # width of the window displaying the curve
            #0を500個作成（Xm）
            self.Xm = np.linspace(0,0,self.windowWidth) # create array that will contain the relevant time series 
            self.Ym = np.linspace(0,0,self.windowWidth)
            self.Zm = np.linspace(0,0,self.windowWidth)
            self.ptr = -self.windowWidth                # set first x position
            self.k = 5 #### 信号計算の代替シミュレーション用初期値
            
            self.initUI() # call the UI set up
            '''
            self.portName = "COM6"
            self.baudrate = 115200
            self.ser = serial.Serial(self.portName,self.baudrate)
            '''
            self.timer = QtCore.QTimer()
            self.timer.timeout.connect(self.updateGraph)
            self.timer.start(30)
        
        # set up the UI
        def initUI(self):
            
            self.layout = QVBoxLayout(self) # create the layout
            self.pginput = CustomPlot(title="Signal from serial port - Realtime plot") # class abstract both the classes
            self.pginfer = CustomPlot(title="signal calculation - inference") # "" "" ""
            self.layout.addWidget(self.pginput)
            self.layout.addWidget(self.pginfer)
            self.show()
        
        # update graph
        def updateGraph(self):
            
            self.Xm[:-1] = self.Xm[1:]          # shift data in the temporal mean 1 sample left
            self.Ym[:-1] = self.Ym[1:]
            self.Zm[:-1] = self.Zm[1:]
            '''
            self.volt = ser.readline().rstrip() # シリアル通信で受け取った情報（文字列）を改行コードがくるまで代入します。byte型で得る
            self.volt = self.volt.decode()
            self.volt = float(self.volt)
            '''
            self.volt = random.randrange(0.0, 6.0) #### 入力信号の代替シミュレーション
            self.Xm[-1] = self.volt             # vector containing the instantaneous values
            self.Ym[-1] = self.volt
            self.ptr += 1                       # update x position for displaying the curve
            self.pginput.curve.setData(self.Xm)               # set the curve with this data
            self.pginput.curve.setPos(self.ptr,0)             # set x position in the graph to 0
            
            self.x = self.Ym[len(self.Ym)-131:len(self.Ym)-1] #適当な関数で変換
            # self.k = cal(x)
            self.k += random.randint(-5, 5) #### 上記信号計算の代替シミュレーション
            self.Zm[-1] = self.k
            self.pginfer.curve.setData(self.Zm)
            self.pginfer.curve.setPos(self.ptr,0)
    
    if __name__ == '__main__':
        app = QApplication(sys.argv)
        window = Window()
        sys.exit(app.exec_())
    
[/code]


CC BY-SA 4.0
2020-11-19T11:16:21.790
72072
2020-11-20T02:48:27.977
2020-11-20T02:48:27.977
26370
26370
72059
answer