Помогите переопределить QMenu

Помогите переопределить QMenu.

main.py :

from PyQt5.QtWidgets import QApplication, QMainWindow
from PyQt5.QtCore import Qt, QRect
from KMenu import KMenu

class MainWindow(QMainWindow):

    def __init__(self):
        super().__init__()

        self.mwidget = QMainWindow(self)
        self.setWindowFlags(Qt.Tool | Qt.FramelessWindowHint | Qt.WindowStaysOnTopHint)
        self.setGeometry(QRect(100, 100, 100, 100))

        self.menu = KMenu(self.mwidget)
        self.menu.setStyleSheet("QMenu {"
            "background-color: #ABABAB; /* sets background of the menu */"
            "border: 1px solid black;"
        "}"
        )

        action = self.menu.addAction('&Exit')
        action.triggered.connect(lambda: quit())

        self.show()

if __name__ == "__main__":
    import sys
    app = QApplication(sys.argv)
    ex = MainWindow()
    sys.exit(app.exec_())

KMenu.py:

#!/usr/bin/env python
# -*- coding: utf-8 -*-

from PyQt5 import QtCore
import PyQt5.QtWidgets as qtw
from PyQt5.QtWidgets import QAction, QMenu

class KMenu(qtw.QMenu):
    def __init__(self, root):
        super().__init__(root)
        self.parent = root
        print('KMenu')

    def setStyleSheet(self, *args, **kwargs):
        self.parent.setStyleSheet(*args, **kwargs)

    def addAction(self, *args, **kwargs):
        self.parent.addAction(*args, **kwargs)

При запуске main.py получаю ошибку:

Traceback (most recent call last):
  File "C:\Users\User\Desktop\main.py", line 32, in <module>
    ex = MainWindow()
  File "C:\Users\User\Desktop\main.py", line 24, in __init__
    action = self.menu.addAction('&Exit')
  File "C:\Users\User\Desktop\KMenu.py", line 18, in addAction
    self.parent.addAction(*args, **kwargs)
TypeError: addAction(self, QAction): argument 1 has unexpected type 'str'

Нужно получить немного видоизмененный класс PyQt5.QtWidgets.QMenu, пример на setStyleSheet:

на входе в KMenu.setStyleSheet хочу передавать в класс переменные,

def setStyleSheet(bg, bd, bdcolor, bgcolor, bdsize):
    ...

на выходе в QMenu.setStyleSheet получить:

"QMenu {"
    "background-color: %1;"
    "border: %2px solid %3;"
"}" %1, %2, %3

Для этого нужно перенаправить в QMenu.

self.parent.setStyleSheet(*args, **kwargs) # не сработает
    # (т.к. ссылается на родительский класс, а не на класс который изменяем) 
QMenu.setStyleSheet(*args, **kwargs) # вроде как должно работать
self.parent.__init__(*args, **kwargs) # чем обусловлено применение?

И еще вопросы по теме: есть ли отличия и какие, при использовании

args, kwargs, *args, *kwargs, **kwargs, cnf={} ?

Как я понимаю **kw это сокращенно **kwargs, название не важно, важно понимание, а cnf={} как я понимаю это аналог *args или *a.
(cnf={} и **kw увидел в исходниках tkinter)

Что значат звездочки и что значит их количество?


Ответы (1 шт):

Автор решения: S. Nick

Я не знаю, правильно ли вас понял, но предложу попробовать. Если будет что-то не понятно - спросите.

import sys
from PyQt5.Qt import *


class Add_Dialog(QDialog):                             
    def __init__(self, parent=None):
        super().__init__(parent)                         
        self.setWindowTitle("Dialog")

        open_message = QLabel("Введите название действия:")
        self.txt = QLineEdit()
        
        self.path_icon = QLineEdit(placeholderText="Введите или выберите icon")
        select_i = QPushButton('Выбрать иконку', clicked=self.select_icon)
        
        save = QPushButton('Save', clicked=self.accept)
        cancel = QPushButton('Cancel', clicked=self.reject)

        grid = QGridLayout(self)
        grid.setSpacing(10)
        grid.addWidget(open_message, 0, 0)
        grid.addWidget(self.txt, 0, 1)
        grid.addWidget(self.path_icon, 1, 0)
        grid.addWidget(select_i, 1, 1)        
        
        grid.addWidget(save, 2, 0)
        grid.addWidget(cancel, 2, 1)
        self.setFixedSize(self.sizeHint())
        
    def select_icon(self): 
        fileName, _ = QFileDialog.getOpenFileName(
            self,
            "Выбрать иконку",
            ".",            
            "PNG Files (*.png);;JPG Files (*.jpg)"
        )  
        self.path_icon.setText(fileName)
        
    def save(self):
        name = self.txt.text()
        path_icon = self.path_icon.text()
        return name, path_icon


