Form Qml e classe in c++
-
ti manca solo da aggiungere
Q_INVOKABLE
davanti a quei metodi.Occhio che subclassare un modello e' un campo minato. Se puoi, dichiara Players come
Q_GADGET
, e usaQStandardItemModel
direttamente:class Player{ Q_GADGET Q_PROPERTY(QString nick MEMBER m_nick) Q_PROPERTY(QString name MEMBER m_name) Q_PROPERTY(QString surname MEMBER m_surname) public: Player(const QString& nick,const QString& name ,const QString& surname) :m_nick(nick),m_name(name),m_surname(surname){} Player() = default; Player(const Player& other)=default; Player& operator=(const Player& other)=default; QString m_nick; QString m_name; QString m_surname; };
class Game : public QObject{ Q_OBJECT Q_PROPERTY(QAbstractItemModel* model READ model) Q_PROPERTY(ModeGame modeGame READ modeGame WRITE setModeGame NOTIFY modeGameChanged) Q_DISABLE_COPY(Game) public: enum ModeGame { U_Mode = 0, D_Mode, T_Mode }; Q_ENUM(ModeGame) Game(QObject* parent = nullptr) :QObject(parent){ m_model=new QStandardItemModel(this); m_model->inserColumn(0); } Q_SLOT void addPlayer(const QString& nick,const QString& name ,const QString& surname){ const int newRow = m_model->rowCount(); m_model->insertRow(newRow); m_model->setData(m_model->index(newRow,0),Player(nick,name,surname)); } Q_SLOT void removePlayer(int index){ m_model->removeRow(index); } QAbstractItemModel* model() const {return m_model} Q_SIGNAL void modeGameChanged(ModeGame modegame); void setModeGame(ModeGame modegame){ if(m_modegame==modegame) return; m_modegame=modegame; modeGameChanged(modegame); } ModeGame modeGame() const{return m_modegame;} private: QAbstractItemModel* m_model; ModeGame m_modegame; };
P.S.
Q_ENUMS
e' deprecato, usaQ_ENUM
: https://woboq.com/blog/q_enum.html -
Perfetto ho capito che la mia soluzione è meglio che la metta di lato e uso la tua. Della tua soluzione però non mi sono chiare due cose e approfitto della tua pazienza per togliermi questi dubbi.
La prima è: perche mi conviene usare dichiarare Players come Q_GADGET?
La seconda: ho un errore con il setData ( m_model->setData(m_model->index(newRow,0),Player(nick,name,surname));)
Ll'ho usato nel tuo stesso modo ma mi sfugge l'errore -
@GiGa91 said in Form Qml e classe in c++:
perche mi conviene usare dichiarare Players come Q_GADGET?
E' semplicemente per incapsulare
Player
. se non lo dichiari come Q_GADGET dovresti, dentroGame
, dichiarare cose tipoQ_INVOKABLE QString getNick(int row);
mentre se e' Q_GADGET puoi usare direttamente le sue proprieta' nel delegateho un errore con il setData
Cosa ti scrive l'errore?
-
Ah giusto non ci avevo pensato.
Per quanto riguarda l'errore, me lo da a causa del fatto che gli sto dando "players". Precisamente questo:
error: no matching function for call to ‘QAbstractItemModel::setData(QModelIndex, Player)’
m_model->setData(m_model->index(newRow,0),Player(nick,name,surname));
no known conversion for argument 2 from ‘Player’ to ‘const QVariant&’
^ -
Quasi tutto chiaro.. mi manca un passaggio solo.
Avendo esportato la classe verso qml:
Game game; engine.rootContext()->setContextProperty("Game", &game);
ho a disposizione i metodi addPlayer e removePlayer che posso usare in modo molto semplice:
onClicked: Game.addPlayer(qsTr(text_input_name.text), qsTr(text_input_surname.text), qsTr(text_input_nick.text))
tramite la property io dovrei poter leggere il modello su Qml
Q_PROPERTY(QAbstractItemModel* model READ model)
Pero non riesco. Ad esempio:
Button { text: "Add" onClicked: { Game.addPlayer(qsTr(text_input_name.text), qsTr(text_input_surname.text), qsTr(text_input_nick.text)) } } Button { text: "Rem" onClicked: Game.removePlayer(index) } ListView { id: listView Layout.fillWidth: true model: Game.model delegate: listDelegate } Component { id: listDelegate Text { id: name_text text: Game.model.data(0,1) } }
-
qsTr
non funziona cosi'. Puo' solo tradurre costanti e, comunque, non vedo il punto di tradurre un nome proprio- Il problema e' nel delegate
text: Game.model.data(0,1)
non funziona cosi'. E' un po' che non uso QML ma se ricordo bene era role.property quindi nel tuo caso qualcosa tipotext: display.nick + " - " + display.name + " " + display.surname
-
ops ho sbagliato a ricopiare. Questo non centra nulla:
text: Game.model.data(0,1)
Il punto che non capisco è proprio questo. Per esempio ti riporto un codice di esempio di Qml:
ListModel { id: nameModel ListElement { name: "Alice"; team: "Crypto" } ListElement { name: "Bob"; team: "Crypto" } ListElement { name: "Jane"; team: "QA" } ListElement { name: "Victor"; team: "QA" } ListElement { name: "Wendy"; team: "Graphics" } } Component { id: nameDelegate Text { text: name; font.pixelSize: 24 anchors.left: parent.left anchors.leftMargin: 2 } }
Nel delegate dovrebbe bastare mettere "name" e in modo trasparente dovrebbe vedermi la propieta di ListElement (nel caso dell'esempio)
Usando la Q_PROPERTY sbaglio a pensare di ottenere la stessa cosa verso il modello Game? -
esatto,
name
in quel caso e' il role in cui salvi "Alice".
Quando chiamim_model->setData
in realta' i parametri sono 3: indice della cella da cambiare, valore da inserire e role del valore. Siccome noi non mettiamo nulla nel terzo parametro il default e'Qt::EditRole
In QML non usiQt::EditRole
ma il nome corrispondente (qui trovi la lista dei nomi per i role standard).
Io usodisplay
invece diedit
perche' inQStandardItemModel
display e edit sono la stessa cosa (ma questo e' un dettaglio irrilevante)
Oradisplay
ritorna unaQVariant
che contiene unPlayer
. Puoi usare.
per accedere alle sue proprieta' quindidisplay.nick
(oedit.nick
se preferisci)quindi:
Component { id: nameDelegate Text { text: edit.nick font.pixelSize: 24 anchors.left: parent.left anchors.leftMargin: 2 } }
dovrebbe funzionare
Edit:
set ti piace di piu' puoi anche modificare il costruttore diGame
cosi':Game(QObject* parent = nullptr) :QObject(parent){ m_model=new QStandardItemModel(this); m_model->inserColumn(0); QHash<int, QByteArray> roleNames = m_model->roleNames(); roleNames.insert(Qt::UserRole,"player"); static_cast<QStandardItemModel*>(m_model)->setItemRoleNames(roleNames); }
e quando chiami setData aggiungi il terzo parametro:
m_model->setData(m_model->index(newRow,0),QVariant::fromValue(Player(nick,name,surname)),Qt::UserRole);
adesso in QML puoi fare:
Component { id: nameDelegate Text { text: player.nick font.pixelSize: 24 anchors.left: parent.left anchors.leftMargin: 2 } }
-
Strano perche' ha funzionato per me. Avevo praticamente la stessa tua domanda: https://forum.qt.io/topic/71433/best-practice-to-add-dynamic-qml-from-c-data/12
Probabilmente devi solo aggiungere
Q_DECLARE_METATYPE(Player)
in player.h eqmlRegisterType<Player>("com.game", 1, 0, "Player");
nella main() -
Perdona la mia ignoranza, ma io posso esportare la classe player tramite qmlRegistrerType? Non è definita come public Qobject e se devo essere sincero ci avevo gia provato ma ottenevo/ottengo questo errore:
error: cannot convert ‘QQmlPrivate::QQmlElement<Player>*’ to ‘QObject*’ for argument ‘1’ to ‘void QQmlPrivate::qdeclarativeelement_destructor(QObject*)’ QQmlPrivate::qdeclarativeelement_destructor(this); ^
Ho aggiunto Q_DECLARE_METATYPE(Player) in players.h
Considerando che a te il codice funzione e la probabilità che io abbia dimenticato qualcosa è alta riporto i miei quattro file:
main.cpp
#include <QGuiApplication> #include <QQmlApplicationEngine> #include <QQmlContext> #include <QtQml> #include "Classe_Game/Game.h" #include "Classe_Players/Players.h" int main(int argc, char *argv[]) { QGuiApplication app(argc, argv); //qmlRegisterType<Player>("com.helloworld.qmlcomponents",1,0,"Player"); QQmlApplicationEngine engine; Game game; engine.rootContext()->setContextProperty("Game", &game); engine.load(QUrl(QStringLiteral("qrc:/main.qml"))); if (engine.rootObjects().isEmpty()) return -1; return app.exec(); }
Players.h
#ifndef PLAYERS_H #define PLAYERS_H #include <QObject> class Player { Q_GADGET Q_PROPERTY(QString nick MEMBER m_nick) Q_PROPERTY(QString name MEMBER m_name) Q_PROPERTY(QString surname MEMBER m_surname) public: Player(const QString& nick,const QString& name ,const QString& surname) :m_nick(nick),m_name(name),m_surname(surname){} Player() = default; Player(const Player& other)=default; Player& operator=(const Player& other)=default; QString m_nick; QString m_name; QString m_surname; }; Q_DECLARE_METATYPE(Player) #endif // PLAYERS_H
Game.h
#ifndef GAME_H #define GAME_H #include <QStandardItemModel> #include <QList> #include "Classe_Players/Players.h" class Game : public QObject{ Q_OBJECT Q_PROPERTY(QAbstractItemModel* model READ model) Q_PROPERTY(ModeGame modeGame READ modeGame WRITE setModeGame NOTIFY modeGameChanged) Q_DISABLE_COPY(Game) public: enum ModeGame{NS, Static, Projector}; Q_ENUM(ModeGame) Game(QObject* parent = nullptr) :QObject(parent){ m_model=new QStandardItemModel(this); m_model->insertColumn(0); QHash<int, QByteArray> roleNames = m_model->roleNames(); roleNames.insert(Qt::UserRole,"player"); static_cast<QStandardItemModel*>(m_model)->setItemRoleNames(roleNames); } Q_SLOT void addPlayer(const QString& nick,const QString& name ,const QString& surname){ const int newRow = m_model->rowCount(); m_model->insertRow(newRow); m_model->setData(m_model->index(newRow,0),QVariant::fromValue(Player(nick,name,surname)),Qt::UserRole); } Q_SLOT void removePlayer(int index){ m_model->removeRow(index); } QAbstractItemModel* model() const { return m_model; } Q_SIGNAL void modeGameChanged(ModeGame modegame); void setModeGame(ModeGame modegame){ if(m_modegame==modegame) return; m_modegame=modegame; modeGameChanged(modegame); } ModeGame modeGame() const{return m_modegame;} private: QAbstractItemModel* m_model; ModeGame m_modegame; }; #endif // GAME_H
main.qml
import QtQuick 2.10 import QtQuick.Window 2.10 import QtQuick.Layouts 1.3 import QtQuick.Controls 1.4 Window { visible: true width: 640 height: 480 title: qsTr("Hello World") ColumnLayout { id: rowLayout objectName: "rowLayout" anchors.fill: parent TextInput { id: text_input_name color: "red" text: "name" Layout.fillWidth: true focus: true } TextInput { id: text_input_surname color: "red" text: "surname" Layout.fillWidth: true focus: true } TextInput { id: text_input_nick color: "red" text: "nick" Layout.fillWidth: true focus: true } Button { text: "Add" onClicked: { Game.addPlayer(text_input_name.text, text_input_surname.text, text_input_nick.text) } } Button { text: "Rem" //onClicked: Game.removePlayer(index) } ListView { width: 200; height: 250 model: Game delegate: Text { text: players.name } } } }
-
@GiGa91 said in Form Qml e classe in c++:
model: Game
il modello non e'
Game
, e'Game.model
text: players.name
il role non e'
players
, e'player
Game.addPlayer(text_input_name.text, text_input_surname.text, text_input_nick.text)
Hai sbagliato l'ordine degli argomenti:
Game.addPlayer(text_input_nick.text, text_input_name.text, text_input_surname.text)
Q_DECLARE_METATYPE(Player)
Non e' necessario, Q_GADGET fa gia' tutto
//onClicked: Game.removePlayer(index)
devi dare un id a ListView (per esepio
id: playerView
) e poi fareonClicked: Game.removePlayer(playerView.currentIndex)