使用 QPainter 在 QWidgets 之间画一条线 [英] Using QPainter to draw a line between QWidgets

查看:137
本文介绍了使用 QPainter 在 QWidgets 之间画一条线的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在开发一个应用程序,我需要能够在两个 QWidget 对象之间画一条线.我已经尝试了很多东西,但我目前的尝试(我认为是在正确的方向,我只是认为我错过了一些东西)是拥有包含小部件(我称之为 DrawWidget 并保存 QWidget 对象添加到的 QGridLayout 覆盖 paintEvent 方法并调用 QPainter::drawLine() 函数.

我遇到的问题是:

  1. 无论我如何尝试获取小部件的位置,线的端点总是在错误的位置
  2. 每当我尝试绘制第二条线时,我绘制的第一条线都会被擦除.

这是包含小部件的 paintEvent 函数:

voidpaintEvent(QPaintEvent *){如果(!drewSinceUpdate){drawSinceUpdate = true;QPainter 画家(this);Painter.setPen(QPen(Qt::black));Painter.drawLine(start->geometry().center(), end->geometry().center());}}

paintEvent的最后一行中,我尝试了很多不同的方法来获取小部件的正确位置,我将在其中发布一些方法(我不记得所有方法了):

painter.drawLine(start->pos(), end->pos());Painter.drawLine(start->mapToGlobal(start->geometry().center()), end->mapToGlobal(end->geometry().center()));Painter.drawLine(this->mapToGlobal(start->geometry().center()), this->mapToGlobal(end->geometry().center()));Painter.drawLine(start->mapTo(this, start->pos()), end->mapTo(this, end->pos()));Painter.drawLine(this->mapFrom(start, start->pos()), this->mapFrom(end, end->pos()));

为了让我的问题更清楚,这里有一个我正在寻找的例子,取自 但这就是我最终得到的:

感谢您提供的任何帮助.

注意:

-startend 都是我使用另一种方法传入的 QWidget 对象

-与DrawWidget相关的层次结构是:

QMainWindow->QScrollArea->DrawWidget->QGridLayout->Items <-- 这些是我想要连接的东西

为了制作一个完整且可验证的示例,这里是相关代码的全部.

MainWindow.cpp:

#include "mainwindow.h"#include "ui_mainwindow.h"#include #include #include #include #include MainWindow::MainWindow(QWidget *parent) :QMainWindow(父),用户界面(新用户界面::主窗口){//设置相关层级ui->setupUi(this);scrollArea = new QScrollArea();setCentralWidget(scrollArea);drawWidget = new DrawWidget();gridLayout = new QGridLayout();gridLayout->setSpacing(300);drawWidget->setLayout(gridLayout);scrollArea->setWidget(drawWidget);scrollArea->setWidgetResizable(true);AddItemSlot();QApplication::connect(scrollArea->horizo​​ntalScrollBar(), SIGNAL(rangeChanged(int,int)), this, SLOT(scrollHorizo​​ntal()));}//这只是创建一个我想要连接的示例小部件QWidget* MainWindow::CreateNewItem(){QWidget* itemWidget = new QWidget();itemWidget->setStyleSheet("background-color: lightgray");QHBoxLayout* singleItemLayout = new QHBoxLayout();itemWidget->setLayout(singleItemLayout);QTextEdit* textEdit = new QTextEdit(std::to_string(counter++).c_str());textEdit->setStyleSheet("background-color:white;");singleItemLayout->addWidget(textEdit);QVBoxLayout* rightSidePanel = new QVBoxLayout();rightSidePanel->setAlignment(Qt::AlignTop);QPushButton* button1 = new QPushButton("顶部按钮");QApplication::connect(button1, SIGNAL(clicked(bool)), this, SLOT(AddItemSlot()));rightSidePanel->addWidget(button1);QWidget* rightPanelWidget = new QWidget();rightSidePanel->setMargin(0);rightPanelWidget->setLayout(rightSidePanel);singleItemLayout->addWidget(rightPanelWidget);itemWidget->setLayout(singleItemLayout);itemWidget->setMinimumWidth(400);itemWidget->setFixedSize(400,200);返回 itemWidget;}主窗口::~主窗口(){删除用户界面;}void MainWindow::scrollHorizo​​ntal(){scrollArea->ensureWidgetVisible(noteItems.back());}void MainWindow::AddItemSlot(){QWidget* w = CreateNewItem();gridLayout->addWidget(w,currRow,currCol++);如果 (!noteItems.empty()){drawWidget->updateEndpoints(noteItems.back(), w);}noteItems.push_back(w);}

主窗口.h

#ifndef MAINWINDOW_H#define MAINWINDOW_H#include #include #include #include #include #include "drawscrollarea.h"#include <向量>命名空间 Ui {类主窗口;}类 MainWindow : 公共 QMainWindow{Q_OBJECT上市:显式主窗口(QWidget *parent = 0);~主窗口();公共插槽:无效滚动水平();void AddItemSlot();私人的:用户界面::主窗口 *用户界面;QWidget* CreateNewItem();int 计数器 = 0,currCol = 0,currRow = 0;std::vector注意事项;QScrollArea* 滚动区域;DrawWidget* drawWidget;QGridLayout* gridLayout;};#endif//MAINWINDOW_H

DrawWidget.cpp:

#include "drawwidget.h"#include #include DrawWidget::DrawWidget(QWidget *parent) : QWidget(parent){}void DrawWidget::paintEvent(QPaintEvent *){如果(!drewSinceUpdate){drawSinceUpdate = true;QPainter 画家(this);Painter.setPen(QPen(Qt::black));for (ConnectedPair pair : items){const QWidget* from = pair.from;const QWidget* to =pair.to;QPoint start = from->mapToGlobal(from->rect().topRight() + QPoint(0, from->height()/2));QPoint end = to->mapToGlobal(to->rect().topLeft() + QPoint(0, to->height()/2));Painter.drawLine(mapFromGlobal(start), mapFromGlobal(end));}}}void DrawWidget::updateEndpoints(QWidget* startIn, QWidget* endIn){drawSinceUpdate = false;items.push_back(ConnectedPair{startIn, endIn});}

DrawWidget.h

#ifndef DRAWWIDGET_H#define DRAWWIDGET_H#include #include <QPainter>#include #include <向量>类 DrawWidget : 公共 QWidget{Q_OBJECT上市:显式 DrawWidget(QWidget *parent = nullptr);void updateEndpoints(QWidget* startIn, QWidget* endIn);虚拟无效paintEvent(QPaintEvent *);信号:私人的:结构连接对{const QWidget* 来自;const QWidget* 到;};std::vector项目;bool drawSinceUpdate = true;};#endif//DRAWWIDGET_H

解决方案

对于这种情况,我们使用函数

I'm working on an application where I need to be able to draw a line between two QWidget objects. I have tried quite a few things, but my current attempt (which I think is in the right direction I just think I'm missing something) is to have the containing widget (which I called DrawWidget and which holds the QGridLayout that the QWidget objects are added to) override the paintEvent method and call the QPainter::drawLine() function.

