Search code examples
pythonpyqtpyqt5qwidgetqmainwindow

QWidget does not appear in the QMainWindow using PyQt5


What I want is an interface which opens windows under a SINGLE window, that is, I do NOT want the interface to open extra windows. I will guide you to my error. When I run my code I get a home page, from there I click on View/Edit --> View/Edit Processed Slices. At this point this is what you should get in the MAIN WINDOW:

PICTURE 1 enter image description here

What I want the interface to do is to see the window in picture 2 whenever I click the blue rectangle. I want it to open the new widget IN THE SAME MAIN WINDOW

PICTURE 2 enter image description here

However, when I click it a new window opens and the previous window remains open (PICTURE 3). This is what I want to avoid, I want only 1 window not 2.

PICTURE 3 enter image description here

Here is the code:

import sys
from PyQt5.QtWidgets import *
from PyQt5.QtGui import *
from PyQt5.QtCore import *
import cv2
import numpy as np
"""
    MAIN WINDOW
"""
class CancerSegWindow(QMainWindow):

    def __init__(self):
        super().__init__()
        self.title = 'Cancer Segmentation GUI               '
        self.initUI()


    def initUI(self):               

        self.central = HOME()
        self.setCentralWidget(self.central)

        ##
        # ACTIONS
        ##

        ##File
            #Exit
        exitAct = QAction(QIcon('E:\BEATSON_PROJECT\python\GUI\exit.png'), 'Exit', self)    # QAction is an abstraction for actions performed with a menubar, toolbar, or with a custom keyboard shortcut
        exitAct.setShortcut('Ctrl+Q')
        exitAct.setStatusTip('Exit application')                                            # Status tip at the bottom
        exitAct.triggered.connect(self.close)                                               # Triggered signal is emitted. The signal is connected to the close() method of the QMainWindow widget.

            #Home
        HomeAct = QAction(QIcon('E:\BEATSON_PROJECT\python\GUI\home.png'), 'Home', self)    
        HomeAct.setStatusTip('Go Home')                                            
        HomeAct.triggered.connect(self.Home) 

        ## View and Edit

            # Slices

                # Processed Slices
        ProcessedAct = QAction('View / Edit Processed Slices', self)  
        ProcessedAct.triggered.connect(self.Processed_Slices)

        self.statusBar().showMessage('Home')                   # First call statusBar creates a status bar at the bottom
                                                                # Subsequent calls return the statusbar object
        ##
        # main MENU bar 
        ##

        menubar = self.menuBar()                # create a menubar

        # File

        fileMenu = menubar.addMenu('File')              # File  (menu)
        fileMenu.addAction(exitAct)                         # Exit

        # View and Edit                                 

        veMenu = menubar.addMenu('View / Edit')       # View and Edit (menu)
        vesMenu = QMenu('View / Edit Slices',self)            # Slices
        vesMenu.addAction(ProcessedAct)                             # Processed
        veMenu.addMenu(vesMenu)

        ##
        # ICONS
        ##
        toolbar = self.addToolBar('Exit')         
        toolbar.addAction(exitAct)

        toolbarHome = self.addToolBar('Home')
        toolbarHome.addAction(HomeAct)
        ##
        # WINDOW
        ##
        self.setGeometry(0, 30, 1366, 697) 
        self.setWindowTitle(self.title)
        self.setWindowIcon(QIcon('E:\BEATSON_PROJECT\python\GUI\medicine.png'))
        self.show()

    def Home (self):
        self.central = HOME()
        self.setCentralWidget(self.central)

    def Processed_Slices (self):
        self.statusBar().showMessage('Displaying Processed Slices. Click on one Slice to View and Edit it individually') 
        self.central = ProcessedSlices()
        self.setCentralWidget(self.central)
        self.setWindowTitle(self.title + self.central.title)

    def Pre_Edit_Processed (self, SliceNo=1):
        self.statusBar().showMessage('Displaying Automatic Processed Slice' + str(SliceNo) + ' You can redraw it manually or modify the existing contour') 
        self.central = PreEditProcessed(SliceNo)
        self.setCentralWidget(self.central)
        self.setWindowTitle(self.title + self.central.title)

"""
    HOME WINDOW
"""
class HOME (QWidget):
    def __init__(self):
        super().__init__()
        #self.central = QPixmap("E:\BEATSON_PROJECT\python\GUI\Home_.png") 
        self.lbl1 = QLabel(self)
        #self.lbl1.setPixmap(self.central)

