Panneau QtQML / Quick correlation

Bonjour à tous! Je suis chef d'équipe pour le développement d'applications bureautiques au sein de la société Rogia Europe. Nous développons des solutions logicielles pour l'industrie pétrolière et gazière.


Il se trouve que notre produit phare StarSteer n'a pas de panneau de corrélation - un outil classique pour les guides de puits. La tâche a été reportée pendant longtemps à cause d'autres tâches plus prioritaires, mais l'automne dernier, nous avons finalement pu la démarrer.


Contourner les questions de la base de code héritée - je le mentionnerai dans l'article - il y avait une question fondamentale - quelle technologie devrais-je utiliser? Nous avions absolument besoin d'OpenGL - qui est déjà utilisé dans MapView et la vue 3D basée sur OpenSceneGraph - mais il est évident qu'il n'est pas nu, et avec des éléments d'interface graphique. OSG est tombé = (. Une technologie qui répond à deux exigences - graphique de scène et GUI sur OpenGL - je n'en connaissais qu'une - Qt QML / Quick. À propos de ce que nous avons et de ce que nous devons partager - à l'intérieur.


Entrée


Nous avons commencé à développer le produit à l'automne 2013. Quel ensemble de bibliothèques utiliser pour moi, en tant que fan de Qt, n'était même pas une question. A ce moment, la question pourrait se poser: utiliser la 4ème ou la 5ème version (encore assez récente) de Qt. Nous avons choisi le cinquième et du haut de notre vol je ne peux que dire: Dieu merci!


L'ensemble du look est développé sur QtGui / Widgets. Toutes les scènes où des graphiques sont affichés (gamma, porosité, résistance, etc.) sont réalisées sur QGraphicsScene / View. Mon conseil est - n'utilisez pas ce bundle pour des choses sérieuses! Arguments: barres de défilement et non déconnecté de l'extérieur (il est à l'extérieur sans les propres modifications de Qt) pour centrer la scène ( qgraphicsview.cpp +458 et plus dans la même méthode pour horizontal; qgraphicsview.cpp +3816 - quel contrôle sur la matrice). Si cela ne vous dérange pas, utilisez - beaucoup de pièces pratiques de la boîte.


Quoi d'autre à ne pas utiliser? NSIS.


Tout était parfait, le produit se développait, les tâches se faisaient, le nombre de clients augmentait. Refactoring ... En général, après un certain temps, QGraphicsScene a commencé à interférer avec nous - nous n'étions pas pressés d'optimiser le dessin de graphiques de 40000 points, lorsque les lignes épaisses étaient activées - tout ce truc sur le CPU était très lent.


En cours de route, nous nous sommes lassés de développer GUY sur des widgets. Soit avec vos mains complètement dans le code, soit un peu dans le concepteur (de Creator, .ui + .cpp). Je voulais des trucs modernes, comme une description déclarative de l'interface graphique.


Faire une liste des technologies sur lesquelles nous allons faire:


  • l'ancienne méthode sur QGraphicsView / Scene;
  • pour chaque piste, utilisez un QOpenGLWidget nu séparé;
  • implémenter toute la fenêtre sur QOpenGLWidget, mais l'interface graphique par nous-mêmes (ou trouver quelque chose);
  • deux points précédents + OSG respectivement;
  • Qt QML / Quick,

seulement six points; discuté. Mon charisme l'emportait et j'ai décidé d'essayer de voir comment le prototype en QML se comportait.


Prototype


J'ai ouvert un exemple de scenegraph \ graph. Regardé et fermé =). J'ai joué pendant plusieurs jours, j'ai regardé d'autres exemples, mais rien n'a approché mon objectif chéri.


Et que fallait-il alors? Voici à quoi pourrait ressembler le panneau de corrélation:



Une structure assez simple est une liste de pistes de puits, à l'intérieur de pistes de courbes, d'échelles; Eh bien, les petites choses. Beaucoup de texte, à l'avenir, certains contrôles - boutons, listes déroulantes, champs de saisie, etc.


J'ai regardé sgengine, j'ai appris à créer deux graphiques de scène et à les dessiner dans la fenêtre désignée. Plus tard, j'ai réalisé qu'avec cette option, QML / Quick ne le serait pas, alors pourquoi ai-je besoin de tout cela?


En fait, je ne me souviens pas quels médicaments, mais pour une raison quelconque, j'ai décidé de me tourner vers les bases de l'infographie. Ainsi, aux dernières étapes de la pixellisation, toutes les coordonnées de la scène sont transférées vers le NKU (Normalized Device Coordinates; mieux connu sous le nom de NDC = Normalized Device Coordinates). Oui, j'ai entendu que la sortie du vertex shader est en fait un espace de clip et après cela, il y a toujours une distorsion affine, mais tout cela est pour une représentation en trois dimensions, et en 2D c'est toujours w = 1 et donc nous pouvons supposer que la sortie immédiatement au NDC.


