c++ 有没有一个Qt小部件看起来像一个显示文本的标签,但如果双击也可以编辑?

esyap4oy  于 2023-05-30  发布在  其他
关注(0)|答案(3)|浏览(116)

我想在Qt的小部件,将像一个电子表格单元格的行为。它可以显示文本,然后当用户双击它时,它变成可编辑的。用户完成编辑并按Enter键后,文本将被保存,控件将不再可编辑。如果用户在编辑时按Escape,则控件将返回到其先前的值。
一种可能的解决办法是对QWidgetQLabelQLineEdit进行子类化。Qt中是否有其他可用的解决方案?

smtd7mpg

smtd7mpg1#

以下版本也实现了与您的答案相同的功能,但不是将QLineEditQLabel子类化,而是仅使用eventFilter(),而不是手动管理可见性,而是让QStackedWidget这样做。

#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"
h6my8fg2

h6my8fg22#

您可以使用QInputdialog来更改QLabel并覆盖mouseDoubleClickEvent以触发输入对话框。
我,正如这里的一些人所了解到的,没有办法从QLabel中拉取编辑过的文本。不改变QLabel的内部代码。
下面是一个使用QInputDialog的例子,作为手段:

//intrlbl.h

#ifndef INTRLBL_H
#define INTRLBL_H

#include <QWidget>
#include <QLabel>
#include <QMouseEvent>


class intrLbl: public QLabel
{
    Q_OBJECT
public:
    intrLbl(QWidget *parent);

    void mouseDoubleClickEvent(QMouseEvent *event) override;
    QString text;

};

#endif // INTRLBL_H


//intrlbl.cpp file

#include "intrlbl.h"
#include <QDebug>
#include <QInputDialog>

intrLbl::intrLbl(QWidget *parent)
{
    this->setText("Text Changeable Via Double Click QInput Dialog");
    this->setFocusPolicy(Qt::ClickFocus);
    this->setWordWrap(false);

}

void intrLbl::mouseDoubleClickEvent(QMouseEvent *event)
{
       QString title
               = QInputDialog::getText(this,
                 tr("Enter your Idea Title:"),
                  tr("Title:"), QLineEdit::Normal,
                  tr("enter your title here"));

      if(!title.isEmpty())
      {
            qDebug() << "Title set to:" << title;
            this->setText(title);
       }
      else
      {
          title = "Title";
          this->setText(title);
      }
}
wz3gfoph

wz3gfoph3#

解决方案之一是有一个QLineEdit,并将其设置为只读,并以一种看起来像标签的方式对其进行样式化。我个人不喜欢这个解决方案,因为它更像是一种黑客方法。我想出了一些在我看来很酷的东西,其中包括子类QWidgetQLabelQLineEdit
让我们首先介绍一个模型,它将在我们的QWidget的子类版本中创建,并且这个模型将被传递给其子widget,即QLabelQLineEdit的子类版本:
型号表头-mymodel.h

#ifndef MYMODEL_H
#define MYMODEL_H

#include <QObject>

class MyModel : public QObject {
    Q_OBJECT
    Q_PROPERTY(Mode mode READ getMode WRITE setMode NOTIFY modeChanged)
    Q_PROPERTY(QString text READ getText WRITE setText NOTIFY textChanged)
public:
    enum class Mode {
        ReadOnly = 0,
        Edit = 1,
    };

    explicit MyModel(QObject* parent = nullptr);

    Mode getMode() const {
        return _mode;
    }

    const QString& getText() const {
        return _text;
    }

signals:
    void modeChanged(Mode mode);
    void textChanged(const QString& text);

public slots:
    void setMode(Mode mode);
    void setText(const QString& text);

private:
    Mode _mode;
    QString _text;
};

#endif // MYMODEL_H

模型实现-mymodel.cpp

#include "mymodel.h"

MyModel::MyModel(QObject *parent)
    : QObject(parent)
    , _mode(MyModel::Mode::ReadOnly)
    , _text(QString()) {
}

void MyModel::setMode(MyModel::Mode mode) {
    if (_mode != mode) {
        _mode = mode;
        emit modeChanged(_mode);
    }
}

void MyModel::setText(const QString &text) {
    if (_text != text) {
        _text = text;
        emit textChanged(text);
    }
}

正如我们所看到的,该模型具有文本,这在QLabelQLineEdit中都很常见,并且它具有一个模式,可以是只读模式或编辑模式。
标签实现是Label的子类。标题-mylabel.h

#ifndef MYLABEL_H
#define MYLABEL_H

#include <QLabel>
#include <QSharedPointer>

#include "mymodel.h"

class MyLabel : public QLabel {
    Q_OBJECT
public:
    explicit MyLabel(QWidget *parent = 0);
    void setModel(QSharedPointer<MyModel> model);

protected:
    void mouseDoubleClickEvent(QMouseEvent *) override;

private:
    QSharedPointer<MyModel> _model;
};

#endif // MYLABEL_H

实施-mylabel.cpp

#include "mylabel.h"

#include <QMouseEvent>

MyLabel::MyLabel(QWidget *parent)
    : QLabel(parent) {
}