"""
    PROCESSED SLICES WINDOW
"""
class ProcessedSlices(QWidget):
    def __init__(self):
        super().__init__()
        self.title = ('- Processed Slices')
        self.initUI()


    def initUI(self): 

        ##
        #CHECKBOXES
        ##

        # Slice 1 
                                                    #CheckBox
        self.cb1 = QCheckBox('Slice 1', self)              
        self.cb1.move(1270, 115)
        self.cb1.toggle()
        self.cb1.stateChanged.connect(self.OpenSlice1)

                                                 #Pixmap (Image) 1
        pixmap1 = QPixmap(310, 330)         # Contour
        pixmap1.fill(Qt.blue)
        #pixmap1 = QPixmap("E:\BEATSON_PROJECT\python\GUI\Processed_Slice_1.png") 
        self.lbl1 = QLabel(self)
        self.lbl1.setGeometry(QRect(QPoint(10,0),QPoint(310,330))) # 
        self.lbl1.setPixmap(pixmap1)

        ##
        # SET GRID to obtain the mouse position
        ##

        grid = QGridLayout()
        self.text = "x: {0},  y: {1}".format(0, 0)          # display the x and y coordinates of a mouse pointer in a label widget

        self.label = QLabel(self.text, self)                # x and y coordinates are displayd in a QLabel widget
        grid.addWidget(self.label, 0, 1270, Qt.AlignTop)


        self.setLayout(grid)


        ##
        #WINDOW
        ##

        #self.setGeometry(0, 25, 1365, 700)
        #self.setWindowTitle('Processed Slices')
        self.show()


    def OpenSlice1(self, state):
        self.lbl1.setVisible(state == Qt.Checked)


    def mousePressEvent(self, e):                                     # The e is the event object. it contains data about the event that was triggered
        x = e.x()                                                     # in our case, a mouse CLICK                                          
        y = e.y()                                                     #  x() and y() methods we determine the x and y coordinates of the mouse pointer
        text = "None selected x: {0},  y: {1}"

        if ( x >= 10 and x <= 310 and y >= 0 and y <= 330 and self.cb1.isChecked()):
            text = "Slice 1 x: {0},  y: {1}".format(x, y)
            self.close()
            self.CSW = CancerSegWindow()
            self.CSW.Pre_Edit_Processed(1)

        self.label.setText(text)

"""
    PROCESSED SLICES CHECK WINDOW
"""
class PreEditProcessed(QWidget):
    def __init__(self, SliceNo=1):
        super().__init__()
        self.title = ('- Check Processed Slices')
        self._SliceNo = SliceNo
        self.initUI()

    def initUI(self):
        #self.draw = Draw(self)
        #self.draw._windows = 1

        # Button to clear both image and drawing
        self.button = QPushButton('Discard and segment MANUALLY ', self)            
        #self.button.clicked.connect(self.editManually)

        # Button to modify contour
        self.BmodContour = QPushButton('Modify existing contour ', self)            
        #self.BmodContour.clicked.connect(self.modContour)

        # Button to finish and compare
        self.BFinish = QPushButton('Finish ', self)         
        #self.BFinish.clicked.connect(self.Finish)

        # Arrange Layout

        self.layout = QVBoxLayout(self)                         
        #self.layout.addWidget(self.draw)       # Show Slice
        self.layout.addWidget(self.button)      # Manually
        self.layout.addWidget(self.BmodContour) # Modify contour
        self.layout.addWidget(self.BFinish)     # Finish and compare

        self.setGeometry(0, 25, 1365, 700)
        self.setWindowTitle('Check Slices')
        self.show()


if __name__ == '__main__':

    app = QApplication(sys.argv)
    ex = CancerSegWindow()
    sys.exit(app.exec_())

Note that the relevant part of the code is inside class ProcessedSlices:

def mousePressEvent(self, e):                                     
        x = e.x()                                                     
        y = e.y()                                                     
        text = "None selected x: {0},  y: {1}"

        if ( x >= 10 and x <= 310 and y >= 0 and y <= 330 and self.cb1.isChecked()):
            text = "Slice 1 x: {0},  y: {1}".format(x, y)
            self.close()
            self.CSW = CancerSegWindow()
            self.CSW.Pre_Edit_Processed(1)

Solution

  • Your problem is that you are creating another instance of the class CancerSegWindow() in the function mousePressEvent(self, e).

    The best way is use pyqtSignal().

    You have to declare the pyqtSignal(int) in ProcessedSlices class:

    class ProcessedSlices(QWidget):
        #here declare signal
        signal = pyqtSignal(int)
        def __init__(self):
        # more code....
    

    And emit the signal in your mousePressEvent(self, e):

    def mousePressEvent(self, e):                                     # The e is the event object. it contains data about the event that was triggered
        x = e.x()                                                     # in our case, a mouse CLICK                                          
        y = e.y()                                                     #  x() and y() methods we determine the x and y coordinates of the mouse pointer
        text = "None selected x: {0},  y: {1}"
    
        if ( x >= 10 and x <= 310 and y >= 0 and y <= 330 and self.cb1.isChecked()):
            text = "Slice 1 x: {0},  y: {1}".format(x, y)
            self.close()
            self.signal.emit(1) # emit signal with SliceNo=1
    
        self.label.setText(text)
    

    Finally, capture it in your Processed_Slices():

    def Processed_Slices (self):
        self.statusBar().showMessage('Displaying Processed Slices. Click on one Slice to View and Edit it individually') 
        self.central = ProcessedSlices()
        self.central.signal.connect(self.Pre_Edit_Processed) #connect signal
        self.setCentralWidget(self.central)
        self.setWindowTitle(self.title + self.central.title)