在Qt5中实现基于单元的小部件

时间:2018-12-16 07:12:53

标签: c++ qt qt5

我正在尝试实现一个类似于Qt Jupyter Notebook中基于单元的小部件,其中多个单元垂直堆叠。每个单元格由QTextEdit和多个按钮组成,通过在文本编辑器中输入更多文本(例如,文本编辑器中没有垂直滚动条),该单元格的垂直尺寸会增大。

但是,我不确定在Qt​​中实现这种小部件的最佳方法是什么。我已经尝试过为QListWidget和每个项目使用自定义小部件(由QTextEdit和几个按钮组成),但是自定义小部件不会通过输入更多文本而垂直增长,而是使用{ {1}}滚动条将出现。我还使用QTextEditQListView实现了第二个版本,但是遇到了多个问题,包括无法(通过委托)与绘制的按钮进行交互。

关于如何实现这种基于单元的窗口小部件的任何建议/示例?

1 个答案:

答案 0 :(得分:1)

您可以继承QTextEdit的子类,并使用document()->size()来监听文档大小的变化(请注意,不是QTextEdit的大小)。然后,您可以调整QTextEdit的父级大小以反映新的大小。要用小部件填充父级,请使用布局。为此,我在下面多次使用QVBoxLayout。 (一个用于在垂直列中容纳多个QTextEdit小部件;第二个用于容纳一个TextEdit)。

#include <QtCore>
#include <QtWidgets>

class DyTextEdit : public QTextEdit
{
    Q_OBJECT
public:
    DyTextEdit(QWidget *parent = nullptr) :
        QTextEdit(parent)
    {
        QTextDocument *doc = document();
        currentSize = doc->size();

        qDebug() << "[Init] Size:" << currentSize;

        //  listen to text/content changes
        connect(this, &QTextEdit::textChanged, this, &DyTextEdit::on_textChanged);

        //  connect <> 
        connect(this, &DyTextEdit::sizeChanged, this, &DyTextEdit::resizeParent);

        doc->setTextWidth(-1);

        emit sizeChanged(currentSize);  //  init
    }

signals:
    //  emitted when document size changes
    void sizeChanged(QSizeF size);

public slots:
    void on_textChanged()
    {
        QSizeF newSize = document()->size();

        qDebug() << "[TextChanged] Size:" << newSize;

        //  detect changes in the document size
        if (newSize != currentSize)
            emit sizeChanged(newSize);  //  emit signal

        //  update size
        currentSize = newSize;
    }

    void resizeParent(QSizeF size)
    {
        //  resize the parent's height, don't bother with the width
        parentWidget()->setFixedHeight(size.height());
    }

private:
    QSizeF currentSize; //  keeps track of current document size

};

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);

    //  holds all dytextedits
    QWidget aggregateWidget;
    QVBoxLayout aggregateLayout(&aggregateWidget);

    //  note: for scalability, use a QList (or QLists)

    //  first textbox
    QWidget widget;
    QVBoxLayout vbLayout(&widget);
    vbLayout.setMargin(0);
    DyTextEdit dytext(&widget);
    vbLayout.addWidget(&dytext);

    //  first button row
    QHBoxLayout hbLayout(&widget);
    hbLayout.setMargin(0);
    QPushButton pb1a("Push this.", &widget);
    hbLayout.addWidget(&pb1a);
    QPushButton pb1b("Push this.", &widget);
    hbLayout.addWidget(&pb1b);

    //  second textbox
    QWidget widget2;
    QVBoxLayout vbLayout2(&widget2);
    vbLayout2.setMargin(0);
    DyTextEdit dytext2(&widget2);
    vbLayout2.addWidget(&dytext2);

    //  second button row
    QHBoxLayout hbLayout2(&widget2);
    hbLayout2.setMargin(0);
    QPushButton pb2a("Push this.", &widget2);
    hbLayout2.addWidget(&pb2a);
    QPushButton pb2b("Push this.", &widget2);
    hbLayout2.addWidget(&pb2b);

    //  add widgets to layout
    aggregateLayout.addWidget(&widget);     //  cell 1
    aggregateLayout.addLayout(&hbLayout);   //  |
    aggregateLayout.addWidget(&widget2);    //  cell 2
    aggregateLayout.addLayout(&hbLayout2);  //  |

    aggregateLayout.setSizeConstraint(QLayout::SetMinAndMaxSize);

    aggregateWidget.show();

    return a.exec();
}

#include "main.moc" //  include if there are QObject classes in main.cpp

请注意,在上面,我使用了QHBoxLayout的按钮行,并将它们添加到与文本框小部件分离的整体聚合布局中。如果您希望将按钮设置为其他格式,则仍然可以使用QGridLayout这样的说法。您还可以将textedits和按钮布局作为子项组合在另一个覆盖了两者的小部件(用作伞)下。这将更清楚地形成小部件的单元格,并且可能更容易移动。 (我已经在Github上上传了一个可行的示例。)

演示(macOS):

这是键入内容并粘贴内容后的屏幕截图。

demonstration using both typed and copied content

请注意,聚合小部件将在删除文本行时收缩。