How update model in QML - qt

For example I have my custom class with model.
class DbWrapper : public QObject
{
Q_OBJECT
explicit DbWrapper(QObject *parent = nullptr);
Q_INVOKABLE void connect();
QSqlDatabase db;
MyModel* myModel;
}
void DbWrapper::connect()
{
///db.open()
myModel = new MyModel(0,db);
/// myModel.select() and another
}
and main.qml for example:
DbWrapper db();
db.connect();
QQmlContext *context = engine.rootContext();
context->setContextProperty("db", &db);
context->setContextProperty("journalModel", db.tableModel);
and in QML for TableView model: journalModel it's working. But I have some problem when me need connect to database after. for example from QML event button click.
onClicked{
db.connect()
}
But I can't review my model in QTableView, how i can solve it? MyModel this custom class from QSqlRelationTableModel.

Related

How can access 'in-memory' sqlite database from QML?

I created an in-memory SQLite database using Qt-C++
const QString m_DRIVER = "QSQLITE";
QSqlDatabase m_db;
m_db = QSqlDatabase::addDatabase(m_DRIVER, "mkddb");
m_db.setDatabaseName(":memory:");
My question is how I can access it in QML?
As long as You have the database connection established in some class of Yours, eg. Database Handler, You can expose it to QML in a very simple way. Here is a very brief concept of how such handler would look like:
DatabaseHandler.h
class DatabaseHandler : public QObject
{
Q_OBJECT
public:
explicit DatabaseHandler(QObject *parent = nullptr);
Q_INVOKABLE void addDatabaseEntry(QString queryString);
private:
QSqlDatabase database = QSqlDatabase::addDatabase("QSQLITE", "mkddb");
QSqlQuery databaseQuery;
};
DatabaseHandler.cpp
DatabaseHandler::DatabaseHandler(QObject *parent) : QObject(parent)
{
database.setDatabaseName(":memory:");
database.open();
databaseQuery = QSqlQuery(database);
}
void DatabaseHandler::addDatabaseEntry(QString queryString)
{
databaseQuery.exec(queryString);
}
The object itself has to be a QObject with a Q_OBJECT macro as on the code above.
To expose the DatabaseHandler to QML, in eg. main.cpp You would have to write this:
qmlRegisterType<DatabaseHandler>("com.organization.databasehandler", 1, 0, "DatabaseHandler");
Then in QML code You can import the database handler and use its methods as long as they're declared with the macro Q_INVOKABLE like in the DatabaseHandler.h:
import com.organization.databasehandler 1.0
ApplicationWindow {
DatabaseHandler {
id: databaseHandler
}
Button {
onClicked: databaseHandler.addDatabaseEntry(/*Query to execute on the database*/)
}
}

Using c++ enum in qml

I have an enum that I use in qml
class SettingManager : public QObject
{
Q_OBJECT
public:
enum BookKinds{
BookKind1=0,
BookKind2=1,
};
Q_ENUMS(BookKinds)
Q_PROPERTY(BookKinds bookKind READ bookKind WRITE setBookKind NOTIFY bookKindChanged)
explicit SettingManager(QObject *parent = nullptr);
void setBookKind(BookKinds dkob);
BookKinds bookKind();
signals:
void bookKindChanged();
};
in main.cpp I registerd SettingManager
qmlRegisterType<SettingManager>("Test",1,0,"SettingManager");
I use this in qml file
onCurrentIndexChanged:
{
if(tbarBookKindForDisplay.currentIndex==0)
{
settingManager.bookKind=BookManager.BookKind1;
}
else if(tbarBookKindForDisplay.currentIndex==1){
settingManager.bookKind=BookManager.BookKind2;
}
}
when CurrentIndex of TabBar changes below error occurs:
Error: Cannot assign [undefined] to int
You register the type as SettingManager but use it as BookManager. The correct code is:
settingManager.bookKind = SettingManager.BookKind1;
You should also use Q_ENUM instead of Q_ENUMS.

Populate ComboBox from QList<QObject*>

