Журнал LinuxFormat - перейти на главную

LXF142:QML

Материал из Linuxformat
Перейти к: навигация, поиск
Технология QML Только начинает свое разви­тие, но ее преимущества видны уже сегодня


QML

Qt и QML: Язык ин­тер­фей­са

При­знав прин­цип раз­де­ле­ния движ­ка и ло­ги­ки про­грам­мы, Ан­д­рей Бо­ров­ский на­хо­дит об­щий язык с ин­тер­фей­сом поль­зо­ва­те­ля.


В про­грам­ми­ро­вании из­дав­на жи­вут, взаи­мо­дей­ст­ву­ют и бо­рют­ся друг с дру­гом два под­хо­да: дек­ла­ра­тив­ный (что на­до сде­лать) и им­пе­ра­тив­ный (как сде­лать). А са­мая за­вет­ная меч­та всех раз­ра­бот­чи­ков – су­пер-про­грам­ма, ко­то­рая пре­вра­ща­ла бы дек­ла­ра­тив­ное опи­сание за­да­чи в им­пе­ра­тив­ное (прав­да, ес­ли та­кая про­грам­ма когда-нибудь поя­вит­ся, раз­ра­бот­чи­ки, в со­вре­мен­ном смыс­ле это­го сло­ва, ис­чез­нут: их окон­ча­тель­но за­ме­нят ди­зайнеры).

Про­бле­ма за­клю­ча­ет­ся еще и в том, что те, кто ре­ша­ет дек­ла­ра­тив­ную и им­пе­ра­тив­ную час­ти за­да­чи, за­час­тую го­во­рят на раз­ных язы­ках и мыс­лят раз­ны­ми ка­те­го­рия­ми. Дек­ла­ра­тив­ная часть мо­жет ме­нять­ся как пер­чат­ки (поч­ти в бу­к­валь­ном смыс­ле), а им­пе­ра­тив­ная го­раз­до бо­лее кон­сер­ва­тив­на. На­гляд­ный при­мер то­му – мно­го­чис­лен­ные при­ло­жения баз дан­ных. На им­пе­ра­тив­ном уровне все они де­ла­ют прак­ти­че­­ски од­но и то же – об­менива­ют­ся дан­ны­ми с СУБД, ис­поль­зуя ко­ман­ды и объ­ек­ты язы­ка SQL (я сам ис­поль­зо­вал один и тот же класс взаи­мо­дей­ст­вия с СУБД в со­вер­шен­но раз­ных при­ло­жениях). А вот внешне, с точ­ки зрения поль­зо­ва­те­лей, при­ло­жения БД мо­гут су­ще­ст­вен­но от­ли­чать­ся (бла­го­да­ря че­му так мно­го ко­де­ров и обес­пе­че­ны ра­бо­той). Под внешней ча­стью при­ло­жения я под­ра­зу­ме­ваю не толь­ко то, как оно вы­гля­дит, но и то, как оно се­бя ве­дет – то, что обыч­но на­зы­ва­ют ло­ги­кой ра­бо­ты при­ло­жения.

Рис. 1 Рис. 1. Замена QDial от QML.

Все эти про­стые на­блю­дения, сде­лан­ные не мной и не сей­час, при­ве­ли раз­ра­бот­чи­ков к фор­му­ли­ров­ке очень важ­но­го прин­ци­па: раз­де­ления движ­ка и ло­ги­ки. Под ло­ги­кой в дан­ном слу­чае понима­ет­ся все, что про­грам­ма де­ла­ет. Дви­жок же ре­ша­ет, как имен­но это бу­дет сде­ла­но. К дан­но­му прин­ци­пу близ­ко при­мы­ка­ет дру­гой – раз­де­ление движ­ка и ин­тер­фей­са. Ин­тер­фейс, как уже бы­ло от­ме­че­но, го­раз­до ча­ще под­вер­га­ет­ся из­менениям, чем дви­жок. Кро­ме то­го, поль­зо­ва­те­ли и ад­минист­ра­то­ры хо­тят иметь воз­мож­ность на­страи­вать его са­мо­стоя­тель­но. Хо­ро­шие при­ме­ры раз­де­ления движ­ка и ин­тер­фей­са – гра­фи­че­­ская оберт­ка во­круг кон­соль­ной про­грам­мы Unix или Web-ин­тер­фейс взаи­мо­дей­ст­вия с неким сер­ве­ром. При этом ин­тер­фейс удоб­но опи­сы­вать в тер­ми­нах дек­ла­ра­тив­но­го язы­ка (Web-ин­тер­фей­сы управ­ления – на­гляд­ный то­му при­мер), а дви­жок го­раз­до про­ще на­пи­сать на им­пе­ра­тив­ном язы­ке.

