5

The application GUI I working requires a combo box for user to select item. When the application starts, the combo box will show a hint text something like "Please select" instead of showing the first item of the combo box. I cannot find any method for setting the hint text in http://doc.qt.io/qt-5/qcombobox.html#currentText-prop.

Thank in advance!

Christophe Weis
  • 2,518
  • 4
  • 28
  • 32
fucai1116
  • 191
  • 3
  • 5
  • 1
    I'm not sure that Qt supports cue banners in its comboboxes. The native Windows control does, however. Send a [`CB_SETCUEBANNER` message](http://msdn.microsoft.com/en-us/library/bb775897.aspx). There are costs to using non-native widgets. – Cody Gray - on strike Jun 13 '11 at 08:38
  • 1
    Thank for your answer. Unfortunately my application runs on Linux OS. – fucai1116 Jun 13 '11 at 08:51

5 Answers5

6

There is an elegant solution if the QComboBox is editable:

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

QComboBoxes that are not editable do not have QLineEdits in them, so this would not work on those.

nwp
  • 9,623
  • 5
  • 38
  • 68
  • this question was asked long time ago, when even line edits hadn't placeholders. – Pie_Jesu Nov 25 '14 at 15:06
  • @nwp using your suggestion blocks the combo box from doing the drop down function. How to fix that? – jxgn Jun 06 '16 at 13:27
  • @XavierGeoffrey It shouldn't do that and didn't for me. Did you maybe run the code every time on the click event instead of only once in the initialization? – nwp Jun 06 '16 at 15:16
2

For newer versions of Qt try QComboBox::setPlaceholderText().

I happened to be working with an older version of Qt (5.11.3) on a Raspberry Pi and needed different solution.

Here is a working pyqt5 example using a proxy model to adjust for the extra item added as the placeholder. (credit to this answer):

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_())
Chris
  • 2,166
  • 1
  • 24
  • 37
1

There is no way to set place holder text for QComboBox. But you can solve this problem. Use setEditText( const QString& ) slot for setting your text. If user selects an item in comboBox, item's text will be set. But if user selects the text, deletes it, and selects other control element (combo box looses its focus), your text won't be there anymore. Its possible to solve by inheriting from QComboBox, and re-implementing focusOutEvent(...), where you check: if ( currentIndex() == -1 ) setEditText( tr( "Please select" ) );. And do not forget to call QComboBox::focusOutEvent(...) first.

AAEM
  • 1,837
  • 2
  • 18
  • 26
Pie_Jesu
  • 1,894
  • 3
  • 16
  • 30
  • 1
    Thank Pie_Jesu. I have updated a little to idea of Pie_Jesu. I add an item containing cue banner text to the first index of combo box in a function for setting cue banner text. On focus out event, I remove the first item of combo box. It worked! – fucai1116 Jun 14 '11 at 11:37
1

In Qt Designer you can set place holder text.

enter image description here

If you add items also in designer and you will run code probably it will start with first item selected.

enter image description here

In code you should just call (in ctor or any init part)

comboBox->setCurrentIndex(-1);
Gelldur
  • 11,187
  • 7
  • 57
  • 68
  • 1
    Unfortunately this does not work in Qt 5.15.2 which is the latest non-commercial Qt 5 version. The API is available but the placeholder text is not displayed. I tested it also with Qt 6.2.1 and in this version it works so this bug was probably fixed. – HiFile.app - best file manager Nov 19 '21 at 11:41
0

Here is a simpler workaround for the placeholder text bug in Qt 5.15.2, which is very important version because it is the last non-commercial version ever for Qt5. It fixes the invisible placeholder text when 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();
}