The issues I'm having are that:

  1. No matter how I try to get the position of the widgets, the endpoints of the line are always in the wrong place
  2. Whenever I try to draw a second line, the first line that I drew gets erased.

Here is the paintEvent function of the containing widget:

void paintEvent(QPaintEvent *)
{
    if (!drewSinceUpdate){
        drewSinceUpdate = true;
        QPainter painter(this);
        painter.setPen(QPen(Qt::black));

        painter.drawLine(start->geometry().center(), end->geometry().center());
    }
}

I have tried many different ways to get the correct position of the widgets in the last line of paintEvent, which I will post some of the ways (I can't remember all of them):

painter.drawLine(start->pos(), end->pos());
painter.drawLine(start->mapToGlobal(start->geometry().center()), end->mapToGlobal(end->geometry().center()));
painter.drawLine(this->mapToGlobal(start->geometry().center()), this->mapToGlobal(end->geometry().center()));
painter.drawLine(start->mapTo(this, start->pos()), end->mapTo(this, end->pos()));
painter.drawLine(this->mapFrom(start, start->pos()), this->mapFrom(end, end->pos()));

And just to make my question clear, here is an example of what I am looking for, taken from QT Diagram Scene Example: But this is what I end up getting:

Thank you for any help you can provide.

NOTE:

-start and end are both QWidget objects which I passed in using another method

-The hierarchy relevant to DrawWidget is:

QMainWindow
->QScrollArea
  ->DrawWidget
    ->QGridLayout
      ->Items       <-- These are the things I want to connect

EDIT: To make a Complete and Verifiable example, here is the entirety of the relevant code.

MainWindow.cpp:

#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QHBoxLayout>
#include <QVBoxLayout>
#include <QTextEdit>
#include <QPushButton>
#include <QScrollBar>

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    // Setting up the relevant hierarchy 
    ui->setupUi(this);
    scrollArea = new QScrollArea();
    setCentralWidget(scrollArea);

    drawWidget = new DrawWidget();
    gridLayout = new QGridLayout();
    gridLayout->setSpacing(300);
    drawWidget->setLayout(gridLayout);

    scrollArea->setWidget(drawWidget);
    scrollArea->setWidgetResizable(true);

    AddItemSlot();

    QApplication::connect(scrollArea->horizontalScrollBar(), SIGNAL(rangeChanged(int,int)), this, SLOT(scrollHorizontal()));
}

// This is just creating a single one of the example widgets which I want to connect
QWidget* MainWindow::CreateNewItem(){
    QWidget* itemWidget = new QWidget();
    itemWidget->setStyleSheet("background-color: lightgray");
    QHBoxLayout* singleItemLayout = new QHBoxLayout();
    itemWidget->setLayout(singleItemLayout);

    QTextEdit* textEdit = new QTextEdit(std::to_string(counter++).c_str());
    textEdit->setStyleSheet("background-color:white;");
    singleItemLayout->addWidget(textEdit);

    QVBoxLayout* rightSidePanel = new QVBoxLayout();
    rightSidePanel->setAlignment(Qt::AlignTop);

    QPushButton* button1 = new QPushButton("Top Button");

    QApplication::connect(button1, SIGNAL(clicked(bool)), this, SLOT(AddItemSlot()));

    rightSidePanel->addWidget(button1);

    QWidget* rightPanelWidget = new QWidget();
    rightSidePanel->setMargin(0);
    rightPanelWidget->setLayout(rightSidePanel);

    singleItemLayout->addWidget(rightPanelWidget);

    itemWidget->setLayout(singleItemLayout);
    itemWidget->setMinimumWidth(400);
    itemWidget->setFixedSize(400,200);

    return itemWidget;
}

