QT QML import ListModel from C++ to QML - qt

How can i change the model of a PathView with c++ code ?
i add an objectName to my pathView to find it, then i change the property like this, but when i do that, my list is empty :
QDeclarativeItem *listSynergie = myClass.itemMain->findChild<QDeclarativeItem*> ("PathViewInscription");
listSynergie->setProperty("model", QVariant::fromValue(dataList));
My data list is fill like this :
QList<QObject*> dataList;
dataList.append(new synergieListObject("Item 1", "1",false));
dataList.append(new synergieListObject("Item 2", "2",true));
dataList.append(new synergieListObject("Item 3", "3",false));
dataList.append(new synergieListObject("Item 4", "4",false));
This is the code of my PathView :
PathView {
objectName: "PathViewInscription"
Keys.onRightPressed: if (!moving) { incrementCurrentIndex(); console.log(moving) }
Keys.onLeftPressed: if (!moving) decrementCurrentIndex()
id: view
anchors.fill: parent
highlight: Image { source: "../spinner_selecter.png"; width: view.width; height: itemHeight+4; opacity:0.3}
preferredHighlightBegin: 0.5
preferredHighlightEnd: 0.5
focus: true
model: appModel
delegate: appDelegate
dragMargin: view.width/2
pathItemCount: height/itemHeight
path: Path {
startX: view.width/2; startY: -itemHeight/2
PathLine { x: view.width/2; y: view.pathItemCount*itemHeight + itemHeight }
}
}
and the code of ListModel :
ListModel {
id: appModel
ListElement { label: "syn1"; value: "1"; selected:false}
ListElement { label: "syn2"; value: "2" ; selected:false}
ListElement { label: "syn3"; value: "3" ; selected:false}
}
what's wrong ?
Thanks !
EDIT :
the code of the appDelegate :
Component {
id: appDelegate
Item {
width: 100; height: 100
Text {
anchors { horizontalCenter: parent.horizontalCenter }
text: label
smooth: true
}
MouseArea {
anchors.fill: parent
onClicked: view.currentIndex = index
}
}
}
the code of my object :
#ifndef SYNERGIELISTOBJECT_H
#define SYNERGIELISTOBJECT_H
#include <QObject>
class synergieListObject : public QObject
{
Q_OBJECT
Q_PROPERTY(QString label READ label WRITE setLabel NOTIFY labelChanged)
Q_PROPERTY(QString value READ value WRITE setValue NOTIFY valueChanged)
Q_PROPERTY(bool selected READ selected WRITE setSelected NOTIFY selectedChanged)
public:
synergieListObject(QObject *parent=0);
synergieListObject(const QString &label,const QString &value,bool selected, QObject *parent=0);
QString label() const;
void setLabel(const QString &label);
QString value() const;
void setValue(const QString &value);
bool selected() const;
void setSelected(const bool &selected);
signals:
void labelChanged();
void valueChanged();
void selectedChanged();
private:
QString m_label;
QString m_value;
bool m_selected;
};
#endif // SYNERGIELISTOBJECT_H
c++ my object :
#include "synergielistobject.h"
synergieListObject::synergieListObject(QObject *parent): QObject(parent)
{
}
synergieListObject::synergieListObject(const QString &label,const QString &value,bool selected, QObject *parent): QObject(parent), m_label(label), m_value(value), m_selected(selected)
{
}
QString synergieListObject::label() const
{
return m_label;
}
void synergieListObject::setLabel(const QString &label)
{
if (label != m_label) {
m_label = label;
emit labelChanged();
}
}
QString synergieListObject::value() const
{
return m_value;
}
void synergieListObject::setValue(const QString &value)
{
if (value != m_value) {
m_value = value;
emit valueChanged();
}
}
bool synergieListObject::selected() const
{
return m_selected;
}
void synergieListObject::setSelected(const bool &selected)
{
if (selected != m_selected) {
m_selected = selected;
emit selectedChanged();
}
}

I have never used QdeclarativeItem to set model in QML . Try this instead
QDeclarativeContext *ctxt = view.rootContext(); ctxt->setContextProperty("model", QVariant::fromValue(dataList));
Declare the model as a property of the root. This way you can set model.Or add a function that takes model as argument and sets the model for the view. Then you can call this function from c++.

Related

Set character format for a QML TextArea using QTextCursor

