<?xml version="1.0" encoding="utf-8"?>
<?xml-stylesheet type="text/css" href="http://wiki2.linuxformat.ru/skins/common/feed.css?97"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/">
	<channel>
		<title>LXF143:QML - История изменений</title>
		<link>http://wiki2.linuxformat.ru/index.php?title=LXF143:QML&amp;action=history</link>
		<description>История изменений этой страницы в вики</description>
		<language>ru</language>
		<generator>MediaWiki 1.11.1</generator>
		<lastBuildDate>Thu, 14 May 2026 03:13:00 GMT</lastBuildDate>
		<item>
			<title>Crazy Rebel: викификация, оформление, иллюстрация</title>
			<link>http://wiki2.linuxformat.ru/index.php?title=LXF143:QML&amp;diff=14253&amp;oldid=prev</link>
			<description>&lt;p&gt;викификация, оформление, иллюстрация&lt;/p&gt;
&lt;p&gt;&lt;b&gt;Новая статья&lt;/b&gt;&lt;/p&gt;&lt;div&gt;: '''Вид­же­ты QML''' Возь­мем са­мый кра­си­вый и пе­ре­та­щим его в на­шу про­грам­му на ''Qt'' [[Категория:Учебники]]&lt;br /&gt;
&lt;br /&gt;
{{Цикл/QML}}&lt;br /&gt;
&lt;br /&gt;
==''QML'': На служ­бе при­ло­же­ний ''Qt''==&lt;br /&gt;
&lt;br /&gt;
: '''Ан­д­рей Бо­ров­ский''' го­тов по­спо­рить, что ''QML'' стал лю­би­мой иг­руш­кой ко­де­ров ''Qt'', и все их уси­лия на­це­ле­ны на ри­со­ва­ние кра­си­вых вид­же­тов ''QML''.&lt;br /&gt;
&lt;br /&gt;
{{Врезка|Содержание=[[Изображение:LXF143_72_1.jpg|300px|Рис. 1]]  Рис. 1. Ис­ход­ный вид­жет в про­грам­ме ''QML''.|Ширина=300px}}&lt;br /&gt;
&lt;br /&gt;
В про­шлый раз мы по­зна­ко­ми­лись с язы­ком опи­сания ин­тер­фей­са ''QML'' и ис­сле­до­ва­ли взаи­мо­дей­ст­вие объ­ек­тов ''QML'' ме­ж­ду со­бой и с объ­ек­та­ми ''Qt''. На этом уро­ке мы восполь­зу­ем­ся ''QML'' для соз­дания «на­стоя­ще­го» вид­же­та, ко­то­рый мож­но бу­дет ис­поль­зо­вать в про­грам­ме на ''Qt''. Этот вид­жет пред­став­ля­ет со­бой пе­ре­ра­бо­тан­ный при­мер ''QML Dial'' из ди­ст­ри­бу­ти­ва ''Qt'' 4.7.1.&lt;br /&gt;
&lt;br /&gt;
Сра­зу пре­ду­пре­ж­даю, что при­ме­ры из этой ста­тьи бу­дут ра­бо­тать с ''Qt'' 4.7.1, 4.7.2 и, на­де­юсь, с бо­лее поздними вер­сия­ми ''Qt'', но не с бо­лее ранними. Так­же хо­чу на­помнить, что при ра­бо­те с ''QML'' в файл про­ек­та ''Qt'' нуж­но до­ба­вить строч­ку&lt;br /&gt;
&lt;br /&gt;
 QT += declarative&lt;br /&gt;
&lt;br /&gt;
Демо ''QML Dial'' из ди­ст­ри­бу­ти­ва ''Qt'' – это са­мо­стоя­тель­ная про­грам­ма, на­пи­сан­ная на ''QML'' (я уже пи­сал о том, что ''QML'' мож­но ис­поль­зо­вать как са­мо­стоя­тель­ный язык про­грам­ми­ро­вания, на­по­до­бие ''JavaScript''). Ме­ня же, как про­грам­ми­ста ''Qt'', боль­ше ин­те­ре­су­ет при­менение ''QML'' для рас­ши­рения воз­мож­но­стей ин­тер­фей­сов про­грамм ''Qt''. Вот та­кое рас­ши­рение мы се­го­дня и на­пи­шем, а за­од­но по­зна­ко­мим­ся с но­вы­ми воз­мож­но­стя­ми ''Qt'' и неко­то­ры­ми но­вы­ми ме­то­да­ми взаи­мо­дей­ст­вия ''Qt'' и ''QML''. Сам объ­ект ''QML Dial'' пред­став­ля­ет со­бой поч­ти фо­то­реа­ли­стич­ную ими­та­цию стре­лоч­но­го ин­ди­ка­то­ра, та­ко­го как спи­до­метр или ин­ди­ка­тор дав­ления жид­ко­сти в тру­бе (рис. 1). Мы не толь­ко «пе­ре­та­щим» этот ин­ди­ка­тор в свою про­грам­му ''Qt'', но и до­полним его неко­то­ры­ми эле­мен­та­ми (рис. 2).&lt;br /&gt;
&lt;br /&gt;
Как мы уже зна­ем, важней­шей за­да­чей ''QML'' яв­ля­ет­ся соз­дание кра­си­вых гра­фи­че­­ских ин­тер­фей­сов, а это, как вы понимае­те, невоз­мож­но без мощ­ных средств ра­бо­ты с рас­тро­вой гра­фи­кой (для про­грам­ми­ста ри­со­вать кра­си­вые ин­тер­фей­сы внут­ри­про­грамм­но, век­тор­ны­ми функ­ция­ми, несколь­ко уто­ми­тель­но). Для ра­бо­ты с изо­бра­жения­ми, хранимы­ми в фай­лах, в язы­ке ''QML'' есть объ­ект '''Image{}'''. Этот объ­ект по­до­бен тэ­гу '''HTML &amp;lt;img…&amp;gt;''', за ис­клю­чением то­го, что он мо­жет го­раз­до боль­ше. В про­стей­шем ва­ри­ан­те ис­поль­зо­вание объ­ек­та '''Image{}''' вы­гля­дит так:&lt;br /&gt;
&lt;br /&gt;
 Image { source: “background.png” }&lt;br /&gt;
