qml
C ++との統合
サーチ…
C ++からのQtQuickビューの作成
C ++から直接QtQuickビューを作成し、QML C ++で定義されたプロパティに公開することは可能です。下のコードでは、C ++プログラムはQtQuickビューを作成し、ビューの高さと幅をプロパティとしてQMLに公開します。
main.cpp
#include <QApplication>
#include <QQmlContext>
#include <QQuickView>
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
// Creating the view and manually setting the QML file it should display
QQuickView view;
view.setSource(QStringLiteral("main.qml"));
// Retrieving the QML context. This context allows us to expose data to the QML components
QQmlContext* rootContext = view.rootContext();
// Creating 2 new properties: the width and height of the view
rootContext->setContextProperty("WINDOW_WIDTH", 640);
rootContext->setContextProperty("WINDOW_HEIGHT", 360);
// Let's display the view
view.show();
return app.exec();
}
main.qml
import QtQuick 2.0
Rectangle {
// We can now access the properties we defined from C++ from the whole QML file
width: WINDOW_WIDTH
height: WINDOW_HEIGHT
Text {
text: qsTr("Hello World")
anchors.centerIn: parent
}
}
C ++からのQtQuickウィンドウの作成
Qt 5.1以降では、QQuickViewの代わりにQQmlApplicationEngineを使用して、QMLスクリプトを読み込んでレンダリングすることができます。
QQmlApplicationEngineでは、ルート要素としてQMLウィンドウタイプを使用する必要があります。
エンジンからルートコンテキストを取得し、コンテキストにグローバルプロパティを追加して、QMLスクリプトの処理時にエンジンがアクセスできるようにすることができます。
main.cpp
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQmlContext>
int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);
QQmlApplicationEngine engine;
QQmlContext* rootContext = engine.rootContext();
rootContext->setContextProperty("WINDOW_WIDTH", 640);
rootContext->setContextProperty("WINDOW_HEIGHT", 360);
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
return app.exec();
}
main.qml
import QtQuick 2.5
import QtQuick.Window 2.2
Window { // Must be this type to be loaded by QQmlApplicationEngine.
visible: true
width: WINDOW_WIDTH //Accessing global context declared in C++
height: WINDOW_HEIGHT //Accessing global context declared in C++
title: qsTr("Hello World")
Component.onCompleted: {
// We can access global context from within JavaScript too.
console.debug( "Width: " + WINDOW_WIDTH )
console.debug( "Height: " + WINDOW_HEIGHT )
}
MouseArea {
anchors.fill: parent
onClicked: {
Qt.quit();
}
}
Text {
text: qsTr("Hello World")
anchors.centerIn: parent
}
}
TreeViewのシンプルなモデルを作成する
Qt 5.5以来、私たちは待っていた新しい素晴らしいツリービューを持っています。 TreeViewは、モデルからアイテムのツリー表現を実装します 。一般的に、他のQMLビュー( ListViewまたはTableView)のように見えます。しかし、TreeViewのデータ構造はより複雑です。
ListViewまたはTableViewのデータは、ノードの1次元配列で表されます。 TreeViewでは、各ノードに独自のノード配列を含めることができます。したがって、 TreeViewの他のビューとは異なり、指定されたノードを取得するには、要素の行または列だけでなく、親ノードも知っている必要があります。
もう一つの主な違いは、 TreeViewはListModelをサポートしていないことです。データを提供するには、 QAbstractItemModelをサブクラス化する必要があります。 Qtには、ローカルファイルシステムへのアクセスを提供するQFileSystemModelや、 データベースへのアクセスを提供するQSqlTableModelなどのモデルクラスを使用する準備ができています。
次の例では、 QAbstractItemModelから派生したモデルを作成します。しかし、この例をより現実的にするためには、 ListModelのようなモデルを作成することをお勧めしますが、QMLからノードを追加できるようにツリーに指定してください。モデルそのものにはデータが含まれておらず、データへのアクセスのみが提供されていることを明確にする必要があります。したがって、データの提供と整理は私どもの責任です。
モデルデータはツリー内に編成されているので、最も単純なノード構造は、擬似コードでは以下のようになります。
Node {
var data;
Node parent;
list<Node> children;
}
C++ 、ノード宣言は次のようにする必要があります。
class MyTreeNode : public QObject
{
Q_OBJECT
public:
Q_PROPERTY(QQmlListProperty<MyTreeNode> nodes READ nodes)
Q_CLASSINFO("DefaultProperty", "nodes")
MyTreeNode(QObject *parent = Q_NULLPTR);
void setParentNode(MyTreeNode *parent);
Q_INVOKABLE MyTreeNode *parentNode() const;
bool insertNode(MyTreeNode *node, int pos = (-1));
QQmlListProperty<MyTreeNode> nodes();
MyTreeNode *childNode(int index) const;
void clear();
Q_INVOKABLE int pos() const;
Q_INVOKABLE int count() const;
private:
QList<MyTreeNode *> m_nodes;
MyTreeNode *m_parentNode;
};
私たちはクラスをQObjectから派生させて、 QMLノードを作成できるようにします。すべての子ノードがnodesプロパティに追加さnodesので、次の2つのコード部分は同じです。
TreeNode {
nodes:[
TreeNode {}
TreeNode {}
]
}
TreeNode {
TreeNode {}
TreeNode {}
}
デフォルトプロパティの詳細については、 このトピックを参照してください。
ノードクラスの実装:
MyTreeNode::MyTreeNode(QObject *parent) :
QObject(parent),
m_parentNode(nullptr) {}
void MyTreeNode::setParentNode(MyTreeNode *parent)
{
m_parentNode = parent;
}
MyTreeNode *MyTreeNode::parentNode() const
{
return m_parentNode;
}
QQmlListProperty<MyTreeNode> MyTreeNode::nodes()
{
QQmlListProperty<MyTreeNode> list(this,
0,
&append_element,
&count_element,
&at_element,
&clear_element);
return list;
}
MyTreeNode *MyTreeNode::childNode(int index) const
{
if(index < 0 || index >= m_nodes.length())
return nullptr;
return m_nodes.at(index);
}
void MyTreeNode::clear()
{
qDeleteAll(m_nodes);
m_nodes.clear();
}
bool MyTreeNode::insertNode(MyTreeNode *node, int pos)
{
if(pos > m_nodes.count())
return false;
if(pos < 0)
pos = m_nodes.count();
m_nodes.insert(pos, node);
return true;
}
int MyTreeNode::pos() const
{
MyTreeNode *parent = parentNode();
if(parent)
return parent->m_nodes.indexOf(const_cast<MyTreeNode *>(this));
return 0;
}
int MyTreeNode::count() const
{
return m_nodes.size();
}
MyTreeNode *MyTreeModel::getNodeByIndex(const QModelIndex &index)
{
if(!index.isValid())
return nullptr;
return static_cast<MyTreeNode *>(index.internalPointer());
}
QModelIndex MyTreeModel::getIndexByNode(MyTreeNode *node)
{
QVector<int> positions;
QModelIndex result;
if(node) {
do
{
int pos = node->pos();
positions.append(pos);
node = node->parentNode();
} while(node != nullptr);
for (int i = positions.size() - 2; i >= 0 ; i--)
{
result = index(positions[i], 0, result);
}
}
return result;
}
bool MyTreeModel::insertNode(MyTreeNode *childNode, const QModelIndex &parent, int pos)
{
MyTreeNode *parentElement = getNode(parent);
if(pos >= parentElement->count())
return false;
if(pos < 0)
pos = parentElement->count();
childNode->setParentNode(parentElement);
beginInsertRows(parent, pos, pos);
bool retValue = parentElement->insertNode(childNode, pos);
endInsertRows();
return retValue;
}
MyTreeNode *MyTreeModel::getNode(const QModelIndex &index) const
{
if(index.isValid())
return static_cast<MyTreeNode *>(index.internalPointer());
return m_rootNode;
}
リスト様のプロパティをQQmlListPropertyを通してQMLに公開するには、次の4つの関数が必要です。
void append_element(QQmlListProperty<MyTreeNode> *property, MyTreeNode *value)
{
MyTreeNode *parent = (qobject_cast<MyTreeNode *>(property->object));
value->setParentNode(parent);
parent->insertNode(value, -1);
}
int count_element(QQmlListProperty<MyTreeNode> *property)
{
MyTreeNode *parent = (qobject_cast<MyTreeNode *>(property->object));
return parent->count();
}
MyTreeNode *at_element(QQmlListProperty<MyTreeNode> *property, int index)
{
MyTreeNode *parent = (qobject_cast<MyTreeNode *>(property->object));
if(index < 0 || index >= parent->count())
return nullptr;
return parent->childNode(index);
}
void clear_element(QQmlListProperty<MyTreeNode> *property)
{
MyTreeNode *parent = (qobject_cast<MyTreeNode *>(property->object));
parent->clear();
}
今度はモデルクラスを宣言しましょう:
class MyTreeModel : public QAbstractItemModel
{
Q_OBJECT
public:
Q_PROPERTY(QQmlListProperty<MyTreeNode> nodes READ nodes)
Q_PROPERTY(QVariantList roles READ roles WRITE setRoles NOTIFY rolesChanged)
Q_CLASSINFO("DefaultProperty", "nodes")
MyTreeModel(QObject *parent = Q_NULLPTR);
~MyTreeModel();
QHash<int, QByteArray> roleNames() const Q_DECL_OVERRIDE;
QVariant data(const QModelIndex &index, int role) const Q_DECL_OVERRIDE;
Qt::ItemFlags flags(const QModelIndex &index) const Q_DECL_OVERRIDE;
QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const Q_DECL_OVERRIDE;
QModelIndex parent(const QModelIndex &index) const Q_DECL_OVERRIDE;
int rowCount(const QModelIndex &parent = QModelIndex()) const Q_DECL_OVERRIDE;
int columnCount(const QModelIndex &parent = QModelIndex()) const Q_DECL_OVERRIDE;
QQmlListProperty<MyTreeNode> nodes();
QVariantList roles() const;
void setRoles(const QVariantList &roles);
Q_INVOKABLE MyTreeNode * getNodeByIndex(const QModelIndex &index);
Q_INVOKABLE QModelIndex getIndexByNode(MyTreeNode *node);
Q_INVOKABLE bool insertNode(MyTreeNode *childNode, const QModelIndex &parent = QModelIndex(), int pos = (-1));
protected:
MyTreeNode *getNode(const QModelIndex &index) const;
private:
MyTreeNode *m_rootNode;
QHash<int, QByteArray> m_roles;
signals:
void rolesChanged();
};
抽象的なQAbstractItemModelからモデルクラスを導き出したので、 data() 、 flags() 、 index() 、 parent() 、 columnCount() 、 rowCount()の次の関数を再定義する必要があります 。私たちのモデルがQMLで動作するためには、 roleNames()を定義します。また、ノードクラスと同様に、 QMLモデルにノードを追加できるようにデフォルトプロパティを定義します。 rolesプロパティはロール名のリストを保持します。
実装:
MyTreeModel::MyTreeModel(QObject *parent) :
QAbstractItemModel(parent)
{
m_rootNode = new MyTreeNode(nullptr);
}
MyTreeModel::~MyTreeModel()
{
delete m_rootNode;
}
QHash<int, QByteArray> MyTreeModel::roleNames() const
{
return m_roles;
}
QVariant MyTreeModel::data(const QModelIndex &index, int role) const
{
if (!index.isValid())
return QVariant();
MyTreeNode *item = static_cast<MyTreeNode*>(index.internalPointer());
QByteArray roleName = m_roles[role];
QVariant name = item->property(roleName.data());
return name;
}
Qt::ItemFlags MyTreeModel::flags(const QModelIndex &index) const
{
if (!index.isValid())
return 0;
return QAbstractItemModel::flags(index);
}
QModelIndex MyTreeModel::index(int row, int column, const QModelIndex &parent) const
{
if (!hasIndex(row, column, parent))
return QModelIndex();
MyTreeNode *parentItem = getNode(parent);
MyTreeNode *childItem = parentItem->childNode(row);
if (childItem)
return createIndex(row, column, childItem);
else
return QModelIndex();
}
QModelIndex MyTreeModel::parent(const QModelIndex &index) const
{
if (!index.isValid())
return QModelIndex();
MyTreeNode *childItem = static_cast<MyTreeNode*>(index.internalPointer());
MyTreeNode *parentItem = static_cast<MyTreeNode *>(childItem->parentNode());
if (parentItem == m_rootNode)
return QModelIndex();
return createIndex(parentItem->pos(), 0, parentItem);
}
int MyTreeModel::rowCount(const QModelIndex &parent) const
{
if (parent.column() > 0)
return 0;
MyTreeNode *parentItem = getNode(parent);
return parentItem->count();
}
int MyTreeModel::columnCount(const QModelIndex &parent) const
{
Q_UNUSED(parent);
return 1;
}
QQmlListProperty<MyTreeNode> MyTreeModel::nodes()
{
return m_rootNode->nodes();
}
QVariantList MyTreeModel::roles() const
{
QVariantList list;
QHashIterator<int, QByteArray> i(m_roles);
while (i.hasNext()) {
i.next();
list.append(i.value());
}
return list;
}
void MyTreeModel::setRoles(const QVariantList &roles)
{
static int nextRole = Qt::UserRole + 1;
foreach(auto role, roles) {
m_roles.insert(nextRole, role.toByteArray());
nextRole ++;
}
}
MyTreeNode *MyTreeModel::getNodeByIndex(const QModelIndex &index)
{
if(!index.isValid())
return nullptr;
return static_cast<MyTreeNode *>(index.internalPointer());
}
QModelIndex MyTreeModel::getIndexByNode(MyTreeNode *node)
{
QVector<int> positions;
QModelIndex result;
if(node) {
do
{
int pos = node->pos();
positions.append(pos);
node = node->parentNode();
} while(node != nullptr);
for (int i = positions.size() - 2; i >= 0 ; i--)
{
result = index(positions[i], 0, result);
}
}
return result;
}
bool MyTreeModel::insertNode(MyTreeNode *childNode, const QModelIndex &parent, int pos)
{
MyTreeNode *parentElement = getNode(parent);
if(pos >= parentElement->count())
return false;
if(pos < 0)
pos = parentElement->count();
childNode->setParentNode(parentElement);
beginInsertRows(parent, pos, pos);
bool retValue = parentElement->insertNode(childNode, pos);
endInsertRows();
return retValue;
}
MyTreeNode *MyTreeModel::getNode(const QModelIndex &index) const
{
if(index.isValid())
return static_cast<MyTreeNode *>(index.internalPointer());
return m_rootNode;
}
一般的に、このコードは標準的な実装とあまり変わりません。たとえば、 単純なツリーの例
C++ロールを定義する代わりに、 QMLからロールを作成する方法を提供します。 TreeViewのイベントとメソッドは、基本的にQModelIndexで動作します 。私は個人的にそれをqmlに渡す感覚はそれほどありません。あなたがそれをモデルに戻すことだけです。
とにかく、私たちのクラスはインデックスをノードに変換する方法を提供し、その逆もあります。クラスをQMLで使用できるようにするには、クラスを登録する必要があります。
qmlRegisterType<MyTreeModel>("qt.test", 1, 0, "TreeModel");
qmlRegisterType<MyTreeNode>("qt.test", 1, 0, "TreeElement");
そして、私たちのモデルをQML TreeViewでどのように使うことができるか、
import QtQuick 2.7
import QtQuick.Window 2.2
import QtQuick.Dialogs 1.2
import qt.test 1.0
Window {
visible: true
width: 800
height: 800
title: qsTr("Tree example")
Component {
id: fakePlace
TreeElement {
property string name: getFakePlaceName()
property string population: getFakePopulation()
property string type: "Fake place"
function getFakePlaceName() {
var rez = "";
for(var i = 0;i < Math.round(3 + Math.random() * 7);i ++) {
rez += String.fromCharCode(97 + Math.round(Math.random() * 25));
}
return rez.charAt(0).toUpperCase() + rez.slice(1);
}
function getFakePopulation() {
var num = Math.round(Math.random() * 100000000);
num = num.toString().split("").reverse().join("");
num = num.replace(/(\d{3})/g, '$1,');
num = num.split("").reverse().join("");
return num[0] === ',' ? num.slice(1) : num;
}
}
}
TreeModel {
id: treemodel
roles: ["name","population"]
TreeElement {
property string name: "Asia"
property string population: "4,164,252,000"
property string type: "Continent"
TreeElement {
property string name: "China";
property string population: "1,343,239,923"
property string type: "Country"
TreeElement { property string name: "Shanghai"; property string population: "20,217,700"; property string type: "City" }
TreeElement { property string name: "Beijing"; property string population: "16,446,900"; property string type: "City" }
TreeElement { property string name: "Chongqing"; property string population: "11,871,200"; property string type: "City" }
}
TreeElement {
property string name: "India";
property string population: "1,210,193,422"
property string type: "Country"
TreeElement { property string name: "Mumbai"; property string population: "12,478,447"; property string type: "City" }
TreeElement { property string name: "Delhi"; property string population: "11,007,835"; property string type: "City" }
TreeElement { property string name: "Bengaluru"; property string population: "8,425,970"; property string type: "City" }
}
TreeElement {
property string name: "Indonesia";
property string population: "248,645,008"
property string type: "Country"
TreeElement {property string name: "Jakarta"; property string population: "9,588,198"; property string type: "City" }
TreeElement {property string name: "Surabaya"; property string population: "2,765,487"; property string type: "City" }
TreeElement {property string name: "Bandung"; property string population: "2,394,873"; property string type: "City" }
}
}
TreeElement { property string name: "Africa"; property string population: "1,022,234,000"; property string type: "Continent" }
TreeElement { property string name: "North America"; property string population: "542,056,000"; property string type: "Continent" }
TreeElement { property string name: "South America"; property string population: "392,555,000"; property string type: "Continent" }
TreeElement { property string name: "Antarctica"; property string population: "4,490"; property string type: "Continent" }
TreeElement { property string name: "Europe"; property string population: "738,199,000"; property string type: "Continent" }
TreeElement { property string name: "Australia"; property string population: "29,127,000"; property string type: "Continent" }
}
TreeView {
anchors.fill: parent
model: treemodel
TableViewColumn {
title: "Name"
role: "name"
width: 200
}
TableViewColumn {
title: "Population"
role: "population"
width: 200
}
onDoubleClicked: {
var element = fakePlace.createObject(treemodel);
treemodel.insertNode(element, index, -1);
}
onPressAndHold: {
var element = treemodel.getNodeByIndex(index);
messageDialog.text = element.type + ": " + element.name + "\nPopulation: " + element.population;
messageDialog.open();
}
}
MessageDialog {
id: messageDialog
title: "Info"
}
}
ノードを追加するにはダブルクリックし、ノード情報は押し続けます。