Раз­ра­бот­чи­ки Qt ре­ши­ли, по-ви­ди­мо­му, за­кре­пить прин­ци­пы раз­де­ления движ­ка и ин­тер­фей­са «на за­ко­но­да­тель­ном уровне», за­ста­вив их бу­к­валь­но го­во­рить на раз­ных язы­ках. Ес­ли основ­ным ин­ст­ру­мен­том реа­ли­за­ции движ­ка по-прежнему оста­ет­ся C++ и все биб­лио­те­ки и вспо­мо­га­тель­ные сред­ст­ва Qt library, то для опи­сания ин­тер­фей­са при­ло­жения пред­ло­жен са­мо­стоя­тель­ный язык QML.

Как вы уже по­ня­ли, QML – дек­ла­ра­тив­ный язык. Он по­зво­ля­ет опи­сы­вать, что имен­но дол­жен ви­деть поль­зо­ва­тель на эк­ране. С по­мо­щью QML мож­но опи­сать внешний вид и рас­по­ло­жение эле­мен­тов управ­ления при­ло­жением. Как вам, на­при­мер, идея за­менить стан­дарт­ный су­хо­ва­тый QDial на вот та­кой гла­мур­ный «спи­до­метр» (рис. 1)?

Рис. 2 Рис. 2. Кар­ты — на стол!


Рис. 3 Рис. 3. Про­гресс ин­ди­ка­то­ров про­грес­са.

А еще с по­мо­щью QML мож­но по­иг­рать в так лю­би­мую офис­ным планк­то­ном ко­сын­ку (рис. 2). Или на­сла­дить­ся ин­ди­ка­то­ра­ми вы­полнения за­да­чи, вы­полнен­ны­ми в пас­тель­ных то­нах (рис. 3).

Нет ну­ж­ды го­во­рить о том, что QML кросс-плат­фор­мен так же, как и са­ма Qt. Но на са­мом де­ле, при­ме­ры из ском­пи­ли­ро­ван­но­го сборника Qt соз­да­ют об­ман­чи­вое впе­чат­ление о те­ку­щих воз­мож­но­стях QML. Тех­но­ло­гия все еще да­ле­ка от со­вер­шен­ст­ва, хо­тя кое-что, ра­зу­ме­ет­ся, мож­но сде­лать уже сей­час.

