【问题标题】:QT/QML c++ Program crash on access a QList from QMLQT/QML c++ 程序在从 QML 访问 QList 时崩溃
【发布时间】:2016-03-08 09:28:53
【问题描述】:

我有 2 个用于数据处理的类(CGameList 和 CGame)。

我在 qml 中定义了一个 GameList (_gamelist) 对象来使用它。
我有一个 Listviews 显示这个 GameList (editGames_open()) 中的游戏。

如果我单击此列表的一个条目,它会打开一个包含此游戏详细视图的新列表 (editGame_open(index))。

这项工作按预期工作。

现在我的问题:
如果我回到列表并尝试再次打开它,我的程序会崩溃(不是每次,有时它会工作 20 倍)。 调用 getGame 后出现崩溃。

如果我使用调试器,我可以看到我的 CGameList 对象看起来很好(数据正确 + 我的 QList 中的项目是正确的),但是在此之后程序崩溃并出现分段错误。
调用堆栈仅显示 QQMlData::wasDeleted 作为最后一个条目。

我认为问题是,我的对象已删除,但我找不到。

我曾尝试将我的 QList 从 QList _games 更改为 QList* _games 但没有成功。

另一件事(我认为是同样的问题): 有时 getGame 会返回一个 NULL 指针(虽然游戏在列表中,但数据有误)。

cgamelist.h

#ifndef CGAMELIST_H
#define CGAMELIST_H

#include <QObject>
#include <QList>
#include <qfile.h>
#include <QTextStream>
#include <QDir>
#include <QStandardPaths>
#include <QDateTime>


#include <cgame.h>

class CGameList : public QObject
{
    Q_OBJECT
    Q_PROPERTY(int itemCount READ getItemCount)
public:
    CGameList(QObject *parent = 0);
    Q_INVOKABLE bool addGame(QString name,int layout);
    Q_INVOKABLE int getItemCount() const;
    Q_INVOKABLE void saveGame(int index);
    Q_INVOKABLE void loadGames(bool force=true);
    Q_INVOKABLE CGame* getGame(int i) const;
    Q_INVOKABLE QString getGamename(int i) const;
    Q_INVOKABLE QString getGamedate(int i) const;
    Q_INVOKABLE void delGame(int i);
private:
    QList<CGame*>* _games;
};

#endif // CGAMELIST_H

cgamelist.cpp

CGameList::CGameList(QObject *parent) : QObject(parent)
{
    _games=new QList<CGame*>();
    _games->clear();
}
...
CGame* CGameList::getGame(int i) const
{

   /* CGame*g=new CGame();
    g->setGamename("test");
    return g;*/
    try
    {
        return _games->at(i);
    }
    catch(...)
    {
        return NULL;
    }
}
...

cgame.h*

#ifndef CGAME_H
#define CGAME_H

#include <QObject>
#include <QString>
#include <QDateTime>

class CGame : public QObject
{
    Q_OBJECT
    Q_PROPERTY(QString gamename READ getGamename WRITE setGamename)
    Q_PROPERTY(int itemCount READ getItemCount)
    Q_PROPERTY(int layout READ getLayout WRITE setLayout)
    Q_PROPERTY(int duration READ getDuration WRITE setDuration)
    Q_PROPERTY(QDateTime date READ getDate WRITE setDate)
public:
    CGame(QObject *parent = 0);
    Q_INVOKABLE QString getGamename() const;
    Q_INVOKABLE void setGamename(QString name);
    Q_INVOKABLE int getItemCount() const;
    Q_INVOKABLE int getLayout() const;
    Q_INVOKABLE void setLayout(int layout);
    Q_INVOKABLE int getDuration() const;
    Q_INVOKABLE void setDuration(int duration);
    Q_INVOKABLE QDateTime getDate() const;
    Q_INVOKABLE void setDate(QDateTime date);

    Q_INVOKABLE QString getEvent(int i) const;
    Q_INVOKABLE void addEvent(QString ename,int time,int sec,int duration);

private:
    QString _name;
    int _layout;
    int _duration;
    QList<QString>* _events;
    QDateTime _date;
};

#endif // CGAME_H

ma​​in.cpp

#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QtQml>

#include "clayoutlist.h"
#include "clayout.h"
#include "clayoutitem.h"
#include "cgame.h"
#include "cgamelist.h"

int main(int argc, char *argv[])
{
    QGuiApplication app(argc, argv);
    //QApplication app(argc, argv);

    QQmlApplicationEngine engine;
    qmlRegisterType<CLayoutList>("STSP.Tag",1,0,"TagLayoutList");
    qmlRegisterType<CLayout>("STSP.Tag",1,0,"TagLayout");
    qmlRegisterType<CLayoutItem>("STSP.Tag",1,0,"TagLayoutItem");
    qmlRegisterType<CGame>("STSP.Tag",1,0,"Game");
    qmlRegisterType<CGameList>("STSP.Tag",1,0,"GameList");
    engine.load(QUrl(QStringLiteral("qrc:/main.qml")));

    return app.exec();
}

