创建教程模式

时间:2017-12-05 18:23:03

标签: c++ qt qt5

我有一个 Qt 应用程序,并希望在用户首次访问该应用程序时显示一些教程。像这样:

enter image description here

我怎样才能在Qt上做这样的事情?

我在Windows上使用Qt 5.4。

2 个答案:

答案 0 :(得分:3)

要执行此任务,我们必须制作半透明背景,我们通过激活属性Qt::WA_TranslucentBackground来实现,然后我们使用QPainterPath绘制矩形减去透明圆圈。

然后使用eventFilter来了解一些事件,例如当它们被显示时,如果你改变大小或位置。

然后创建一个结构来存储数据,在这种情况下是圆的中心,半径,文本的位置和文本本身。

然后添加next和return按钮,并在插槽中处理页面更改的逻辑。

<强> tutowidget.h

#ifndef TUTOWIDGET_H
#define TUTOWIDGET_H

#include <QWidget>
class QButtonGroup;

class TutoWidget : public QWidget
{
    Q_OBJECT
    struct Pages{
        QPoint center;
        int radius;
        QPoint  pText;
        QString text;
    };
public:
    TutoWidget(QWidget *parent);
    void addPage(const QPoint &center, int radius, const QPoint &pText, const QString & text);
    bool eventFilter(QObject *watched, QEvent *event);
protected:
    void paintEvent(QPaintEvent *);
private slots:
    void onClicked(int id);
private:
    QWidget *mParent;
    QButtonGroup *group;
    QVector<Pages> pages;
    int currentIndex = -1;
};

#endif // TUTOWIDGET_H

<强> tutowidget.cpp

#include "tutowidget.h"

#include <QVBoxLayout>
#include <QButtonGroup>
#include <QPushButton>
#include <QEvent>
#include <QTimer>
#include <QPainter>

TutoWidget::TutoWidget(QWidget *parent):QWidget(0)
{
    setWindowFlags(Qt::FramelessWindowHint|Qt::Popup);
    setAttribute(Qt::WA_TranslucentBackground, true);
    mParent = parent;
    mParent->installEventFilter(this);

    QVBoxLayout *vlay = new QVBoxLayout(this);
    vlay->addItem(new QSpacerItem(20, 243, QSizePolicy::Minimum, QSizePolicy::Expanding));
    QHBoxLayout *hlay = new QHBoxLayout;
    vlay->addLayout(hlay);
    group = new QButtonGroup(this);
    const QStringList nameBtns{"Return", "Next"};
    for(int i=0; i < nameBtns.length(); i++){
        QPushButton* btn = new QPushButton(nameBtns[i]);
        btn->setFlat(true);
        group->addButton(btn, i);
        hlay->addWidget(btn);
    }
    connect(group, static_cast<void (QButtonGroup::*)(int)>(&QButtonGroup::buttonClicked),
            this, static_cast<void (TutoWidget::*)(int)>(&TutoWidget::onClicked));
    hlay->addItem(new QSpacerItem(40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum));
    group->button(0)->hide();
}

void TutoWidget::addPage(const QPoint &center, int radius, const QPoint &pText, const QString &text)
{
    pages << Pages{center, radius, pText, text};
    if(currentIndex == -1){
        currentIndex = 0;
        update();
    }
}

bool TutoWidget::eventFilter(QObject *watched, QEvent *event){
    if(watched == mParent){

        switch (event->type()) {
        case QEvent::Show:
            QTimer::singleShot(10,  this, &QWidget::show);
            break;
        case QEvent::Close:
            close();
            break;
        case QEvent::Move:
            move(mParent->mapToGlobal(QPoint(0, 0)));
            break;
        case QEvent::Resize:
            resize(mParent->size());
            break;
        default:
            break;
        }
    }
    return QWidget::eventFilter(watched, event);
}

void TutoWidget::paintEvent(QPaintEvent *){
    QPainter painter(this);
    painter.setPen(Qt::NoPen);
    painter.setRenderHint(QPainter::Antialiasing);
    painter.setBrush(QColor(100, 100, 100, 200));
    QPainterPath path;
    if(currentIndex != -1){
        QPoint center = pages[currentIndex].center;
        int radius = pages[currentIndex].radius;
        QString text = pages[currentIndex].text;
        QPoint pText = pages[currentIndex].pText;
        path.moveTo(center + radius/2*QPoint(1, 0));
        path.arcTo(QRect(center-radius/2*QPoint(1, 1),radius*QSize(1, 1)), 0, 360);
        path.addText(pText, font(), text);
    }
    path.addRect(rect());
    painter.drawPath(path);
}

void TutoWidget::onClicked(int id)
{
    if(id == 0){
        if(currentIndex > 0)
            currentIndex--;
    }
    else if(id == 1){
        if(currentIndex < pages.count()-1)
            currentIndex++;
    }
    update();
    group->button(0)->setVisible(currentIndex!=0);
    group->button(1)->setVisible(currentIndex!=(pages.count()-1));
}

示例:

tuto = new TutoWidget(this); // this is the widget
tuto->addPage(QPoint(200, 200), 40, QPoint(100, 100), "some text1");
tuto->addPage(QPoint(300, 300), 60, QPoint(200, 100), "some text2");
tuto->addPage(QPoint(100, 200), 100, QPoint(200, 50), "some text3");
tuto->addPage(QPoint(200, 100), 80, QPoint(100, 200), "some text4");

https://www.researchgate.net/publication/301453161_SCUT_Multi-Class_Imbalanced_Data_Classification_using_SMOTE_and_Cluster-based_Undersampling)depending

enter image description here

完整示例位于以下enter image description here

答案 1 :(得分:2)

开箱即用没有任何帮助。

您可以拥有一个通常隐藏的叠加窗口小部件,仅在“教程”模式下显示。您可以使用QPainterPath轻松打孔,首先添加整个小部件的矩形,然后在想要打孔的位置添加一个圆,然后填充生成的路径。

您可以通过映射到教程覆盖小部件来获取gui元素的绝对位置。

我建议您实现一个抽象的Tutorial类,或者至少为您希望拥有教程的每个小部件实现的指针/文本对的基本列表,然后在教程中注册该小部件的每个元素 - 指针到gui元素和教程文本。然后,当调用该窗口小部件的教程时,叠加窗口小部件变得可见,并允许迭代窗口小部件的特定元素,这些元素具有与之关联的信息,并在迭代时重绘以显示圆和文本。