the image_model.rs is working

The basic functions are all working properly. Now the model works by
using diesel to connect the sql database and retrieve all the items
and organize them. Then it'll ensure any additions and deletions are
correct and happen first on the database before adding them to the
model.

There is still a C++ proxyModel inbetween QML and Rust, but
this proxyModel interfaces with the Rust model instead of the C++
SqlTableModel.
This commit is contained in:
Chris Cochrun 2023-04-06 05:49:28 -05:00
parent caed6e6367
commit fc2d0492fa
9 changed files with 170 additions and 79 deletions

2
Cargo.lock generated
View file

@ -272,6 +272,7 @@ dependencies = [
"cxx-qt-lib", "cxx-qt-lib",
"diesel", "diesel",
"dirs", "dirs",
"libsqlite3-sys",
"serde", "serde",
"serde_derive", "serde_derive",
] ]
@ -282,6 +283,7 @@ version = "0.24.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "898745e570c7d0453cc1fbc4a701eb6c662ed54e8fec8b7d14be137ebeeb9d14" checksum = "898745e570c7d0453cc1fbc4a701eb6c662ed54e8fec8b7d14be137ebeeb9d14"
dependencies = [ dependencies = [
"cc",
"pkg-config", "pkg-config",
"vcpkg", "vcpkg",
] ]

View file

@ -23,6 +23,7 @@ cxx-qt-lib = "0.5.1"
# home = "0.5.4" # home = "0.5.4"
dirs = "5.0.0" dirs = "5.0.0"
diesel = { version = "2.0.3", features = ["sqlite"] } diesel = { version = "2.0.3", features = ["sqlite"] }
libsqlite3-sys = { version = ">=0.17.2, <0.26.0", features = ["bundled"] }
# ffmpeg-next = "6.0.0" # ffmpeg-next = "6.0.0"
# cxx-qt-build generates C++ code from the `#[cxx_qt::bridge]` module # cxx-qt-build generates C++ code from the `#[cxx_qt::bridge]` module

View file

@ -208,14 +208,15 @@ QVariantMap ImageSqlModel::getImage(const int &row) {
ImageProxyModel::ImageProxyModel(QObject *parent) ImageProxyModel::ImageProxyModel(QObject *parent)
:QSortFilterProxyModel(parent) :QSortFilterProxyModel(parent)
{ {
m_imageModel = new ImageSqlModel; m_imageModel = new ImageModel;
m_imageModel->testDatabase();
setSourceModel(m_imageModel); setSourceModel(m_imageModel);
setDynamicSortFilter(true); setDynamicSortFilter(true);
setFilterRole(Qt::UserRole + 1); setFilterRole(1);
setFilterCaseSensitivity(Qt::CaseInsensitive); setFilterCaseSensitivity(Qt::CaseInsensitive);
} }
ImageSqlModel *ImageProxyModel::imageModel() { ImageModel *ImageProxyModel::imageModel() {
return m_imageModel; return m_imageModel;
} }
@ -226,21 +227,21 @@ QModelIndex ImageProxyModel::idx(int row) {
} }
QVariantMap ImageProxyModel::getImage(const int &row) { QVariantMap ImageProxyModel::getImage(const int &row) {
auto model = qobject_cast<ImageSqlModel *>(sourceModel()); auto model = qobject_cast<ImageModel *>(sourceModel());
QVariantMap image = model->getImage(mapToSource(index(row, 0)).row()); QVariantMap image = model->getItem(mapToSource(index(row, 0)).row());
return image; return image;
} }
void ImageProxyModel::deleteImage(const int &row) { void ImageProxyModel::deleteImage(const int &row) {
auto model = qobject_cast<ImageSqlModel *>(sourceModel()); auto model = qobject_cast<ImageModel *>(sourceModel());
model->deleteImage(row); model->removeItem(row);
} }
void ImageProxyModel::deleteImages(const QVector<int> &rows) { void ImageProxyModel::deleteImages(const QVector<int> &rows) {
auto model = qobject_cast<ImageSqlModel *>(sourceModel()); auto model = qobject_cast<ImageModel *>(sourceModel());
qDebug() << "DELETING!!!!!!!!!!!!!!!!!!!!!!!" << rows; qDebug() << "DELETING!!!!!!!!!!!!!!!!!!!!!!!" << rows;
for (int i = rows.size() - 1; i >= 0; i--) { for (int i = rows.size() - 1; i >= 0; i--) {
qDebug() << "deleting" << rows.at(i); qDebug() << "deleting" << rows.at(i);
model->deleteImage(rows.at(i)); model->removeItem(rows.at(i));
} }
} }

View file

