Home > OS >  FigureCanvas Fail To Appear When Receiving Signal From Popup
FigureCanvas Fail To Appear When Receiving Signal From Popup

Time:04-02

I'm hoping I can get some help with a problem I've been stuck on all day. I've looked through previous questions but nothing seems to quite match up with the issue I'm facing. Perhaps a fresh pair of eyes can guide me in the right direction. I'll include my code at the end of my question.

Background:

I'm working on a simple application that consists of the main window and a popup window. The main window contains only one button that opens the popup window when pressed. The popup window contains two checkbox options, an ok button, and a cancel button. When pressed, the popup's ok button returns a signal to the main window. This signal contains a list of 1s and 0s, depending on the status of the checkboxes. The purpose of the list is so that a function in the main window can determine which canvases to plot (either plot 1, plot 2, or both).

It should be noted that the main window is organized using a grid layout, and each canvas is meant to be displayed on a row in column 3 with a corresponding label in column 4. The desired outcome when creating both canvases is as shown in the figure: Desired Outcome

Problem:

Everything works fine until the end when the signal returns to the slot at popup_input() within the main window. No errors occur but the canvases simply do not appear. Taking the code for creating the figures from popup_input() and placing it instead into open_options() does seem to work and gives the figure as shown above. However, my goal is to have the plots appear after the user makes their selection. Does anyone have any idea why the canvases are failing to appear?

import sys
from PyQt5 import QtWidgets, QtGui, QtCore
from PyQt5.QtWidgets import QDialog, QApplication, QMainWindow, QLabel, QPushButton
from PyQt5.QtGui import QFont

import matplotlib.pyplot as plt
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.figure import Figure
from matplotlib.ticker import FormatStrFormatter

class Options(QMainWindow):
    popup_response = QtCore.pyqtSignal(object)
    def __init__(self):
        super().__init__()
        self._title = 'Plotting Options'
        self.setWindowTitle(self._title)

        self.selected_plots = [1, 1] # Always have both options selected by default

        self.option1 = QtWidgets.QCheckBox('Option 1')
        self.option1.setChecked(True)
        self.option2 = QtWidgets.QCheckBox('Option 2')
        self.option2.setChecked(True)

        self.checkbox_layout = QtWidgets.QHBoxLayout()
        self.checkbox_layout.addWidget(self.option1)
        self.checkbox_layout.addWidget(self.option2)

        self.ok_button = QtWidgets.QPushButton('OK', self)
        self.ok_button.clicked.connect(lambda: self.clicked_ok())
        self.cancel_button = QtWidgets.QPushButton('Cancel', self)
        self.cancel_button.clicked.connect(lambda: self.clicked_cancel())
        self.button_layout = QtWidgets.QHBoxLayout()
        self.button_layout.addWidget(self.ok_button)
        self.button_layout.addWidget(self.cancel_button)

        self._popup = QtWidgets.QWidget()
        self.setCentralWidget(self._popup)
        self.layout = QtWidgets.QGridLayout(self._popup)
        self.layout.addLayout(self.checkbox_layout, 0, 0)
        self.layout.addLayout(self.button_layout, 1, 0)


    def finalize_selected_plots(self):
        if self.option1.isChecked() == False:
            self.selected_plots[0] = 0
        if self.option2.isChecked() == False:
            self.selected_plots[1] = 0
        return self.selected_plots

    def clicked_ok(self):
        self.plots = self.finalize_selected_plots()

        # Send selection back to Results Window
        main_window = MainWindow()
        static_reply = self.plots
        self.popup_response.connect(main_window.popup_input)
        self.popup_response.emit(static_reply)
        self.plots = [1,1]
        self.close()

    def clicked_cancel(self):
        self.close()

class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        self.exPopup = Options()
        self.data_to_plot = [[1, 5, 10], [2, 4, 6], [6, 4, 2]] # Dummy values

        self._main = QtWidgets.QWidget()
        self.setCentralWidget(self._main)

        self.button_options = QtWidgets.QPushButton('Plot Options', self)
        self.button_options.clicked.connect(self.open_options)

        self.option_layout = QtWidgets.QHBoxLayout()
        self.option_layout.addWidget(self.button_options)

        self.layout = QtWidgets.QGridLayout(self._main)
        self.layout.addLayout(self.option_layout, 0, 2)

    def open_options(self):
        self.exPopup.show()


    @QtCore.pyqtSlot(object)
    def popup_input(self, reply):
        plot_title_font_size = 15
        data = self.data_to_plot

        if reply[0] != 0:
            self.figure_xy1 = FigureCanvas(Figure(figsize=(5, 5)))
            self._figure1 = self.figure_xy1.figure.subplots()
            self._figure1.grid()

            self.layout.addWidget(self.figure_xy1, 1, 3)
            self.figure_label1 = QLabel('Test Plot 1', self)
            self.figure_label1.setFont(QFont('Times', plot_title_font_size))
            self.figure_label1.setAlignment(QtCore.Qt.AlignLeft)
            self.layout.addWidget(self.figure_label1, 1, 4)

            x = data[0]
            y = data[1]
            self._figure1.plot(x, y, '-')
            self._figure1.set_xlabel('x')
            self._figure1.set_ylabel('y1')
            self._figure1.figure.canvas.draw()

        if reply[1] != 0:
            self.figure_xy2 = FigureCanvas(Figure(figsize=(5, 5)))
            self._figure2 = self.figure_xy2.figure.subplots()
            self._figure2.grid()

            self.layout.addWidget(self.figure_xy2, 2, 3)
            self.figure_label2 = QLabel('Test Plot 2', self)
            self.figure_label2.setFont(QFont('Times', plot_title_font_size))
            self.figure_label2.setAlignment(QtCore.Qt.AlignLeft)
            self.layout.addWidget(self.figure_label2, 2, 4)

            x = data[0]
            y = data[2]
            self._figure2.plot(x, y, '-')
            self._figure2.set_xlabel('x')
            self._figure2.set_ylabel('y2')
            self._figure2.figure.canvas.draw()


