How to keep the blue background color after right clicking the selected text in QLabel to generate a QMenu?

125 Views Asked by At

I want to keep the selected text keep the blue background color,but when i right clicked the text to generate a menu,the selected text will turn gray.how should i do. when i clicked the qlabel text to make it selected,the result is like this picture. And when i right click the selected text,the blue color turn gray. here is my demo code.

enter image description here

enter image description here

import pyperclip
import sys
from PySide2.QtCore import  Qt
from PySide2.QtGui import QCursor
from PySide2.QtWidgets import QLabel,QMenu,QAction,QHBoxLayout,QApplication,QWidget

class moreInfoLabel(QLabel):
    def __init__(self, *args, **kw):
        super().__init__(*args, **kw)
        self.setTextInteractionFlags(Qt.TextSelectableByMouse)
        self.setContextMenuPolicy(Qt.CustomContextMenu)
        self.customContextMenuRequested.connect(self.rightMenuShow)#开放右键策略
        self.setStyleSheet('''
        QMenu {
            border: Opx;
            font-size: 9pt;
            width:100px;
            background-color:white
        }

        QMenu::item { 
            width:160px;
            font-family: "Microsoft YaHei";
            border: 0px;
            min-height: 15px;
            max-height: 26px;
            padding:10px 15px 10px 15px;
            font-size: 9pt; 
        }
        
        QMenu::item:selected { 
            background-color: #f9f9f9;
        }
        QLabel{
            font-size:13px;
            font-family:微软雅黑;

        }
        ''')

    def rightMenuShow(self, pos):
        print(self)
        menu = QMenu(self)
        menu.addAction(QAction('复制', menu))
        menu.addAction(QAction('全选', menu))

        menu.triggered.connect(self.menuSlot)
        menu.exec_(QCursor.pos())
        self.setStyleSheet('''
        QMenu {
            border: Opx;
            font-size: 9pt;
            width:100px;
            background-color:white
        }

        QMenu::item { 
            width:160px;
            font-family: "Microsoft YaHei";
            border: 0px;
            min-height: 15px;
            max-height: 26px;
            padding:10px 15px 10px 15px;
            font-size: 9pt; 
        }
        
        QMenu::item:selected { 
            background-color: #f9f9f9;
        }
        QLabel{
            font-size:13px;
            font-family:微软雅黑;
            background-color: #f9f9f9;
        }
        ''')
    
    def menuSlot(self, act):
        if act.text() == '复制':
            pyperclip.copy(self.selectedText())
        elif act.text() == '全选':
            len1 = len(self.text())
            self.setSelection(0,len1)
            self.selectedText = self.text

class Demo(QWidget):

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

        self.moreinfo = moreInfoLabel("YOUJIAN")
        self.layout = QHBoxLayout()
        self.layout.addWidget(self.moreinfo)
        self.setLayout(self.layout)



if __name__ == "__main__":
    app = QApplication()
    demo = Demo()
    demo.show()
    sys.exit(app.exec_())
    

Is there any way to solve this problem

2

There are 2 best solutions below

0
musicamante On BEST ANSWER

When the menu is shown, the widget uses the Inactive color group of the palette, which is the one normally used when the widget is in a window that is not active.

The simple solution is to temporarily replace the Highlight and HighlightedText color role for that group and restore the original palette afterwards. This step is necessary for consistency, as the default Inactive colors should be kept in case the window is actually unfocused.

Note that in the following example I did some changes:

  • the custom context menu signal is normally used when managing the menu from another object, like its parent, but when it's actions have results in the same class it's better to use the contextMenuEvent() handler;
  • it's better to check the menu result against the triggered QAction, not its text;
  • the pyperclip module is unnecessary as Qt already provides QClipboard;
  • do not overwrite self.selectedText with self.text, otherwise self.selectedText() will always return the full text of the label, even if there is no selection;
class MoreInfoLabel(QLabel):
    _defaultPalette = None
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.setTextInteractionFlags(Qt.TextSelectableByMouse|Qt.TextEditable)
        self.setStyleSheet('''
        QMenu {
            border: Opx;
            font-size: 9pt;
            width:100px;
            background-color:white
        }

        QMenu::item { 
            width:160px;
            font-family: "Microsoft YaHei";
            border: 0px;
            min-height: 15px;
            max-height: 26px;
            padding:10px 15px 10px 15px;
            font-size: 9pt; 
        }
        
        QMenu::item:selected { 
            background-color: #f9f9f9;
        }
        QLabel{
            font-size:13px;
            font-family:微软雅黑;
        }
        ''')

    def contextMenuEvent(self, event):
        oldPalette = self.palette()
        tempPalette = self.palette()
        tempPalette.setBrush(tempPalette.Inactive, tempPalette.Highlight, 
            oldPalette.highlight())
        tempPalette.setBrush(tempPalette.Inactive, tempPalette.HighlightedText, 
            oldPalette.highlightedText())
        self.setPalette(tempPalette)

        menu = QMenu(self)
        copyAction = menu.addAction('复制')
        selectAction = menu.addAction('全选')

        res = menu.exec_(event.globalPos())
        self.setPalette(oldPalette)

        if res == copyAction:
            QApplication.clipboard().setText(self.selectedText())
        elif res == selectAction:
            self.setSelection(0, len(self.text()))

That said, QLabel already provides the menu entries you're trying to replace. If you did that for localization purposes, then overriding the default menu behavior is just wrong, as the problem is elsewhere.

0
Alexander On

I am sure it is possible to achieve what you are asking, however I would suggest a simpler alternative.

Instead of using a QLabel, It would be much simpler to achieve similar or identical behavior using a QLineEdit. You can set the line edit to read only and setFrame(False) to make it appear identical to a QLabel, with the added bonus that maintaining the current selection when opening a context menu is the default behavior for a QLine edit.

For example:

In the image below, the one on the right is the QLineEdit...

import pyperclip
import sys
from PySide2.QtCore import  Qt
from PySide2.QtGui import QCursor
from PySide2.QtWidgets import QLabel,QMenu,QAction,QHBoxLayout,QApplication,QWidget, QLineEdit
QSS = ("""QMenu {
    border: Opx;
    font-size: 9pt;
    width:100px;
    background-color:white
}
QMenu::item:selected {
    background-color: #f9f9f9;
}
QMenu::item {
    width:160px;
    font-family: "Microsoft YaHei";
    border: 0px;
    min-height: 15px;
    max-height: 26px;
    padding:10px 15px 10px 15px;
    font-size: 9pt;
}
QLabel{
    font-size:13px;
    font-family:微软雅黑;
    color: black;
    background-color: #f9f9f9;
}"""   # Added styles below to make it look identical to label
"""QLineEdit{
    font-size:13px;
    font-family:微软雅黑;
    color: black;
    background-color:#f9f9f9;
}""")

class Demo(QWidget):

    def __init__(self):
        super(Demo, self).__init__()
        self.moreinfo = QLabel("YOUJIAN")
        self.layout = QHBoxLayout()
        self.layout.addWidget(self.moreinfo)
        self.setLayout(self.layout)
        self.moreinfo.setTextInteractionFlags(Qt.TextSelectableByMouse)
        ########################################
        self.lineedit = QLineEdit(self)            # <--- added these lines
        self.layout.addWidget(self.lineedit)
        self.lineedit.setText("YOUJAIN")
        self.lineedit.setFrame(False)
        self.lineedit.setReadOnly(True)
        self.setStyleSheet(QSS)

if __name__ == "__main__":
    app = QApplication()
    demo = Demo()
    demo.show()
    sys.exit(app.exec_())

enter image description here