单击小部件时更改 QHBoxLayout 边框颜色

问题描述 投票:0回答:1

我正在尝试创建一个简单的 Pyqt 应用程序,它有一个按钮和一个 QVBoxLayout。当用户单击按钮时,我希望它添加一行(QHBoxlayout),其中包含多个小部件,例如 QLineedit、QLabel 和 QButton。这样用户就可以添加多行。一旦用户单击任何这些行,我希望整行更改其边框颜色,以便用户知道他站在哪一行。

我尝试以下代码:

import sys
from PyQt5.QtWidgets import QApplication, QMainWindow, QVBoxLayout, QWidget, QPushButton, QHBoxLayout, QLineEdit
from PyQt5 import QtCore


class MyHBoxLayout(QWidget):
    selected = QtCore.pyqtSignal()

    def __init__(self, parent=None):
        super().__init__(parent)

        self.layout = QHBoxLayout()
        self.setLayout(self.layout)
        self.setStyleSheet("border: 1px solid black;")

        self.line_edit = QLineEdit()
        self.button = QPushButton('Remove')
        self.layout.addWidget(self.line_edit)
        self.layout.addWidget(self.button)

        self.button.clicked.connect(self.removeMe)
        self.line_edit.installEventFilter(self)

    def removeMe(self):
        self.setParent(None)
        self.deleteLater()

    def mousePressEvent(self, event):
        self.selected.emit()

    def eventFilter(self, source, event):
        if source == self.line_edit and event.type() == QtCore.QEvent.MouseButtonPress:
            self.selected.emit()
        return super().eventFilter(source, event)


class MyApp(QMainWindow):
    def __init__(self):
        super().__init__()

        self.initUI()

    def initUI(self):
        self.setWindowTitle('Highlight Selected QHBoxLayout')
        self.setGeometry(100, 100, 400, 300)

        central_widget = QWidget(self)
        self.setCentralWidget(central_widget)

        central_layout = QVBoxLayout()
        central_widget.setLayout(central_layout)

        add_button = QPushButton('Add QHBoxLayout', self)
        central_layout.addWidget(add_button)
        add_button.clicked.connect(self.addHBoxLayout)

        self.container_widget = QWidget()
        container_layout = QVBoxLayout()
        self.container_widget.setLayout(container_layout)
        central_layout.addWidget(self.container_widget)

        self.selected_hbox = None

    def addHBoxLayout(self):
        hbox = MyHBoxLayout()

        container_layout = self.container_widget.layout()
        container_layout.addWidget(hbox)

        hbox.selected.connect(self.onHBoxLayoutSelected)

    def onHBoxLayoutSelected(self):
        sender = self.sender()

        if self.selected_hbox:
            self.selected_hbox.setStyleSheet("border: 2px solid black;")

        sender.setStyleSheet("border: 2px solid blue;")
        self.selected_hbox = sender


if __name__ == '__main__':
    app = QApplication(sys.argv)
    window = MyApp()
    window.show()
    sys.exit(app.exec_())

但它会单独更改所有小部件的边框,而不是整行。

我该如何修复它?

(我有另一个错误,当我在删除一行后单击 QHBoxlayout 时,应用程序会崩溃,但此时我专注于边框颜色)。

谢谢你

python pyqt qtstylesheets pyqt6
1个回答
0
投票

首先,您没有绘制 QHBoxLayout 的边框,因为布局管理器是不是小部件,并且不绘制任何内容。您尝试做的是绘制 container 的边框,但是您的 QSS 语法是错误的,因为您正在设置一个通用属性,它将为目标小部件 及其子部件应用边框。

在容器或复杂的小部件上设置通用属性也是错误的,因为某些小部件需要设置特定的子控件属性,如果不这样做,您最终会得到基本的原始样式(基于“windows”样式) )这非常难看,并且当缺少属性时通常无法按预期正确绘制小部件。

此外,您看不到容器的边框,因为默认情况下,QWidget 子类

不会绘制样式表设置的任何内容,除非正确实现paintEvent()

或设置
WA_StyledBackground
属性。

首先要做的是在 QSS 中使用正确的

选择器类型,并按照上面的说明设置属性:

self.setStyleSheet("MyHBoxLayout { border: 1px solid black; }") self.setAttribute(Qt.WA_StyledBackground)
上面将仅显示 

MyHBoxLayout

 实例的边框,而不是其子实例。

然后,如果你想切换颜色,你需要使用相同的语法,但更好的方法是在

MyHBoxLayout

的专用函数中执行此操作,而不是在外部:

class MyHBoxLayout(QWidget): ... def setSelected(self, selected): color = 'blue' if selected else 'black' self.setStyleSheet( "MyHBoxLayout {{ border: 1px solid {}; }}".format(color)) class MyApp(QMainWindow): ... def onHBoxLayoutSelected(self): sender = self.sender() if sender == self.selected_hbox: # already selected, no need to go further return if self.selected_hbox: self.selected_hbox.setSelected(False) sender.setSelected(True) self.selected_hbox = sender
请注意,仅删除小部件本身是不够的。您必须记住,PyQt 是围绕 C++ Qt 库的 Python 绑定,因此 Python 中使用的对象只是 Qt 对象的

包装器,删除 Qt 对象并不会删除其 python 引用。

您的程序崩溃,因为 Qt 对象不再存在(在终端或提示符中运行您的程序,您将看到完整的错误),因此任何访问它的尝试都会导致内存访问错误。

为了避免此类问题,您可以使用一个简单的

try/except

块:

if self.selected_hbox: try: self.selected_hbox.setSelected(False) except RuntimeError: pass
不过,更好的方法是在删除小部件之前使用进一步的信号:

class MyHBoxLayout(QWidget): selected = pyqtSignal() aboutToBeRemoved = pyqtSignal() ... def removeMe(self): self.aboutToBeRemoved.emit() # no need to call `setParent(None)` if you're going to delete the widget self.deleteLater() class MyApp(QMainWindow): ... def addHBoxLayout(self): ... hbox.aboutToBeRemoved.connect(self.onHBoxRemove) def onHBoxRemove(self): if self.sender() is self.selected_hbox: self.selected_hbox = None
请注意,上面的代码将显示比所需区域更大的边框,这是因为默认的 

addWidget()

 函数始终尝试使添加的小部件占据布局项中的所有可用空间。

为了防止这种情况,您有两种选择:

    添加
  1. alignment
     参数:
container_layout.addWidget(hbox, alignment=Qt.AlignVCenter)

    为容器设置合适的大小策略:
self.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Maximum)
最后,您只考虑鼠标单击来显示“聚焦”行,但这并不考虑键盘导航。

执行您想要的操作的正确方法是检查

FocusIn

 事件类型。

© www.soinside.com 2019 - 2024. All rights reserved.