@ -8,6 +8,7 @@
#include <qqml.h> #include <qqml.h>
#include <qurl.h> #include <qurl.h>
#include <qvariant.h> #include <qvariant.h>
#include "cxx-qt-gen/image_model.cxxqt.h"
class ImageSqlModel : public QSqlTableModel class ImageSqlModel : public QSqlTableModel
{ {
@ -50,13 +51,13 @@ private:
class ImageProxyModel : public QSortFilterProxyModel class ImageProxyModel : public QSortFilterProxyModel
{ {
Q_OBJECT Q_OBJECT
Q_PROPERTY(ImageSqlModel *imageModel READ imageModel) Q_PROPERTY(ImageModel *imageModel READ imageModel)
public: public:
explicit ImageProxyModel(QObject *parent = nullptr); explicit ImageProxyModel(QObject *parent = nullptr);
~ImageProxyModel() = default; ~ImageProxyModel() = default;
ImageSqlModel *imageModel(); ImageModel *imageModel();
Q_INVOKABLE QModelIndex idx(int row); Q_INVOKABLE QModelIndex idx(int row);
public slots: public slots:
@ -65,7 +66,7 @@ public slots:
Q_INVOKABLE void deleteImages(const QVector<int> &rows); Q_INVOKABLE void deleteImages(const QVector<int> &rows);
private: private:
ImageSqlModel *m_imageModel; ImageModel *m_imageModel;
}; };

View file

@ -142,8 +142,6 @@ int main(int argc, char *argv[])
// QScopedPointer<QQuickView> preswin(new QQuickView); // QScopedPointer<QQuickView> preswin(new QQuickView);
QScopedPointer<ServiceItemModel> serviceItemModel(new ServiceItemModel); QScopedPointer<ServiceItemModel> serviceItemModel(new ServiceItemModel);
QScopedPointer<SlideObj> slideobject(new SlideObj); QScopedPointer<SlideObj> slideobject(new SlideObj);
QScopedPointer<ImageModel> imageModel(new ImageModel);
imageModel.get()->testDatabase();
Settings *settings = new Settings; Settings *settings = new Settings;
settings->setup(); settings->setup();

View file

@ -90,7 +90,7 @@ Item {
itemIcon: "folder-pictures-symbolic" itemIcon: "folder-pictures-symbolic"
itemSubtitle: { itemSubtitle: {
if (fileValidation) if (fileValidation)
model.filePath; model.path;
else else
"file is missing" "file is missing"
} }
@ -299,11 +299,11 @@ Item {
} }
function addImg(url) { function addImg(url) {
imageProxyModel.imageModel.newImage(url); imageProxyModel.imageModel.newItem(url);
selectedLibrary = "image"; selectedLibrary = "image";
imageLibraryList.currentIndex = imageProxyModel.imageModel.rowCount(); imageLibrary.libraryList.currentIndex = imageProxyModel.imageModel.count();
console.log(imageProxyModel.imageModel.getImage(imageLibraryList.currentIndex)); console.log(imageProxyModel.getImage(imageLibrary.libraryList.currentIndex));
const image = imageProxyModel.imageModel.getImage(imageLibraryList.currentIndex); const image = imageProxyModel.getImage(imageLibrary.libraryList.currentIndex);
showPassiveNotification("newest image: " + image.title); showPassiveNotification("newest image: " + image.title);
if (!editMode) if (!editMode)
editMode = true; editMode = true;

View file

@ -52,6 +52,10 @@ Controls.Page {
Component.onCompleted: { Component.onCompleted: {
changeServiceItem(0); changeServiceItem(0);
presentation.forceActiveFocus(); presentation.forceActiveFocus();
imageProxyModel.setSourceModel(ImageModel);
console.log("^^^^^");
console.log(imageProxyModel.model);
console.log("^^^^^");
/* const loaded = ServiceItemModel.loadLastSaved(); */ /* const loaded = ServiceItemModel.loadLastSaved(); */
/* if (!loaded) */ /* if (!loaded) */
/* showPassiveNotification("Failed loading last file"); */ /* showPassiveNotification("Failed loading last file"); */

View file

@ -1,5 +1,12 @@
#[cxx_qt::bridge] #[cxx_qt::bridge]
mod image_model { mod image_model {
use crate::image_model::image_model::Image;
use crate::models::*;
use crate::schema::images::dsl::*;
use diesel::sqlite::SqliteConnection;
use diesel::{delete, insert_into, prelude::*};
use std::path::{Path, PathBuf};
unsafe extern "C++" { unsafe extern "C++" {
include!(< QAbstractListModel >); include!(< QAbstractListModel >);
include!("cxx-qt-lib/qhash.h"); include!("cxx-qt-lib/qhash.h");
@ -10,6 +17,8 @@ mod image_model {
type QVariant = cxx_qt_lib::QVariant; type QVariant = cxx_qt_lib::QVariant;
include!("cxx-qt-lib/qstring.h"); include!("cxx-qt-lib/qstring.h");
type QString = cxx_qt_lib::QString; type QString = cxx_qt_lib::QString;
include!("cxx-qt-lib/qurl.h");
type QUrl = cxx_qt_lib::QUrl;
include!("cxx-qt-lib/qmodelindex.h"); include!("cxx-qt-lib/qmodelindex.h");
type QModelIndex = cxx_qt_lib::QModelIndex; type QModelIndex = cxx_qt_lib::QModelIndex;
include!("cxx-qt-lib/qvector.h"); include!("cxx-qt-lib/qvector.h");
@ -30,6 +39,7 @@ mod image_model {
#[cxx_qt::qobject(base = "QAbstractListModel")] #[cxx_qt::qobject(base = "QAbstractListModel")]
#[derive(Default, Debug)] #[derive(Default, Debug)]
pub struct ImageModel { pub struct ImageModel {
highest_id: i32,
images: Vec<self::Image>, images: Vec<self::Image>,
} }
@ -51,12 +61,6 @@ mod image_model {
// use crate::entities::{images, prelude::Images}; // use crate::entities::{images, prelude::Images};
// use sea_orm::{ConnectionTrait, Database, DbBackend, DbErr, Statement, ActiveValue}; // use sea_orm::{ConnectionTrait, Database, DbBackend, DbErr, Statement, ActiveValue};
use crate::models::*;
use diesel::prelude::*;
use diesel::sqlite::SqliteConnection;
use std::path::PathBuf;
use crate::image_model::image_model::Image;
impl qobject::ImageModel { impl qobject::ImageModel {
#[qinvokable] #[qinvokable]
pub fn clear(mut self: Pin<&mut Self>) { pub fn clear(mut self: Pin<&mut Self>) {
@ -68,61 +72,128 @@ mod image_model {
} }
#[qinvokable] #[qinvokable]
pub fn test_database(&self) { pub fn test_database(mut self: Pin<&mut Self>) {
use crate::schema::images::dsl::*; let db = &mut self.as_mut().get_db();
const DATABASE_URL: &str = "sqlite:///home/chris/.local/share/librepresenter/Libre Presenter/library-db.sqlite3";
const DB_NAME: &str = "library_db";
let db = &mut SqliteConnection::establish(DATABASE_URL)
.unwrap_or_else(|_| panic!("error connecting to {}", DATABASE_URL));
let results = images let results = images
.load::<crate::models::Image>(db) .load::<crate::models::Image>(db)
.expect("Error loading images"); .expect("Error loading images");
self.as_mut().set_highest_id(0);
println!("SHOWING IMAGES"); println!("SHOWING IMAGES");
println!("--------------");
for image in results { for image in results {
println!("{}", image.title); println!("{}", image.title);
println!("{}", image.id); println!("{}", image.id);
println!("--------------\n");
println!("{}", image.path); println!("{}", image.path);
println!("--------------");
if self.as_mut().highest_id() < &image.id {
self.as_mut().set_highest_id(image.id);
} }
let img = self::Image {
id: image.id,
title: QString::from(&image.title),
path: QString::from(&image.path),
};
self.as_mut().add_image(img);
}
println!("--------------------------------------");
println!("{:?}", self.as_mut().images());
println!("--------------------------------------");
} }
#[qinvokable] #[qinvokable]
pub fn remove_item(mut self: Pin<&mut Self>, index: i32) { pub fn remove_item(mut self: Pin<&mut Self>, index: i32) -> bool {
if index < 0 || (index as usize) >= self.images().len() { if index < 0 || (index as usize) >= self.images().len() {
return; return false;
} }
let db = &mut self.as_mut().get_db();
let image_id = self.images().get(index as usize).unwrap().id;
let result = delete(images.filter(id.eq(image_id))).execute(db);
match result {
Ok(_i) => {
unsafe { unsafe {
self.as_mut() self.as_mut()
.begin_remove_rows(&QModelIndex::default(), index, index); .begin_remove_rows(&QModelIndex::default(), index, index);
self.as_mut().images_mut().remove(index as usize); self.as_mut().images_mut().remove(index as usize);
self.as_mut().end_remove_rows(); self.as_mut().end_remove_rows();
} }
println!("removed-item-at-index: {:?}", image_id);
println!("new-Vec: {:?}", self.as_mut().images());
true
}
Err(_e) => {
println!("Cannot connect to database");
false
}
}
}
fn get_db(self: Pin<&mut Self>) -> SqliteConnection {
const DATABASE_URL: &str = "sqlite:///home/chris/.local/share/librepresenter/Libre Presenter/library-db.sqlite3";
SqliteConnection::establish(DATABASE_URL)
.unwrap_or_else(|_| panic!("error connecting to {}", DATABASE_URL))
// self.rust().db = db;
} }
#[qinvokable] #[qinvokable]
pub fn add_item(mut self: Pin<&mut Self>, id: i32, title: QString, path: QString) { pub fn new_item(mut self: Pin<&mut Self>, url: QUrl) {
const DATABASE_URL: &str = "sqlite://library-db.sqlite3"; println!("LETS INSERT THIS SUCKER!");
const DB_NAME: &str = "library_db"; let file_path = PathBuf::from(url.path().to_string());
let name = file_path.file_stem().unwrap().to_str().unwrap();
let image_id = self.rust().highest_id + 1;
let image_title = QString::from(name);
let image_path = url.to_qstring();
let db = SqliteConnection::establish(DATABASE_URL) if self.as_mut().add_item(image_id, image_title, image_path) {
.unwrap_or_else(|_| panic!("error connecting to {}", DATABASE_URL)); println!("filename: {:?}", name);
self.as_mut().set_highest_id(image_id);
} else {
println!("Error in inserting item");
}
}
let image = self::Image { id, title, path }; #[qinvokable]
// let model = images::ActiveModel { pub fn add_item(
// id: ActiveValue::set(id), mut self: Pin<&mut Self>,
// title: ActiveValue::set(title.to_string()), image_id: i32,
// path: ActiveValue::set(path.to_string()), image_title: QString,
// ..Default::default() image_path: QString,
// }; ) -> bool {
// let res = Images::insert(model).exec(db).await?; let db = &mut self.as_mut().get_db();
// println!("{:?}", db);
let image = self::Image {
id: image_id,
title: image_title.clone(),
path: image_path.clone(),
};
println!("{:?}", image);
let result = insert_into(images)
.values((
id.eq(&image_id),
title.eq(&image_title.to_string()),
path.eq(&image_path.to_string()),
))
.execute(db);
println!("{:?}", result);
match result {
Ok(_i) => {
self.as_mut().add_image(image); self.as_mut().add_image(image);
println!("{:?}", self.as_mut().images());
// Ok(()) true
}
Err(_e) => {
println!("Cannot connect to database");
false
}
}
} }
fn add_image(mut self: Pin<&mut Self>, image: self::Image) { fn add_image(mut self: Pin<&mut Self>, image: self::Image) {
@ -136,27 +207,41 @@ mod image_model {
} }
} }
#[qinvokable] // #[qinvokable]
pub fn insert_item( // pub fn insert_item(
mut self: Pin<&mut Self>, // mut self: Pin<&mut Self>,
id: i32, // image_id: i32,
title: QString, // image_title: QString,
path: QString, // image_path: QString,
index: i32, // index: i32,
) { // ) {
let image = Image { id, title, path }; // let image = Image {
// id: image_id,
// title: image_title,
// path: image_path,
// };
// let db = self.db();
self.as_mut().insert_image(image, index); // let i = image_id;
} // let t = image_title.to_string();
// let p = image_path.to_string();
fn insert_image(mut self: Pin<&mut Self>, image: self::Image, id: i32) { // use crate::schema::images::dsl::*;
unsafe { // let result = insert_into(images)
self.as_mut() // .values((id.eq(&i), title.eq(&t), path.eq(&p)))
.begin_insert_rows(&QModelIndex::default(), id, id); // .execute(db);
self.as_mut().images_mut().insert(id as usize, image);
self.as_mut().end_insert_rows(); // self.as_mut().insert_image(image, index);
} // }
}
// fn insert_image(mut self: Pin<&mut Self>, image: self::Image, id: i32) {
// unsafe {
// self.as_mut()
// .begin_insert_rows(&QModelIndex::default(), id, id);
// self.as_mut().images_mut().insert(id as usize, image);
// self.as_mut().end_insert_rows();
// }
// }
#[qinvokable] #[qinvokable]
pub fn get_item(self: Pin<&mut Self>, index: i32) -> QMap_QString_QVariant { pub fn get_item(self: Pin<&mut Self>, index: i32) -> QMap_QString_QVariant {
@ -252,7 +337,7 @@ mod image_model {
let mut roles = QHash_i32_QByteArray::default(); let mut roles = QHash_i32_QByteArray::default();
roles.insert(0, cxx_qt_lib::QByteArray::from("id")); roles.insert(0, cxx_qt_lib::QByteArray::from("id"));
roles.insert(1, cxx_qt_lib::QByteArray::from("title")); roles.insert(1, cxx_qt_lib::QByteArray::from("title"));
roles.insert(2, cxx_qt_lib::QByteArray::from("path")); roles.insert(2, cxx_qt_lib::QByteArray::from("filePath"));
roles roles
} }

View file

@ -4,6 +4,5 @@ use diesel::prelude::*;
pub struct Image { pub struct Image {
pub id: i32, pub id: i32,
pub title: String, pub title: String,
#[diesel(column_name = "filePath")]
pub path: String, pub path: String,
} }