Search code examples
pythonpyqt5qlabelqwidgetaction

QWidgetAction:hover in QMenuBar isn't working and doesn't want to hover


I am using class QMainWindow, as hovering doesn't want to work by any way.

Code below: -

#First imports
import PyQt5, sys

from PyQt5 import QtWidgets, QtGui, QtCore
from PyQt5.QtCore import QTimer, Qt
from PyQt5.QtWidgets import QApplication, QMainWindow, QLabel, QFileDialog, QMdiSubWindow, QMdiArea, QToolBar, QAction, QTextEdit, QVBoxLayout, QWidget, QMenuBar, QWidgetAction, QMenu
from PyQt5.QtGui import QTextOption, QFont, QPixmap, QImage, QIcon

class UI(QMainWindow):
    def __init__(self):
        super(UI, self).__init__()
    
#The code below is inside QMainWindow class and win = the class UI(QMainWindow) then win.show()

        self.setStyleSheet("""

        QMenuBar {
            background-color: white;
            color: black;
            border: 3px solid silver;
            font-weight: bold;
            font-size: 23px;
        }

        QMenuBar::item {
            background-color: white;
            border: 2px solid silver;
        }

        QMenuBar::item::selected {
            background-color: grey;
        }
        
        QLabel:hover { 
            background-color: grey;
        }

/*note that this is a tag: I tried both QLabel and QWidgetAction :hover method and its not working*/
        
        """)

        self.Menu = QMenuBar(self)
        self.Menu.resize(self.width(), 40)
        self.Menu.move(0, 0)
        
        self.File = self.Menu.addMenu('File')
        
        self.FileNew = QWidgetAction(self.File)
        self.New = QLabel("New")

        self.New.setMouseTracking(True)
        self.New.setAlignment(QtCore.Qt.AlignCenter)
        self.New.setStyleSheet("border: 2px solid silver; "
                       "background-color: white; color: black; "
                       "padding: 4 4 4 4px")
        self.FileNew.setDefaultWidget(self.New)

        self.File.addAction(self.FileNew)

def Start1():
    app = QApplication(sys.argv)
    win = UI()
    win.show()
    app.exec_()

Start1()

I searched in many threads and docs about :hover method and tried it but it didn't work for me. And when I hover with my mouse on the QWidgetAction it stills frozen.


Solution

  • There are lots of issues in your code, affecting the problem to different levels, but the most important one is that you are practically overwriting the style sheet for the label, completely ignoring the style sheets that could be inherited by the parent.

    Your current code also doesn't add the action to the menu, I'm going to assume that you just forgot it, but that's quite important nonetheless.

    When setting style sheets, it's almost always necessary to use property selectors (even in "final widgets" that do not contain further children), especially if pseudo states like :hover may be inherited.

    The following rule is applied on the window:

            QLabel:hover { 
                background-color: grey;
            }
    

    But then, since you're using generic properties in the label style sheet, they will override any previously set rule. Consider the original code:

            self.New.setStyleSheet("border: 2px solid silver; "
                           "background-color: white; color: black; "
                           "padding: 4 4 4 4px")
    

    Which is almost identical to this:

            self.New.setStyleSheet("""
                * { 
                    border: 2px solid silver; 
                    background-color: white; color: black; 
                    padding: 4 4 4 4px;
                }
            """)
    

    The wildcard selector applies the properties to any widget in any state, including the :hover one.

    A more appropriate solution could be to use the correct selector, which sets the default properties for any state, but also specify the case that negates the hover state:

            self.New.setStyleSheet("""
                QLabel { 
                    border: 2px solid silver; 
                    color: black; 
                    padding: 4 4 4 4px;
                }
                QLabel:!hover {
                    background-color: white;
                }
            """)
    

    In reality, both the style sheets (window and label) should probably be merged, and using more correct selectors:

            self.setStyleSheet("""
                /* ...QMenuBar as above */
    
                QMenu > QLabel { 
                    border: 2px solid silver; 
                    color: black; 
                    padding: 4 4 4 4px;
                    background-color: white;
                }
    
                QMenu > QLabel:hover { 
                    background-color: grey;
                }
    

    I strongly recommend you to read the whole QSS documentation, especially the syntax, reference and examples pages (but not excluding the others). In any case, the rule of thumb is that you should always consider selector types, and generic properties should only be used if you are completely sure of what you are doing.

    As said, there are other relevant issues:

    • the menu bar is just added as a child of the window and never set as an actual menubar; if you need a custom menu bar, you must always properly set it by using setMenuBar(); if you are not using a QMainWindow, a similar function is also provided for all QLayout classes;
    • QMainWindow already provides a menu bar that can be automatically created (see menuBar() if none already exists), so, unless you really need to use a custom subclass, you should use that instead;
    • as said, you never add the action to the menu; even if you did it in your real code, remember that you must also do that after calling setDefaultWidget(), otherwise the widget would not be shown;
    • capitalized names are normally reserved to class and constant names (see the official Style Guide for Python Code, but it's also convention in most programming languages), and you should avoid to use that style for variables and attributes, as it makes code less readable;