QtQML /快速关联面板

大家好! 我是Rogia Europe公司开发桌面应用程序的团队负责人。 我们为石油和天然气行业开发软件解决方案。


碰巧我们的旗舰产品StarSteer没有相关面板-井指南的经典工具。 由于其他优先级更高的任务,该任务被推迟了很长时间,但是去年秋天我们终于能够启动它。


绕过遗留代码库的问题-我将在本文中提及它-一个基本问题-我应该使用哪种技术? 我们肯定需要OpenGL-已经在基于OpenSceneGraph的MapView和3d视图中使用过OpenGL-但是很明显它并不裸露,并且带有GUI元素。 OSG下降=(。一项满足两项要求的技术-OpenGL上的场景图和GUI-我只知道一个-Qt QML / Quick。关于我们得到了什么,我们必须分享什么。


参赛作品


我们于2013年秋季开始开发该产品。 作为Qt粉丝,为我使用什么库甚至不是问题。 那时,可能会出现问题:使用Qt的第4版或第5版(仍是最近的版本)。 我们选择了第五名,从飞行的高度上我只能说:谢谢上帝!


整体外观是在QtGui / Widgets上开发的。 显示图形的所有场景(伽玛,孔隙率,电阻等)都在QGraphicsScene / View上进行。 我的建议是-不要将此捆绑包用于严肃的事情! 参数:滚动条,并且不与外部断开连接(没有Qt 自己进行编辑的外部)逻辑,用于将场景居中( qgraphicsview.cpp +458和以上版本用于水平对齐qgraphicsview.cpp +3816-用于控制矩阵的逻辑 )。 如果这不打扰您,请使用-包装盒中的许多方便物品。


还有什么不用的? NSIS。


一切都很完美,产品正在开发,任务正在完成,客户数量正在增长。 重构...通常,一段时间后,QGraphicsScene开始干扰我们-在打开粗线时我们不急着优化40,000点的图形的绘制-CPU上的所有这些东西都很慢。


一路上,我们已经厌倦了在小部件上开发GUY。 完全用手工编写代码,或者用一点点在设计器中(来自Creator, .ui + .cpp)。 我想要现代的东西,例如对图形界面的声明式描述。


列出了我们将要使用的技术:


  • QGraphicsView / Scene上的老式方法;
  • 对于每个轨道,请使用单独的裸QOpenGLWidget;
  • 在QOpenGLWidget上实现整个窗口,但是由我们自己创建GUI(或查找某些东西);
  • 前两点分别是+ OSG;
  • Qt QML /快速,

只有六点; 讨论过。 我的魅力不胜枚举,决定尝试查看QML中的原型的行​​为。


样机


我打开了一个示例景图\图形。 查找并关闭=)。 我玩了几天,看了其他例子,但是没有什么能达到我的目标。


那需要什么呢? 这是相关面板的外观:



一个相当简单的结构是一系列的井眼轨迹,在其中跟踪曲线,比例尺。 好吧,小事情。 将来会有很多文本,一些控件-按钮,下拉列表,输入字段等等。


我研究了sgengine,学习了如何创建两个场景图并将其绘制在指定的视口中。 后来我意识到,使用此选项,QML / Quick将不会,那么为什么我需要所有这些?


实际上,我不记得使用哪种药物,但是出于某种原因,我决定转向计算机图形学的基础知识。 因此,在栅格化的最后阶段,场景的所有坐标都将转换为NKU(归一化设备坐标;更好地称为NDC =归一化设备坐标)。 是的,我听说顶点着色器的输出实际上是一个剪辑空间,此后仍然存在仿射失真,但是所有这些都是针对三维表示的,在2D中,它始终为w = 1,因此我们可以假定输出为立即到NDC。


好,NDC,下一步是什么? 如果窗口的宽度为800像素,则零像素中心的NDC坐标为-1; 799th的中心坐标为1。简而言之,ndcX = -1 + 2 * i /799。现在想象有一个从100到300的矩形,我想将整个场景绘制成一个窗口而不是整个窗口。 利用这些零碎的知识,我将对ndcX100,ndcX300进行计数,然后将它们放入顶点着色器中,然后按标准


gl_Position = matrix * position; 

在[ndcX100;]中线性“包装” gl_Position.x。 ndcX300]。 我们对垂直组件也是如此。 此技巧将使您可以在场景的任何选定矩形中生成场景。 有了这些知识,图表示例开始发生变化。 您可以在这里查看教区。 所有盐在shaders/line.vsh


SceneItem /场景


接下来的三个月是TK的写作,结果是12张A4 =)。 同时,我们考虑了架构。 他们采用了MVC ... aka MVP ... aka分层MVC / MVP ...甚至PAC-所有这些都是惯例,良好的分解很重要。


通常,我们准备了一个场景示例。 可在此处获取-SceneSample 。 事实证明,有一个用于在QtQML / Quick上使用图形创建应用程序的框架。 请不要忘记,此代码仍充当示例。 是的,已经准备好了一半,看起来或多或少整洁,但还没有准备好。


场景是主要角色。 此类监视其NDC坐标并更新相应的矩阵。 SceneCamera与他是密友。 下一个值得一提的实体是SceneItem。 它本身是无用的,只包含一些基本逻辑。 继承它-就像LineStrip-并实现您所需要的。 同时,在updatePaintNode您需要使用SceneMaterial-FlatColorMaterial的派生类作为参考。 其余的实体也执行=),各种操纵器,工具。 QML中没有抛出很多类,没有这样的C ++就无法做到; 你还记得还没准备好吗?


第二个困难是,如果我们想在新场景中使用控件,则将无法执行此操作。 我们认为,决定我们不需要它,并平静地继续开发。


该方法的优点:


  • 一切都绘制在场景的一栏中;
  • 我们没有修正Qt-仍然可以在舞台上添加常规QML控件,以使它们与曲线(或其他SceneItem)之间的z顺序正确;
  • 与其他方法相比,内存消耗更低。

缺点


  • 复杂的城市机器
  • 需要具备OpenGL和GLSL的知识;
  • 半成品。

当然,我们在开发过程中遇到了一些困难。 其中之一是


Z顺序错误


当我们第一次尝试显示带有曲线的场景时,我们看到了这样的图片:



乍一看,还不清楚,“我们做得对!” 有人模糊地猜测到gl_Position.z是某种错误,但是为什么很难理解为什么在晚上。 我们没有放弃:我们认为Qt会纠正着色器并添加代码来更改gl_Position.z。 一段时间后,它突然降临:我们通过z的变化弄乱了矩阵的数据,然后Qt将其值传送给它们! 因此,来自QML的item.z值被映射到来自OpenGL的z( SceneMaterial.cpp +20 ):



带剪辑的错误:true


聊天后,业务团队将发送一个屏幕,坐标网格的左行消失。



我们的问答对程序造成了折磨,并找到了稳定播放的步骤:我们将显示器的缩放比例设置为不为100%的倍数,并且线条“闪烁”。 Artyom坐了下来,想了一想,发现当clip: true且项目为矩形时,使用了glScissor,但其参数为整数像素坐标! 在QML的项目中,它们是真实的,结果是该行的光栅化下降到下一个/上一个像素,并且剪刀被剪切到当前位置。


我们固定了这样的场景: width: Math.round(metrics.width + leftPadding + 2 * rightPadding + 0.5) 。 因此,场景项必须始终具有整数坐标以避免此类伪像。


最后,我将带来KDPV



谢谢大家的关注!

Source: https://habr.com/ru/post/zh-CN480334/


All Articles