Найкращі прийоми Qt Quick Компоненти

QML надає зручний спосіб розбиття коду під назвою «Компоненти». Найпростішим способом створення компонента, який можна буде згодом використовувати багаторазово, є додавання нового файлу до робочої директорії головного QML-файлу.

Також компоненти можна упаковувати як модулі (Qt Components є таким модулем) і публікувати у вигляді плагінів. Цей пост присвячений використанню компонентів для написання чистого та легко підтримуваного QML-коду.

Створення нових компонентів

Перший приклад показав простоту створення додаткових компонентів, тому не бійтеся їх використовувати. Не робіть написання коду, придатного для багаторазового використання, своєю першочерговою метою. Прагніть інкапсулювання деталей реалізації та зменшення зв'язності (decoupling) компонентів. Компоненти мають бути невеликими. Дотримуючись цих правил, ви автоматично прийдете до коду, який згодом можна буде використовувати багаторазово.

Давайте подивимося на цей приклад простих аналогових годинників:

Цей код містить компонент Clock, який при запуску виглядає так, як показано на скріншоті знизу. Незважаючи на те, що він використовується в додатку один раз, був сенс виділити його з основного файлу.

найкращі

По-перше, це робить простий і зрозумілою логіку, що залишилася у файлі main.qml: таймер, що оновлює години, хвилини і секунди компонента Clock - це все, що розробнику необхідно бачити в main.qml, якщо він захоче додати йому функціональності.

По-друге, наш компонент Clock може не перейматися власним розташуванням у вікні. Припустимо, є елемент Row, який використовує наш компонент Clock N раз. Якби в кореневому елементі компонента Clock був код'anchors.fill: parent', ми не могли б використовувати його екземпляри в елементі Row: кожен екземпляр Clock займав би весь width_row, замість width_row / N. Саме тому QML забороняє використання більшості якорів (anchors) в елементах, що поміщаються в елемент Row. Якщо ми хочемо, щоб наш компонент Clock залишався придатним для багаторазового використання, ми не повинні робити численні припущення щодо його майбутнього використання. p align="justify"> Резюмуючи, кореневий елемент компонента не повинен містити якоря до свого батька або використовувати жорстко задані менеджери розміщення (layouts).

У той же час компонент Clock задає фіксовані значення своїм довжиною та шириною. З семантичної точки зору це розміри нашого компонента за умовчанням. Вони не обмежують використання нашого компонента, адже розмір його екземплярів можна змінити за потреби. Компоненти з нульовими розмірами вважаються невидимими елементами, тому налаштування ненульових розмірів за замовчуванням дозволяє уникнути дурних помилок.

Також існують інші, менш очевидні переваги, такі як створення складових (composed) елементів та інкапсуляція деталей реалізації.

Створення складового елемента (назвемо його ComposedElement) із простих елементів ElementA, ElementB і ElementC спрощує додавання нових властивостей та дій до елементів. Ми можемо додати нові елементи ElementD та ElementE до нашого ComposedElement без необхідності зміни ElementA, ElementB або ElementC. Наші прості елементи ізольовані один від одного і тому не можуть просто так зламатися, якщо один з них раптом зміниться.

Компоненти можуть бути поділені на публічну та приватну частини, так само як це робиться у класах C++ та Java. Публічний компонент API є сумою всіх його властивостей і методів,визначених у кореневому елементі (включаючи успадковані їм властивості). Це означає, що такі властивості можуть бути змінені, такі методи можуть бути викликані користувачами компонента.

Будь-яка властивість або метод, визначені у вкладеному елементі (не кореневому), можуть вважатися повністю приватним API. Це дозволяє інкапсулювати деталі реалізації і має, зрештою, стати простою справою при створенні компонентів розробниками.

Щоб довести користь від інкапсуляції, ми можемо видалити внутрішній елемент 'impl' з Clock.qml та запустити програму знову (наприклад, через "$qmiviewer main.qml"). Жодних нових помилок не буде видно, оскільки публічний API компонента Clock не було змінено. Це означає, що ми можемо вільно змінювати 'impl', знаючи, що жодних сторонніх ефектів від таких змін інших компонентів не з'явиться.

Ми можемо навіть розширити цю ідею і дозволити Clock.qml завантажувати будь-який елемент 'impl' динамічно, залежно від ситуації. Це вводить концепцію поліморфізму QML; реалізація такого механізму залишається читачеві як вправу.

Якщо ми маємо добре спроектований, мінімальний публічний API для кожного компонента, ми можемо зосередитися на розробці інтерфейсів, а не конкретної реалізації.

Повторне використання компонентів

Перевіримо, чи можемо ми багато разів використовувати компонент Clock.qml. Він чудово підходить для створення годинника, що відображає світовий час. У цьому випадку нам не потрібна секундна стрілка на них. Ми можемо трохи змінити поведінку Clock.qml, щоб останній не відображав годин, хвилин або секунд, якщо їх значення менше нуля. Для елемента Image з >

Ми не змінювали публічний API. Ми також вважаємо, що використання в аналоговому годинникуНегативних значень для секунд все одно не має жодного сенсу. Тому ми впевнені, що ця зміна не вплине на роботу існуючих програм, які використовують компонент Clock.

У нашому прикладі зі світовим годинником ми, крім усього іншого, можемо відображати локальну інформацію про погоду. Ми можемо використовувати один з доступних погодних сервісів API разом з XmlListModel, який дозволяє декларативно витягувати дані з API. Таймер, який раніше використовувався лише для оновлення часу на годиннику, тепер буде використаний і для оновлення даних про погоду раз на годину. Зверніть увагу, як вводиться сигнал оновлення, який з'єднується з функцією оновлення даних XmlListModel.

Змінений приклад відображає світовий годинник та локальну інформацію про погоду для трьох міст.