if __name__ == '__main__':
    app = QApplication(sys.argv)
    widget = QtWidgets.QStackedWidget()
    main_window = MainWindow()
    widget.addWidget(main_window)
    widget.setWindowTitle("Main Window")
    widget.show()

    try:
        sys.exit(app.exec_())
    except:
        print("Exiting")

CodePudding user response:

I have found a solution to my issue. With the help of jmacey over on Reddit's pyqt page, here's what I figured out:

  • My popup should inherit QDialog vs QMainWindow
  • Simplified dialog button scheme by utilizing QDialogButtonBox
  • Include accepted and rejected signals for my ok and cancel buttons, respectively. I also included an extra signal for clicked_ok that would perform my desired function as the dialog closes.
  • popup dialog should be executed from my main window. If I click ok in my dialog, then it will run my code related to plotting.

Here are my updated classes:

class Options(QDialog):
    popup_response = QtCore.pyqtSignal(object)
    def __init__(self, parent=None):
        super().__init__(parent)
        self._title = 'Plotting Options'
        self.setWindowTitle(self._title)
        self.setGeometry(100, 100, 300, 200)

        self.selected_plots = [1, 1] # Always have both options selected by default

        self.option1 = QtWidgets.QCheckBox('Option 1')
        self.option1.setChecked(True)
        self.option2 = QtWidgets.QCheckBox('Option 2')
        self.option2.setChecked(True)

        self.checkbox_layout = QtWidgets.QHBoxLayout()
        self.checkbox_layout.addWidget(self.option1)
        self.checkbox_layout.addWidget(self.option2)


        self.button_box = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel)
        self.button_box.accepted.connect(self.clicked_ok)
        self.button_box.rejected.connect(self.reject)


        self.popup_layout = QtWidgets.QVBoxLayout()
        self.popup_layout.addLayout(self.checkbox_layout)
        self.popup_layout.addWidget(self.button_box)
        self.setLayout(self.popup_layout)

    def finalize_selected_plots(self):
        if self.option1.isChecked() == False:
            self.selected_plots[0] = 0
        if self.option2.isChecked() == False:
            self.selected_plots[1] = 0
        return self.selected_plots

    def clicked_ok(self):
        print("clicked ok")
        self.plots = self.finalize_selected_plots()

        # Send selection back to Results Window
        # main_window = MainWindow()
        # static_reply = self.plots
        # self.popup_response.connect(main_window.popup_input)
        # self.popup_response.emit(static_reply)
        self.plots = [1,1]
        self.close()
        

class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        #self.exPopup = Options()
        self.data_to_plot = [[1, 5, 10], [2, 4, 6], [6, 4, 2]] # Dummy values

        self._main = QtWidgets.QWidget()
        self.setCentralWidget(self._main)

        self.button_options = QtWidgets.QPushButton('Plot Options', self)
        #self.button_options.clicked.connect(self.open_options)
        self.button_options.clicked.connect(self.popup_input)

        self.option_layout = QtWidgets.QHBoxLayout()
        self.option_layout.addWidget(self.button_options)

        self.layout = QtWidgets.QGridLayout(self._main)
        self.layout.addLayout(self.option_layout, 0, 2)

    # def open_options(self):
    #   self.exPopup.show()


    #@QtCore.pyqtSlot(object)
    def popup_input(self):
        plot_title_font_size = 15
        data = self.data_to_plot

        self.grab_popup = Options(self)

        if self.grab_popup.exec():
            print("Pressed ok")
            if reply[0] != 0:
                self.figure_xy1 = FigureCanvas(Figure(figsize=(5, 5)))
                self._figure1 = self.figure_xy1.figure.subplots()
                self._figure1.grid()

                self.layout.addWidget(self.figure_xy1, 1, 3)
                self.figure_label1 = QLabel('Test Plot 1', self)
                self.figure_label1.setFont(QFont('Times', plot_title_font_size))
                self.figure_label1.setAlignment(QtCore.Qt.AlignLeft)
                self.layout.addWidget(self.figure_label1, 1, 4)

                x = data[0]
                y = data[1]
                self._figure1.plot(x, y, '-')
                self._figure1.set_xlabel('x')
                self._figure1.set_ylabel('y1')
                self._figure1.figure.canvas.draw()

            if reply[1] != 0:
                self.figure_xy2 = FigureCanvas(Figure(figsize=(5, 5)))
                self._figure2 = self.figure_xy2.figure.subplots()
                self._figure2.grid()

                self.layout.addWidget(self.figure_xy2, 2, 3)
                self.figure_label2 = QLabel('Test Plot 2', self)
                self.figure_label2.setFont(QFont('Times', plot_title_font_size))
                self.figure_label2.setAlignment(QtCore.Qt.AlignLeft)
                self.layout.addWidget(self.figure_label2, 2, 4)

                x = data[0]
                y = data[2]
                self._figure2.plot(x, y, '-')
                self._figure2.set_xlabel('x')
                self._figure2.set_ylabel('y2')
                self._figure2.figure.canvas.draw()

        else:
            print("Pressed Cancel")
  • Related