【问题标题】:Qt how to redirect widget painting to parent widget?Qt如何将小部件绘画重定向到父小部件?
【发布时间】:2017-10-08 17:43:19
【问题描述】:

我正在创建一些用于绘图的自定义 qt 设计器小部件插件。通过这些小部件,用户可以像 Microsoft Visio 一样使用 qt Designer 进行绘图(希望如此)。

如下截图所示,有一个SvPage对象page_0作为容器,它包含一个SvArc小部件和一个SvCircle小部件。

一切都很好,除了当一个小部件 (A) 覆盖另一个小部件 (B) 时,用户无法轻松选择小部件 B。

为了解决这个问题,我正在尝试做:

  1. 将每个绘图小部件(例如SvArc、SvCircle)的大小设置为非常小(40px * 40 px);

  2. 将绘图小部件的内容直接绘制到其父小部件 (SvPage)。在SvPage::PaintEvent(QPaintEvent <em>event)</em>中,它会迭代所有子绘图部件,并调用每个子对象的doPaint(QPainter painter)方法。

3.自动刷新绘图小部件(例如,当移动SvArc小部件时,它在SvPage上的绘图应该自动更新),在绘图小部件的SvArc::PaintEvent(QPaintEvent *event)中,它会触发SvPage更新它的绘画。

但是在第 3 步中,有一个问题会导致递归重绘问题: 因为 SvArc::PaintEvent() 会触发 SvPage::PaintEvent(),然后 SvPage::PaintEvent() 会再次触发 SvArc::PaintEvent(),因为 SvArc 小部件是 SvPage 小部件的子小部件。

那么,问题是将小部件绘画重定向到父小部件是否是个好主意?如果是,如何解决递归重绘问题?如果没有,有什么好的?

代码(简化):

void SvPage::paintEvent(QPaintEvent *event)
{
    initPainter();
    QList<SvWidget*> widgets = this->findChildren<SvWidget*>();
    for (int i = 0; i < widgets.count(); i++)
    {
        SvWidget* w = widgets.at(i);
        w->doPaint(this->painter);
    }
    destoryPainter();
}

void SvWidget::paintEvent(QPaintEvent *event)
{
    Q_UNUSED(event);
    emit signalDoPaint();
}

void SvArc::doPaint(QPainter* painter)
{
    painter->drawArc(x, y, w, h, a alen);
}

【问题讨论】:

  • 我认为从 UX 的角度来看,使用对象树(“Object Inspector”)使小部件可以选择会更有效和实用,因为您可能会遇到完全覆盖的小部件的情况覆盖。

标签: c++ qt widget


【解决方案1】:

我正在创建一些用于绘图的自定义 Qt Designer 小部件插件。通过这些小部件,用户可以像 Microsoft Visio 一样使用 Qt Designer 进行绘图(希望如此)。

您在 Qt Designer 中重用的功能非常少,可以很容易地分解到一个单独的项目中。对您来说唯一有价值的是属性检查器窗格。

对于其他一切,使用小部件是实现它的最复杂的方式。使用 QGraphicsSceneQGraphicsView 并开始使用 90% 的功能已经实现并准备就绪。

QGraphicsScene 中实现一个基本的矢量插图系统是一项下午的工作。您可以在几天内完成 Windows 2.x 时代早期 Corel Draw 的功能。这证明了场景框架和现代开发框架的强大功能。

【讨论】:

  • 有没有开源的qt项目做这个工作?我想试一试。
  • github.com/dalboris/vpaint - 几乎是最先进的,如果你问我的话。
【解决方案2】:

你把事情搞砸了。

每个小部件都应该负责自己的绘图。这就是 Qt 的工作原理。

您可以使用单个小部件作为某些对象的管理器并绘制它们,但是这些对象不必是小部件,它们可以是简单的数据表示。在这种情况下,对象不会与任何绘画相关,而是由管理器小部件负责。

但是,这种方法效率会降低。因为当您有多个独立的小部件时,绘制引擎可以轻松检测更改并仅有效地重新绘制需要更新的部分。

在您的情况下,您要么必须进行大量多余的重绘,要么实施更复杂的项目管理,这将是一项复杂的任务,绝对不值得付出努力,如果您甚至可以开始的话有,你可能不是。

您当前的方法非常糟糕。我建议只使用常规的小部件,按照它们的实际尺寸,进行实际的绘画。你实现和管理它会容易得多,计算机绘制它会容易得多。

至于在重叠的小部件之间进行选择,QWidget 的设计并不是为了方便这一点。小部件应该放在布局中,而不是重叠。这就是为什么它的childAt() 函数只能在给定坐标处返回一个小部件。

您真正应该做的是使用QGraphicsSceneQGraphicsViewQGraphicsItem。与小部件类似,图形项将有效地处理自己的绘图,不同之处在于 API 是为图形设计的,当您有重叠项时,QGraphicsScene::items() 将为您提供该位置所有项的列表,因此您可以选择最上面以外的项目。

【讨论】:

  • 感谢您的回答。如果坚持使用常规小部件,您能否告诉我如何最小化覆盖问题?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2010-10-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多