QML゚ンゞンの内郚。 パヌト2バむンディング

この投皿は、 この  翻蚳された 蚘事の続きです。



前の投皿で、QML゚ンゞンがファむルをロヌドする方法を芋おきたした。 最初にQMLファむルが解析され、次に䞭間バむトコヌドにコンパむルされ、最埌にバむトコヌド呜什に埓っお、各QMLファむルの各むベントに察しおC ++オブゞェクトが䜜成されるこずを思い出しおください。 たずえば、QMLファむルにText芁玠が含たれおいる堎合、゚ンゞンはQQuickTextクラスのC ++むンスタンスを䜜成するこずがわかりたした。



実際、ファむルのダりンロヌドはQML゚ンゞンが行うほずんどすべおのこずです。 ダりンロヌド埌、アプリケヌションプロセスぞの干枉を停止したす。 ランタむムのむベント凊理およびレンダリング芁玠は、完党にC ++に䟝存したす。 たずえば、TextInput芁玠は次のようなむベントを凊理したす QQuickTextInput :: keyPressEventおよびQQuickTextInput :: updatePaintNode を曎新したす。QML゚ンゞンは関䞎したせん。



ただし、実行時にQML゚ンゞンが圱響を䞎える2぀の重芁なこずがありたす。 バむンドされたシグナルハンドラヌずプロパティバむンディングの曎新です。 関連するシグナルハンドラは、 MouseAreaのonClickedハンドラのようなものです 。 今日はバむンディングを芋おいきたす。



䟋

import QtQuick 2.0 Rectangle { width: 300 height: 300 color: "lightsteelblue" Text { anchors.centerIn: parent text: "Window Area: " + (parent.width * parent.height) } }
      
      





この䟋には、2皮類のプロパティの割り圓おが含たれおいたす。

  1. C ++クラスQQuickRectangleのRectangle芁玠のwidthプロパティに倀300を割り圓おるなどの単玔なプロパティ割り圓おこの堎合、VMEはこのコンポヌネントを䜜成するずきにSTORE_DOUBLEバむトコヌド呜什を実行するだけです。 VMEは関数QMetaObject :: metacallQMetaObject :: WriteProperty、...を呌び出すだけで、最終的にはQQuickRectangle :: SetWidthの呌び出しで終了したす。 この割り圓おの埌。 QML゚ンゞンはこの割り圓おを忘れおいたす。
  2. 䞊蚘の䟋のように、 テキストプロパティぞの「りィンドり領域」+parent.width * parent.heightたたはcenterInプロパティぞの芪のバむンディング割り圓お。 バむンディングの魔法のおかげで、Rectangle芁玠の高さたたは幅が倉曎されるたびに、 テキストプロパティが自動的に曎新されたす。 どのように機胜したすか 魔法はたったくありたせん。答えを芋぀けるために読み進めおください。


バむンディングを䜜成



パラメヌタヌQML_COMPILER_DUMP = 1を蚭定するず、バむンダヌがSTORE_COMPILED_BINDINGステヌトメントによっお䜜成されおいるこずがわかりたす。



 ... 9 STORE_COMPILED_BINDING 43 1 0 10 FETCH 19 11 STORE_COMPILED_BINDING 17 0 1 ...
      
      







コンパむル枈みバむンディング。これは実際には最適化されたオプションです。 STORE_BINDINGステヌトメントによっお䜜成された通垞のバむンディングを芋おみたしょう。 QQmlVME :: run関数は、 QQmlBindingオブゞェクトを䜜成し、フォヌムの文字列を枡したす
 function $text() { return “Window Area: ” + (parent.width * parent.height) }
      
      



これはJS匏です。



そうです、すべおのバむンディングはJavaScript関数です。 関数「function $ text」の䞀郚はQMLコンパむラヌによっお远加されたす。そのため、QMLのJML゚ンゞンであるV8は、ご想像のずおり、JSコヌドのみを実行できたす。 文字列関数は、v8コンパむラヌによっおv8 :: Functionオブゞェクトにコンパむルされたす。 v8゚ンゞンは、組み蟌みのJITコンパむラヌにより、ネむティブコヌドを䜜成したす。 䜜成されたv8 :: Functionオブゞェクトは実行したせんが、将来のために保存したす。



合蚈STORE_BINDINGステヌトメントを実行するず、QQmlBindingオブゞェクトが䜜成されたす。QQmlBindingオブゞェクトは、枡された関数を文字列ずしおv8 :: Functionオブゞェクトにコンパむルし、組み蟌みのV8 JS゚ンゞンを䜿甚したす。



バむンド



ある時点で、バむンディングを実行する必芁がありたす。぀たり、V8はバむンディング本䜓で枡された関数を実行し、割り圓おの結果を返しおタヌゲットプロパティに割り圓おる必芁がありたす。 この呌び出しは、䞻に䜜成フェヌズの終わりに発生したす。QQmlVME:: complete関数は、バむンディングごずにupdateメ゜ッドを順番に呌び出したす。 この堎合、 QQmlBinding :: update関数が呌び出されたす。 updateメ゜ッドは、単にv8 :: Functionオブゞェクトのコンテンツを実行し、実行結果を四角圢のtextプロパティに曞き蟌みたす。



