Qt GraphicsScene XOR Line 或 Line 在单独的层中?
Posted
技术标签:
【中文标题】Qt GraphicsScene XOR Line 或 Line 在单独的层中?【英文标题】:Qt GraphicsScene XOR Line or Line in separate layer? 【发布时间】:2017-04-17 21:10:05 【问题描述】:我已经开始学习 Qt,并尝试提高我的基本 C++ 技能。 在 GraphicsScene 中,我尝试使用鼠标(鼠标事件)绘制一条线。 当我开始在 GraphicsScene 中绘制一条线时,会从原点绘制一条细虚线,在该原点我首先单击到当前鼠标位置并随鼠标移动,然后单击第二个点。为了抹去它,我把它画成黑色。如果我将鼠标悬停在已经画出的线条上,你会在它们上面看到黑色的画线。要在不留下标记的情况下取消绘制,GraphicsScene 上的 XOR 操作会派上用场,或者如果我可以在不同的层中绘制而不接触另一层可能会很方便。但我还不知道该怎么做。该示例位于https://github.com/JackBerkhout/QT_Draw001 在 line.cpp 中是函数 setLineP2(int x, int y),它绘制和擦除那条细虚线。 请问有人可以帮我吗?
【问题讨论】:
你能不能给我解释一下 嗨,由于消息长度的限制,我需要将其拆分为多个 cmets。假设我已经画了一条粗紫色线(第一条)。然后我开始画第二条线,我们先点击起点。不如拖动鼠标。我们看到一条细的白色虚线,其端点跟随鼠标。 所以我把它画成白色。然后为了更新它的位置,我首先取消绘制它(仅使用背景颜色),然后使用白色再次在新位置上绘制它。当我将线拖到第一个上面时,我们可以看到我们在那个上面画了很多黑线。最后,当我点击端点时,会画出紫色的粗线。黑线标记在第一行上仍然可见,这是我们在第一次单击后移动鼠标时越过的。 如果我们可以在线路构建阶段使用第二层,我们就不会在第一层出现(黑色)标记。或者,如果我们使用 XOR 笔模式,它会通过在同一位置第二次绘制来再次重建底层颜色。我希望这能更好地解释它。在Windows上的VS中,这很简单,但是我使用Linux,而在QT中则不简单,因为它似乎缺乏图形场景中的异或功能。 我已经修改了你的代码,看看是不是你想要的:github.com/eyllanesc/***/tree/master/GraphicsScene 【参考方案1】:主要的误解是将QGraphicsScene
视为某种位图:它不是!它是可以呈现自身的项目的集合,以及它们的空间索引。在场景中,如果您希望删除/隐藏某些内容,您不能对其进行过度绘制 - 只需根据需要删除/隐藏相关项目即可。场景将处理所有细节 - 这就是它的用途
此时您必须忘记 GDI-anything。您不是在此处的原始 DC 上绘画。即使在使用原始 GDI 时,您也不想在窗口的 DC 上绘画,因为它会闪烁,您应该在位图上绘画并将位图 blit 到窗口。
例如,您的 eraseScene
方法在场景顶部添加了一个矩形,由于保留了所有先前的项目(您可以遍历它们),因此浪费了内存和资源,而它应该做的只是 clear
场景(或其等价物):
void MainWindow::eraseScreen(void)
[...]
scene->addRect(0, 0, width()+1000, height()+1000, pen, brush);
对比正确的:
void MainWindow::eraseScreen(void)
scene->clear();
下面是一个完整的示例,它近似于您在代码中可能要执行的操作。 它有 120 行长。有点难以弄清楚你到底要做什么,因为你的代码非常复杂 - 在问题中用简单的术语描述确切的行为很有用。
该示例使用QPainterPath
来保存QPainterPathItem
呈现的MoveTo
和LineTo
元素列表。它还使用QGraphicsLineItem
来显示瞬态线。
MyScene::PathUpdater
用于包含修改路径的上下文,并确保维护适当的前置条件和后置条件。即:
由于QPainterPath
是隐式共享的,您应该清除QGraphicsPathItem
持有的路径以避免不必要的隐式复制。这是修改m_path
之前的必要前提。
m_path
修改后,必须更新路径项,并发出新状态。
以下其他点值得注意:
按值保存成员会导致显着缺少任何内存管理代码(!) - 编译器为我们完成了这一切。你不会在任何地方找到一个new
或delete
。它们不是必需的,而且我们不会因为不手动执行此操作而支付额外费用。现代 C++ 应该是这个样子。
显示MainWindow
和MyScene
之间的明显区别。 MainWindow
对 MyScene
的细节一无所知,反之亦然。 main
中的代码充当两者之间的适配器。
利用 C++11。
SO 测试用例和示例所需的简洁风格:为了学习,最好将它们全部保存在一个文件中,以便轻松查看代码的所有部分。它只有 120 行,而如果跨文件拆分则超过两倍。我们的大脑利用参考的位置。拆分代码会让你更难理解。
另见
-
Another demo of interactive item creation。
A more advanced example of status notifications。
// https://github.com/KubaO/***n/tree/master/questions/scene-polygon-7727656
#include <QtWidgets>
class MainWindow : public QWidget
Q_OBJECT
QGridLayout m_layoutthis;
QPushButton m_new"New";
QPushButton m_erase"Erase All";
QLabel m_label;
QGraphicsView m_view;
public:
MainWindow()
m_layout.addWidget(&m_new, 0, 0);
m_layout.addWidget(&m_erase, 0, 1);
m_layout.addWidget(&m_label, 0, 2);
m_layout.addWidget(&m_view, 1, 0, 1, 3);
m_view.setBackgroundBrush(Qt::black);
m_view.setAlignment(Qt::AlignBottom | Qt::AlignLeft);
m_view.scale(1, -1);
connect(&m_new, &QPushButton::clicked, this, &MainWindow::newItem);
connect(&m_erase, &QPushButton::clicked, this, &MainWindow::clearScene);
void setScene(QGraphicsScene * scene)
m_view.setScene(scene);
Q_SIGNAL void newItem();
Q_SIGNAL void clearScene();
Q_SLOT void setText(const QString & text) m_label.setText(text);
;
class MyScene : public QGraphicsScene
Q_OBJECT
public:
struct Status
int paths;
int elements;
;
private:
bool m_newItem = ;
Status m_status = 0, 0;
QPainterPath m_path;
QGraphicsPathItem m_pathItem;
QGraphicsLineItem m_lineItem;
struct PathUpdater
Q_DISABLE_COPY(PathUpdater)
MyScene & s;
PathUpdater(MyScene & scene) : s(scene)
s.m_pathItem.setPath(); // avoid a copy-on-write
~PathUpdater()
s.m_pathItem.setPath(s.m_path);
s.m_status = 0, s.m_path.elementCount();
for (auto i = 0; i < s.m_status.elements; ++i)
auto element = s.m_path.elementAt(i);
if (element.type == QPainterPath::MoveToElement)
s.m_status.paths++;
emit s.statusChanged(s.m_status);
;
void mousePressEvent(QGraphicsSceneMouseEvent *event) override
PathUpdater updater(*this);
auto pos = event->scenePos();
m_lineItem.setLine(0, 0, pos.x(), pos.y());
m_lineItem.setVisible(true);
if (m_path.elementCount() == 0 || m_newItem)
m_path.moveTo(pos);
m_path.lineTo(pos.x()+1,pos.y()+1); // otherwise lineTo is a NOP
m_newItem = ;
void mouseMoveEvent(QGraphicsSceneMouseEvent *event) override
PathUpdater updater(*this);
auto pos = event->scenePos();
m_lineItem.setLine(0, 0, pos.x(), pos.y());
m_path.setElementPositionAt(m_path.elementCount()-1, pos.x(), pos.y());
void mouseReleaseEvent(QGraphicsSceneMouseEvent *) override
m_lineItem.setVisible(false);
public:
MyScene()
addItem(&m_pathItem);
addItem(&m_lineItem);
m_pathItem.setPen(Qt::red);
m_pathItem.setBrush(Qt::NoBrush);
m_lineItem.setPen(Qt::white);
m_lineItem.setVisible(false);
Q_SLOT void clear()
PathUpdater updater(*this);
m_path = ;
Q_SLOT void newItem()
m_newItem = true;
Q_SIGNAL void statusChanged(const MyScene::Status &);
Status status() const return m_status;
;
int main(int argc, char *argv[])
using Q = QObject;
QApplication appargc, argv;
MainWindow w;
MyScene scene;
w.setMinimumSize(600, 600);
w.setScene(&scene);
Q::connect(&w, &MainWindow::clearScene, &scene, &MyScene::clear);
Q::connect(&w, &MainWindow::newItem, &scene, &MyScene::newItem);
auto onStatus = [&](const MyScene::Status & s)
w.setText(QStringLiteral("Paths: %1 Elements: %2").arg(s.paths).arg(s.elements));
;
Q::connect(&scene, &MyScene::statusChanged, onStatus);
onStatus(scene.status());
w.show();
return app.exec();
#include "main.moc"
【讨论】:
@JackBerkhout 查看编辑。我在上面提供的 Qt 示例比使用原始 Borland C++ 及其图形库所做的任何事情都简单得多。 嗨库巴,非常感谢!现在我明白了那部分。我首先在 Udemy.com 上注册了一些 C++ 和 QT 课程……要学习的东西很多。以上是关于Qt GraphicsScene XOR Line 或 Line 在单独的层中?的主要内容,如果未能解决你的问题,请参考以下文章
Qt:选择的项目出现在 QGraphicsScene 的顶部
为啥所有选中的 QGraphicsItem 都没有收到 mouseMove 事件?