I was following an example on web to populate combobox, but it did not wok for me and I do not know why!. I have two classes stock and DbCon, stock has three private fields along with public accessor and mutators. DbCon has a Q_Property and two public function, one returns a database connection and the other creates and returns stock list as a QList<QObject*>. In main.cpp I have created a contextual property named "data" to access DbCon from QML.
in main.qml I have
....
ComboBox{
model: data.stockModel
textRole: "code"
}
....
in main.cpp
DbCon db;
engine.rootContext()->setContextProperty("data", &db);
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
in dbcon.h
class DbCon:public QObject
{
Q_OBJECT
Q_PROPERTY(QList<QObject*> stockModel READ stockModel)
public:
explicit DbCon(QObject *parent = 0);
QSqlDatabase db();
QList<QObject*> stockModel();
};
in implementation of QList<QObject*> stockModel() of dbcon.h
QList<QObject*> data;
....
while (query.next()) {
stock *s = new stock();
....
data.append(s);
}
return data;
and in stock.h
class stock : public QObject
{
Q_OBJECT
private:
QString m_name;
QString m_code;
int m_id;
public:
explicit stock(QObject *parent = 0);
QString name();
void setname(QString &name);
QString code();
void setcode(QString &code);
int id();
void setid(int &id);
};
When I run the application I get the following message in application output
QQmlExpression: Expression qrc:/main.qml:16:20 depends on non-NOTIFYable properties:
QQuickComboBox::data
and I do not get anything in combobox!
If I create another contextual property in main.cpp in this way
engine.rootContext()->setContextProperty("myModel", QVariant::fromValue(data));
and set myModel as model for combobox, it works fine. But I want to do it in this way because onCurrentIndexChanged I will call another function that returns another QList<QObject*> for a TableView of another qml file.
EDIT: Entrie qml
import QtQuick 2.5
import QtQuick.Window 2.2
import QtQuick.Controls 2.0
Window {
visible: true
width:600
height:600
property string contentSource
Column{
spacing:10
ComboBox{
model: data.stockModel
textRole: "code"
}
Loader {
id: content
width: parent.width
height:400
}
Row{
spacing:10
Button{
text: "Page1"
onClicked: content.source = "Page1.qml"
}
Button{
text: "Page2"
onClicked: content.source = "Page2.qml"
}
}
}
}
By changing data to dataStore in main.cpp and data.stockModel to dataStore.stockModel in main.qml I get following error
file:///C:/Qt/Qt5.7.0/5.7/mingw53_32/qml/QtQuick/Controls.2/ComboBox.qml:62:15: Unable to assign [undefined] to QString
You have two issues:
Your stockModel property should be NOTIFYable, which means that you should define the property with e.g. Q_PROPERTY(QList<QObject*> stockModel READ stockModel NOTIFY stockModelChanged) and provide a void stockModelChanged(const QList<QObject *> &) signal in the DbCon class.
stock::name() must be a property too, so you need to declare that with Q_PROPERTY(QString name READ name NOTIFY nameChanged) and provide a void nameChanged(const QString &) signal in the stock class as well.

How to show on QML (Qt) from SQLite BLOB data as image?

