使用QPainter在QWidgets之间画一条线
Using QPainter to draw a line between QWidgets
我正在开发一个需要能够在两个 QWidget
对象之间画一条线的应用程序。我已经尝试了很多东西,但我目前的尝试(我 认为 是在正确的方向我只是觉得我错过了一些东西)是包含小部件(我称之为DrawWidget
并包含添加了 QWidget
对象的 QGridLayout
)覆盖 paintEvent
方法并调用 QPainter::drawLine()
函数。
我遇到的问题是:
- 无论我如何尝试获取小部件的位置,线的端点总是在错误的位置
- 每当我尝试绘制第二条线时,我绘制的第一条线就会被擦除。
这是包含小部件的 paintEvent
函数:
void paintEvent(QPaintEvent *)
{
if (!drewSinceUpdate){
drewSinceUpdate = true;
QPainter painter(this);
painter.setPen(QPen(Qt::black));
painter.drawLine(start->geometry().center(), end->geometry().center());
}
}
我尝试了很多不同的方法来获得 paintEvent
最后一行中的小部件的正确位置,我将 post 一些方法(我不记得所有的他们):
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()));
为了让我的问题更清楚,这里是我正在寻找的示例,摘自 QT Diagram Scene Example:
但这就是我最终得到的:
感谢您提供的任何帮助。
注意:
-start
和 end
都是 QWidget
我用另一种方法传入的对象
-与DrawWidget
相关的层级是:
QMainWindow
->QScrollArea
->DrawWidget
->QGridLayout
->Items <-- These are the things I want to connect
编辑:为了制作一个完整且可验证的示例,这里是相关代码的全部。
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
对于这种情况,我们使用函数 mapToGlobal() and mapfromGlobal(),因为 pos() returns 相对于父级的位置,如果小部件具有不同的父级,这可能会导致问题。
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));
}
}
可以找到完整的示例here。
我正在开发一个需要能够在两个 QWidget
对象之间画一条线的应用程序。我已经尝试了很多东西,但我目前的尝试(我 认为 是在正确的方向我只是觉得我错过了一些东西)是包含小部件(我称之为DrawWidget
并包含添加了 QWidget
对象的 QGridLayout
)覆盖 paintEvent
方法并调用 QPainter::drawLine()
函数。
我遇到的问题是:
- 无论我如何尝试获取小部件的位置,线的端点总是在错误的位置
- 每当我尝试绘制第二条线时,我绘制的第一条线就会被擦除。
这是包含小部件的 paintEvent
函数:
void paintEvent(QPaintEvent *)
{
if (!drewSinceUpdate){
drewSinceUpdate = true;
QPainter painter(this);
painter.setPen(QPen(Qt::black));
painter.drawLine(start->geometry().center(), end->geometry().center());
}
}
我尝试了很多不同的方法来获得 paintEvent
最后一行中的小部件的正确位置,我将 post 一些方法(我不记得所有的他们):
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()));
为了让我的问题更清楚,这里是我正在寻找的示例,摘自 QT Diagram Scene Example:
注意:
-start
和 end
都是 QWidget
我用另一种方法传入的对象
-与DrawWidget
相关的层级是:
QMainWindow
->QScrollArea
->DrawWidget
->QGridLayout
->Items <-- These are the things I want to connect
编辑:为了制作一个完整且可验证的示例,这里是相关代码的全部。
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
对于这种情况,我们使用函数 mapToGlobal() and mapfromGlobal(),因为 pos() returns 相对于父级的位置,如果小部件具有不同的父级,这可能会导致问题。
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));
}
}
可以找到完整的示例here。