I'm trying to create a very simple rich text editor component in QML. The requirements are that the user must be able to quickly format individual words inside a textbox separately. So for example, the user could press "Ctrl+B" in the middle of typing and from that point onward the text becomes bold, when the user press "Ctrl+B" again the subsequently typed text has a normal font weight:
So looking around I found that there is an official example provided by the Qt company to do this. So I extrapolated this to a very basic app:
// DocumentHandler.cpp
#pragma once
#include <QObject>
#include <QQuickTextDocument>
#include <QTextCharFormat>
class DocumentHandler : public QObject {
Q_OBJECT
Q_PROPERTY(QQuickTextDocument *document READ document WRITE setDocument NOTIFY documentChanged)
Q_PROPERTY(int cursorPosition READ cursorPosition WRITE setCursorPosition NOTIFY cursorPositionChanged)
Q_PROPERTY(int selectionStart READ selectionStart WRITE setSelectionStart NOTIFY selectionStartChanged)
Q_PROPERTY(int selectionEnd READ selectionEnd WRITE setSelectionEnd NOTIFY selectionEndChanged)
Q_PROPERTY(bool modified READ modified WRITE setModified NOTIFY modifiedChanged)
Q_PROPERTY(bool bold READ bold WRITE setBold NOTIFY boldChanged)
public:
QQuickTextDocument *document() const;
void setDocument(QQuickTextDocument *newDocument);
int cursorPosition() const;
void setCursorPosition(int newCursorPosition);
int selectionStart() const;
void setSelectionStart(int newSelectionStart);
int selectionEnd() const;
void setSelectionEnd(int newSelectionEnd);
bool modified() const;
void setModified(bool newModified);
bool bold() const;
void setBold(bool newBold);
signals:
void documentChanged();
void cursorPositionChanged();
void selectionStartChanged();
void selectionEndChanged();
void modifiedChanged();
void boldChanged();
private:
void mergeFormatOnWordOrSelection(const QTextCharFormat & format);
void reset();
QTextCursor textCursor() const;
QTextDocument* textDocument() const;
QQuickTextDocument * m_document = nullptr;
int m_cursorPosition = -1;
int m_selectionStart = 0;
int m_selectionEnd = 0;
};
// DocumentHandler.cpp
#include "documenthandler.h"
#include <QTextCursor>
QQuickTextDocument *DocumentHandler::document() const
{
return m_document;
}
void DocumentHandler::setDocument(QQuickTextDocument *newDocument)
{
if (m_document == newDocument)
return;
if(m_document)
m_document->textDocument()->disconnect(this);
m_document = newDocument;
if(m_document){
QObject::connect(m_document->textDocument(), &QTextDocument::modificationChanged,
this, &DocumentHandler::modifiedChanged);
}
emit documentChanged();
}
int DocumentHandler::cursorPosition() const
{
return m_cursorPosition;
}
void DocumentHandler::setCursorPosition(int newCursorPosition)
{
if (m_cursorPosition == newCursorPosition)
return;
m_cursorPosition = newCursorPosition;
reset();
emit cursorPositionChanged();
}
int DocumentHandler::selectionStart() const
{
return m_selectionStart;
}
void DocumentHandler::setSelectionStart(int newSelectionStart)
{
if (m_selectionStart == newSelectionStart)
return;
m_selectionStart = newSelectionStart;
emit selectionStartChanged();
}
int DocumentHandler::selectionEnd() const
{
return m_selectionEnd;
}
void DocumentHandler::setSelectionEnd(int newSelectionEnd)
{
if (m_selectionEnd == newSelectionEnd)
return;
m_selectionEnd = newSelectionEnd;
emit selectionEndChanged();
}
bool DocumentHandler::modified() const
{
return m_document && m_document->textDocument()->isModified();
}
void DocumentHandler::setModified(bool newModified)
{
if (m_document)
m_document->textDocument()->setModified(newModified);
}
void DocumentHandler::mergeFormatOnWordOrSelection(const QTextCharFormat &format)
{
QTextCursor cursor = textCursor();
if (!cursor.hasSelection())
cursor.select(QTextCursor::WordUnderCursor);
cursor.mergeCharFormat(format);
}
void DocumentHandler::reset()
{
emit boldChanged();
}
QTextCursor DocumentHandler::textCursor() const
{
QTextDocument* doc = textDocument();
if(!doc)
return QTextCursor();
QTextCursor cursor = QTextCursor(doc);
if(m_selectionStart != m_selectionEnd){
cursor.setPosition(m_selectionStart);
cursor.setPosition(m_selectionEnd, QTextCursor::KeepAnchor);
}
else{
cursor.setPosition(m_cursorPosition);
}
return cursor;
}
QTextDocument *DocumentHandler::textDocument() const
{
return m_document ? m_document->textDocument() : nullptr;
}
bool DocumentHandler::bold() const
{
auto cursor = textCursor();
if(cursor.isNull())
return false;
return cursor.charFormat().fontWeight() == QFont::Bold;
}
void DocumentHandler::setBold(bool newBold)
{
QTextCharFormat format;
format.setFontWeight(newBold ? QFont::Bold : QFont::Normal);
mergeFormatOnWordOrSelection(format);
emit boldChanged();
}
// main.qml
import QtQuick 2.15
import QtQuick.Window 2.15
import QtQuick.Controls 2.15
import DocumentHandler 1.0
ApplicationWindow {
width: 640
height: 480
visible: true
title: qsTr("Hello World")
color: "lightpink"
header: ToolBar {
id: appToolbar
ToolButton {
text: "Bold"
checkable: true
checked: documentHandler.bold
onClicked: {
documentHandler.bold = !documentHandler.bold
}
}
}
DocumentHandler {
id: documentHandler
document: textInput.textDocument
cursorPosition: textInput.cursorPosition
selectionStart: textInput.selectionStart
selectionEnd: textInput.selectionEnd
}
TextArea {
id: textInput
anchors.fill: parent
font.pixelSize: 30
wrapMode: TextEdit.WrapAtWordBoundaryOrAnywhere
focus: true
selectByMouse: true
persistentSelection: true
textFormat: Qt.RichText
}
}
If you try to run this app, type something like "hello" and the press the "Bold" button the app correctly makes the "hello" bold. However, there is a problem I'm struggling to get past:
if you click on the button when the textarea is empty, then typing doesn't give you bold text. In fact, it seems that it is not possible to change the font weight of the textarea prior to having some text in it. Also, if you type a word, then press space and then click the button: nothing happens.
Anyone got any clue on what's the root cause of this behaviour?