OK, NDC, quelle est la prochaine étape? Que si la largeur de votre fenêtre est de 800 pixels, alors la coordonnée NDC du centre du pixel zéro est -1; la coordonnée centrale du 799th est 1. En bref, ndcX = -1 + 2 * i / 799. Imaginez maintenant qu'il y a un rectangle de 100 à 300 et je veux dessiner la scène entière non pas dans une fenêtre entière, mais dedans. En utilisant cette connaissance fragmentaire, je compterai ndcX100, ndcX300, puis les jetterai dans le vertex shader et là, après le standard


gl_Position = matrix * position; 

«envelopper» gl_Position.x de façon linéaire dans [ndcX100; ndcX300]. Nous faisons de même pour la composante verticale. Cette astuce vous permettra de faire apparaître des scènes dans n'importe quel rectangle sélectionné de la scène. Avec cette connaissance, l'exemple de graphique a commencé à subir des modifications. Vous pouvez regarder la paroisse ici - graphique ; tout le sel dans shaders/line.vsh .


SceneItem / Scene


Les trois mois suivants ont été l'écriture de TK, il s'est avéré 12 feuilles de A4 =). En parallèle, nous avons considéré l'architecture. Ils ont pris MVC ... c'est MVP ... c'est Hierarchical MVC / MVP ... ou même PAC - ce sont toutes des conventions, une bonne décomposition est importante.


En général, nous avons préparé un exemple de scène. Les sources sont disponibles ici - SceneSample . Il s'est avéré un certain cadre pour créer des applications avec des graphiques sur QtQML / Quick. N'oubliez pas que ce code sert toujours d'exemple. Oui, déjà à moitié prêt et semble plus ou moins soigné, mais pas prêt.


La scène est le principal acteur. Cette classe surveille ses coordonnées NDC et met à jour les matrices correspondantes. SceneCamera est un ami proche de lui. La prochaine entité qui mérite d'être mentionnée est SceneItem. Il est inutile en soi, il ne contient qu'une logique de base; héritez-le - comme LineStrip - et implémentez ce dont vous avez besoin. Dans le même temps, dans updatePaintNode vous devez utiliser des dérivés de SceneMaterial - FlatColorMaterial comme référence. Les entités restantes font aussi quelque chose =), toutes sortes de manipulateurs, d'outils. La plupart des classes ne sont pas lancées en QML, et vous ne pouvez pas vous passer d'un tel C ++; vous souvenez-vous de ce qui n'est pas prêt?


La deuxième difficulté est que si nous voulons utiliser des contrôles à l'intérieur d'une nouvelle scène, nous ne pourrons pas le faire. Nous avons pensé, décidé que nous n'en avions pas besoin et avons calmement poursuivi le développement.


Avantages de l'approche:


  • tout est dessiné dans une colonne de la scène;
  • nous n'avons pas corrigé Qt - il reste possible d'ajouter des contrôles QML normaux à la scène afin que l'ordre z entre eux et les courbes (ou tout autre SceneItem) soit correct;
  • consommation de mémoire inférieure par rapport à d'autres approches.

Inconvénients


  • machine urbaine complexe;
  • la connaissance d'OpenGL et de GLSL est requise;
  • solution semi-finie.

Bien sûr, nous avons rencontré quelques difficultés lors du développement. L'un d'eux était


Bug avec z-order


Lorsque nous avons essayé pour la première fois d'afficher une scène avec des courbes, nous avons vu de telles images:



À première vue, ce n'était pas clair, "nous avons tout fait correctement!" On a vaguement deviné que gl_Position.z était en quelque sorte erroné, mais pourquoi il était difficile de comprendre pourquoi la nuit. Nous n'avons pas abandonné: nous avons vu que Qt corrige les shaders et ajoute le code pour changer gl_Position.z, nous avons pensé. Au bout d'un moment, il nous est apparu: nous avons foiré les données de la matrice par le changement de z, et Qt leur transfère ses valeurs! Ainsi, la valeur item.z de QML est mappée à z d'OpenGL ( SceneMaterial.cpp +20 ):



Bug avec clip: vrai


Une fois dans un chat, une équipe commerciale envoie un écran où la ligne gauche de la grille de coordonnées a disparu.



Notre Q&A a tourmenté le programme et a trouvé des étapes pour une lecture stable: nous avons réglé la mise à l'échelle du moniteur sur un multiple de 100% et les lignes «flash». Artyom s'est assis, a pensé et trouvé que lorsque clip: true et que l'élément est rectangulaire, glScissor est utilisé, mais ses arguments sont des coordonnées de pixels entières! Au niveau des éléments de QML, ils sont réels et il s'est avéré que la pixellisation de la ligne est tombée au pixel suivant / précédent, et les ciseaux ont été coupés au courant.


Nous avons corrigé la scène comme ceci: width: Math.round(metrics.width + leftPadding + 2 * rightPadding + 0.5) . Par conséquent, l'élément de scène doit toujours avoir des coordonnées entières afin d'éviter de tels artefacts.


En conclusion, je vais apporter KDPV



Merci à tous pour votre attention!

Source: https://habr.com/ru/post/fr480334/


All Articles