MainWindow::~MainWindow()
{
    delete ui;
}

void MainWindow::scrollHorizontal()
{
    scrollArea->ensureWidgetVisible(noteItems.back());
}

void MainWindow::AddItemSlot()
{
    QWidget* w = CreateNewItem();
    gridLayout->addWidget(w,currRow, currCol++);
    if (!noteItems.empty()){
        drawWidget->updateEndpoints(noteItems.back(), w);
    }
    noteItems.push_back(w);
}

MainWindow.h

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QGridLayout>
#include <QWidget>
#include <QMainWindow>
#include <QScrollArea>
#include <drawwidget.h>
#include "drawscrollarea.h"
#include <vector>

namespace Ui {
class MainWindow;
}

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    explicit MainWindow(QWidget *parent = 0);
    ~MainWindow();

public slots:
    void scrollHorizontal();
    void AddItemSlot();

private:
    Ui::MainWindow *ui;
    QWidget* CreateNewItem();
    int counter = 0, currCol = 0, currRow = 0;
    std::vector<QWidget*> noteItems;

    QScrollArea* scrollArea;
    DrawWidget* drawWidget;
    QGridLayout* gridLayout;
};

#endif // MAINWINDOW_H

DrawWidget.cpp:

#include "drawwidget.h"
#include <QDebug>
#include <QRect>

DrawWidget::DrawWidget(QWidget *parent) : QWidget(parent)
{

}

void DrawWidget::paintEvent(QPaintEvent *)
{
    if (!drewSinceUpdate){
        drewSinceUpdate = true;
        QPainter painter(this);
        painter.setPen(QPen(Qt::black));

        for (ConnectedPair pair : items){
            const QWidget* from = pair.from;
            const QWidget* to =pair.to;

            QPoint start =  from->mapToGlobal(from->rect().topRight() +  QPoint(0, from->height()/2));
            QPoint end = to->mapToGlobal(to->rect().topLeft() +  QPoint(0, to->height()/2));

            painter.drawLine(mapFromGlobal(start), mapFromGlobal(end));
        }
    }
}

void DrawWidget::updateEndpoints(QWidget* startIn, QWidget* endIn){
    drewSinceUpdate = false;
    items.push_back(ConnectedPair{startIn, endIn});
}

DrawWidget.h

#ifndef DRAWWIDGET_H
#define DRAWWIDGET_H

#include <QWidget>
#include <QPainter>
#include <QtCore>
#include <vector>

class DrawWidget : public QWidget
{
    Q_OBJECT
public:
    explicit DrawWidget(QWidget *parent = nullptr);
    void updateEndpoints(QWidget* startIn, QWidget* endIn);
    virtual void paintEvent(QPaintEvent *);
signals:

private:
    struct ConnectedPair {
        const QWidget* from;
        const QWidget* to;
    };

    std::vector<ConnectedPair> items;
    bool drewSinceUpdate = true;
};

#endif // DRAWWIDGET_H

解决方案

For this case we use the function mapToGlobal() and mapfromGlobal(), since pos() returns a position with respect to the parent and this can cause problems if the widget has different parents.

drawwidget.h

#ifndef DRAWWIDGET_H
#define DRAWWIDGET_H

#include <QWidget>

class DrawWidget : public QWidget
{
    Q_OBJECT
public:
    explicit DrawWidget(QWidget *parent = nullptr);

    void addWidgets(const QWidget *from, const QWidget *to);

protected:
    void paintEvent(QPaintEvent *);

private:
    struct WidgetsConnected {
        const QWidget* from;
        const QWidget* to;
    };

    QList<WidgetsConnected> list;

};

#endif // DRAWWIDGET_H

drawwidget.cpp

#include "drawwidget.h"

#include <QPainter>
DrawWidget::DrawWidget(QWidget *parent) : QWidget(parent)
{
}

void DrawWidget::addWidgets(const QWidget * from, const QWidget * to)
{
    list.append(WidgetsConnected{from , to});
    update();
}

void DrawWidget::paintEvent(QPaintEvent *)
{
    QPainter painter(this);
    for(const WidgetsConnected el: list){
        const QWidget* from = el.from;
        const QWidget* to = el.to;

        QPoint start =  from->mapToGlobal(from->rect().topRight() +  QPoint(0, from->height()/2));
        QPoint end = to->mapToGlobal(to->rect().topLeft() +  QPoint(0, to->height()/2));

        painter.drawLine(mapFromGlobal(start), mapFromGlobal(end));
    }
}

The complete example can be found here.

这篇关于使用 QPainter 在 QWidgets 之间画一条线的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