Несколько слов о QML.

Jan 03, 2013 09:22

Cовсем недавно я начал изучать QML и даже писать на нем один проектик. Так как в этом проектике нету особенно ничего сверхъестественного и в осноном везде используются кастомные элементы с кастомным дизайном, то, оказалось, QML для этого подходит просто замечательно.  3 строчки и у тебя готова форма с нужным бекграундом. 6 строчек и готова кнопка подо все состояния с различными картинками. (количество строк сказано от балды. Там поболе будет. Штук 6 и 12 соответственно, где-то). И к этой заразе мгновенно привыкаешь и начиная другой проект, в котором нужен кастомный дизайн, так и хочется не мучаться и написать это с использованием QML. Но не всегда это возможно. Иногда есть ограничения по размеру и тащить с собой еще 3 метра QtDeclarative ну совсем не вариант, а жаль. Ведь приходиться мучаться с Qt StyleSheet и гребаным paintEvent.

Радует в QML то, что там заранее есть куча всяких свистоперделок, как то flipable item, напримере. И это хорошо. В QML достаточно реализации всякой всячины, что бы не использовать вообще плюсовый код. Например перемещение QML виджета мышью реализуется просто через этот код:

copy to clipboardподсветка кода
  1. property variant previousPosition  
  2. onPressed:  
  3. {  
  4.     previousPosition = Qt.point (mouseX, mouseY)  
  5. }  
  6. onPositionChanged:  
  7. {  
  8.     if (pressedButtons == Qt.LeftButton)  
  9.     {  
  10.         var dx = mouseX - previousPosition.x  
  11.         var dy = mouseY - previousPosition.y  
  12.         loginScreenWidget.pos = Qt.point(loginScreenWidget.pos.x + dx,  
  13.                 loginScreenWidget.pos.y + dy)  
  14.     }  
  15. }  


где loginScreenWidget это наш QDeclarativeView.

Хотя и без минусов не обойтись. Например главный минус, что QML не является top window и не может рисваться вне QDeclarativeView. С такой проблемой я столкнулся, когда мне надо было делать ComboBox, выпадающий список которого выходил за границы окна. Я соснул тут и пришлось уменьшать размер итема и ограничивать их видимое количество 2-мя.
Вторая, имхо, немалая проблема, заключается в том, что практически все приходится делать руками. Тоесть готовых компонентов практически нету, а если и есть в qml-components, то я не смог нормально завести. Поэтому почти все писал сам используя где-то чужие наработки, а где-то сам с нуля. Так я очень долго мучался, пытаясь найти готовый и нормальный combobox. В итоге нашел какой-то еле работающий и допилил его до нужного состояния уже сам.
Еще немалый минус в том, что для использования полноценного внешних моделей, а-ля QAbstractItemModel в QML приходится костылять методы вида:

copy to clipboardподсветка кода
  1. QVariant LanguagesModel::getName (int idx) const  
  2. {  
  3.     return itemData (index (idx, 0)) [LanguageName];  
  4. }  


где LanguageName - это роль. Потому что я так и не разобрался, что нужно возвращать из модели, что бы QML мог это использовать и сам получать из этого значения по роли. Я пробовал возвращать QObject*, QMap, QVariant и у меня все равно ничего не получилось. Поэтому осталось использовать такой говнокод.

Еще мне очень понравился принцип Q_PROPERTY во взаимодействии с QML. Очень удобно. Например на базе проперти я реализовывал количество элементов в модели:

copy to clipboardподсветка кода
  1. class ServerFilterProxyModel : public QSortFilterProxyModel  
  2. {  
  3.     Q_OBJECT  
  4.     Q_PROPERTY (int count READ GetCount NOTIFY countChanged);  
  5.   
  6. public:  
  7.     explicit ServerFilterProxyModel(QObject *parent = 0);  
  8.   
  9.     int GetCount () const;  
  10. protected:  
  11.     bool filterAcceptsRow (int sourceRow, const QModelIndex& sourceParent) const;  
  12.   
  13. public slots:  
  14.     void handleSourceRowsInserted (const QModelIndex& parent,  
  15.             int start, int end);  
  16.   
  17. signals:  
  18.     void countChanged ();  
  19. };  