void MyLabel::setModel(QSharedPointer<MyModel> model) {
    _model = model;
}

void MyLabel::mouseDoubleClickEvent(QMouseEvent *) {
    _model->setText(text());
    _model->setMode(MyModel::Mode::Edit);
}

我们的类MyLabel有一个setModel()方法,它将从其父类中获取模型。我们覆盖了mouseDoubleClickEvent(),尽管我们将模型的文本设置为标签中的任何文本,并将模式设置为编辑,因为当双击时我们想要编辑文本。
现在让我们来看看QLineEdit。我们的QLineEdit版本,称为MyLineEdit,正在监听键盘事件,当按下Enter和Esc键时,它要么将文本保存到模型中,要么将其丢弃。然后将模式更改为只读。
MyLineEdit.h

#ifndef MYLINEEDIT_H
#define MYLINEEDIT_H

#include <QLineEdit>
#include <QSharedPointer>

#include "mymodel.h"

class MyLineEdit : public QLineEdit {
    Q_OBJECT
public:
    MyLineEdit(QWidget* parent = nullptr);
    void setModel(QSharedPointer<MyModel> model);

protected:
    void keyPressEvent(QKeyEvent* event) override;
    void focusOutEvent(QFocusEvent*);

private:
    QSharedPointer<MyModel> _model;
};

#endif // MYLINEEDIT_H

下面是实现-MyLineEdit.cpp

#include "mylineedit.h"

#include <QKeyEvent>

MyLineEdit::MyLineEdit(QWidget *parent)
    : QLineEdit(parent) {
}

void MyLineEdit::setModel(QSharedPointer<MyModel> model) {
    _model = model;
}

void MyLineEdit::keyPressEvent(QKeyEvent *event) {
    if (event->key() == Qt::Key_Enter) {
        _model->setText(text());
        _model->setMode(MyModel::Mode::ReadOnly);
    } else if (event->key() == Qt::Key_Escape) {
        _model->setMode(MyModel::Mode::ReadOnly);
    } else {
        QLineEdit::keyPressEvent(event);
    }
}

void MyLineEdit::focusOutEvent(QFocusEvent *) {
    _model->setText(text());
    _model->setMode(MyModel::Mode::ReadOnly);
}

现在我们有了模型,我们有了QLabel版本和QLineEdit版本。我们现在需要的是一个父控件,它将包含这两个控件,监听来自模型的信号,并根据信号更改其外观。这个类是从QWidget派生的,叫做MyEditableLabel
MyEditableLabel.h

#ifndef MYEDITABLELABEL_H
#define MYEDITABLELABEL_H

#include <QSharedPointer>
#include <QWidget>

#include "mylabel.h"
#include "mylineedit.h"

class MyEditableLabel : public QWidget {
    Q_OBJECT
public:
    explicit MyEditableLabel(QWidget *parent = nullptr);
    QString getText() const {return _text;}
private:
    MyLabel *_label;
    MyLineEdit *_lineEdit;
    QSharedPointer<MyModel> _model;

private slots:
    void onModeChanged(MyModel::Mode mode);
    void onTextChanged(const QString &text);

private:
    QString _text;
};

#endif // MYEDITABLELABEL_H

MyEditableLabel.cpp:#include“myeditablelabel.h”

#include <QHBoxLayout>

MyEditableLabel::MyEditableLabel(QWidget *parent)
    : QWidget(parent) {
    _model = QSharedPointer<MyModel>(new MyModel());
    _model->setText("Click me!");
    _label = new MyLabel(this);
    _label->setModel(_model);
    _lineEdit = new MyLineEdit(this);
    _lineEdit->setModel(_model);
    _lineEdit->setReadOnly(false);

    QHBoxLayout *mainLayout = new QHBoxLayout();
    mainLayout->setMargin(0);
    mainLayout->setSpacing(0);
    mainLayout->addWidget(_label);
    mainLayout->addWidget(_lineEdit);
    setLayout(mainLayout);

    connect(_model.data(), &MyModel::modeChanged, this, &MyEditableLabel::onModeChanged);
    onModeChanged(_model->getMode());
    connect(_model.data(), &MyModel::textChanged, this, &MyEditableLabel::onTextChanged);
    onTextChanged(_model->getText());
}

void MyEditableLabel::onModeChanged(MyModel::Mode mode) {
    _lineEdit->setVisible(mode == MyModel::Mode::Edit);
    _lineEdit->selectAll();
    _label->setVisible(mode == MyModel::Mode::ReadOnly);
}

void MyEditableLabel::onTextChanged(const QString &text) {
    _lineEdit->setText(text);
    _label->setText(text);
    _text = text;
}

Usage:使用这个非常简单。如果你使用的是Qt Creator设计器,那么你想画一个QWidget,然后右键单击它并将其升级为MyEditableLabel,这样就完成了。如果你没有使用Qt Creator设计器,那么你只需要创建一个MyEditableLabel的示例,你就可以开始工作了。
Improvements:最好不要在MyEditableLabel的构造函数中创建模型,而是在MyEditableLabel中有一个setModel方法。

相关问题