Search code examples
pythonpyqt5windows-11

Darken MainWindow when QDialog is called


I have a QMainWindow object that regularly calls QDialogs, but the visibility of the frameless QDialog boxes is very low when over another window. I am trying to add a dark overlay onto the MainWindow whenever a QDialog is called, but regardless if I use QGraphicsOpacityEffect or set a stylesheet, it never shows up.

Specifications: Windows 11 Environment, Python3.11, PyQt5

Here is using QGraphicsOpacityEffect function:

def darken_window(self):
    self.overlay = QWidget(self)
    self.overlay.setGeometry(self.geometry())
    self.overlay.setWindowModality(Qt.ApplicationModal)
    opacity_effect = QGraphicsOpacityEffect()
    opacity_effect.setOpacity(0.5)
    self.overlay.setGraphicsEffect(opacity_effect)
    self.overlay.show()

Here it is using setStyleSheet:

def darken_window(self):
    self.overlay = QWidget(self)
    self.overlay.setGeometry(self.geometry())
    self.overlay.setStyleSheet("background-color: rgba(255,0,0,128);")
    self.overlay.setWindowModality(Qt.ApplicationModal)
    self.overlay.show()

Here is the full code for the mainWindow:

class MainWindow(QMainWindow):
    """
    Main application window with title, banner, result widgets and navigation buttons
        
        Parameters:
            None
    """
    def __init__(self):
        super().__init__()
        self.results = empty_results
        self.img_states = (
            QPixmap(resource_path("images/failure.png")).scaled(50, 50),
            QPixmap(resource_path("images/success.png")).scaled(50, 50),
            QPixmap(resource_path("images/pending.png")).scaled(50, 50),
            QPixmap(resource_path("images/blanked.png")).scaled(50, 50),
            QPixmap(resource_path("images/warning.png")).scaled(50, 50))
        self.setWindowTitle("D-NA")
        self.setGeometry(100, 100, 500, 700)
        self.central_widget = QWidget(self)
        self.setCentralWidget(self.central_widget)
        self.setMouseTracking(True)
        self.center()

        layout = QVBoxLayout(self.central_widget)

        image_label = QLabel(self)
        image = QPixmap(resource_path("images/banner.png"))
        image_label.setPixmap(image)
        image_label.setAlignment(Qt.AlignmentFlag.AlignCenter)
        layout.addWidget(image_label)
        
        self.result_labels: dict = {}

        for key, result in self.results.items():
            h_layout = QHBoxLayout()
            result_widget = HoverWidget(key.upper(), self.img_states[3], result)
            layout.addWidget(result_widget)
            layout.addLayout(h_layout)
            self.result_labels[key] = [result_widget]

        button_layout = QHBoxLayout()
        self.analyse_button = QPushButton("Analyze", self)
        self.report_button = QPushButton("Show Report", self)
        self.close_button = QPushButton("Close", self)
        
        self.analyse_button.clicked.connect(self.analyse_pressed)
        self.report_button.clicked.connect(self.report_pressed)
        self.close_button.clicked.connect(self.close_pressed)
        button_layout.addWidget(self.analyse_button)
        button_layout.addWidget(self.report_button)
        button_layout.addWidget(self.close_button)
        layout.addLayout(button_layout)
        
        self.report_button.setEnabled(False)
        self.show()
        self.initialize()
        self.populate_devices()
    
    def center(self):
        qr = self.frameGeometry()
        cp = QDesktopWidget().availableGeometry().center()
        qr.moveCenter(cp)
        self.move(qr.topLeft())
    
    def initialize(self):
        login = LoginScreen()
        result = self.darken_window(login)
        if result == QDialog.Accepted:
            self.username, self.session, self.account_id = login.getData()
    
    def darken_window(self, dialog):
        self.overlay = QWidget(self)
        self.overlay.setGeometry(self.geometry())
        self.overlay.setStyleSheet("background-color: rgba(255,0,0,128);")
        print(self.overlay.styleSheet())
        self.overlay.setWindowModality(Qt.ApplicationModal)
        self.overlay.show()

        result = dialog.exec_()
        self.overlay.close()
        return result

    def populate_devices(self):
        self.events = list_available_events(f'{url}incl/list_elements.php', self.session, self.account_id)
        popup_window = EventSelection(self.events)
        result = popup_window.exec_()
        if result == QDialog.Accepted:
            event_ids = popup_window.getData()
            self.devices = list_available_devices(f'{url}incl/list_elements.php', self.session, event_ids)
            popup_window = PopUpWindow("Info", f'Found {len(self.devices) if self.devices else 0} units in the {len(event_ids)} selected events', 
                            (int(self.x() + self.width()/2), 
                            int(self.y() + self.height()/2)), 1000)
            popup_window.show()
        
    def update_result(self, name: str, value: int, result: int):
        self.result_labels[name][0].progress_bar.setValue(value)
        self.result_labels[name][0].image_label.setPixmap(self.img_states[result])

    def analyse_pressed(self):
        if self.worker is not None:
            popup_window = PopUpYesNo("Clear results and analyze again?")
            result = popup_window.exec_()
            if result != QDialog.Accepted:
                return
        self.worker = None
        self.worker = Worker(empty_results, url, self.session, self.account_id, self.devices)
        self.worker.update_result_signal.connect(self.update_result)
        self.worker.finished.connect(self.worker_finished)
        self.report_button.setEnabled(False)
        self.analyse_button.setEnabled(False)
        for i in empty_results.keys():
            self.result_labels[i][0].progress_bar.setValue(0)
            self.result_labels[i][0].image_label.setPixmap(self.img_states[3])
            
        self.worker.start()

    def report_pressed(self):
        text = generate_report(self.username, self.results)
        self.popup_window = ResultsWindow(text, self)
        self.popup_window.show()

    def close_pressed(self):
        sys.exit()

    def worker_finished(self):
        if self.worker is not None: 
            self.results = self.worker.get_results()
            self.worker.exit()
        self.report_button.setEnabled(True)
        self.analyse_button.setEnabled(True)

