0

I got the following code for Python GUI. I found I cannot update the data running in the main window to the pop-up window.

In this example I use some random data to mimic the video frames and use a timer to update the data (the updateData() funciton).

I want to see the green curve in the pop-up window changes... But I cannot find an effective way to pass the changing data to the pop-up window.

Should I use the pyqtSignal() to define a custom signal? I read this page. Should I define the updateData() slot function as a custom signal, then connect to the other slot function plot_data() ?

"""
1/15/2021  Yuanhang Zhang

"""

from PyQt5.QtWidgets import QMainWindow,QApplication,QGridLayout,QWidget, QPushButton,QDockWidget
from PyQt5.QtCore import Qt,QTimer
from PyQt5.QtGui import QPixmap,QFont
import numpy as np
import pyqtgraph as pg
from pyqtgraph import GradientWidget

class power_window(QMainWindow):
    # this window has no parent. It will appear as a free-floating window as we want.
    def __init__(self,parent):
        super().__init__()
        self.setWindowTitle("Total power in the frame")
        self.main_widget = QWidget()  
        self.main_layout = QGridLayout()  
        self.main_widget.setLayout(self.main_layout)  
        self.setCentralWidget(self.main_widget)  
 
        self.plot_plt = pg.PlotWidget()   # this is our plot canvas
        self.plot_plt.showGrid(x=True,y=True) # show grid
        self.main_layout.addWidget(self.plot_plt, 1, 0, 3, 3)
 
        # self.plot_plt.setYRange(max=100,min=0)

        self.data_list = []
        parent.timer.timeout.connect(self.plot_data) # update plot
        # parent.updateData.change.connect(self.plot_data) # not working, may need to use pyqtSignal for custom signals
        self.frame_sum_data = parent.updateData()
        self.plot_data()

    def plot_data(self):
        self.data_list.append(self.frame_sum_data)
        self.plot_plt.plot().setData(self.data_list,pen='g') # change the color of the pen



class CameraGUI(QMainWindow):
    def __init__(self):
        super().__init__()

        ## Create some random data to mimic video data
        # scale: Standard deviation (spread or “width”) of the distribution. 
        # loc: Mean (“centre”) of the distribution.
        self.data = np.random.normal(size=(15, 30, 30), loc=1024, scale=200).astype(np.uint16)
        self.i = 0
        self.power_gui = None  # for the pop up window
        self.initializeUI()
     
    def initializeUI(self):
        """
        Initialize the window and display its contents to the screen
        """
        self.setGeometry(100,100,1000,800) # (x, y, width, height)
        self.setWindowTitle('Camera GUI')
        
        self.main_widget =  pg.GraphicsLayoutWidget()
        self.setCentralWidget(self.main_widget)  # set the default widget of the MainWindow
        
        self.createCameraWidgets()
        
        self.show()  
 
    
   
    def updateLUT(self):      ## change colormap (color look up table)
        lut = self.gradient.getLookupTable(256)
        return lut

    def action_on_button_clicked(self):  # for the pop-up windown
        # https://www.learnpyqt.com/tutorials/creating-multiple-windows/ 
        if self.power_gui is None:
            # self.power_gui = power_window(np.sum(self.data[self.i]))
            self.power_gui = power_window(self)
            self.power_gui.show()


    def createCameraWidgets(self):
        """
        Setup widgets using QGridLayout
        """
        self.gradient = GradientWidget(parent = self.main_widget,orientation='top')
        self.gradient.sigGradientChanged.connect(self.updateLUT)

        self.dock_widget = QDockWidget(self)
        self.dock_widget.setAllowedAreas(Qt.AllDockWidgetAreas)
        
        # set initial location of dock widget in main window
        self.addDockWidget(Qt.TopDockWidgetArea,self.dock_widget)

        self.button = QPushButton('Power',self)   
        self.button.clicked.connect(self.action_on_button_clicked)  ## --> not updating the data ??
        self.dock_widget.setWidget(self.button)

        ## add view box
        self.view1 = self.main_widget.addViewBox(row = 1, col = 0)
        self.view2 = self.main_widget.addViewBox(row = 1, col = 1)

        ## lock the aspect ratio so pixels are always square
        self.view1.setAspectLocked(True)
        self.view2.setAspectLocked(True)
    
        ## Create image item
        self.img1 = pg.ImageItem(border='w')
        self.img2 = pg.ImageItem(border='w')

        self.view1.addItem(self.img1)
        self.view2.addItem(self.img2)

        # timer of the main window
        self.timer = QTimer()
        self.timer.setInterval(200)      # every 200 ms will emit timeout signal
        self.timer.timeout.connect(self.updateData)  # when timeout signal is emit, it will run updatedata() function
        self.timer.start()

        print(np.sum(self.data[self.i]))  # this will only run once
        print(self.i)


    def updateData(self):
        ## Display the data
        self.img1.setImage(self.data[self.i],lut = self.updateLUT())    # when i changes, set to display in img1
        self.data2 = np.log(np.abs(np.fft.fft2(self.data[self.i])))  # calculate data2 beased on data[i]
        self.i = (self.i+1) % self.data.shape[0]   # update i
        self.img2.setImage(self.data2,lut = self.updateLUT())

        # print(self.i)
        print(np.sum(self.data[self.i])) # this will keep running every 200 ms
        return np.sum(self.data[self.i])



## run the program
if __name__ =="__main__":
    import sys
    app = QApplication(sys.argv)               
    window = CameraGUI()
    window.updateData()
    sys.exit(app.exec_())
yuanhang
  • 91
  • 1
  • 7

2 Answers2

1

I did some changes to your power_window class:

class power_window(QMainWindow):
    # this window has no parent. It will appear as a free-floating window as we want.
    def __init__(self,parent):
        super().__init__()
        self.setWindowTitle("Total power in the frame")
        self.main_widget = QWidget()  
        self.main_layout = QGridLayout()  
        self.main_widget.setLayout(self.main_layout)  
        self.setCentralWidget(self.main_widget)  
 
        self.plot_plt = pg.PlotWidget()   # this is our plot canvas
        self.plot_plt.showGrid(x=True,y=True) # show grid
        self.main_layout.addWidget(self.plot_plt, 1, 0, 3, 3)
 
        # self.plot_plt.setYRange(max=100,min=0)
        self.parent = parent
        self.data_list = []
        parent.timer.timeout.connect(self.plot_data) # update plot
        # parent.updateData.change.connect(self.plot_data) # not working, may need to use pyqtSignal for custom signals
        
        self.plot_data()

    def plot_data(self):
        self.frame_sum_data = self.parent.updateData()
        self.data_list.append(self.frame_sum_data)
        self.plot_plt.plot().setData(self.data_list,pen='g') # change the color of the pen

I think the problem is because your program calls the updateData() on the __init__ so it only gets the first value and never again updates that value.

Tell me if it does what you want.

  • Hi @Alejandro, yes, it works. but I found this approach may not be the best one. Once the `timeout` signal emits, `updateData()` slot function in the `CameraGUI` class will run once, and also the `plot_data()` slot function in the `power_window()` class will run and invoke the `updataData()` again to get its return value. So `updateData()` runs twice in total! I have another approach now: I move the signal/slot of `plot_data()` out to the `action_on_button_clicked()` and get the updated data for the pop-up window in the `updataData()` function. :) – yuanhang Jan 20 '21 at 03:45
0

graph of the Python GUI demo Above is what the demo looks like. The pop-up window shows the 'real-time' sum-up values in the left view. The right view shows the FFT of the left.

yuanhang
  • 91
  • 1
  • 7