"Unable to open file"

This is one of my first projects using QML: I started making a simple game character and a few rooms, and to make the walls'hitboxes I stored them all into a file and included a FileIO class I saw in this site, adapted to return the entire file instead of just one line.
The program, though, tells me it isn't able to open the file (I have put it in the project's directory)
//main.qml (ignore line 5)
import QtQuick 2.15
import QtQuick.Shapes 1.12
import FileIO 1.0
// Whats this? UwU a byzantiuwm?!?!!? UwU i wove byzantiuwm, especiwwawwy in the #memes channew wewe byzantiuwm is the tawk of ze town! OwO
Rectangle {
width: 639
height: 480
id: sus // I hate myself
color: "#00ff00"
Shape {
property string d: "S"
property int room: 1
id: player
x: xPos
y: yPos
z: 1
visible: true
property int xPos: 297
property int yPos: 219
parent: sus
width: 45
height: 45
focus: true
Keys.onPressed:
{
if(event.key === Qt.Key_Down){
yPos+=3; d="S"
}
if(event.key === Qt.Key_Up){
yPos-=3; d="N"
}
if(event.key === Qt.Key_Left){
xPos-=3; d="W"
}
if(event.key === Qt.Key_Right){
xPos+=3; d="E"
}
event.accepted = true;
}
ShapePath{
strokeColor: "transparent"
fillColor: "transparent"
PathLine{x:45 ;y: 0}
PathLine{x:45 ;y:45}
PathLine{x: 0 ;y:45}
}
Image {
id: sprite
parent: player
width: parent.width
height: parent.height
smooth: false
source: "Player/Player_" + player.d + ".png"
}
}
Image {
id: background
x: 0
y: 0
z: 0
width: 639
height: 480
visible: true
smooth: false
parent: sus
source: "Rooms/" + player.room + ".png"
fillMode: Image.Stretch
}
Shape {
id: wallHitBox
x: 0
y: 0
z: 50
width: sus.width
height: sus.height
//visible: false
FileIO{
id: fileReader
source: "Rooms/Hitboxes_or_something.txt"
onError: {console.log(msg)}
}
property var the: fileReader.read()
ShapePath{
strokeColor: "transparent"
fillColor: "blue" //"transparent"
PathSvg{
path: wallHitBox.the[player.room];
}
}
}
}
//fileio.cpp
#include "fileio.h"
#include <QFile>
#include <QTextStream>
FileIO::FileIO(QObject *parent) :
QObject(parent)
{
}
QVector<QString> FileIO::read()
{
if (mSource.isEmpty()){
emit error("source is empty");
return QVector<QString>();
}
QFile file(mSource);
QVector<QString> fileContent;
if ( file.open(QIODevice::ReadOnly) ) {
QString line;
QTextStream t( &file );
do {
line = t.readLine();
fileContent.resize(fileContent.size()+1);
fileContent[fileContent.size()] = line;
} while (!line.isNull());
file.close();
} else {
emit error("Unable to open the file");
return QVector<QString>();
}
return fileContent;
}
bool FileIO::write(const QString& data)
{
if (mSource.isEmpty())
return false;
QFile file(mSource);
if (!file.open(QFile::WriteOnly | QFile::Truncate))
return false;
QTextStream out(&file);
out << data;
file.close();
return true;
}
#define FILEIO_H
#include <QObject>
class FileIO : public QObject
{
Q_OBJECT
public:
Q_PROPERTY(QString source
READ source
WRITE setSource
NOTIFY sourceChanged)
explicit FileIO(QObject *parent = 0);
Q_INVOKABLE QVector<QString> read();
Q_INVOKABLE bool write(const QString& data);
QString source() { return mSource; };
public slots:
void setSource(const QString& source) { mSource = source; };
signals:
void sourceChanged(const QString& source);
void error(const QString& msg);
private:
QString mSource;
};
#endif // FILEIO_H
Can someone please help me?

returning a custom QObject subclass from QAbstractListModel and using it in a ListView

