c++qtqwidgetqlabelqlineedit

Is there a Qt widget that looks like a label to display text but can also be edited if double-clicked?


I want a widget in Qt that will act like a spreadsheet cell does. It can display text, then when the user double-clicks on it, it becomes editable. Once the user is done with editing and presses Enter, the text gets saved and the control is not editable anymore. If the user hits Escape while editing, then the control returns to its previous value.

One possible solution is sub-classing QWidget, QLabel and QLineEdit. Are there any other solutions available in Qt?


Solution

  • The following version also implements the same functionalities of your answer but instead of subclassing the QLineEdit and the QLabel only use eventFilter() and instead of managing the visibility manually let QStackedWidget do it.

    #include <QApplication>
    #include <QFormLayout>
    #include <QKeyEvent>
    #include <QLabel>
    #include <QLineEdit>
    #include <QStackedWidget>
    #include <QVBoxLayout>
    
    class MyEditableLabel: public QWidget{
        Q_OBJECT
        Q_PROPERTY(QString text READ text WRITE setText NOTIFY textChanged)
    public:
        MyEditableLabel(QWidget *parent=nullptr):
            QWidget(parent),
            mLabel(new QLabel),
            mLineEdit(new QLineEdit)
        {
            setLayout(new QVBoxLayout);
            layout()->setMargin(0);
            layout()->setSpacing(0);
            layout()->addWidget(&stacked);
    
            stacked.addWidget(mLabel);
            stacked.addWidget(mLineEdit);
            mLabel->installEventFilter(this);
            mLineEdit->installEventFilter(this);
            setSizePolicy(mLineEdit->sizePolicy());
            connect(mLineEdit, &QLineEdit::textChanged, this, &MyEditableLabel::setText);
        }
    
        bool eventFilter(QObject *watched, QEvent *event){
            if (watched == mLineEdit) {
                if(event->type() == QEvent::KeyPress){
                    QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event);
                    if(keyEvent->key() == Qt::Key_Return ||
                            keyEvent->key() == Qt::Key_Escape ||
                            keyEvent->key() == Qt::Key_Enter)
                    {
                        mLabel->setText(mLineEdit->text());
                        stacked.setCurrentIndex(0);
                    }
                }
                else if (event->type() == QEvent::FocusOut) {
                    mLabel->setText(mLineEdit->text());
                    stacked.setCurrentIndex(0);
                }
            }
            else if (watched == mLabel) {
                if(event->type() == QEvent::MouseButtonDblClick){
                    stacked.setCurrentIndex(1);
                    mLineEdit->setText(mLabel->text());
                    mLineEdit->setFocus();
                }
            }
            return QWidget::eventFilter(watched, event);
        }
        QString text() const{
            return mText;
        }
        void setText(const QString &text){
            if(text == mText)
                return;
            mText == text;
            emit textChanged(mText);
        }
    signals:
        void textChanged(const QString & text);
    private:
        QLabel *mLabel;
        QLineEdit *mLineEdit;
        QStackedWidget stacked;
        QString mText;
    };
    
    int main(int argc, char *argv[])
    {
        QApplication a(argc, argv);
        QWidget w;
        QFormLayout *lay = new QFormLayout(&w);
    
        MyEditableLabel el;
        lay->addRow("MyEditableLabel: ", &el);
        lay->addRow("QLineEdit: ", new QLineEdit);
        w.show();
    
        return a.exec();
    }
    
    #include "main.moc"