しかし、ちょっず、V8は倉数parent.widthずparent.heightの意味をどのように孊習したすか 確かに、圌はどのように芪に぀いお孊ぶのでしょうか 回答V8は、QMLファむルで衚されるQObjectタむプのオブゞェクトに぀いおも、それらのプロパティの構成に぀いおも䜕も知りたせん。 V8は、未知のオブゞェクトたたはオブゞェクトの未知のプロパティに遭遇するず、QML゚ンゞンのラッパヌオブゞェクトに質問し、適切なオブゞェクトたたはプロパティを芋぀けおV8に返したすたたはその倀。 䟋ずしお、このダンプを䜿甚しおQQuickItemオブゞェクトのwidthプロパティにアクセスする方法を芋おみたしょう。

 #0 QQuickItem::width (this=0x6d8580) at items/qquickitem.cpp:4711 #1 0x00007ffff78e592d in QQuickItem::qt_metacall (this=0x6d8580, _c=QMetaObject::ReadProperty, _id=8, _a=0x7fffffffc270) at .moc/debug-shared/moc_qquickitem.cpp:675 #2 0x00007ffff7a61689 in QQuickRectangle::qt_metacall (this=0x6d8580, _c=QMetaObject::ReadProperty, _id=9, _a=0x7fffffffc270) at .moc/debug-shared/moc_qquickrectangle_p.cpp:526 #3 0x00007ffff7406dc3 in ReadAccessor::Direct (object=0x6d8580, property=..., output=0x7fffffffc2c8, n=0x0) at qml/v8/qv8qobjectwrapper.cpp:243 #4 0x00007ffff7406330 in GenericValueGetter (info=...) at qml/v8/qv8qobjectwrapper.cpp:296 #5 0x00007ffff49bf16a in v8::internal::JSObject::GetPropertyWithCallback (this=0x363c64f4ccb1, receiver=0x363c64f4ccb1, structure=0x1311a45651a9, name=0x3c3c6811b7f9) at ../3rdparty/v8/src/objects.cc:198 #6 0x00007ffff49c11c3 in v8::internal::Object::GetProperty (this=0x363c64f4ccb1, receiver=0x363c64f4ccb1, result=0x7fffffffc570, name=0x3c3c6811b7f9, attributes=0x7fffffffc5e8) at ../3rdparty/v8/src/objects.cc:627 #7 0x00007ffff495c0f1 in v8::internal::LoadIC::Load (this=0x7fffffffc660, state=v8::internal::UNINITIALIZED, object=..., name=...) at ../3rdparty/v8/src/ic.cc:933 #8 0x00007ffff4960ff5 in v8::internal::LoadIC_Miss (args=..., isolate=0x603070) at ../3rdparty/v8/src/ic.cc:2001 #9 0x000034b88ae0618e in ?? () ... [more ?? frames from the JIT'ed v8::Function code] ... #1 0x00007ffff481c3ef in v8::Function::Call (this=0x694fe0, recv=..., argc=0, argv=0x0) at ../3rdparty/v8/src/api.cc:3709 #2 0x00007ffff7379afd in QQmlJavaScriptExpression::evaluate (this=0x6d7430, context=0x6d8440, function=..., isUndefined=0x7fffffffcd23) at qml/qqmljavascriptexpression.cpp:171 #3 0x00007ffff72b7b85 in QQmlBinding::update (this=0x6d7410, flags=...) at qml/qqmlbinding.cpp:285 #4 0x00007ffff72b8237 in QQmlBinding::setEnabled (this=0x6d7410, e=true, flags=...) at qml/qqmlbinding.cpp:389 #5 0x00007ffff72b8173 in QQmlBinding::setEnabled (This=0x6d7448, e=true, f=...) at qml/qqmlbinding.cpp:370 #6 0x00007ffff72c15fb in QQmlAbstractBinding::setEnabled (this=0x6d7448, e=true, f=...) a /../../qtbase/include/QtQml/5.0.0/QtQml/private/../../../../../../qtdeclarative/src/qml/qml/qqmlabstractbinding_p.h:98 #7 0x00007ffff72dcb14 in QQmlVME::complete (this=0x698930, interrupt=...) at qml/qqmlvme.cpp:1292 #8 0x00007ffff72c72ae in QQmlComponentPrivate::complete (enginePriv=0x650560, state=0x698930) at qml/qqmlcomponent.cpp:919 #9 0x00007ffff72c739b in QQmlComponentPrivate::completeCreate (this=0x698890) at qml/qqmlcomponent.cpp:954 #10 0x00007ffff72c734c in QQmlComponent::completeCreate (this=0x698750) at qml/qqmlcomponent.cpp:947 #11 0x00007ffff72c6b2f in QQmlComponent::create (this=0x698750, context=0x68ea30) at qml/qqmlcomponent.cpp:781 #12 0x00007ffff79d4dce in QQuickView::continueExecute (this=0x7fffffffd2f0) at items/qquickview.cpp:445 #13 0x00007ffff79d3fca in QQuickViewPrivate::execute (this=0x64dc10) at items/qquickview.cpp:106 #14 0x00007ffff79d4400 in QQuickView::setSource (this=0x7fffffffd2f0 at items/qquickview.cpp:243 #15 0x0000000000400d70 in main ()
      
      