How can I return a custom QObject sub-class from QAbstractListModel and use it in a QML ListView.
I tried to return the objects as display role and I use in my qml display.property to access properties, it works fine but I saw on some posts people using model as the qobject from qml and accessing properties as model.property. Am I missing something?.
Another question: If I want to expose the object at the ListView level and using it to set some other panel like a master-view detail is exposing the role (in my case display) as a variant property in the delegate and setting it at the listview level with the onCurrentItemChanged signal is the correct way to do it ??
this is what I am trying but it does not work:
#ifndef NOTE_H
#define NOTE_H
#include <QObject>
class Note : public QObject
{
Q_OBJECT
Q_PROPERTY(QString note READ note WRITE setNote NOTIFY noteChanged)
Q_PROPERTY(int id READ id WRITE setId NOTIFY idChanged)
QString m_note;
int m_id;
public:
explicit Note(QObject *parent = 0);
Note(QString note, int id, QObject *parent = 0);
QString note() const
{
return m_note;
}
int id() const
{
return m_id;
}
signals:
void noteChanged(QString note);
void idChanged(int id);
public slots:
void setNote(QString note)
{
if (m_note == note)
return;
m_note = note;
emit noteChanged(note);
}
void setId(int id)
{
if (m_id == id)
return;
m_id = id;
emit idChanged(id);
}
};
#endif // NOTE_H
the view model:
#ifndef NOTESVIEWMODEL_H
#define NOTESVIEWMODEL_H
#include <QAbstractListModel>
#include <QVector>
#include "note.h"
class NotesViewModel : public QAbstractListModel
{
Q_OBJECT
QVector<Note*> notes;
public:
NotesViewModel();
QVariant data(const QModelIndex &index, int role) const override;
int rowCount(const QModelIndex &parent) const override;
};
#endif // NOTESVIEWMODEL_H
implementation of the view model:
NotesViewModel::NotesViewModel()
{
notes.append(new Note("note 1", 1));
notes.append(new Note("note 2", 2));
notes.append(new Note("note 3", 3));
notes.append(new Note("note 4", 4));
notes.append(new Note("note 5", 5));
}
QVariant NotesViewModel::data(const QModelIndex &index, int role) const
{
qDebug() << "fetching data : " << index.row();
if(!index.isValid()) return QVariant();
if(index.row() >= 5) return QVariant();
if(role == Qt::DisplayRole)
return QVariant::fromValue(notes[index.row()]);
return QVariant();
}
int NotesViewModel::rowCount(const QModelIndex &parent) const
{
Q_UNUSED(parent)
return notes.count();
}
Since you've already answered your second question, let me take on the first one, i.e. display.propertyName vs model.propertyName
Basically the first one is just a short hand for model.display.propertyName, i.e. the "display" property of the model's data at the given index is being accessed. In your case that returns an object, which has properties on its on.
The model.propertyName could also be written as just propertyName, meaning the model's data() method is called with "role" being the numerical equivalent for "propertyName".
This mapping from "propertyName" to its numerical equivalent is done with the QAbstractItemModel::roleNames() method.
Its default implementation has some base mappings such as mapping "display" to Qt::DisplayRole.
I played a little with the qml ListView trying to figure out how to expose the currentItem data to the outside world. This is how I managed to do it, maybe it will be of use for newbies like me.
import QtQuick 2.7
import QtQuick.Controls 2.0
import QtQuick.Layouts 1.0
ApplicationWindow {
visible: true
width: 640
height: 480
title: qsTr("Hello World")
ListModel {
id: modell
ListElement {
fname: "houssem"
age: 26
}
ListElement {
fname: "Anna"
age: 26
}
ListElement {
fname: "Nicole"
age: 26
}
ListElement {
fname: "Adam"
age: 27
}
}
ListView {
id: lv
height: 100
width: 200
clip: true
model: modell
property string selectedName: currentItem.name
delegate: Component {
Item {
id: mainItem
width: ListView.view.width
height: 80
property string name: fname
Text {
text: "name " + fname + " age " + age
}
MouseArea {
anchors.fill: parent
onClicked: mainItem.ListView.view.currentIndex = index
}
}
}
}
Text {
anchors.right: parent.right
text: lv.selectedName
}
}
As you can see in the code, to expose the data of currentItem I had just to declare properties in the delegate Item (name property of type string in the present example) and then bind a property on ListView (selectedName in this example) to the currentItem.property, this way the property on the ListView will be updated automatically when I select other items from the list and I will have access to this items form other Items of the UI.

QML Camera - take a Square photo

