我如何使用 QComboBox.setPlaceholderText?

How do i use QComboBox.setPlaceholderText?

在 Qt 5.15 中引入了 placeholderText 属性 - link to documentation

但是 使用 setPlaceholderText 对我没有任何作用。当 运行 下面的代码我没有在 QComboBox 中得到任何文本(当然除非我 select 三项之一)

这是一个错误还是我遗漏了什么?我怎样才能使这项工作?

import sys
from PyQt5 import QtWidgets
from PyQt5 import QtCore


class MainWindow(QtWidgets.QMainWindow):
    def __init__(self):
        super().__init__()

        central_w = QtWidgets.QWidget()
        self.setCentralWidget(central_w)
        vbox = QtWidgets.QVBoxLayout()
        central_w.setLayout(vbox)

        self.combo_box = QtWidgets.QComboBox()
        self.combo_box.addItems(["one", "two", "three"])
        self.combo_box.setPlaceholderText("Some placeholder text here")
        self.combo_box.setCurrentIndex(-1)
        vbox.addWidget(self.combo_box)


if __name__ == "__main__":
    app = QtWidgets.QApplication(sys.argv)
    main_window = MainWindow()
    main_window.show()
    app.exec_()

我找不到显示占位符文本的方法。我试过组合框中根本没有任何项目,但即使这样也不显示占位符文本


这些是我的版本 运行:


PS:如果你想达到类似的效果(即:在可点击区域有一个文本,点击后显示一个带有不同选项的下拉菜单)你可以使用 QPushButtonsetMenu 功能。文档:https://doc.qt.io/qt-5/qpushbutton.html#setMenu

查看Qt源码的修改,发现Qt 5.15.0为了增加placeHolderText的功能,修改了currentText():

// https://code.qt.io/cgit/qt/qtbase.git/tree/src/widgets/widgets/qcombobox.cpp?h=5.15.0#n2344
QString QComboBox::currentText() const
{
    Q_D(const QComboBox);
    if (d->lineEdit)
        return d->lineEdit->text();
    else if (d->currentIndex.isValid())
        return d->itemText(d->currentIndex);
    else
        return d->placeholderText;
}

但这会产生不良影响,已在 QTBUG-86580 中报告,因此该功能已在 Qt 5.15.2 中删除:

QString QComboBox::currentText() const
{
    Q_D(const QComboBox);
    if (d->lineEdit)
        return d->lineEdit->text();
    if (d->currentIndex.isValid())
        return d->itemText(d->currentIndex);
    return {};
}

忘记更正 placeHolder 不再可见的副作用。我已经报告了错误:QTBUG-90522.

综合以上,有以下备选方案:

  • 使用 PyQt5/PySide2 5.15.0 或 5.15.1。

  • 覆盖 paintEvent 方法以将 placeHolderText 设置为 currentText:

import sys
from PyQt5 import QtCore, QtGui, QtWidgets

# or
# from PySide2 import QtCore, QtGui, QtWidgets


class ComboBox(QtWidgets.QComboBox):
    # https://code.qt.io/cgit/qt/qtbase.git/tree/src/widgets/widgets/qcombobox.cpp?h=5.15.2#n3173
    def paintEvent(self, event):
        
        painter = QtWidgets.QStylePainter(self)
        painter.setPen(self.palette().color(QtGui.QPalette.Text))

        # draw the combobox frame, focusrect and selected etc.
        opt = QtWidgets.QStyleOptionComboBox()
        self.initStyleOption(opt)
        painter.drawComplexControl(QtWidgets.QStyle.CC_ComboBox, opt)

        if self.currentIndex() < 0:
            opt.palette.setBrush(
                QtGui.QPalette.ButtonText,
                opt.palette.brush(QtGui.QPalette.ButtonText).color().lighter(),
            )
            if self.placeholderText():
                opt.currentText = self.placeholderText()

        # draw the icon and text
        painter.drawControl(QtWidgets.QStyle.CE_ComboBoxLabel, opt)


class MainWindow(QtWidgets.QMainWindow):
    def __init__(self):
        super().__init__()

        self.combo_box = ComboBox()
        self.combo_box.addItems(["one", "two", "three"])
        self.combo_box.setPlaceholderText("Some placeholder text here")
        self.combo_box.setCurrentIndex(-1)

        central_w = QtWidgets.QWidget()
        self.setCentralWidget(central_w)
        vbox = QtWidgets.QVBoxLayout(central_w)
        vbox.addWidget(self.combo_box)


if __name__ == "__main__":
    app = QtWidgets.QApplication(sys.argv)
    main_window = MainWindow()
    main_window.show()
    app.exec_()

作为 eyllanesc 答案的补充,非常精确,这里只是 Qt 5.15.2 中占位符文本错误的一个更简单的解决方法,这是非常重要的版本,因为它是有史以来最后一个非商业版本Qt5。它是 C++,但很容易翻译成 Python.

#include <QApplication>
#include <QComboBox>
#include <QLabel>
#include <QVBoxLayout>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    QComboBox combo;
    combo.show();
    combo.addItem("AAAA");
    combo.addItem("BBBB");

    // The following line does not work e.g. in Qt 5.15.2.
    //combo.setPlaceholderText("Select something...");

    // This is a simple workaround:
    auto space = QString(" ");
    auto placeholder = new QLabel(space + "Select something...");
    combo.setLayout(new QVBoxLayout());
    combo.layout()->setContentsMargins(0, 0, 0, 0);
    combo.layout()->addWidget(placeholder);
    QObject::connect(&combo, &QComboBox::currentIndexChanged, &combo, [placeholder](int index){ placeholder->setVisible(index == -1); });
    combo.setCurrentIndex(-1);

    return a.exec();
}