如何为 QComboBox 的弹出菜单指定不同于框本身显示的字符串?

How to specify different strings for a QComboBox's popup menu than are displayed in the box itself?

我有一个 Qt/C++ 应用程序,它在拥挤的 window 中包含许多(不可编辑的)QComboBox 小部件。用户可以单击这些组合框以从各种类型的单位中进行选择。为了保持 window 宽度易于管理,QComboBoxes 需要尽可能瘦。

从概念上讲,组合框中可用的单位类型是:

feet
meters
milliseconds
[etc]

...但是为了让 QComboBoxes 尽可能瘦,它们在屏幕上用缩写表示,例如:

f
m
mS
[etc]

...到目前为止一切顺利,但管理层现在希望显示非缩写字符串("feet"、"meters"、"milliseconds" 等)在用户单击 QComboBox 时出现的弹出菜单中出现...,同时在框本身中保留缩写形式。这在逻辑上似乎是可行的(因为弹出菜单只是短暂出现,在 GUI 的其余部分之前,没有根本原因不能使它变宽),但我不清楚如何使用 QComboBox 实现它。

是否有 "right way" 可以做到这一点(除了破解 Qt 组合框代码)?

由于缺乏一个好的方法来做到这一点,我找到了一个丑陋的方法......至少在 MacOS/X 和 Windows 下,以下代码对我来说似乎工作得很好。 (我没有在其他 OS 下尝试过,但我希望它也能在其他地方使用):

#include <QComboBox>
#include <QIdentityProxyModel>
#include <QStandardItemModel>
#include <QProxyStyle>

static const char * shortLabels[] = {"mS",           "f",    "m",      [... and so on...] };
static const char * longLabels[]  = {"milliseconds", "feet", "meters", [... and so on...] };

// A wrapper-facade data-model object that will provide the long label strings, but only when appropriate
class LongLabelsProxyModel : public QIdentityProxyModel
{
public:
    LongLabelsProxyModel(QAbstractItemModel * source, QObject * parent = 0) : QIdentityProxyModel(parent), forceShortLabelsCounter(0)
    {
       setSourceModel(source);
    }

    virtual QVariant data(const QModelIndex &index, int role) const
    {
        return ((forceShortLabelsCounter == 0)&&((role == Qt::DisplayRole)||(role == Qt::EditRole)))
               ? QVariant(QString(longLabels[index.row()])) :  
                 QIdentityProxyModel::data(index, role);
    }

    void BeginForceShortNames() {forceShortLabelsCounter++;}
    void EndForceShortNames()   {forceShortLabelsCounter--;}

private:
    int forceShortLabelsCounter;
};

#ifndef __APPLE__
// Under MacOS/X, this class isn't necessary, because OS/X uses a native/non-Qt popup widget.
// For other OS's, however, we need to make the Qt popup widget wider so the long labels can be fully visible
class WiderPopupProxyStyle : public QProxyStyle
{
public:
    WiderPopupProxyStyle(QStyle * baseStyle) : QProxyStyle(baseStyle) {/* empty */}

    virtual QRect subControlRect(ComplexControl cc, const QStyleOptionComplex *option, SubControl sc, const QWidget *widget) const
    {
        QRect r = QProxyStyle::subControlRect(cc, option, sc, widget);
        if ((cc == QStyle::CC_ComboBox)&&(sc == QStyle::SC_ComboBoxListBoxPopup)) r.setWidth(r.width()*2);
        return r;
    }
};
#endif

// My customized combo-box class that will show long strings in the popup menu
class DelayUnitsComboBox : public EnumComboBoxComponent
{
public:
    DelayUnitsComboBox(QWidget * parent = NULL ) : QComboBox(parent)
    {
#ifndef __APPLE__
       // otherwise the popup list is the same width as the combo box
       // itself, which isn't wide enough to see the long strings
       setStyle(new WiderPopupProxyStyle(style()));
#endif

       setModel(new LongLabelsProxyModel(new QStandardItemModel(0, 1, this), this));

       // Officially populate the QComboBox with the short labels
       for (int i=0; i<((sizeof(shortLabels)/sizeof(shortLabels[0])); i++) addItem(shortLabels[i]);
    }

    // overridden so that when drawing the actual QComboBox, we still use the shorter names
    virtual void paintEvent(QPaintEvent * p)
    {
        LongLabelsProxyModel * llpm = static_cast<LongLabelsProxyModel*>(model());

        llpm->BeginForceShortNames();
        EnumComboBoxComponent::paintEvent(p);
        llpm->EndForceShortNames();
    }
};

委托会更简单:

class Delegate : public QStyledItemDelegate {
public:
    QString displayText(const QVariant & value, const QLocale & locale) const override {
        // value contains one of your short labels {"mS", "f", "m"}
        return /* Just return the corresponding long label */;
    }
};

yourComboBox->setItemDelegate(new Delegate);