How can I take a photo using Camera in QML but with custom dimensions, like a square photo for example? I am trying to implement it with QQuickImageProvider but I am not understanding why it is not working.
QML code
Camera {
id: camera
captureMode: Camera.CaptureStillImage
imageCapture {
onImageCaptured: {
manipulaImagem.imagem = preview;
previewImage.source = "image://ProvedorImagens/" + preview;
//previewImage.source = manipulaImagem.recortarFotoPerfil(preview, viewfinder.mapRectToSource(Qt.rect(viewfinder.x, viewfinder.y, viewfinder.width, viewfinder.height)));
}
}
}
Image {
id: previewImage
fillMode: Image.PreserveAspectFit
anchors.top: parent.top
width: parent.width
height: parent.width
}
I am trying to resolve this problem by 2 ways:
First:
QImage manipulaImagem::requestImage(const QString &id, QSize *size, const QSize &requestedSize)
{
QUrl caminhoImagem(id);
QString imageId = caminhoImagem.path().remove(0, 1);
QImage imagem1(id);
if(imagem1.isNull())
{
qDebug() << "Erro";
}
else
{
qDebug() << "OK";
}
return imagem1;
}
I call this method from QML using a image provider: previewImage.source = "image://ProvedorImagens/" + preview;
In this function using QImage imagem1(id) or QImage imagem1(imageId), both return me a NULL image.
It returns me message: QML Image: Failed to get image from provider: image://provedorimagens/image://camera/preview_1
The other way is another function:
QImage manipulaImagem::recortarFotoPerfil(const QString &imagem)
{
QUrl caminhoImagem(imagem);
QQmlEngine *engine = QQmlEngine::contextForObject(this)->engine();
QQmlImageProviderBase *imageProviderBase = engine->imageProvider(caminhoImagem.host());
QQuickImageProvider *imageProvider = static_cast<QQuickImageProvider*>(imageProviderBase);
QSize imageSize;
QString imageId = caminhoImagem.path().remove(0, 1);
QImage imagem1 = imageProvider->requestImage(imageId, &imageSize, imageSize);
imagem1 = imageProvider->requestImage(imageId, &imageSize, imageSize);
if(imagem1.isNull())
{
qDebug() << "Erro";
}
else
{
qDebug() << "OK";
}
return imagem1;
}
This function gets the image but when I return it I receive a message:
Error: Cannot assign QImage to QUrl
I call this function directly from QML:
previewImage.source = manipulaImagem.recortarFotoPerfil(preview);
Thanks for the help.
Answering my own question
Problem solved. Here is the solution step by step:
1 - Create a class that inherits from QQuickImageProvider and QObject and inside it create a Image member (QImage) that is the image to be provided.
class provedorImagem : public QObject, public QQuickImageProvider
Implement the virtual requestImage method. This is the method that will return the image to Qml
QImage requestImage(const QString &id, QSize *size, const QSize &requestedSize)
Create a method to load the provider’s image to return
void provedorImagem::carregaImagem(QImage imagemRecebida)
{
imagem = imagemRecebida;
}
Now set it as the engine image provider in the main.cpp file
provedorImagem *provedorImg = new provedorImagem;
engine.rootContext()->setContextProperty("ProvedorImagem", provedorImg);
2 - Create another class that inherits from QObject.
class processaImagem : public QObject
Inside this class you must implement a method that will get the image from camera provider, perform the image modifications and return the modified image.
PS: The p_caminhoImagem is a property that I created inside the processaImagem class that receives the camera preview path.
QImage processaImagem::carregaImagem()
{
QUrl caminhoImagem(p_caminhoImagem);
QQmlEngine *engine = QQmlEngine::contextForObject(this)->engine();
QQmlImageProviderBase *imageProviderBase = engine->imageProvider(caminhoImagem.host());
QQuickImageProvider *imageProvider = static_cast<QQuickImageProvider*>(imageProviderBase);
QSize imageSize;
QString imageId = caminhoImagem.path().remove(0, 1);
QImage imagem = imageProvider->requestImage(imageId, &imageSize, imageSize);
if(imagem.isNull())
{
imagem = QImage();
}
else
{
//Perform the modifications
}
return imagem;
}
3 - Now is the main part. The image requestImage provider method must receive the modified image from the processaImagem class to provide it to QML. To do it the provider class pointer must be accessible to the QML file, so, in the main.cpp file just make the pointer available to QML as a property
engine.rootContext()->setContextProperty("ProvedorImagem", provedorImg);
and register the processaImagem class as a QML type
qmlRegisterType<processaImagem>("ProcessaImagemQml", 1, 0, "ProcessaImagem");
Now we link it inside the QML file
ProvedorImagem.carregaImagem(processaImagem.carregaImagem());
4 - It is done. Now just request the image from the provider:
imagemPreview.source = "image://provedor/imagemEditada_" + camera.numeroImagem.toString();
Here is the entire code:
main.cpp
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QtQml>
#include "processaimagem.h"
#include "provedorimagem.h"
int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);
qmlRegisterType<processaImagem>("ProcessaImagemQml", 1, 0, "ProcessaImagem");
QQmlApplicationEngine engine;
provedorImagem *provedorImg = new provedorImagem;
engine.rootContext()->setContextProperty("ProvedorImagem", provedorImg);
engine.addImageProvider("provedor", provedorImg);
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
return app.exec();
}
main.qml
import QtQuick 2.4
import QtQuick.Window 2.2
import QtQuick.Controls 1.3
import QtMultimedia 5.4
import ProcessaImagemQml 1.0
Window {
visible: true
width: 360
height: 640
maximumHeight: 640
minimumHeight: 640
maximumWidth: 360
minimumWidth: 360
title: "Camera Preview Test"
Rectangle {
id: principal
anchors.fill: parent
ProcessaImagem {
id: processaImagem
caminhoImagem: camera.caminhoPreview
caminhoSalvar: camera.caminhoSalvar
rectRecorte: camera.rectRecorte
tamanhoImagem: camera.tamanhoImagem
anguloOrientacaoCamera: camera.orientation
posicaoCamera: camera.position
onCaminhoImagemChanged: {
rectRecorte = cameraView.mapRectToSource(Qt.rect(cameraView.x, cameraView.y, cameraView.width, cameraView.height));
tamanhoImagem = Qt.size(cameraView.sourceRect.width, cameraView.sourceRect.height);
ProvedorImagem.carregaImagem(processaImagem.carregaImagem());
}
onCaminhoSalvarChanged: {
removeImagemSalva();
}
}
Rectangle {
id: cameraRectangle
width: parent.width
height: parent.width
anchors.top: parent.top
color: "lightGrey"
visible: true
Camera {
id: camera
property string caminhoPreview: ""
property string caminhoSalvar: ""
property int numeroImagem: 0
captureMode: Camera.CaptureStillImage
imageCapture {
onImageCaptured: {
camera.caminhoPreview = preview;
camera.stop();
imagemPreview.source = "image://provedor/imagemEditada_" + camera.numeroImagem.toString();
camera.numeroImagem = camera.numeroImagem + 1;
imagemPreviewRectangle.visible = true;
cameraRectangle.visible = false;
}
onImageSaved: {
camera.caminhoSalvar = path;
}
}
}
VideoOutput {
id: cameraView
visible: true
focus: visible
anchors.fill: parent
source: camera
orientation: camera.orientation
fillMode: VideoOutput.PreserveAspectCrop
}
}
Rectangle {
id: imagemPreviewRectangle
width: parent.width
height: parent.width
anchors.top: parent.top
color: "lightGrey"
visible: false
Image {
id: imagemPreview
fillMode: Image.PreserveAspectFit
anchors.fill: parent
}
}
Rectangle {
id: controleRectangle
width: parent.width
height: parent.height - cameraRectangle.height
color: "grey"
anchors.top: cameraRectangle.bottom
Button {
id: tirarFotoButton
text: "Tirar foto"
anchors.left: parent.left
anchors.top: parent.top
onClicked: {
camera.imageCapture.capture();
}
}
Button {
id: novaFotoButton
text: "Tirar nova foto"
anchors.right: parent.right
anchors.top: parent.top
onClicked: {
camera.start();
imagemPreviewRectangle.visible = false;
cameraRectangle.visible = true;
}
}
}
}
}
processaimagem.h
#ifndef PROCESSAIMAGEM_H
#define PROCESSAIMAGEM_H
#include <QObject>
#include <QImage>
#include <QQmlEngine>
#include <QQmlContext>
#include <QQuickImageProvider>
#include <QFile>
#include "provedorimagem.h"
class processaImagem : public QObject
{
Q_OBJECT
Q_PROPERTY(QString caminhoImagem READ caminhoImagem WRITE setCaminhoImagem NOTIFY caminhoImagemChanged)
Q_PROPERTY(QString caminhoSalvar READ caminhoSalvar WRITE setCaminhoSalvar NOTIFY caminhoSalvarChanged)
Q_PROPERTY(QRect rectRecorte READ rectRecorte WRITE setRectRecorte NOTIFY rectRecorteChanged)
Q_PROPERTY(QSize tamanhoImagem READ tamanhoImagem WRITE setTamanhoImagem NOTIFY tamanhoImagemChanged)
Q_PROPERTY(int anguloOrientacaoCamera READ anguloOrientacaoCamera WRITE setAnguloOrientacaoCamera NOTIFY anguloOrientacaoCameraChanged)
Q_PROPERTY(int posicaoCamera READ posicaoCamera WRITE setPosicaoCamera NOTIFY posicaoCameraChanged)
public slots:
QImage carregaImagem();
void removeImagemSalva();
public:
processaImagem(QObject *parent = 0);
QString caminhoImagem() const;
void setCaminhoImagem(const QString valor);
QString caminhoSalvar() const;
void setCaminhoSalvar(const QString valor);
QRect rectRecorte() const;
void setRectRecorte(const QRect valor);
QSize tamanhoImagem() const;
void setTamanhoImagem(const QSize valor);
int anguloOrientacaoCamera() const;
void setAnguloOrientacaoCamera(const int valor);
int posicaoCamera() const;
void setPosicaoCamera(const int valor);
private:
QString p_caminhoImagem = "";
QString p_caminhoSalvar = "";
QRect p_rectRecorte = QRect(0, 0, 0, 0);
QSize p_tamanhoImagem = QSize(0, 0);
int p_anguloOrientacaoCamera = 0;
int p_posicaoCamera = 0;
signals:
void caminhoImagemChanged();
void caminhoSalvarChanged();
void rectRecorteChanged();
void tamanhoImagemChanged();
void anguloOrientacaoCameraChanged();
void posicaoCameraChanged();
};
#endif // PROCESSAIMAGEM_H
processaimagem.cpp
#include "processaimagem.h"
#include <QDebug>
processaImagem::processaImagem(QObject *parent)
{
}
QImage processaImagem::carregaImagem()
{
QUrl caminhoImagem(p_caminhoImagem);
QQmlEngine *engine = QQmlEngine::contextForObject(this)->engine();
QQmlImageProviderBase *imageProviderBase = engine->imageProvider(caminhoImagem.host());
QQuickImageProvider *imageProvider = static_cast<QQuickImageProvider*>(imageProviderBase);
QSize imageSize;
QString imageId = caminhoImagem.path().remove(0, 1);
QImage imagem = imageProvider->requestImage(imageId, &imageSize, imageSize);
if(imagem.isNull())
{
qDebug() << "Erro ao carregar a imagem";
imagem = QImage();
}
else
{
if((p_anguloOrientacaoCamera == 90) || (p_anguloOrientacaoCamera == 270))
{
int larguraImagem = p_tamanhoImagem.width();
int alturaImagem = p_tamanhoImagem.height();
p_tamanhoImagem.setWidth(alturaImagem);
p_tamanhoImagem.setHeight(larguraImagem);
int recorteX = p_rectRecorte.x();
int recorteY = p_rectRecorte.y();
int recorteLargura = p_rectRecorte.width();
int recorteAltura = p_rectRecorte.height();
p_rectRecorte.setRect(recorteY, recorteX, recorteAltura, recorteLargura);
if(imagem.size().width() > imagem.size().height())
{
QTransform rotacao;
rotacao.rotate(360 - p_anguloOrientacaoCamera);
imagem = imagem.transformed(rotacao);
qDebug() << "Rodou";
}
}
if(imagem.width() != p_tamanhoImagem.width())
{
imagem = imagem.scaled(p_tamanhoImagem);
}
imagem = imagem.copy(p_rectRecorte);
}
return imagem;
}
void processaImagem::removeImagemSalva()
{
QFile::remove(p_caminhoSalvar);
}
QString processaImagem::caminhoImagem() const
{
return p_caminhoImagem;
}
void processaImagem::setCaminhoImagem(const QString valor)
{
if (valor != p_caminhoImagem)
{
p_caminhoImagem = valor;
emit caminhoImagemChanged();
}
}
QString processaImagem::caminhoSalvar() const
{
return p_caminhoSalvar;
}
void processaImagem::setCaminhoSalvar(const QString valor)
{
if (valor != p_caminhoSalvar)
{
p_caminhoSalvar = valor;
emit caminhoSalvarChanged();
}
}
QRect processaImagem::rectRecorte() const
{
return p_rectRecorte;
}
void processaImagem::setRectRecorte(const QRect valor)
{
bool alterou = false;
if (valor.x() != p_rectRecorte.x())
{
p_rectRecorte.setX(valor.x());
alterou = true;
}
if (valor.y() != p_rectRecorte.y())
{
p_rectRecorte.setY(valor.y());
alterou = true;
}
if (valor.width() != p_rectRecorte.width())
{
p_rectRecorte.setWidth(valor.width());
alterou = true;
}
if (valor.height() != p_rectRecorte.height())
{
p_rectRecorte.setHeight(valor.height());
alterou = true;
}
if(alterou)
{
emit rectRecorteChanged();
}
}
QSize processaImagem::tamanhoImagem() const
{
return p_tamanhoImagem;
}
void processaImagem::setTamanhoImagem(const QSize valor)
{
bool alterou = false;
if (valor.width() != p_tamanhoImagem.width())
{
p_tamanhoImagem.setWidth(valor.width());
alterou = true;
}
if (valor.height() != p_tamanhoImagem.height())
{
p_tamanhoImagem.setHeight(valor.height());
alterou = true;
}
if(alterou)
{
emit tamanhoImagemChanged();
}
}
int processaImagem::anguloOrientacaoCamera() const
{
return p_anguloOrientacaoCamera;
}
void processaImagem::setAnguloOrientacaoCamera(const int valor)
{
if (valor != p_anguloOrientacaoCamera)
{
p_anguloOrientacaoCamera = valor;
emit anguloOrientacaoCameraChanged();
}
}
int processaImagem::posicaoCamera() const
{
return p_posicaoCamera;
}
void processaImagem::setPosicaoCamera(const int valor)
{
if (valor != p_posicaoCamera)
{
p_posicaoCamera = valor;
emit posicaoCameraChanged();
}
}
provedorimagem.h
#ifndef PROVEDORIMAGEM_H
#define PROVEDORIMAGEM_H
#include <QObject>
#include <QImage>
#include <QQuickImageProvider>
class provedorImagem : public QObject, public QQuickImageProvider
{
Q_OBJECT
public:
provedorImagem();
QImage requestImage(const QString &id, QSize *size, const QSize &requestedSize);
public slots:
void carregaImagem(QImage imagemRecebida);
private:
QImage imagem;
};
#endif // PROVEDORIMAGEM_H
provedorimagem.cpp
#include "provedorimagem.h"
#include <QDebug>
provedorImagem::provedorImagem() : QQuickImageProvider(QQuickImageProvider::Image)
{
}
QImage provedorImagem::requestImage(const QString &id, QSize *size, const QSize &requestedSize)
{
if(imagem.isNull())
{
qDebug() << "Erro ao prover a imagem";
}
return imagem;
}
void provedorImagem::carregaImagem(QImage imagemRecebida)
{
imagem = imagemRecebida;
}