&lt;br /&gt;
Эта кон­ст­рук­ция за­гру­жа­ет фон на­ше­го ин­ди­ка­то­ра (опи­сание внешнего ви­да ин­ди­ка­то­ра хранит­ся в фай­ле '''Dial.qml''', рас­по­ло­жен­ном в ди­рек­то­рии '''Dial''' и со­пут­ст­вую­щих фай­лах с рас­ши­рением '''png'''.) На пер­вый взгляд тут все по­нят­но без осо­бых по­яснений. Мы за­гру­жа­ем изо­бра­жение из фай­ла '''background.png'''... Стоп. А ку­да мы его за­гру­жа­ем? Где оно бу­дет рас­по­ло­же­но? Вспомним, что ви­зу­аль­ная часть ''QML'' осно­ва­на на ''Qt Graphics View Framework'', а эта сис­те­ма по умол­чанию стре­мит­ся рас­по­ло­жить гра­фи­че­­ские эле­мен­ты так, что­бы гео­мет­ри­че­­ский центр сце­ны сов­па­дал с цен­тром ок­на вы­во­да. Так что по умол­чанию изо­бра­жение '''background.png''' про­сто зай­мет ме­сто в цен­тре ок­на на­ше­го вид­же­та (что нам и тре­бу­ет­ся).&lt;br /&gt;
&lt;br /&gt;
{{Врезка|Содержание=[[Изображение:LXF143_73_1.jpg|300px|Рис. 2]] Рис. 2. Вид­жет на его ос­но­ве в на­шей про­грам­ме ''Qt''.|Ширина=300px}}&lt;br /&gt;
&lt;br /&gt;
По­ми­мо соб­ст­вен­ных свойств, к ка­ко­вым от­но­сит­ся, на­при­мер, свой­ст­во '''source''', объ­ект '''Image{}''' унас­ле­до­вал от ба­зо­вых объ­ек­тов ''QML'' та­кие свой­ст­ва как '''x, y, scale, rotation, transform, anchors''' и мно­гие дру­гие. Сам дви­жок, вы­пол­няю­щий от­ри­сов­ку изо­бра­жения, об­ла­да­ет мно­ги­ми по­лез­ны­ми воз­мож­но­стя­ми. На­при­мер, ес­ли за­гру­жае­мый фор­мат под­дер­жи­ва­ет аль­фа-ка­нал, то при вы­во­де изо­бра­жения учи­ты­ва­ет­ся уро­вень про­зрач­но­сти его эле­мен­тов.&lt;br /&gt;
&lt;br /&gt;
Рас­смот­рим неко­то­рые свой­ст­ва объ­ек­та '''Image{}'''. Ес­ли мы хо­тим вы­ло­жить сце­ну фо­но­вым ри­сун­ком как плит­кой, на­до до­ба­вить свой­ст­во&lt;br /&gt;
&lt;br /&gt;
 fillMode: Image.Tile&lt;br /&gt;