copy to clipboardподсветка кода
  1. ServerFilterProxyModel::ServerFilterProxyModel (QObject *parent)  
  2. : QSortFilterProxyModel (parent)  
  3. {  
  4.     setDynamicSortFilter (true);  
  5.     setFilterCaseSensitivity (Qt::CaseInsensitive);  
  6. }  
  7.   
  8. int ServerFilterProxyModel::GetCount () const  
  9. {  
  10.     return sourceModel ()->rowCount ();  
  11. }  
  12.   
  13. bool ServerFilterProxyModel::filterAcceptsRow (int sourceRow, const QModelIndex& sourceParent) const  
  14. {  
  15.     QModelIndex index = sourceModel()->index (sourceRow, 0, sourceParent);  
  16.   
  17.     return sourceModel ()->data (index, ServersModel::ServerName)  
  18.             .toString().contains (filterRegExp ());  
  19. }  
  20.   
  21. void ServerFilterProxyModel::handleSourceRowsInserted (const QModelIndex& parent,  
  22.         int start, int end)  
  23. {  
  24.     Q_UNUSED (parent);  
  25.     Q_UNUSED (start);  
  26.     Q_UNUSED (end);  
  27.     emit countChanged ();  
  28. }  


Это реализация своей filter proxy модели, на базе QSortFilterProxyModel. слот handleSourceRowsInserted подключен к сигналу. QAbstractItemModel::rowsInserted.

Ах да. Еще я столкнулся с проблемой с MouseArea. А именно с ситуацией, когда mouseArea родительского виджета полностью покрывает child widget со своей MouseArea. В этом случае mouse events от родительской mouseArea не доходят до ребенка.
Для наглядности покажу код:

copy to clipboardподсветка кода
  1. Item  
  2. {  
  3.     id: wrapper;  
  4.     width: 306; height: 40  
  5.   
  6.     FlipableImage  
  7.     {  
  8.         id: flipableImage  
  9.         width: 30  
  10.         height: 30  
  11.   
  12.         anchors.verticalCenter: wrapper.verticalCenter;  
  13.         anchors.left:  wrapper.left  
  14.         anchors.leftMargin: 10  
  15.   
  16.         frontImage: serverIconSource;  
  17.         backImage: "qrc:/images/resources/images/log.png";  
  18.   
  19.   
  20.         MouseArea  
  21.         {  
  22.             id: mouseArea;  
  23.             anchors.fill: flipableImage  
  24.             hoverEnabled: true;  
  25.   
  26.             onEntered:  
  27.                 flipableImage.flipped = !flipableImage.flipped;  
  28.             onExited:  
  29.                 flipableImage.flipped = !flipableImage.flipped;  
  30.         }  
  31.     }  
  32.   
  33.     MouseArea  
  34.     {  
  35.         id: itemMouseArea  
  36.         anchors.fill: parent;  
  37.         hoverEnabled: true;  
  38.         onEntered:  
  39.         {  
  40.             serversView.currentIndex = index;  
  41.         }  
  42.     }  
  43. }  


В этом случаее Flipable итем не будет "флипаться", потому что события мыши onEntered и onExited до него не дойдут, а остановятся на родителе.
На самом деле починить это оказалось очень просто =)

copy to clipboardподсветка кода
  1. Item  
  2. {  
  3.     id: wrapper;  
  4.     width: 306; height: 40  
  5.   
  6.     FlipableImage  
  7.     {  
  8.         id: flipableImage  
  9.         width: 30  
  10.         height: 30  
  11.   
  12.         anchors.verticalCenter: wrapper.verticalCenter;  
  13.         anchors.left:  wrapper.left  
  14.         anchors.leftMargin: 10  
  15.   
  16.         frontImage: serverIconSource;  
  17.         backImage: "qrc:/images/resources/images/log.png";  
  18.     }  
  19.   
  20.     MouseArea  
  21.     {  
  22.         id: itemMouseArea  
  23.         anchors.fill: parent;  
  24.         hoverEnabled: true;  
  25.         z: 0  
  26.         onEntered:  
  27.         {  
  28.             serversView.currentIndex = index;  
  29.         }  
  30.     }  
  31.   
  32.     MouseArea  
  33.     {  
  34.         id: mouseArea;  
  35.         anchors.fill: flipableImage  
  36.         hoverEnabled: true;  
  37.         z: 1  
  38.         onEntered:  
  39.             flipableImage.flipped = !flipableImage.flipped;  
  40.         onExited:  
  41.             flipableImage.flipped = !flipableImage.flipped;  
  42.     }  
  43. }  


Как Вы видите мы просто делаем эти обе mouseArea на одном уровне и у одной из них изменяем параметр Z. Почему не изменить его сразу? Дело в том, что он работает только для sibling items.

Вот такой мой первый опыт работы с QML. Будет что-то интересное - еще напишу.

P.S. Вспомнил. Qt Creator Qml Designer фейлится, если использовать картинки из ресурсов. Это печально очень. Поэтому приходится почти все делать на глаз. Могу только посоветовать до конца разработки на QML не выносить картинки в ресурсы.

программирование, qtcreator, programming, qml, qt, coding is my life, С++, qsortfilterproxymodel

Previous post Next post
Up