使用 QPainter 在 Widget 之间画一条线

2023-12-20

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

我遇到的问题是:

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

这里是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,我将发布一些方法(我不记得所有的方法):

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 http://doc.qt.io/qt-5/qtwidgets-graphicsview-diagramscene-example.html: Example of what I want But this is what I end up getting:

What I get Thank you for any help you can provide.

NOTE:

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

- 相关的层次结构DrawWidget is:

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

编辑:为了制作一个完整且可验证的示例,这里是完整的相关代码。

主窗口.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);
}

主窗口.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

对于这种情况,我们使用该函数映射到全局() http://doc.qt.io/qt-5/qwidget.html#mapToGlobal and 映射自全局() http://doc.qt.io/qt-5/qwidget.html#mapFromGlobal,因为 pos() 返回相对于父级的位置,如果小部件具有不同的父级,这可能会导致问题。

绘图组件.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

绘制控件.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 https://github.com/eyllanesc/stackoverflow/tree/master/questions/45319476.

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

使用 QPainter 在 Widget 之间画一条线 的相关文章

  • 转换 const void*

    我有一个函数返回一个const void 我想用它的信息作为char 我可以将它投射为 C 风格的罚款 char variable但是当我尝试使用reinterpret cast like reinterpret cast
  • 如何在类文件中使用 Url.Action() ?

    如何在 MVC 项目的类文件中使用 Url Action Like namespace 3harf public class myFunction public static void CheckUserAdminPanelPermissi
  • MVC3中设置下拉列表中的所选项目

    我必须为视图中的下拉列表设置所选项目 但它不起作用 View div class editor label Html LabelFor model gt model Gender div div class editor field Htm
  • 传递 constexpr 对象

    我决定给予新的C 14的定义constexpr旋转并充分利用它 我决定编写一个小的编译时字符串解析器 然而 我正在努力保持我的对象constexpr将其传递给函数时 考虑以下代码 include
  • extern 声明和函数定义都在同一文件中

    我只是浏览了一下gcc源文件 在gcc c 我发现了类似的东西 extern int main int char int main int argc char argv 现在我的疑问是extern是告诉编译器特定的函数不在这个文件中 但可以
  • cpp.react库的C++源代码中奇怪的“->* []”表达式

    这是我在文档中找到的 C 片段cpp react 库 https github com schlangster cpp react implicit parallelism auto in D MakeVar 0 auto op1 in g
  • 在 C# 中,如何根据在 gridview 行中单击的按钮引用特定产品记录

    我有一个显示产品网格视图的页面 该表内有一列 其中有一个名为 详细信息 的超链接 我想这样做 以便如果用户单击该特定产品的详细信息单元格 将打开一个新页面 提供有关该产品的更多信息 我不确定如何确定哪个Product记录链接的详细信息以及我
  • 获取没有显式特征的整数模板参数的有符号/无符号变体

    我希望定义一个模板类 其模板参数始终是整数类型 该类将包含两个成员 其中之一是类型T 另一个作为类型的无符号变体T 即如果T int then T Unsigned unsigned int 我的第一直觉是这样做 template
  • C++中判断unicode字符是全角还是半角

    我正在编写一个终端 控制台 应用程序 该应用程序应该包装任意 unicode 文本 终端通常使用等宽 固定宽度 字体 因此要换行文本 只需计算字符数并观察单词是否适合一行并采取相应的操作 问题是 Unicode 表中的全角字符在终端中占用了
  • 在 C# 中为父窗体中的子窗体控件添加事件处理程序

    我有两种形式 一种是带有按钮和文本框的父表单 单击该按钮时 将打开一个对话框 该子窗体又包含一个文本框和一个按钮 现在我想要的是 每当子表单文本框中的文本更改时 父表单文本框中的文本会自动更改 为了获得这个 我所做的是 Form3 f3 n
  • 在 C 中使用枚举而不是 #defines 作为编译时常量是否合理?

    在 C 工作了一段时间后 我将回到 C 开发领域 我已经意识到 在不必要的时候应该避免使用宏 以便让编译器在编译时为您做更多的工作 因此 对于常量值 在 C 中我将使用静态 const 变量或 C 11 枚举类来实现良好的作用域 在 C 中
  • 如何最好地以编程方式将 `__attribute__ ((unused))` 应用于这些自动生成的对象?

    In my makefile我有以下目标 它将文本 HTML 资源 编译 为unsigned char数组使用xxd i http linuxcommand org man pages xxd1 html 我将结果包装在匿名命名空间和标头保
  • 如何在 C# 中创建异步方法?

    我读过的每一篇博客文章都会告诉您如何在 C 中使用异步方法 但由于某些奇怪的原因 从未解释如何构建您自己的异步方法来使用 所以我现在有这段代码使用我的方法 private async void button1 Click object se
  • 比较:接口方法、虚方法、抽象方法

    它们各自的优点和缺点是什么 接口方法 虚拟方法 抽象方法 什么时候应该选择什么 做出这一决定时应牢记哪些要点 虚拟和抽象几乎是一样的 虚方法在基类中有一个实现 可以选择重写 而抽象方法则没有 并且must在子类中被覆盖 否则它们是相同的 在
  • C++ 对象用 new 创建,用 free() 销毁;这有多糟糕?

    我正在修改一个相对较大的 C 程序 不幸的是 并不总是清楚我之前的人使用的是 C 还是 C 语法 这是在一所大学的电气工程系 我们 EE 总是想用 C 来做所有事情 不幸的是 在这种情况下 人们实际上可以逃脱惩罚 但是 如果有人创建一个对象
  • 将 Lambda 表达式树与 IEnumerable 结合使用

    我一直在尝试了解有关使用 Lamba 表达式树的更多信息 因此我创建了一个简单的示例 这是代码 如果作为 C 程序粘贴到 LINQPad 中 它可以工作 void Main IEnumerable
  • Visual Studio 2015 - Web 项目上缺少共享项目参考选项卡

    我从 MSDN 订阅升级到 Visual Studio 2015 因为我非常兴奋地阅读有关共享项目的信息 当我们想要做的只是重用代码时 不再需要在依赖项中管理 21382 个 nuget 包 所以我构建了一个测试共享项目 其中包含一些代码
  • 了解 Lambda 表达式和委托 [关闭]

    Closed 这个问题需要多问focused help closed questions 目前不接受答案 我已经尝试解决这个问题很长一段时间了 阅读在线博客和文章 但到目前为止还没有成功 什么是代表 什么是 Lambda 表达式 两者的优点
  • MySqlConnectionStringBuilder - 使用证书连接

    我正在尝试连接到 Google Cloud Sql 这是一个 MySql 解决方案 我能够使用 MySql Workbench 进行连接 我如何使用 C 连接MySqlConnectionStringBuilder 我找不到提供这三个证书的
  • 如何在 C 中将 char 连接到 char* ?

    我怎样才能前置char c to char myChar 我有c值为 A and myChar值为 LL 我怎样才能前置c to myChar使 ALL 这应该有效 include

随机推荐