Remove rows from QAbstractListModel

I have a custom model which derives from QAbstractListModel which is exposed to QML. I need to support operations to add new items and remove existing items. While insertion operation works without any problems, removal operation causes the application to crash while calling endRemoveRows() function.
void GPageModel::addNewPage()
{
if(m_pageList.count()<9)
{
beginInsertRows(QModelIndex(),rowCount(),rowCount());
GPage * page = new GPage();
QQmlEngine::setObjectOwnership(page,QQmlEngine::CppOwnership);
page->setParent(this);
page->setNumber(m_pageList.count());
page->setName("Page " + QString::number(m_pageList.count()+1));
m_pageList.append(page);
endInsertRows();
}
}
void GPageModel::removePage(const int index)
{
if(index>=0 && index<m_pageList.count())
{
beginRemoveRows(QModelIndex(),index,index);
qDebug()<<QString("beginRemoveRows(QModelIndex(),%1,%1)").arg(index);
GPage * page = m_pageList.at(index);
m_pageList.removeAt(index);
delete page;
endRemoveRows();
}
}
The class GPage derives from QObject. I am struck trying to figure out what is causing the app to crash while trying to call endRemoveRows(). I get "ASSERT failure in QList::at: "index out of range"" when endRemoveRows() is called.How do I remove the rows from a QAbstracListModel? Is there any other way?
I am using Qt 5.1.0 on a Windows 7 64 bit machine.
The code below works fine for me. Your problem is probably elsewhere. This is for Qt 5 due to use of Qt Quick Controls.
There are two views accessing the same model, this visually confirms that the model emits proper signals to inform the views of the changes. The page additions and removals are done via the standard insertRows and removeRows methods, exported through Q_INVOKABLE. There's no need for any custom methods on this model, so far. The Q_INVOKABLE is a workaround for some missing functionality for the interface between QML and QAbstractItemModel.
main.cpp
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQuickWindow>
#include <QAbstractListModel>
#include <QQmlContext>
#include <QtQml>
class GPage : public QObject {
Q_OBJECT
Q_PROPERTY(QString name NOTIFY nameChanged MEMBER m_name)
Q_PROPERTY(int number NOTIFY numberChanged MEMBER m_number)
QString m_name;
int m_number;
public:
GPage(QObject * parent = 0) : QObject(parent), m_number(0) {}
GPage(QString name, int number, QObject * parent = 0) :
QObject(parent), m_name(name), m_number(number) {}
Q_SIGNAL void nameChanged(const QString &);
Q_SIGNAL void numberChanged(int);
};
class PageModel : public QAbstractListModel {
Q_OBJECT
QList<GPage*> m_pageList;
public:
PageModel(QObject * parent = 0) : QAbstractListModel(parent) {}
~PageModel() { qDeleteAll(m_pageList); }
int rowCount(const QModelIndex &) const Q_DECL_OVERRIDE {
return m_pageList.count();
}
QVariant data(const QModelIndex &index, int role) const Q_DECL_OVERRIDE {
if (role == Qt::DisplayRole || role == Qt::EditRole) {
return QVariant::fromValue<QObject*>(m_pageList.at(index.row()));
}
return QVariant();
}
bool setData(const QModelIndex &index, const QVariant &value, int role) Q_DECL_OVERRIDE {
Q_UNUSED(role);
GPage* page = value.value<GPage*>();
if (!page) return false;
if (page == m_pageList.at(index.row())) return true;
delete m_pageList.at(index.row());
m_pageList[index.row()] = page;
QVector<int> roles;
roles << role;
emit dataChanged(index, index, roles);
return true;
}
Q_INVOKABLE bool insertRows(int row, int count, const QModelIndex &parent = QModelIndex()) Q_DECL_OVERRIDE {
Q_UNUSED(parent);
beginInsertRows(QModelIndex(), row, row + count - 1);
for (int i = row; i < row + count; ++ i) {
QString const name = QString("Page %1").arg(i + 1);
GPage * page = new GPage(name, i + 1, this);
m_pageList.insert(i, page);
QQmlEngine::setObjectOwnership(page, QQmlEngine::CppOwnership);
}
endInsertRows();
return true;
}
Q_INVOKABLE bool removeRows(int row, int count, const QModelIndex &parent = QModelIndex()) Q_DECL_OVERRIDE {
Q_UNUSED(parent);
beginRemoveRows(QModelIndex(), row, row + count - 1);
while (count--) delete m_pageList.takeAt(row);
endRemoveRows();
return true;
}
};
int main(int argc, char *argv[])
{
PageModel model1;
QGuiApplication app(argc, argv);
QQmlApplicationEngine engine;
model1.insertRows(0, 1);
engine.rootContext()->setContextProperty("model1", &model1);
qmlRegisterType<GPage>();
engine.load(QUrl("qrc:/main.qml"));
QObject *topLevel = engine.rootObjects().value(0);
QQuickWindow *window = qobject_cast<QQuickWindow *>(topLevel);
window->show();
return app.exec();
}
#include "main.moc"
main.qml
import QtQuick 2.0
import QtQml.Models 2.1
import QtQuick.Controls 1.0
ApplicationWindow {
width: 300; height: 300
Row {
width: parent.width
anchors.top: parent.top
anchors.bottom: column.top
Component {
id: commonDelegate
Rectangle {
width: view.width
implicitHeight: editor.implicitHeight + 10
color: "transparent"
border.color: "red"
border.width: 2
radius: 5
TextInput {
id: editor
anchors.margins: 1.5 * parent.border.width
anchors.fill: parent
text: edit.name // "edit" role of the model, to break the binding loop
onTextChanged: {
display.name = text;
model.display = display
}
}
}
}
ListView {
id: view
width: parent.width / 2
height: parent.height
model: DelegateModel {
id: delegateModel1
model: model1
delegate: commonDelegate
}
spacing: 2
}
ListView {
width: parent.width / 2
height: parent.height
model: DelegateModel {
model: model1
delegate: commonDelegate
}
spacing: 2
}
}
Column {
id: column;
anchors.bottom: parent.bottom
Row {
Button {
text: "Add Page";
onClicked: model1.insertRows(delegateModel1.count, 1)
}
Button {
text: "Remove Page";
onClicked: model1.removeRows(pageNo.value - 1, 1)
}
SpinBox {
id: pageNo
minimumValue: 1
maximumValue: delegateModel1.count;
}
}
}
}
main.qrc
<RCC>
<qresource prefix="/">
<file>main.qml</file>
</qresource>
</RCC>

Resources