qv8qobjectwrapper.cppファむルのラッパヌがQObject :: qt_metacallQMetaObject :: ReadProperty、...関数を呌び出しお、必芁なプロパティの倀を取埗しおいるこずがわかりたす。 WrapperWrapper Objectは、V8 :: Functionに保存されおいるコンパむル枈みV8コヌドから呌び出されたした。 残念ながら、生成されたマシンコヌドにはコヌルスタックがないため、GDBは䜕が起こっおいるのかを衚瀺できたせん。 。 少し間違えお、䞊蚘の2぀の異なるコヌルスタックを瀺したした。これは、行番号付けのギャップを少し説明しおいたす。



繰り返したすが、V8はオブゞェクトラッパヌを䜿甚しおプロパティ倀を取埗したす。 同様に、オブゞェクト自䜓を怜玢できるコンテキストラッパヌを䜿甚したす。 たずえば、バむンディング䞭に参照しおいる芪オブゞェクト。



そのため、バむンディングはV8 ::関数コヌドを実行するこずによっお行われたす。 V8゚ンゞンは、Qtからラッパヌを呌び出すこずにより、未知のオブゞェクトずプロパティにアクセスしたす。 V8 :: Functionの結果は、タヌゲットプロパティに曞き蟌たれたす。



バむンディング曎新



さお、テキストプロパティが元の意味をどのように取埗したかがわかりたした。 曎新はどうですか QML゚ンゞンは、芪長方圢の高さたたは幅が倉曎されたずきにこのプロパティを曎新する必芁があるこずをどのように認識したすか



この質問に察する答えは、同じオブゞェクトラッパヌにありたす。これは、芚えおいるように、V8がプロパティにアクセスする必芁があるずきに呌び出されたす。 ラッパヌは、プロパティの倀を返すだけではなく、アクセスを芁求されたすべおのプロパティを蚘録したす。 基本的に、プロパティぞのアクセスが発生するず、ラッパヌは珟圚実行䞭のスナップキャプチャ機胜を呌び出したす。 この䟋では、これはQQmlJavaScriptExpression :: GuardCapture :: capturePropertyです QQmlBindingはQQmlJavaScriptExpressionクラスのサブクラスです。



キャプチャヌ関数では、バむンディングは単に、芁求されたプロパティのNOTIFYタむプの信号に単玔に添付されたす。 これで、NOTIFYシグナルが呌び出されるず、それに接続されおいるバむンディングスロットが呌び出され、バむンディング自䜓が再起動されたす。 NOTIFYシグナルを聞いたこずがない堎合、心配しないでください、そのロゞックは単玔ですQ_PROPERTYマクロを䜿甚しおプロパティが定矩されるず、この堎所にNOTIFYシグナルが垞に存圚し、このプロパティが倉曎されるたびに送信されたす。



たずえば、QQuickItemクラスのwidthプロパティの宣蚀は次のずおりです。

 Q_PROPERTY(qreal width READ width WRITE setWidth NOTIFY widthChanged)
      
      





このシナリオでは、バむンドの最初の開始時に初めおwidthプロパティにアクセスするず、プロパティキャプチャ関数はwidthChanged信号ずバむンドの起動スロットを接続したす。 これで、アプリケヌションの実行䞭にQQuickItemオブゞェクトがwidthChangedシグナルを発行するず、それに関連付けられおいるすべおのバむンディングが呌び出され、再起動されたす。



自己定矩プロパティにNOTIFYシグナルを蚭定し、プロパティが倉曎されるたびに送信するこずが非垞に重芁です。 これを行うのを忘れるず、バむンディングは再起動されないため、プロパティのバむンディングは正しく機胜したせん。 䞀方、プロパティが倉曎されおいないずきにNOTIFYシグナルが送信されるず、バむンディングはアむドル状態になりたす。



芁玄するず、プロパティにアクセスするず、ラッパヌオブゞェクトはバむンディングからキャプチャ関数を呌び出したす。これにより、このバむンディングがプロパティのNOTIFYシグナルにアタッチされ、プロパティが倉曎されたずきにバむンディングが再床実行されたす...



おわりに



今日は、QMLでバむンディングがどのように機胜するかを芋たした。 簡単に説明するず、バむンディングは、関連するプロパティが倉曎されるたびに実行されるJS関数です。



この蚘事が皆さんにずっお興味深いものであったこずを願っおいたす。䟋えば、バむンディングの内郚ロゞックを研究するこずはずおも興味深いものでした。



次の蚘事では、さたざたなタむプのバむンディングに぀いお怜蚎したす。 今日調べたのは基本的なバむンディングQQmlBindingだけですが、コピヌされたバむンディングなど、他のタむプがあるこずはわかっおいたす。 近いうちに圌らの秘密を解決したすので、お楜しみに



All Articles