&lt;br /&gt;
Свой­ст­ва '''scale, rotation''' и '''transform''', как вы уже до­га­да­лись, по­зво­ля­ют вы­пол­нять пре­об­ра­зо­вания изо­бра­жения, та­кие как мас­шта­би­ро­вание и вра­щение. Вот как, на­при­мер, вы­пол­ня­ет­ся вра­щение тени под стрел­кой на­ше­го ин­ди­ка­то­ра:&lt;br /&gt;
&lt;br /&gt;
 transform: Rotation {&lt;br /&gt;
   origin.x: 9; origin.y: 67&lt;br /&gt;
   angle: needleRotation.angle&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
Вся эта кон­ст­рук­ция на­хо­дит­ся в те­ле объ­ек­та '''image''', от­ве­чаю­ще­го за вы­вод тени под стрел­кой (у нас ведь поч­ти фо­то­реа­лизм, так что тень долж­на дви­гать­ся вме­сте со стрел­кой). В объ­ек­те '''Rotation''' мы за­да­ем ко­ор­ди­на­ты цен­тра вра­щения и угол по­во­ро­та (ес­ли кто не по­нял, в этом чу­дес­ном язы­ке двое­то­чие яв­ля­ет­ся опе­ра­то­ром при­сваи­вания).&lt;br /&gt;
&lt;br /&gt;
А как вы­пол­ня­ет­ся вра­щение са­мой стрел­ки? Тут все еще ин­те­реснее. Вот как вы­гля­дит опи­сание стрел­ки:&lt;br /&gt;
&lt;br /&gt;
 Image {&lt;br /&gt;
  id: needle&lt;br /&gt;
  x: 98; y: 33&lt;br /&gt;
  smooth: true&lt;br /&gt;
  source: “needle.png”&lt;br /&gt;
  transform: Rotation {&lt;br /&gt;
    id: needleRotation&lt;br /&gt;
    origin.x: 5; origin.y: 65&lt;br /&gt;
    //! [needle angle]&lt;br /&gt;
	  angle: root.angle&lt;br /&gt;
    Behavior on angle {&lt;br /&gt;
      SpringAnimation {&lt;br /&gt;
        spring: 1.4&lt;br /&gt;
        damping: .15&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
    //! [needle angle]&lt;br /&gt;
  }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
Боль­шая часть это­го опи­сания долж­на быть вам уже по­нят­на. Мы уста­нав­ли­ва­ем ко­ор­ди­на­ты стрел­ки; при­своение зна­чения '''true''' свой­ст­ву '''smooth''' при­во­дит к то­му, что при вы­полнении пре­об­ра­зо­ваний изо­бра­жения стрел­ки (в на­шем слу­чае это вра­щение) вы­пол­ня­ет­ся спе­ци­аль­ная фильт­рация, сгла­жи­ваю­щая эф­фек­ты «ле­сен­ки», ко­то­рые мо­гут в хо­де этих пре­об­ра­зо­ваний возник­нуть. Сравните ко­ор­ди­на­ты стрел­ки и ко­ор­ди­на­ты тени. Ме­ж­ду про­чим, по­сколь­ку на изо­бра­жении (файл '''needle.png''') стрел­ка смот­рит вверх, наш ин­ди­ка­тор по умол­чанию ука­зы­ва­ет на зна­чение в се­ре­дине диа­па­зо­на. Мож­но бы­ло бы из­менить это, по­вер­нув стрел­ку на кар­тин­ке или вы­полнив необ­хо­ди­мое вра­щение при инициа­ли­за­ции, но мы оста­вим все как есть, по­то­му что так ин­те­реснее.&lt;br /&gt;
&lt;br /&gt;
Опи­сание объ­ек­та '''Rotation''' на­чи­на­ет­ся так же, как и в слу­чае с те­нью, од­на­ко даль­ше сле­ду­ет стран­ное. Кон­ст­рук­ция '''Behavior on angle {}''' ука­зы­ва­ет, что долж­на де­лать стрел­ка, когда ее угол по­во­ро­та ме­ня­ет­ся. Объ­ект '''SpringAnimation''' соз­да­ет спе­ци­аль­ный анима­ци­он­ный эф­фект при дви­жении стрел­ки – эф­фект за­ту­хаю­щих ко­ле­баний. Свой­ст­во '''spring''' ука­зы­ва­ет, на­сколь­ко ве­ли­ка долж­на быть из­на­чаль­ная ам­пли­ту­да ко­ле­баний, а свой­ст­во '''damping''' оп­ре­де­ля­ет ско­рость их за­ту­хания. В ре­зуль­та­те стрел­ка на­ше­го ин­ди­ка­то­ра бу­дет колебать­ся как на­стоя­щая стрел­ка на пру­жине.&lt;br /&gt;
&lt;br /&gt;
С по­мо­щью свой­ст­ва '''visible''', ко­то­рым об­ла­да­ют все объ­ек­ты ''QML'', в том чис­ле и объ­ект '''image''', мы мо­жем управ­лять ви­ди­мо­стью этих объ­ек­тов.&lt;br /&gt;
&lt;br /&gt;
Воз­мож­но, вас удив­ля­ет, что свой­ст­во '''angle''' объ­ек­та '''needleRotation''' ме­ня­ет­ся вся­кий раз, когда ме­ня­ет­ся свой­ст­во '''root.angle'''. В та­ких язы­ках, как ''C++'', од­на опе­ра­ция при­сваи­вания оз­на­ча­ет од­но из­менение зна­чения; но в ''QML'', по­хо­же, опе­ра­ция при­сваи­вания про­дол­жа­ет ра­бо­тать по­сто­ян­но, и из­менение зна­чения свой­ст­ва в пра­вой час­ти вы­ра­жения при­сваи­вания при­во­дит к из­менению зна­чения свой­ст­ва в ле­вой час­ти, когда бы оно ни слу­чи­лось. И это дей­ст­ви­тель­но так. В ''QML'' этот ме­ханизм на­зван свя­зы­ванием свойств ['''property binding''']. Стро­ка&lt;br /&gt;
&lt;br /&gt;
 angle: root.angle&lt;br /&gt;
&lt;br /&gt;
свя­зы­ва­ет ме­ж­ду со­бой свой­ст­ва '''root.angle''' и '''angle''', так что из­менение зна­чения пер­во­го свой­ст­ва всегда бу­дет при­во­дить к из­менению зна­чения вто­ро­го. Свя­зы­вание свойств – это осо­бая фор­ма при­сваи­вания, ко­то­рая ис­поль­зу­ет­ся всегда, когда сле­ва от опе­ра­то­ра при­сваи­вания ука­за­но свой­ст­во объ­ек­та, а спра­ва – лю­бое син­так­си­че­­ски кор­рект­ное вы­ра­жение язы­ка ''JavaScript'' (свой­ст­во, функ­ция и т. д). То есть фак­ти­че­­ски свя­зы­вание свойств яв­ля­ет­ся стан­дарт­ным ме­то­дом при­сваи­вания в ''QML'', при ко­то­ром свой­ст­во, стоя­щее сле­ва от опе­ра­то­ра при­сваи­вания, ста­но­вит­ся псев­донимом вы­ра­жения, стоя­ще­го спра­ва (вот по­че­му свя­зы­вать мож­но не толь­ко свой­ст­ва, но и та­кие «пас­сив­ные» объ­ек­ты, как функ­ции, ко­то­рые са­ми не мо­гут ниче­го иниции­ро­вать). Свя­зы­вание свойств – это тот ме­ханизм, ко­то­рый по­зво­ля­ет объ­ек­там ''QML'' об­менивать­ся дан­ны­ми друг с дру­гом, по­доб­но то­му, как объ­ек­ты ''Qt'' об­менива­ют­ся дан­ны­ми друг с дру­гом с по­мо­щью сиг­на­лов и сло­тов. Точ­но так же, как в слу­чае сиг­на­лов и сло­тов, свя­зы­вание уже свя­зан­ных свойств мож­но из­менить с по­мо­щью эле­мен­та ''QML'' '''PropertyChanges {}'''. Мне очень нра­вит­ся свя­зы­вание свойств, и ес­ли бы я про­ек­ти­ро­вал но­вый объ­ект­но-ори­ен­ти­ро­ван­ный язык про­грам­ми­ро­вания, то обя­за­тель­но вклю­чил бы в него этот ме­ханизм.&lt;br /&gt;
&lt;br /&gt;
Даль­ше в ис­ход­ном при­ме­ре на осно­вание стрел­ки на­кла­ды­ва­ют­ся кол­па­чок и стек­ло с бли­ком, изо­бра­жения ко­то­рых хра­нят­ся в фай­ле '''overlay.png'''. Об­ра­ти­те внимание, что прак­ти­че­­ски вез­де мы ис­поль­зу­ем спо­соб­ность фор­ма­та PNG соз­да­вать час­тич­но про­зрач­ные изо­бра­жения, ина­че та­кой слож­ный эле­мент управ­ления у нас про­сто не по­лу­чил­ся бы. Об­ра­ти­те внимание так­же на то, что по­ря­док рас­по­ло­жения изо­бра­жений по­верх друг дру­га со­от­вет­ст­ву­ет по­ряд­ку сле­до­вания пред­став­ляю­щих их объ­ек­тов в тек­сте про­грам­мы ''QML''. В об­щем-то это ес­те­ст­вен­но, ес­ли учесть, что вид­жет соз­да­ет­ся по ме­ре вы­полнения про­грам­мы.&lt;br /&gt;
&lt;br /&gt;
Ко все­му это­му ве­ли­ко­ле­пию я до­ба­вил со­всем не­мно­го.&lt;br /&gt;
&lt;br /&gt;
С объ­ек­том '''Rectangle''' мы уже встре­ча­лись:&lt;br /&gt;
&lt;br /&gt;
 Rectangle {&lt;br /&gt;
 x: 61&lt;br /&gt;
 y: 118&lt;br /&gt;
 width: 80&lt;br /&gt;
 height: 36&lt;br /&gt;
 color: “black”&lt;br /&gt;
 border.color: “#888888”&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
Но­вое здесь – свой­ст­во '''border.color''', по­зво­ляю­щее за­дать цвет границы пря­мо­угольника. Объ­ект '''Text''' дуб­ли­ру­ет зна­чение, ко­то­рое ука­зы­ва­ет ин­ди­ка­тор.&lt;br /&gt;
&lt;br /&gt;
 Text {&lt;br /&gt;
 color: “green”&lt;br /&gt;
 text: root.angle/2 + 50&lt;br /&gt;
 x: 80&lt;br /&gt;
 y: 114&lt;br /&gt;
 font.pointSize: 24; font.bold: true&lt;br /&gt;
 style: Text.Raised&lt;br /&gt;
 styleColor: “black”&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
Здесь я хо­тел бы об­ра­тить ва­ше внимание толь­ко на один мо­мент. По­сколь­ку по умол­чанию стрел­ка ин­ди­ка­то­ра смот­рит вверх, фак­ти­че­­ские уг­лы по­во­ро­та сле­ду­ет ука­зы­вать от­но­си­тель­но это­го по­ло­жения, при­чем по­вер­ну­тая на неко­то­рый угол стрел­ка при но­вом по­во­ро­те ин­тер­пре­ти­ру­ет но­вый угол так, как ес­ли бы она на­хо­ди­лась в ис­ход­ном по­ло­жении.&lt;br /&gt;
&lt;br /&gt;
===Соз­да­ем вид­жет===&lt;br /&gt;
&lt;br /&gt;
Пе­рей­дем ко вто­рой час­ти на­шей ра­бо­ты – пре­вра­щению мо­ду­ля ''QML'' в вид­жет ''Qt''. Необ­хо­ди­мые для это­го основ­ные опе­ра­ции мы и­зучи­ли в про­шлый раз. Но и те­перь нам есть что до­ба­вить. Пред­ста­ви­те­лем вид­же­та ''QML'' в на­шей про­грам­ме ''Qt'' яв­ля­ет­ся класс '''Dial''' (фай­лы '''Dial.h, Dial.cpp'''). Здесь я при­ве­ду толь­ко объ­яв­ление клас­са, осталь­ное вы най­де­те на дис­ке.&lt;br /&gt;
&lt;br /&gt;
 class Dial : public QObject&lt;br /&gt;
 {&lt;br /&gt;
  Q_OBJECT&lt;br /&gt;
  Q_PROPERTY(int angle READ angle WRITE setAngle NOTIFY angleChanged)&lt;br /&gt;
 public:&lt;br /&gt;
  Dial();&lt;br /&gt;
  int angle();&lt;br /&gt;
  void setAngle(int a);&lt;br /&gt;
 signals:&lt;br /&gt;
  void angleChanged();&lt;br /&gt;
  private:&lt;br /&gt;
  int m_angle;&lt;br /&gt;
 };&lt;br /&gt;
&lt;br /&gt;
Тем, кто чи­тал пре­ды­ду­щую ста­тью и зна­ет ''Qt'', тут все долж­но быть по­нят­но. Класс экс­пор­ти­ру­ет един­ст­вен­ное свой­ст­во '''angle'''. На­пом­ню толь­ко, что ме­тод '''setAngle()''' дол­жен яв­ным об­ра­зом эми­ти­ро­вать сиг­нал '''angleChanged()''', ина­че вид­жет ''QML'' никогда не уз­на­ет, что угол из­менил­ся. Зна­то­ки сиг­на­лов и сло­тов ''Qt'' мо­гут спро­сить, по­че­му в сиг­на­ле '''angleChanged()''' не пе­ре­да­ет­ся но­вое зна­чение уг­ла по­во­ро­та. Ес­ли бы сиг­нал пред­на­зна­чал­ся для дру­гих клас­сов ''Qt'', я бы так и сде­лал, но сиг­нал пред­на­зна­чен для вид­же­та ''QML'', а со­от­вет­ст­вую­щий объ­ект это­го вид­же­та все рав­но про­чи­та­ет зна­чение свой­ст­ва '''angle''' с по­мо­щью ме­то­да '''angle()''' (ука­зан­но­го по­сле клю­че­во­го сло­ва '''READ''' в мак­ро­се '''Q_PROPERTY'''). Так что пе­ре­да­вать ка­кой-ли­бо па­ра­метр в сиг­на­ле ''Qt'' про­сто нет нужды.&lt;br /&gt;
&lt;br /&gt;
===Вид­жет в окне про­грам­мы===&lt;br /&gt;
&lt;br /&gt;
Соз­дание вид­же­та в окне ''Qt'' то­же не пред­став­ля­ет со­бой ниче­го осо­бен­но но­во­го, за ис­клю­чением од­но­го мо­мен­та, о ко­то­ром бу­дет ска­за­но ниже. Вот как мы соз­да­ем вид­жет (это фраг­мент фай­ла '''dialcontrol.cpp'''):&lt;br /&gt;
&lt;br /&gt;
 QDeclarativeView *qmlView = new QDeclarativeView;&lt;br /&gt;
 dial = new Dial();&lt;br /&gt;
 qmlView-&amp;gt;rootContext()-&amp;gt;setContextProperty(“Dial”, dial);&lt;br /&gt;
 qmlView-&amp;gt;setSource(QUrl(“qrc:/Dial/Dial.qml”));&lt;br /&gt;
 QVBoxLayout *layout = new QVBoxLayout(this);&lt;br /&gt;
 layout-&amp;gt;addWidget(qmlView);&lt;br /&gt;
&lt;br /&gt;
Вы, на­вер­ное, сра­зу об­ра­ти­ли внимание на то, ка­кую ссыл­ку мы ис­поль­зу­ем для за­груз­ки ис­ход­но­го тек­ста мо­ду­ля ''QML''. В про­шлый раз мы ис­поль­зо­ва­ли ссыл­ку на файл Linux. Это да­ва­ло нам ог­ром­ную сво­бо­ду в плане мо­ди­фи­ка­ции внешнего ви­да ок­на на­шей про­грам­мы, но де­ла­ло ее за­ви­си­мой от рас­по­ло­жения фай­лов ''QML''. Те­перь мы по­сту­па­ем ина­че и вклю­ча­ем файл '''Dial.qml''' и все со­пут­ст­вую­щие ему фай­лы в мо­дуль ре­сур­сов ''Qt''.&lt;br /&gt;
&lt;br /&gt;
В ре­зуль­та­те опи­сание вид­же­та ''QML'' станет ча­стью про­грам­мы ''Qt'', и нам уже не при­дет­ся бес­по­ко­ить­ся о том, где хра­нят­ся со­от­вет­ст­вую­щие фай­лы. Обыч­но в мо­ду­ли ре­сур­сов вклю­ча­ют пик­то­грам­мы и эле­мен­ты ин­тер­на­цио­на­ли­за­ции при­ло­жения, но ничто не ме­ша­ет нам вклю­чить в них мо­ду­ли ''QML'', тем бо­лее что дви­жок ''Qt QML'' уме­ет ра­бо­тать с про­стран­ст­вом ре­сур­сов при­ло­жения (и с дру­ги­ми про­стран­ст­ва­ми URL) как с локаль­ной фай­ло­вой сис­те­мой. Так что ес­ли вклю­чен­но­му в мо­дуль ре­сур­сов мо­ду­лю ''QML'' по­на­до­бит­ся файл, то­же вклю­чен­ный в мо­дуль ре­сур­сов, мо­дуль ''QML'' смо­жет без про­блем за­гру­зить его. Глав­ное, что­бы ссыл­ки на фай­лы бы­ли от­но­си­тель­ны­ми, а не аб­со­лют­ны­ми.&lt;br /&gt;
&lt;br /&gt;
Что­бы пре­вра­тить мо­дуль ''QML'' в ре­сурс про­грам­мы ''Qt'', нам по­на­до­бит­ся файл опи­сания ре­сур­сов '''*.qrc'''. Ра­бо­тать с фай­ла­ми с рас­ши­рением '''qrc''' мож­но мно­ги­ми спо­со­ба­ми – с по­мо­щью ди­зайнера гра­фи­че­­ских ин­тер­фей­сов ''Qt'' или с по­мо­щью про­грам­мы ''Qt Creator'', но мож­но обой­тись и тек­сто­вым ре­дак­то­ром, ведь фор­мат '''QRC''' осно­ван на '''XML'''. Вот как, на­при­мер, вы­гля­дит файл '''QRC''' для на­шей про­грам­мы (файл '''dialcontrol.qrc'''):&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;!DOCTYPE RCC&amp;gt;&amp;lt;RCC version=”1.0”&amp;gt;&lt;br /&gt;
 &amp;lt;qresource&amp;gt;&lt;br /&gt;
 &amp;lt;file&amp;gt;Dial/background.png&amp;lt;/file&amp;gt;&lt;br /&gt;
 &amp;lt;file&amp;gt;Dial/Dial.qml&amp;lt;/file&amp;gt;&lt;br /&gt;
 &amp;lt;file&amp;gt;Dial/DialControl.qrc&amp;lt;/file&amp;gt;&lt;br /&gt;
 &amp;lt;file&amp;gt;Dial/needle.png&amp;lt;/file&amp;gt;&lt;br /&gt;
 &amp;lt;file&amp;gt;Dial/needle_shadow.png&amp;lt;/file&amp;gt;&lt;br /&gt;
 &amp;lt;file&amp;gt;Dial/overlay.png&amp;lt;/file&amp;gt;&lt;br /&gt;
 &amp;lt;/qresource&amp;gt;&lt;br /&gt;
 &amp;lt;/RCC&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Внут­ри тэ­га '''&amp;lt;file&amp;gt;''' мы про­сто ука­зы­ва­ем путь к фай­лу ре­сур­са от­но­си­тель­но рас­по­ло­жения фай­ла '''QRC'''. Те­перь при сбор­ке при­ло­жения вид­жет ''QML'' бу­дет вклю­чен в на­шу про­грам­му. В ре­зуль­та­те на­ша про­грам­ма боль­ше не за­ви­сит от рас­по­ло­жения фай­лов ''QML'', но хо­ро­шо это или пло­хо?&lt;br /&gt;
&lt;br /&gt;
С од­ной сто­ро­ны, это уп­ро­ща­ет уста­нов­ку про­грам­мы: вам не нуж­но ду­мать о том, где долж­ны быть раз­ме­ще­ны фай­лы ''QML'' в со­от­вет­ст­вии со стан­дар­том '''XDG''', а ес­ли вы пи­ше­те кросс-плат­фор­мен­ное при­ло­жение, ва­ша жизнь уп­ро­ща­ет­ся еще боль­ше. С дру­гой сто­ро­ны, при та­ком под­хо­де те­ря­ет­ся од­но из важней­ших пре­иму­ществ ''QML'' как сред­ст­ва опи­сания ин­тер­фей­сов про­грамм ''Qt'': воз­мож­ность ра­дикаль­но сменить ин­тер­фейс без по­втор­ной сбор­ки при­ло­жения.&lt;br /&gt;
&lt;br /&gt;
Су­ще­ст­ву­ет еще сво­его ро­да ком­про­мисс­ный ва­ри­ант: ском­пи­ли­ро­вать вид­жет ''QML'' как внешний двоич­ный ре­сурс про­грам­мы ''Qt'' (для это­го слу­жит ути­ли­та ''rcc''). С од­ной сто­ро­ны, ра­бо­тать с внешним фай­лом ре­сур­са в про­грам­ме ''Qt'' не на­мно­го сложнее, чем со встро­ен­ным; с дру­гой сто­ро­ны, внешний файл ре­сур­са мо­жет быть за­менен без по­втор­ной сбор­ки про­грам­мы. Од­на­ко и у это­го под­хо­да есть свой ми­нус: ди­зайнеру ин­тер­фей­сов при­дет­ся иметь де­ло со спе­ци­аль­ны­ми ин­ст­ру­мен­та­ми ''Qt'', та­ки­ми как ''rcc'', тогда как в слу­чае рас­по­ло­жения вид­же­та ''QML'' це­ли­ком в соб­ст­вен­ных фай­лах ди­зайнеру для из­менения ин­тер­фей­са бу­дет доста­точ­но тек­сто­во­го ре­дак­то­ра и ре­дак­то­ра ''GIMP'' (или, на ху­дой конец, ''Photoshop''). В об­щем, наи­бо­лее ра­зум­ный вы­бор за­ви­сит от кон­крет­ных це­лей – хо­ти­те ли вы, что­бы ка­ж­дый поль­зо­ва­тель, осво­ив­ший ''QML'' и рас­тро­вую гра­фи­ку, мог «сшить но­вую одеж­ку» для ва­шей про­грам­мы, или нет.&lt;br /&gt;
&lt;br /&gt;
===Еще о ме­то­де setContextProperty()===&lt;br /&gt;
&lt;br /&gt;
До сих пор мы ис­поль­зо­ва­ли ме­тод '''setContextProperty()''' для пе­ре­да­чи ука­за­те­ля на соз­дан­ные на­ми объ­ек­ты со свой­ст­ва­ми. Но его воз­мож­но­сти го­раз­до ши­ре. Пре­ж­де все­го, от­ме­тим, что су­ще­ст­ву­ет и дру­гой ва­ри­ант ме­то­да '''setContextProperty()''':&lt;br /&gt;
&lt;br /&gt;
 void	 setContextProperty ( const QString &amp;amp; name, const QVariant &amp;amp; value )&lt;br /&gt;
&lt;br /&gt;
То есть, со свой­ст­вом кон­тек­ста ''QML'' мож­но свя­зать не толь­ко объ­ект, но и лю­бое зна­че­ние, при­во­ди­мое к ти­пу''' QVariant'''. На­при­мер:&lt;br /&gt;
&lt;br /&gt;
 qmlView-&amp;gt;rootContext()-&amp;gt;setContextProperty(“HelloText”, trUtf8(“Hello World!”);&lt;br /&gt;
&lt;br /&gt;
Объ­ек­ты, ко­то­рые мы пе­ре­да­ем кон­тек­стам, мо­гут экс­пор­ти­ро­вать в ''QML'' не толь­ко свой­ст­ва, но и ме­то­ды. Что­бы мо­дуль ''QML'' уви­дел ме­тод объ­ек­та, этот ме­тод дол­жен быть объ­яв­лен в раз­де­ле '''public slots'''. На­при­мер, ес­лидо­ба­вить в класс '''Dial''' ме­тод &lt;br /&gt;
&lt;br /&gt;
 public slots:&lt;br /&gt;
 int angleToPos(int angle)&lt;br /&gt;
 {&lt;br /&gt;
 …&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
то в фай­ле '''Dial.qml''' мож­но на­пи­сать так:&lt;br /&gt;
&lt;br /&gt;
 property int angleToPos : dial.angleToPos(angle)&lt;br /&gt;
&lt;br /&gt;
===Сбор ин­фор­ма­ции об ошиб­ках===&lt;br /&gt;
&lt;br /&gt;
По­сколь­ку на­ша слож­ная кон­ст­рук­ция ''QML'' мо­жет ну­ж­дать­ся в от­лад­ке, в про­грам­ме пре­ду­смот­рен спо­соб вы­во­да ин­фор­ма­ции об ошиб­ках, ко­то­рые возника­ют в хо­де вы­полнения про­грам­мы на ''QML''. Во­об­ще-то в ОС Linux все со­об­щения об ошиб­ках ''QML'' вы­во­дят­ся в стан­дарт­ный по­ток вы­во­да той кон­со­ли, с ко­то­рой бы­ла за­пу­ще­на про­грам­ма. В ОС Windows ''Qt'' это­го по­че­му-то не де­ла­ет: да­же ес­ли за­пустить про­грам­му из ок­на команд­ной стро­ки Windows, ника­ких со­общений об ошиб­ках мы не уви­дим (да­же ес­ли ошиб­ки есть). В лю­бом слу­чае, на­ше при­ло­жение гра­фи­че­­ское, и его не обя­за­тель­но бу­дут за­пускать из ок­на кон­со­ли, так что непло­хо об­за­вес­тись соб­ст­вен­ным ме­то­дом вы­во­да ин­фор­ма­ции об ошиб­ках, осно­ван­ном на гра­фи­че­­ском ин­тер­фей­се.&lt;br /&gt;
&lt;br /&gt;
Спи­сок ак­ту­аль­ных оши­бок воз­вра­ща­ет­ся ме­то­дом '''errors()''' ''QML''-вид­же­та в ви­де объ­ек­та&lt;br /&gt;
&lt;br /&gt;
 QList&amp;lt;QDeclarativeError&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
Объ­ект клас­са '''QDeclarativeError''' вклю­ча­ет несколь­ко свойств и ме­то­дов, из ко­то­рых мы восполь­зу­ем­ся ме­то­дом '''toString()'''. Этот ме­тод воз­вра­ща­ет ин­фор­ма­цию об ошиб­ке на че­ло­ве­че­­ском язы­ке в пе­ре­мен­ной '''QString''':&lt;br /&gt;
&lt;br /&gt;
 errors = qmlView-&amp;gt;errors();&lt;br /&gt;
 for(int i = 0; i &amp;lt; errors.count(); i++)&lt;br /&gt;
 MessageBox::critical(this, “QML Error”, errors.at(i).toString());&lt;br /&gt;
&lt;br /&gt;
где '''errors''' – объ­ект вы­шеука­зан­но­го клас­са, про­из­вод­но­го от '''Qlist'''.&lt;br /&gt;
&lt;br /&gt;
Вы, на­вер­ное, об­ра­ти­ли внимание: я на­пи­сал, что ме­тод '''errors()''' воз­вра­ща­ет ин­фор­ма­цию об ак­ту­аль­ных ошиб­ках, то есть о тех, ко­то­рые су­ще­ст­ву­ют на мо­мент его вы­зо­ва. Тем, кто при­вык к ком­пи­ли­руе­мым язы­кам про­грам­ми­ро­вания, это мо­жет быть непри­выч­но, но в ди­на­ми­че­­ски ис­пол­няе­мых про­грам­мах ''QML'' ошиб­ки мо­гут возникать и ис­че­зать ди­на­ми­че­­ски, и не ка­ж­дая ошиб­ка обязательно при­во­дит к ава­рий­но­му за­вер­шению про­грам­мы.&lt;br /&gt;
&lt;br /&gt;
В свя­зи с этим по­лез­но сравнить вы­вод про­грам­мы на кон­соль Linux (ку­да по­сту­па­ет ин­фор­ма­ция обо всех ошиб­ках) с на­шим вы­во­дом с по­мо­щью клас­са '''MessageBox'''. Та­ким об­ра­зом, на­при­мер, я уз­нал, что свя­зы­вать корневой кон­текст с объ­ек­том '''dial''' луч­ше до за­груз­ки тек­ста про­грам­мы ''QML'', ведь вы­полнение про­грам­мы на­чи­на­ет­ся немед­лен­но по­сле вы­зо­ва ме­то­да '''setSource()'''. Ес­ли сра­зу по­сле вы­зо­ва '''setSource()''' про­грам­ма на ''QML'' не смо­жет инициа­ли­зи­ро­вать свой объ­ект '''Dial''', это вы­зо­вет ошиб­ку, ко­то­рая, од­на­ко бу­дет уст­ранена, как толь­ко мы ука­жем про­грам­ме, чем имен­но сле­ду­ет инициа­ли­зи­ро­вать этот объ­ект. Ина­че говоря, инициа­ли­зи­ро­вать корневой кон­текст мож­но и до вы­зо­ва '''setSource()''', и по­сле это­го вы­зо­ва, но пер­вый спо­соб ра­бо­та­ет чи­ще.&lt;br /&gt;
&lt;br /&gt;
===По­следние штри­хи===&lt;br /&gt;
&lt;br /&gt;
По­следнее, что мы сде­ла­ем для то­го, что­бы наш вид­жет вы­гля­дел, как лю­бой дру­гой вид­жет ''Qt'' – уда­лим бе­лый фон, за­бот­ли­во соз­дан­ный для нас объ­ек­том '''QDeclarativeView''' (не за­бы­вай­те, что это по­то­мок '''QGraphicsView'''). Мы сде­ла­ем это так:&lt;br /&gt;
&lt;br /&gt;
 qmlView-&amp;gt;setBackgroundRole(QPalette::Background);&lt;br /&gt;
&lt;br /&gt;
По­сколь­ку наш вид­жет толь­ко по­ка­зы­ва­ет зна­чения, но не по­зво­ля­ет их вво­дить, нам сле­ду­ет до­ба­вить в ок­но про­грам­мы еще один вид­жет, пред­на­зна­чен­ный для управ­ления вид­же­том ''QML''. Как и в ис­ход­ном при­ме­ре про­грам­мы ''QML'', мы восполь­зу­ем­ся для это­го пол­зун­ком, толь­ко в на­шем слу­чае это бу­дет объ­ект клас­са '''QSlider'''. Наш вид­жет бу­дет реа­ги­ро­вать на сиг­нал '''sliderChanged()''' это­го объ­ек­та. Мож­но бы­ло бы до­ба­вить в класс '''Dial''' слот и свя­зать этот слот с сиг­на­лом '''sliderChanged()''' на­пря­мую; я остав­ляю вам это в ка­че­­ст­ве до­машнего за­дания.&lt;br /&gt;
&lt;br /&gt;
В сле­дую­щий раз мы, на­конец, рас­смот­рим про­грам­му на чис­том ''QML'' (для вы­полнения ко­то­рой все рав­но требуется ути­ли­та ''Qt'').&lt;/div&gt;</description>
			<pubDate>Fri, 06 Jul 2012 10:18:37 GMT</pubDate>			<dc:creator>Crazy Rebel</dc:creator>			<comments>http://wiki2.linuxformat.ru/index.php/%D0%9E%D0%B1%D1%81%D1%83%D0%B6%D0%B4%D0%B5%D0%BD%D0%B8%D0%B5:LXF143:QML</comments>		</item>
	</channel>
</rss>