I tried to change the color of the overlay to see it better, but it doesn't help. Nothing I've tried darkens the main window.

Any help would be greatly appreciated.

EDIT: This might be relevant, so I added the login screen QDialog below.

Also add that I am using qdarktheme which overrides the PyQt themes, but I tried with it both enabled and disabled, with the same results, so I don't expect my problem comes from there.

class LoginScreen(QDialog):
    """
    Simple login screen implementation to login
        Use getData() to retrieve username, session and account ID
        
        Parameters:
            None
    """
    def __init__(self):
        super().__init__()
        self.setWindowTitle("D-NA")
        self.setWindowFlags(Qt.WindowType.Tool | Qt.WindowStaysOnTopHint | Qt.FramelessWindowHint)
        self.setGeometry(100, 100, 300, 100)
        self.center()
        
        layout = QVBoxLayout()
        image_label = QLabel(self)
        image = QPixmap(resource_path("images/login.png"))
        image_label.setPixmap(image)
        image_label.setAlignment(Qt.AlignmentFlag.AlignCenter)
        layout.addWidget(image_label)
        layout.addSpacing(10)
        
        username_layout = QHBoxLayout()
        self.username_label = QLabel("Username:", self)
        self.username_input = QLineEdit(self)
        username_layout.addWidget(self.username_label)
        username_layout.addWidget(self.username_input)
        layout.addLayout(username_layout)

        password_layout = QHBoxLayout()
        self.password_label = QLabel("Password:", self)
        self.password_input = QLineEdit(self)
        self.password_input.setEchoMode(QLineEdit.Password)
        password_layout.addWidget(self.password_label)
        password_layout.addWidget(self.password_input)
        layout.addLayout(password_layout)

        remember_layout = QHBoxLayout()
        remember_layout.setContentsMargins(0,0,int(self.width()/2),0)
        self.remember_check = QCheckBox()
        self.remember_label = QLabel("Remember me", self)
        remember_layout.addWidget(self.remember_check)
        remember_layout.addWidget(self.remember_label)
        layout.addLayout(remember_layout)
        
        button_layout = QHBoxLayout()
        self.login_button = QPushButton("Login", self)
        self.login_button.clicked.connect(self.login)
        self.close_button = QPushButton("Close", self)
        self.close_button.clicked.connect(self.close)
        button_layout.addWidget(self.login_button)
        button_layout.addWidget(self.close_button)
        layout.addLayout(button_layout)

        try:
            with open(f'{os.path.expanduser("~")}/.login_info', 'r') as file:
                self.username_input.setText(file.readline().strip())
                self.password_input.setFocus()
                self.remember_check.setChecked(True)
        except:
            pass

        self.setLayout(layout)
        self.password_input.returnPressed.connect(self.login)
        self.popup_widget = None
    
    def closeEvent(self, event):
        sys.exit()
        
    def getData(self):
        return self.username, self.session, self.account_id
    
    def center(self):
            qr = self.frameGeometry()
            cp = QDesktopWidget().availableGeometry().center()
            qr.moveCenter(cp)
            self.move(qr.topLeft())
            
    def login(self):
        self.username = self.username_input.text()
        password = self.password_input.text()
        self.session, self.account_id = create_https_session(f'{url}admin/gate.php', self.username, password)
        if self.session is not None:
            if self.remember_check.isChecked():
                with open(f'{os.path.expanduser("~")}/.login_info', 'w+') as file:
                    file.write(self.username)
            self.accept()
            self.hide()
        else:
            self.popup_widget = PopUpWindow("Error", self.account_id, 
                                            (int(self.x() + self.width()/2), 
                                             int(self.y() + self.height()/2)), 1000)
            self.popup_widget.show()
            self.password_input.clear()

Solution

  • Thanks to @musicamante in the comments, I finally realized I was setting it as a relative geometry, outside the frame itself.

    Thank you so much, I feel like an idiot, but it works!

    Basically, I should have used self.overlay.setGeometry(self.rect()) instead of self.geometry() and that fixed it!

    Thanks again!