class StyleSheet_Dialog(QDialog):                             
    def __init__(self, parent=None):
        super().__init__(parent)                         
        self.setWindowTitle("StyleSheet_Dialog")

        label = QLabel("Введите bd:")
        self.spinBox = QSpinBox()
        self.spinBox.setMaximum(7)
        self.spinBox.setProperty("value", 2)
        
        self.bg = QLineEdit('#ABABAB', placeholderText="Введите или выберите bg")
        s_bg = QPushButton('Выбрать bg', clicked=self.select_bg)
        self.bdcolor = QLineEdit('#42240C', placeholderText="Введите или выберите bdcolor")
        s_bdcolor = QPushButton('Выбрать bdcolor', clicked=self.select_bdcolor)
        
        save = QPushButton('Save', clicked=self.accept)
        cancel = QPushButton('Cancel', clicked=self.reject)

        grid = QGridLayout(self)
        grid.setSpacing(10)
        grid.addWidget(label, 0, 0)
        grid.addWidget(self.spinBox, 0, 1)
        grid.addWidget(self.bg, 1, 0)
        grid.addWidget(s_bg, 1, 1)        
        grid.addWidget(self.bdcolor, 2, 0)
        grid.addWidget(s_bdcolor, 2, 1)    
        grid.addWidget(save, 3, 0)
        grid.addWidget(cancel, 3, 1)
        self.setFixedSize(self.sizeHint())
        
    def select_bg(self): 
        dlg = QColorDialog(self)
        if dlg.exec_():
            self.bg.setText(dlg.currentColor().name())

    def select_bdcolor(self): 
        dlg = QColorDialog(self)
        if dlg.exec_():
            self.bdcolor.setText(dlg.currentColor().name())
    
    def save(self):
        bd = self.spinBox.value()
        bg = self.bg.text()
        bdcolor = self.bdcolor.text()
        return bg, bd, bdcolor


class Menu(QMenu):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.setMinimumSize(150, 50)

        self.bg = '#ABABAB'
        self.bd = 2
        self.bdcolor = '#42240C'
        
        self._setStyleSheet(self.bg, self.bd, self.bdcolor)
        
    def _addAction(self, text='New_Action',icon=""):
        self.addAction(QIcon(icon), text)

    def _setStyleSheet(self, bg, bd, bdcolor):
        self.setStyleSheet('''
            QMenu {{
                background: {bg};
                border: {bd}px solid {bdcolor};
            }}
            QMenu::item {{
                color: #11144C;
                font-size: 17px;
            }}
            QMenu::item:selected {{ 
                background-color: #654321;
                color: #fff;
            }}
        '''.format(bg=bg, bd=bd, bdcolor=bdcolor,))      
        

class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        self.centralWidget = QWidget()
        self.setCentralWidget(self.centralWidget)

        menuBar = self.menuBar()
        
        self.menu = Menu("My_Menu", self)
        self.menu.triggered.connect(self._add_handle)
        
        self.menu.addAction(QIcon("im.png"), "Hello World")

        menuBar.addMenu(self.menu)

        self.label = QLabel("<h1>Hello World</h1>")
        self.label.setAlignment(Qt.AlignHCenter | Qt.AlignVCenter)        

        self.button = QPushButton('addAction', self)
        self.button.clicked.connect(self.add_action)
        self.button2 = QPushButton('setStyleSheet', self)
        self.button2.clicked.connect(self.style_sheet_action)
        
        layout = QGridLayout(self.centralWidget)
        layout.addWidget(self.label, 0, 0, 1, 2)
        layout.addWidget(self.button, 1, 0)
        layout.addWidget(self.button2, 1, 1)

        self.styleSheet_dialog = StyleSheet_Dialog()
        
    def add_action(self): 
        add_dialog = Add_Dialog()
        if add_dialog.exec_() == QDialog.Accepted:
            name, path_icon = add_dialog.save()
            if not name:
                return        
        else:
            return
        self.menu._addAction(name, path_icon)

    def style_sheet_action(self): 
        if self.styleSheet_dialog.exec_() == QDialog.Accepted:
            bg, bd, bdcolor = self.styleSheet_dialog.save()
        else:
            return        
        self.menu._setStyleSheet(bg, bd, bdcolor)

    def _add_handle(self, action):  
        print(f'action = {action.text()}')


if __name__ == '__main__':
    app = QApplication(sys.argv)
    w = MainWindow()
    w.resize(400, 300)
    w.show()
    sys.exit(app.exec_())

введите сюда описание изображения

введите сюда описание изображения

введите сюда описание изображения

введите сюда описание изображения

введите сюда описание изображения

→ Ссылка