Вернем­ся к опи­санию язы­ка QML. По­ми­мо дек­ла­ра­тив­ных эле­мен­тов, язык вклю­ча­ет свой соб­ст­вен­ный ва­ри­ант ECMAScript (близ­кий к JavaScript) с пол­ным на­бо­ром им­пе­ра­тив­ных эле­мен­тов. Так что на QML мож­но не толь­ко ин­тер­фей­сы, но и пи­сать про­грам­мы, а Qt ис­поль­зо­вать лишь в ка­че­­ст­ве ин­тер­пре­та­то­ра. Соб­ст­вен­но го­во­ря, в со­став но­вей­ших ди­ст­ри­бу­ти­вов Qt уже вхо­дит та­кой ин­тер­пре­та­тор – ути­ли­та qmlviewer, ко­то­рая по­з­во­ляет на­сла­дить­ся все­ми доступ­ны­ми на дан­ный мо­мент воз­мож­но­стя­ми тех­но­ло­гии QML, не на­пи­сав ни строч­ки ко­да на C++. Ес­ли уж го­во­рить на­чис­то­ту, QML весь­ма по­хож на со­вре­мен­ный HTML, с той разницей, что вы­пол­ня­ет­ся он не в брау­зе­ре, а в при­ло­жении Qt, и мо­жет взаи­мо­дей­ст­во­вать с объ­ек­та­ми это­го при­ло­жения. Когда я го­во­рю, что QML по­хож на HTML 5, я имею в ви­ду сход­ст­во функ­цио­наль­ных воз­мож­но­стей, но не син­так­си­са. Вот про­стей­шая про­грам­ма – «Hello World» ми­ра QML (этот при­мер взят из офи­ци­аль­ной до­ку­мен­та­ции Qt):

 import QtQuick 1.0
 Rectangle {
  id: page
  width: 500; height: 200
  color: “lightgray”
  Text {
   id: helloText
   text: “Hello world!”
   y: 30
   anchors.horizontalCenter: page.horizontalCenter
   font.pointSize: 24; font.bold: true
 }

Пре­ж­де чем раз­би­рать при­мер, на­до ска­зать па­ру слов об из­мен­чи­вом син­так­си­се. При пе­ре­хо­де от Qt 4.7 к Qt 4.7.1 син­так­сис QML был несколь­ко пре­об­ра­зо­ван. На­при­мер, в вер­сии 4.7 в на­ча­ле про­грам­мы вме­сто «import QtQuick 1.0» мы долж­ны пи­сать «import Qt 4.7». По­сколь­ку я ве­рю, что из­менения, про­изо­шед­шие при пе­ре­хо­де от Qt 4.7 к Qt 4.7.1, от­ра­жа­ют дальней­шие уст­рем­ления раз­ра­бот­чи­ков, я бу­ду поль­зо­вать­ся син­так­си­сом Qt 4.7.1. А вот в са­мой до­ку­мен­та­ции по Qt все еще мож­но встре­тить при­ме­ры, ис­поль­зую­щие ста­рый син­так­сис. Будь­те вни­ма­тель­ны!

Итак, пер­вая стро­ка про­грам­мы QML

import QtQuick 1.0

ука­зы­ва­ет, где хра­нят­ся оп­ре­де­ления ис­поль­зуе­мых в про­грам­ме струк­тур. Ес­ли вы поль­зуе­тесь Qt 4.7, эта стро­ка долж­на вы­гля­деть ина­че, о чем бы­ло ска­за­но вы­ше. Да­лее сле­ду­ет корневой объ­ект на­шей про­грам­мы. Как и в HTML, и в XML эле­мен­ты QML об­ра­зу­ют ие­рар­хи­че­­ские струк­ту­ры. В об­щем ви­де они имеют такой вид:

ОБЪЕКТ {
 ОБЪЕКТ {
  …
 }
 …
}

В рас­смот­рен­ном вы­ше при­ме­ре объ­ект Rectangle (ко­рень ие­рар­хии) вклю­ча­ет до­черний объ­ект Text, пред­на­зна­чен­ный для вы­во­да тек­ста. Но это не все. По­ми­мо до­черних объ­ек­тов, у эле­мен­тов язы­ка QML (так же как у тэ­гов HTML) име­ют­ся па­ра­мет­ры (их еще на­зы­ва­ют свой­ст­ва­ми). Как вы мо­же­те ви­деть, для эле­мен­та Rectangle за­да­ют­ся та­кие па­ра­мет­ры, как ши­ри­на, вы­со­та и цвет. Кро­ме то­го, у объ­ек­та Rectangle есть па­ра­метр id, ко­то­рый по­зво­ля­ет про­грам­ме QML от­ли­чать один объ­ект Rectangle от дру­го­го. Этот па­ра­метр не яв­ля­ет­ся обя­за­тель­ным, и его мож­но опустить, ес­ли дру­гие час­ти про­грам­мы не ссы­ла­ют­ся на дан­ный кон­крет­ный объ­ект, од­на­ко в на­шем слу­чае он не­об­хо­дим. Рас­смот­рим стро­ку

anchors.horizontalCenter: page.horizontalCenter

ко­то­рая уста­нав­ли­ва­ет го­ри­зон­таль­ное рас­по­ло­жение эле­мен­та Text внут­ри пря­мо­угольника. В об­щем, нетруд­но до­га­дать­ся, что мы хо­тим, что­бы центр (по длине) стро­ки тек­ста сов­па­дал с цен­тром пря­мо­угольника. Вот тут-то нам и тре­бу­ет­ся его ин­ди­ви­ду­аль­ный иден­ти­фи­ка­тор.

Да­лее офи­ци­аль­ный ту­то­ри­ал QML ре­ко­мен­ду­ет со­хранить текст в фай­ле с рас­ши­рением .qml и вы­полнить его в про­грам­ме qmlviewer. Я не бу­ду при­во­дить эк­ран­ный снимок то­го, что вы там уви­ди­те (ско­рее все­го, вы и са­ми уже до­га­да­лись).

На QML мож­но взгля­нуть и ина­че – а имен­но, как на язык сце­на­ри­ев для QGraphicsView. Дей­ст­ви­тель­но, на сто­роне про­грам­мы Qt важ­ную роль в ра­бо­те QML иг­ра­ет класс QDeclarativeView, ко­то­рый яв­ля­ет­ся по­том­ком клас­са QGraphicsView. Та­ким об­ра­зом, мы име­ем раз­ви­тие тех­но­ло­гии Graphics View Framework, про ко­то­рую я пи­сал когда-то, что од­но из ее пред­на­зна­чений – по­строение слож­ных гра­фи­че­­ских ин­тер­фей­сов. QML мож­но ис­поль­зо­вать и для на­пи­сания сце­на­ри­ев для объ­ек­тов QGraphicsScene. QtScript для это­го то­же по­дой­дет, с той разницей, что QtScript на­це­лен на ра­бо­ту с объ­ек­та­ми при­ло­жения Qt, ко­то­рые мо­гут соз­да­вать­ся и унич­то­жать­ся при­ло­жением, тогда как QML в боль­шей сте­пени ори­ен­ти­ро­ван на ра­бо­ту с гра­фи­че­­ски­­ми эле­мен­та­ми, соз­дан­ны­ми в опи­са­тель­ной час­ти са­мо­го язы­ка.

А мож­но ли вы­полнить про­стей­шую про­грам­му на QML из на­шей соб­ст­вен­ной про­грам­мы, на­пи­сан­ной в Qt? Ра­зу­ме­ет­ся! За­помните пра­ви­ло: все, что мо­жет по­ка­зать qmlviewer, мо­жем по­ка­зать и мы, хо­тя иногда для это­го при­дет­ся из­ряд­но по­тру­дить­ся. Во­об­ще на­до от­ме­тить, что на дан­ном эта­пе раз­ра­бот­чи­ки язы­ка QML со­сре­до­то­чи­лись в основ­ном на опи­сании са­мо­го язы­ка, и ре­сур­сов на эту те­му в Се­ти уже пре­доста­точ­но. Во­про­сы взаи­мо­дей­ст­вия QML и Qt осве­ща­ют­ся не так под­роб­но (ско­рее все­го, у раз­ра­бот­чи­ков про­сто не дош­ли ру­ки до это­го). Но мы по­про­бу­ем немно­го ис­пра­вить по­ло­жение дел, ведь глав­ный до­ку­мент – ис­ход­ные тек­сты ин­ст­ру­мен­тов Qt, пред­на­зна­чен­ных для ра­бо­ты с QML – у нас есть!

До­пустим, у нас есть ок­но Dialog, ко­то­рое яв­ля­ет­ся по­том­ком клас­са QDialog. До­ба­вить в него вид­жет, спо­соб­ный ото­бра­зить вы­вод опи­сан­ной вы­ше про­грам­мы QML, не про­сто, а очень про­сто. Вот кон­ст­рук­тор на­ше­го ок­на, в ко­то­ром до­бав­ля­ет­ся вид­жет:

 Dialog::Dialog(QWidget *parent) :
  QDialog(parent),
  ui(new Ui::Dialog)
 {
  ui->setupUi(this);
  QDeclarativeView *qmlView = new QDeclarativeView;
  qmlView->setSource(QUrl::fromLocalFile(«/home/andrei/qml/helloworld.qml»));
  QVBoxLayout *layout = new QVBoxLayout(this);
  layout->addWidget(qmlView);
  qmlView->show();
 }

Как уже бы­ло ска­за­но, про­стей­шее сред­ст­во ото­бра­жения QML в Qt – класс QDeclarativeView, эк­зем­п­ляр ко­то­ро­го мы и соз­да­ем пер­вым де­лом (объ­ект qmlView). Да­лее нам сле­ду­ет за­гру­зить файл про­грам­мы QML в соз­дан­ный объ­ект; для это­го слу­жит ме­тод setSource, ко­то­ро­му ссыл­ка на файл про­грам­мы QML пе­ре­да­ет­ся в ви­де объ­ек­та QUrl. Данный факт на­ме­ка­ет нам на то, что в пер­спек­ти­ве мы бу­дем за­гру­жать QML-оберт­ки на­ших про­грамм Qt из Се­ти. Да­лее вид­жет qmlView до­бав­ля­ет­ся в ок­но обыч­ным для про­грамм­но соз­дан­ных вид­же­тов спо­со­бом. В ре­зуль­та­те в окне на­шей про­грам­мы поя­вит­ся то же, что вы уже ви­де­ли в окне, соз­дан­ном ути­ли­той qmlviewer (ра­зу­ме­ет­ся, путь к фай­лу .qml, про­пи­сан­ный в ар­гу­мен­те ме­то­да setSource(), вам на­до за­менить на свой соб­ст­вен­ный).

Для сбор­ки про­грам­мы, в файл про­ек­та (тот, что с рас­ши­ре­ни­ем .pro) не­об­хо­ди­мо до­ба­вить стро­ку

QT += declarative

Эта стро­ка ука­жет, что в про­ект сле­ду­ет вклю­чить ссыл­ки на биб­лио­те­ки Qt, от­вет­ст­вен­ные за ра­бо­ту с QML. Опи­сание клас­са QDeclarativeView (един­ст­вен­но­го клас­са, пред­на­зна­чен­но­го для QML, ко­то­рый нам по­ка что по­на­до­бил­ся) хранит­ся в за­го­ло­воч­ном фай­ле <QtDeclarative/QDeclarativeView>.

На­ша про­грам­ма де­мон­ст­ри­ру­ет кар­тин­ку, соз­дан­ную с по­мо­щью про­стей­шей про­грам­мы QML, и вы мо­же­те по­экс­пе­ри­мен­ти­ро­вать с ко­дом фай­ла helloworld.qml и убе­дить­ся в том, что да­же су­ще­ст­вен­но из­менен­ные фай­лы бу­дут ото­бра­жать­ся в на­шей про­грам­ме точ­но так же (ина­че го­во­ря, мы дей­ст­ви­тель­но име­ем раз­де­ление движ­ка и ин­тер­фей­са). Од­на­ко пить шам­пан­ское по­ка ра­но, тем бо­лее что этот на­пи­ток пло­хо спо­соб­ст­ву­ет эф­фек­тив­но­му про­грам­ми­ро­ванию. Хо­тя на­ша про­грам­ма уме­ет по­ка­зы­вать кар­тин­ки, соз­дан­ные с по­мо­щью QML (че­ст­но го­во­ря, не все), эти кар­тин­ки и са­ма про­грам­ма никак не взаи­мо­дей­ст­ву­ют ме­ж­ду со­бой, а ведь в таком взаи­мо­дей­ст­вии и за­клю­ча­ет­ся смысл ис­поль­зо­вания QML.

Итак, пой­дем даль­ше. На­ша сле­дую­щая за­да­ча за­клю­ча­ет­ся в том, что­бы на­ла­дить взаи­мо­дей­ст­вие ме­ж­ду ко­дом Qt и QML, а это, в свою оче­редь, удобнее сде­лать, ес­ли код QML ин­те­рак­ти­вен, то есть мо­жет реа­ги­ро­вать на дей­ст­вия поль­зо­ва­те­ля.

Начнем с про­грам­мы на QML, бла­го она не на­мно­го сложнее пре­ды­ду­щей. Этот файл на­зы­ва­ет­ся interactive.qml. Как и все ос­таль­ные фай­лы ис­ход­ни­ков дан­но­го при­ме­ра, вы най­де­те его на дис­ке.

 import QtQuick 1.0
  Rectangle {
    width: 120
    height: 40
    color: palette.background
 
  TextInput {
	 id: ti	
     anchors.centerIn: parent
     color: palette.foreground
     text: “blue”
  }
 
  MouseArea {
     anchors.fill: parent
     onClicked: {
	 palette.background = ti.text;
		 ti.focus = true;
     }
  }
 }

В этой про­грам­ме мы встре­ча­ем два но­вых объ­ек­та QML: MouseArea и TextInput. MouseArea, как и под­ска­зы­ва­ет на­зва­ние – это об­ласть, ко­то­рая реа­ги­ру­ет на со­бы­тия мы­ши. Стро­ка

anchors.fill: parent

в те­ле эле­мен­та MouseArea оз­на­ча­ет, что дан­ный эле­мент дол­жен за­полнить все про­стран­ст­во сво­его ро­ди­тель­ско­го эле­мен­та, в дан­ном слу­чае – эле­мен­та Rectangle. Еще у эле­мен­та MouseArea есть ряд со­бы­тий, по­хо­жих на со­бы­тия объ­ек­та Qt. Об­ра­бот­чик со­бы­тия onClicked вы­зы­ва­ет­ся вся­кий раз, когда поль­зо­ва­тель щел­ка­ет мы­шью в об­лас­ти эле­мен­та MouseArea, то есть, в на­шем слу­чае, в об­лас­ти пря­мо­угольника Rectangle. Пре­ж­де чем раз­би­рать­ся, что де­ла­ет наш об­ра­бот­чик со­бы­тия, рас­смот­рим дру­гой но­вый эле­мент про­грам­мы – объ­ект TextInput. Этот эле­мент пред­на­зна­чен для вво­да стро­ки тек­ста (та­ким об­ра­зом, в на­шей про­грам­ме це­лых два объ­ек­та принима­ют ввод поль­зо­ва­те­ля). У объ­ек­та TextInput есть иден­ти­фи­ка­тор ti, ко­то­рый ис­поль­зу­ет­ся в об­ра­бот­чи­ке со­бы­тия onClicked. Бла­го­да­ря стро­ке

ti.focus = true;

эле­мент TextInput по­лу­ча­ет фо­кус вво­да вся­кий раз, когда мы щел­ка­ем мы­шью в пря­мо­угольнике. Па­ра­метр text эле­мен­та TextInput со­дер­жит вве­ден­ный поль­зо­ва­те­лем текст. Кро­ме то­го, этот па­ра­метр мож­но ис­поль­зо­вать для на­чаль­но­го за­дания тек­ста, ко­то­рый бу­дет ото­бра­жать­ся по умол­чанию. Ос­та­лось изу­чить вол­шеб­ный объ­ект palette, ко­то­рый, су­дя по все­му, управ­ля­ет цве­том пря­мо­угольника (свой­ст­во palette.background) и тек­ста стро­ки вво­да (свой­ст­во palette.foreground).

Из­на­чаль­но эле­мент вво­да со­дер­жит текст «blue», и имен­но это зна­чение бу­дет при­свое­но свой­ст­ву palette.background при щелч­ке мы­шью. В ре­зуль­та­те цвет пря­мо­угольника станет синим. А цвет тек­ста станет жел­тым (до­полнитель­ным к синему до бе­ло­го). Так ра­бо­та­ет объ­ект palette. Ес­ли в стро­ке тек­ста вы вве­де­те yellow, то по­сле щелч­ка мы­шью уви­ди­те, как текст и пря­мо­угольник «по­ме­ня­ют­ся» цве­та­ми. Мо­же­те по­экс­пе­ри­мен­ти­ро­вать и с дру­ги­ми со­че­тания­ми, уч­тя при этом, что нуж­но вво­дить на­звания цве­тов, при­ня­тые в QML. А от­ку­да взял­ся этот объ­ект palette? Он не яв­ля­ет­ся ча­стью QML, а соз­дан qmltest, про­грам­мой Qt, ис­ход­ные тек­сты ко­то­рой вы най­де­те на дис­ке. Глав­ное до­полнение на­шей про­грам­мы Qt по сравнению с пре­ды­ду­щей – класс CustomPalette. Вот его объ­яв­ле­ние (файл palette.h):

 class CustomPalette : public QObject
 {
   Q_OBJECT
   Q_PROPERTY(QColor background READ background WRITE setBackground NOTIFY backgroundChanged)
   Q_PROPERTY(QColor foreground READ foreground WRITE setForeground NOTIFY foregroundChanged)
 public:
   CustomPalette();
   QColor background() const;
   void setBackground(const QColor &c);
   QColor foreground() const;
   void setForeground(const QColor &c);
 signals:
   void foregroundChanged();
   void backgroundChanged();
 private:
   QColor m_background;
   QColor m_foreground;
 };

Как мы ви­дим, этот класс объ­яв­ля­ет два свой­ст­ва: background и foreground. Свой­ст­ва, объ­яв­лен­ные с по­мо­щью мак­ро­са Q_PROPERTY(), не име­ют осо­бо­го смыс­ла для про­грам­мы, на­пи­сан­ной на C++, за­то они очень по­лез­ны, когда объ­ек­ты Qt взаи­мо­дей­ст­ву­ют с ко­дом, на­пи­сан­ном на дру­гих язы­ках, в том чис­ле и на QML.

Что про­ис­хо­дит в про­грам­ме Qt, когда мы при­сваи­ва­ем зна­чение свой­ст­ву background? Вы­зы­ва­ет­ся ме­тод setBackground(), ко­то­рый, как вы мо­же­те убе­дить­ся, оз­на­ко­мив­шись с реа­ли­за­ци­ей клас­са (файл palette.cpp), при­сваи­ва­ет но­вое зна­чение пе­ре­мен­ной m_background; так что при чтении свой­ст­ва background, за ко­то­рое от­ве­ча­ет ме­тод background(), оно бу­дет воз­вра­ще­но. Да­лее ме­тод setBackground() ин­вер­ти­ру­ет по­лу­чен­ный цвет и при­сваи­ва­ет ин­вер­ти­ро­ван­ное зна­чение пе­ре­мен­ной m_foreground, так что те­перь свой­ст­во foreground вернет зна­чение, до­пол­няю­щее background до бе­ло­го.

В кон­це ме­тод setBackground() эми­ти­ру­ет два сиг­на­ла: backgroundChanged() и foregroundChanged(). Об­ра­ти­те внимание, что в са­мой про­грам­ме Qt об­ра­бот­чи­ков для этих сиг­на­лов не пре­ду­смот­ре­но. Де­ло в том, что эти сиг­на­лы пред­на­зна­че­ны для про­грам­мы QML. Они долж­ны опо­вес­тить со­от­вет­ст­вую­щие эле­мен­ты про­грам­мы о том, что цве­та из­менились. Имен­но по­это­му име­на этих сиг­на­лов сто­ят по­сле клю­че­во­го сло­ва NOTIFY мак­ро­са Q_PROPERTY(). Что­бы по­нять, как это ра­бо­та­ет, вернем­ся к опи­санию объ­ек­та Rectangle. Стро­ка

color: palette.background

ука­зы­ва­ет на то, что зна­чение цве­та этот объ­ект дол­жен по­лу­чать из свой­ст­ва palette.background, и, бла­го­да­ря на­ли­чию сиг­на­ла backgroundChanged(), это бу­дет про­ис­хо­дить ав­то­ма­ти­че­­ски вся­кий раз, когда свой­ст­ву palette.background бу­дет при­свое­но но­вое зна­чение. Точ­но так же бу­дет вес­ти се­бя и свой­ст­во palette.foreground.

Об­ра­ти­те внимание так­же на то, что хо­тя в про­грам­ме QML мы ис­поль­зу­ем тек­сто­вые обо­зна­чения цве­тов, свой­ст­ва клас­са CustomPalette опе­ри­ру­ют зна­чения­ми QColor. Впро­чем, класс QColor об­ла­да­ет сред­ст­ва­ми для кон­вер­та­ции тек­сто­вых обо­зна­чений цве­тов в дру­гие их пред­став­ления, так что ниче­го осо­бен­но­го тут нет. Вот, соб­ст­вен­но го­во­ря, и вся ма­гия на­шей про­грам­мы. Ос­та­лось свя­зать объ­ект клас­са CustomPalette с объ­ек­том palette про­грам­мы QML. Для на­ча­ла за­гру­зим про­грам­му в объ­ект qmlView. Тут для нас нет ниче­го но­во­го. Но нам необ­хо­ди­мо пре­доста­вить про­грам­ме QML доступ к объ­ек­ту клас­са CustomPalette.

Для понимания то­го, как это про­ис­хо­дит, необ­хо­ди­мо немно­го уг­лу­бить­ся в ие­рар­хию клас­сов Qt, от­ве­чаю­щих за взаи­мо­дей­ст­вие с QML. Как бы­ло ска­за­но вы­ше, про­грам­ма на QML пред­став­ля­ет со­бой ие­рар­хию объ­ек­тов, та­ких как Rectangle или TextInput. При за­груз­ке про­грам­мы QML в объ­ект QDeclarativeView для ка­ж­до­го объ­ек­та QML соз­да­ет­ся его «пред­ста­ви­тель» – объ­ект QDeclarativeContext. Эти объ­ек­ты об­ра­зу­ют ие­рар­хию, со­от­вет­ст­вую­щую ие­рар­хии объ­ек­тов QML, и пред­на­зна­ча­ют­ся, вы пра­виль­но по­ня­ли, для об­ме­на дан­ны­ми ме­ж­ду про­грам­ма­ми (точнее, ме­ж­ду со­от­вет­ст­вую­щи­ми объ­ек­та­ми) QML и Qt. Корнево­му объ­ек­ту про­грам­мы QML (в на­шем слу­чае – объ­ек­ту Rectangle) со­от­вет­ст­ву­ет объ­ект QDeclarativeContext, ука­за­тель на ко­то­рый воз­вра­ща­ет ме­тод rootContext() объ­ек­та qmlView клас­са QDeclarativeView. Дан­ные, ко­то­рые мы пе­ре­да­ем это­му объ­ек­ту, ста­но­вят­ся ви­ди­мы­ми для корнево­го ком­понен­та про­грам­мы QML и по умол­чанию для всех его до­чер­них ком­по­нен­тов. Та­ким об­ра­зом, строч­ка

qmlView->rootContext()->setContextProperty(“palette”, new CustomPalette);

из фай­ла qmltest.cpp за­да­ет зна­чение свой­ст­ва (па­ра­мет­ра) palette объ­ек­та Rectangle. Ина­че этот па­ра­метр про­сто не имел бы смыс­ла. Об­ра­ти­те внимание, что до­черние эле­мен­ты на­сле­ду­ют свой­ст­во palette. Что­бы свя­зать все вы­ше ска­зан­ное в еди­ное це­лое, рас­смот­рим текст кон­ст­рук­то­ра клас­са qmltest (файл qmltest.cpp, ко­то­рый вы най­де­те на дис­ке):

Рис. 4 Рис. 4. Наш пример взаимодействия Qt и QML.

 qmltest::qmltest(QWidget *parent, Qt::WFlags flags)
	 : QDialog(parent, flags)
 {
	 ui.setupUi(this);
 QDeclarativeView *qmlView = new QDeclarativeView;
 qmlView->setSource(QUrl::fromLocalFile(/home/andrei/qml/interactive.qml));
	 qmlView->rootContext()->setContextProperty(“palette”,new CustomPalette);
 QVBoxLayout *layout = new QVBoxLayout(this);
 layout->addWidget(qmlView);
 }

Не за­будь­те, опять же, сменить путь к фай­лу interactive.qml на свой соб­ст­вен­ный. Ре­зуль­тат про­ил­лю­ст­ри­ро­ван рис. 4.

По­сколь­ку на­ша про­грам­ма косвен­но взаи­мо­дей­ст­ву­ет с объ­ек­том клас­са QDeclarativeContext, в нее необ­хо­ди­мо до­ба­вить за­го­ло­воч­ный файл <QtDeclarative / QDeclarativeContext>.

Тех­но­ло­гия QML толь­ко на­чи­на­ет свое раз­ви­тие, и мно­гие ба­зо­вые ве­щи все еще при­хо­дит­ся вы­пол­нять вруч­ную. Но я еще пом­ню вре­ме­на, когда об ин­тег­ри­ро­ван­ной сре­де ви­зу­аль­ной раз­ра­бот­ки для Qt на­по­до­бие Delphi мож­но бы­ло толь­ко меч­тать, а те­перь мы име­ем Qt Creator, ко­то­рый не толь­ко ни в чем не усту­па­ет Delphi IDE, но, по-мо­ему, пре­восхо­дит ее. Так что по­яв­ление ви­зу­аль­но­го ин­ст­ру­мен­та для ра­бо­ты с QML, я ду­маю, не за го­ра­ми.

Персональные инструменты
купить
подписаться
Яндекс.Метрика