QComboBox - 如何在组合框中设置提示文本

5
我正在处理的应用程序GUI需要一个下拉框供用户选择项目。当应用程序启动时,该下拉框将显示一条提示文本,类似于“请选择”,而不是显示下拉框的第一项。我找不到任何设置提示文本的方法在http://doc.qt.io/qt-5/qcombobox.html#currentText-prop上。提前感谢!

1
我不确定Qt是否支持在其组合框中使用提示文本。然而,原生的Windows控件是支持的。可以发送CB_SETCUEBANNER消息。使用非原生小部件会有一些成本。 - Cody Gray
1
感谢您的回答。不幸的是,我的应用程序运行在Linux操作系统上。 - fucai1116
5个回答

6

如果QComboBox是可编辑的,那么有一个优雅的解决方案:点击此处了解更多

myQComboBox->lineEdit()->setPlaceHolderText("Please select");

不可编辑QComboBox中没有QLineEdit,因此这种方法对它们无效。


这个问题很久以前就被提出了,那时甚至连行编辑都没有占位符。 - Pie_Jesu
@nwp 使用您的建议会阻止组合框执行下拉功能。如何解决? - jxgn
@XavierGeoffrey 它不应该这样做,而且我没有遇到过这种情况。你是不是在单击事件上每次都运行代码,而不是仅在初始化时运行一次? - nwp

2
针对Qt的更新版本,请尝试使用QComboBox::setPlaceholderText()
我恰好正在树莓派上使用一个旧版本的Qt(5.11.3)并需要不同的解决方案。
这里是一个工作中的pyqt5示例,使用代理模型来调整添加为占位符的额外项。(感谢此答案):
import sys
from PyQt5.QtCore import Qt, QT_VERSION_STR, QAbstractProxyModel, QModelIndex, QItemSelection
from PyQt5.QtGui import QStandardItemModel, QStandardItem
from PyQt5.QtWidgets import (QApplication, QGridLayout, QWidget, QComboBox)
from typing import Any

class Main(QWidget):
    def __init__(self):
        super().__init__()
        self.setGeometry(50,50,320,200)
        self.setWindowTitle(f"Qt Version {QT_VERSION_STR}")

        layout = QGridLayout()

        cmbox = QComboBox()

        model = QStandardItemModel()
        
        for i in range(1, 11):
            model.appendRow(QStandardItem(f"Item {i}"))

        cmbox.setModel(ProxyModel(model, '---PlaceholderText---'))
        cmbox.setCurrentIndex(0)
        layout.addWidget(cmbox, 0, 0)
        

        self.setLayout(layout)
        self.show()


class ProxyModel(QAbstractProxyModel):
    def __init__(self, model, placeholderText='---', parent=None):
        super().__init__(parent)
        self._placeholderText = placeholderText
        self.setSourceModel(model)
        
    def index(self, row: int, column: int, parent: QModelIndex = ...) -> QModelIndex:
        return self.createIndex(row, column)

    def parent(self, index: QModelIndex = ...) -> QModelIndex:
        return QModelIndex()

    def rowCount(self, parent: QModelIndex = ...) -> int:
        return self.sourceModel().rowCount()+1 if self.sourceModel() else 0

    def columnCount(self, parent: QModelIndex = ...) -> int:
        return self.sourceModel().columnCount() if self.sourceModel() else 0

    def data(self, index: QModelIndex, role: int = Qt.DisplayRole) -> Any:
        if index.row() == 0 and role == Qt.DisplayRole:
            return self._placeholderText
        elif index.row() == 0 and role == Qt.EditRole:
            return None
        else:
            return super().data(index, role)

    def mapFromSource(self, sourceIndex: QModelIndex):
        return self.index(sourceIndex.row()+1, sourceIndex.column())

    def mapToSource(self, proxyIndex: QModelIndex):
        return self.sourceModel().index(proxyIndex.row()-1, proxyIndex.column())

    def mapSelectionFromSource(self, sourceSelection: QItemSelection):
        return super().mapSelection(sourceSelection)

    def mapSelectionToSource(self, proxySelection: QItemSelection):
        return super().mapSelectionToSource(proxySelection)
    
    def headerData(self, section: int, orientation: Qt.Orientation, role: int = Qt.DisplayRole):
        if not self.sourceModel():
            return None
        if orientation == Qt.Vertical:
            return self.sourceModel().headerData(section-1, orientation, role)
        else:
            return self.sourceModel().headerData(section, orientation, role)

    def removeRows(self, row: int, count: int, parent: QModelIndex = ...) -> bool:
        return self.sourceModel().removeRows(row, count -1)

if __name__ == '__main__':
    app = QApplication(sys.argv)
    ex = Main()
    sys.exit(app.exec_())

1

无法为QComboBox设置占位符文本。但是您可以解决此问题。使用setEditText(const QString&)槽来设置您的文本。如果用户选择了comboBox中的项目,则该项目的文本将被设置。但是,如果用户选择文本,删除它,并选择其他控件元素(组合框失去焦点),则您的文本将不再存在。可以通过继承QComboBox并重新实现focusOutEvent(...)来解决这个问题,在其中检查:if (currentIndex() == -1) setEditText(tr("Please select"));。不要忘记首先调用QComboBox::focusOutEvent(...)


1
感谢Pie_Jesu。我对Pie_Jesu的想法进行了一些更新。我在设置cue banner文本的函数中添加了一个包含cue banner文本的项目到组合框的第一个索引。在焦点离开事件中,我删除了组合框的第一个项目。它奏效了! - fucai1116

1
在Qt Designer中,您可以设置占位符文本。

enter image description here

如果您在设计器中添加了项目,并运行代码,那么它可能会从第一个项目开始选择。

enter image description here

在代码中,你应该在构造函数或任何初始化部分中直接调用:

comboBox->setCurrentIndex(-1);

1
不幸的是,在最新的非商业Qt 5版本(即Qt 5.15.2)中,此方法无法正常工作。API可用,但占位符文本未显示。我还在Qt 6.2.1中进行了测试,该版本可以正常工作,因此这个错误可能已经被修复了。 - HiFile.app - best file manager

0

这里有一个更简单的解决方案,用于解决Qt 5.15.2中的占位符文本错误,这是非常重要的版本,因为它是Qt5的最后一个非商业版本。它修复了当combo.isEditable() == false时不可见的占位符文本。

#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();
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接