My code is like below.
Name - as TEXT field,
Photo - as BLOB data
class SqlQueryModel: public QSqlQueryModel
{
Q_OBJECT
QHash<int,QByteArray> *hash;
public:
explicit SqlQueryModel(QObject * parent) : QSqlQueryModel(parent)
{
hash = new QHash<int,QByteArray>;
hash->insert(Qt::UserRole, QByteArray("Name"));
hash->insert(Qt::UserRole + 1, QByteArray("Photo"));
}
inline RoleNameHash roleNames() const { return *hash; }
};
Selecting data
view = new QQuickView();
QSqlQueryModel *someSqlModel = new SqlQueryModel(this);
someSqlModel->setQuery("SELECT Name, Photo FROM some_table");
QQmlContext *context = view->rootContext();
context->setContextProperty("someSqlModel", someSqlModel);
view->setSource(QUrl("qrc:///MainView.qml"));
view->show();
Binding in QML
ListView {
id: someListView
model: SqlContactModel {}
delegate: ItemDelegate {
text: Name
Image {
id: Photo
source: ???
}
}
}
How to show on QML (Qt) from SQLite BLOB data as image?
You have three options:
let the model hand out some ID and use that with a QQuickImageProvider
let the model hand out a QImage and write a custom item that can display that
let the model hand out the image data as a data URI
For (2) the simplest solution is a QQuickPaintedItem derived class, something like this
class QImageItem : public QQuickPaintedItem
{
Q_OBJECT
Q_PROPERTY(QImage image READ image WRITE setImage NOTIFY imageChanged)
public:
explicit QImageItem(QQuickItem *parent = Q_NULLPTR) : QQuickPaintedItem(parent) {}
QImage image() const { return m_image; }
void setImage(const QImage &image);
void paint(QPainter *painter) Q_DECL_OVERRIDE;
private:
QImage m_image;
};
void QImageItem::setImage(const QImage &image)
{
m_image = image;
emit imageChanged();
update();
setImplicitWidth(m_image.width());
setImplicitHeight(m_image.height());
}
void QImageItem::paint(QPainter *painter)
{
if (m_image.isNull()) return;
painter.drawImage(m_image.scaled(width(), height()));
}
Register as usual with qmlRegisterType<QImageItem>("SomeModuleName", 1, 0, "SomeTypeName") and in QML import SomeModuleName 1.0 and use SomeTypeName in instead of Image, with the QImage returned by the model bound to the item's image property

Qt signal/slot connection does not work

I am having a very strange problem with QObject::connect method. First please take a look at this very straightforward code:
class B : public QWidget {
Q_OBJECT
public:
explicit B(QWidget* parent = 0) : QWidget(parent) { }
signals:
void event();
}
class A : public QObject {
Q_OBJECT
public:
explicit A(QWidget* parent = 0) : QObject(parent) { b = new B(parent); init(); }
void init() { QObject::connect(b, SIGNAL(event()), this, SLOT(handler())); }
public slots:
void handler() { /*spit out some text*/ }
private:
B* b;
}
An object of A does not respond to signals emitted from object of B. I am confident that the signal is emitted as expected. The QObject::connect method return true indicating success. I ran qmake, moc and the moc_.cpp* files seems correct.
I wonder where my did I make mistake?
Edit I:
Here is the code I am working on, it is stripped down so only the relevant parts are shown:
class ListController : public QObject {
Q_OBJECT
public:
explicit ListController(Model* model, QWidget* parent = 0) : QObject(parent) { compositeView = new CompositeView(parent); initConnections(); }
void initConnections() { QObject::connect(compositeView->getListView(), SIGNAL(event()), this, SLOT(handler())); }
public slots:
void handler() { qDebug()<<"signal is received ..."; }
private:
CompositeView* view;
};
class CompositeView: public QGroupBox {
Q_OBJECT
public:
explicit CompositeView(QWidget* parent = 0) : QGroupBox(parent) { listView = new ListView(this); }
ListView* getListView() const { return listView; }
private:
ListView* listView;
};
class ListView : public QListWidget {
Q_OBJECT
public:
explicit ListView(QWidget* parent = 0) : QListWidget(parent) { }
protected:
void dropEvent(QDropEvent *event) { emit signal(); }
signals:
void signal();
};
I create a new ListController object inside a QWidget subclass passing itself as a parent and providing an appropriate Model object.
Edit II
The ListController returns CompositeView object to the main widget. the main widget adds the the composite view to its layout. At this point the parent of the CompositeView and its children is changed. Which might be the source of the problem.
The answer of this problem was way much easier than I expected.
I think I made a mistake of doing the following steps:
ListController is created on the stack.
The CompositeView object is returned and added to the main widget layout.
ListController object goes silently out of scope and gets destroyed and consequently the connection.
Comment 13 from the top was actually the solution. Thanks a lot tmpearce for your advice.
singals:
void signal();
I doubt, if it the actual code you are working, then please check the typo error.

Resources