ma​​in.qml

import QtQuick 2.5
import QtQuick.Controls 1.4
import QtQuick.Dialogs 1.2
import QtQuick.Window 2.2

import STSP.Tag 1.0

ApplicationWindow {
    TagLayoutList {
        id: _layoutlist
    }
    GameList
    {
        id:_gamelist
    }


    visible: true

    MainForm {
        id: mainform
        anchors.fill: parent
    }
    MessageDialog{
        id:info

    }
    ...
    function editGames_open()
    {
        _gamelist.loadGames()
        mainform.p_gameslistmodel.clear()
        var i
        for (i = 0; i < _gamelist.getItemCount(); i++) {

            mainform.p_gameslistmodel.append({
                                                  name: _gamelist.getGamename(i),
                                                  date: _gamelist.getGamedate(i),
                                                  gameindex: i
                                              })
        }
        mainform.p_editgames.delmode=false
        mainform.p_editgames.visible = true
        mainform.p_startmenu.visible=false
    }
    function editGame_open(index)
    {
        mainform.p_eventlistmodel.clear()
        var game={}
        try
        {
        game=_gamelist.getGame(index)
        }
        catch(exc)
        {
            console.log("Serious Error 2 "+exc)
            return
        }

        if(game==null)
        {
            console.log("Reload Games")
            _gamelist.loadGames()
            game=_gamelist.getGame(index)
        }
        if(game==null)
        {
            console.log("Error Game not found")
            return
        }

        var i
        var event
        var events
        var t,s,m,h
        for(i=0;i<game.getItemCount();i++)
        {
            event=game.getEvent(i).split('#')[1]
            //console.log(event)
            events=event.split(',')
            t=events[1]
            s=t%60
            t=(t-s)/60
            m=t%60
            h=(t-m)/60
            mainform.p_eventlistmodel.append({
                                                  name: events[0],
                                                  time: h+":"+(m<10?"0":"")+m+":"+(s<10?"0":"")+s,
                                                  eventindex: i
                                              })
        }
        mainform.p_editgame.gname=game.getGamename()
        t=game.getDuration()
        s=t%60
        t=(t-s)/60
        m=t%60
        h=(t-m)/60
        mainform.p_editgame.gtime=h+":"+(m<10?"0":"")+m+":"+(s<10?"0":"")+s
        mainform.p_editgames.delmode=false
        mainform.p_editgames.visible=false
        mainform.p_editgame.visible=true

    }

    function editGame_back()
    {
        mainform.p_editgame.visible=false
        mainform.p_editgames.visible=true
    }

【问题讨论】:

  • 很高兴您发布了代码来显示问题,但是您省略了足够多的代码,以至于无法让它运行。 :p 你应该试着把它缩小到一个更小的例子。

标签: c++ qt qml


【解决方案1】:

不能调试你的程序很难说,但听起来 QML 是你的CGame 对象的taking ownership:

当数据从 C++ 传输到 QML 时,数据的所有权始终属于 C++。此规则的例外是从显式 C++ 方法调用返回 QObject:在这种情况下,QML 引擎假定对象的所有权,除非已通过调用 QQmlEngine: 将对象的所有权明确设置为保留在 C++ 中:指定了 QQmlEngine::CppOwnership 的 setObjectOwnership()。

此外,QML 引擎尊重 Qt C++ 对象的正常 QObject 父所有权语义,并且永远不会取得已经有父对象的 QObject 实例的所有权。

最简单的解决方案是在将每个 CGame 对象返回给 QML 之前为其分配一个父对象。或者,您可以对每个对象执行以下操作:

QQmlEngine::setObjectOwnership(game, QQmlEngine::CppOwnership);

【讨论】:

  • 感谢这个技巧,我更改了我的代码和测试人员。直到现在我也没有崩溃,它按预期工作。
  • 谢谢!向我的对象添加父对象也对我有帮助:不再崩溃。
【解决方案2】:

我认为您将从 C++ 创建您的 QList,并像 contextProperty 一样放入 qml(您不需要在 qml 中声明该类,但您仍然需要注册类型),但问题是您需要刷新(再次执行对于模型中的每一次更改,contextPropertie 的声明语句,是的,是更好的用户 QObject*。

也许最好尝试使用 QmlListProperty(我的情况是 Use QQmlListProperty to show and modify QList in Qml),在 youtube 中存在其他使用该类的教程)或 QAbstractListModel(我以前没有使用过该类,但告诉我稍微好一点某些情况下)。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-12-06
    • 2021-03-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多