【问题标题】:Expose 2D C++ Game Board to QML using QAbstractItemModel使用 QAbstractItemModel 将 2D C++ 游戏板暴露给 QML
【发布时间】:2016-09-26 16:32:41
【问题描述】:

我正在用 C++ 编写一个简单的 Snake 游戏,其中的游戏板模型包含一个二维状态向量 (std::vector<std::vector<board::state>>)。现在我想将这个棋盘暴露给 QML,这样它基本上就是某种可以访问模型状态的网格/棋盘。 我已经阅读了很多关于这个主题的内容,但仍然无法充分理解机制来解决我的问题。将文档、教程和博客条目应用于我的问题是我的障碍。

我为我的游戏板模型继承了QAbstractItemModel 并实现了必要的功能。现在我想进入 QML 并在那里使用/显示我的模型的内容。

这是我的代码:

board.h

#pragma once

#include <vector>

#include <QAbstractItemModel>

class board : public QAbstractItemModel
{
  Q_OBJECT

public:
  enum class state
  {
    empty,
    snake,
    fruit
  };

  board(int x, int y);

  state get_state(int x, int y) const;
  void  set_state(int x, int y, state state);

  QModelIndex index(int row, int column, const QModelIndex& parent = QModelIndex()) const;
  QModelIndex parent(const QModelIndex& index) const;
  int rowCount(const QModelIndex& parent = QModelIndex()) const;
  int columnCount(const QModelIndex& parent = QModelIndex()) const;
  QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const;

private:
  std::vector<std::vector<state>> m_board;
};

Q_DECLARE_METATYPE(board::state)

board.cpp

#include "board.h"

board::board(int x, int y) :
  m_board(x, std::vector<board::state>(y, board::state::empty))
{
}

//----------------------------------------------------------------------------------------------------------------------

board::state board::get_state(int x, int y) const
{
  if((size_t) x >= m_board.size() || x < 0)
    return board::state::empty;

  if((size_t) y >= m_board.at(0).size() || y < 0)
    return board::state::empty;

  return m_board.at(x).at(y);
}

//----------------------------------------------------------------------------------------------------------------------

void board::set_state(int x, int y, state state)
{
  if(get_state(x, y) == state)
    return;

  m_board.at(x).at(y) = state;
}

//----------------------------------------------------------------------------------------------------------------------

QModelIndex board::index(int row, int column, const QModelIndex&) const
{
  if((size_t) row >= m_board.size() || row < 0)
    return QModelIndex();

  if((size_t) column >= m_board.at(0).size() || column < 0)
    return QModelIndex();

  return createIndex(row, column);
}

//----------------------------------------------------------------------------------------------------------------------

QModelIndex board::parent(const QModelIndex& index) const
{
  if((size_t) index.row() >= m_board.size() || index.row() < 0)
    return QModelIndex();

  if((size_t) index.column() >= m_board.at(0).size() || index.column() < 0)
    return QModelIndex();

  return createIndex(index.row(), index.column());
}

//----------------------------------------------------------------------------------------------------------------------

int board::rowCount(const QModelIndex&) const
{
  return m_board.size();
}

//----------------------------------------------------------------------------------------------------------------------

int board::columnCount(const QModelIndex&) const
{
  return m_board.at(0).size();
}

//----------------------------------------------------------------------------------------------------------------------

QVariant board::data(const QModelIndex& index, int role) const
{
  if(!index.isValid())
    return QVariant();

  if(role != Qt::DisplayRole)
    return QVariant();

  if((size_t) index.row() >= m_board.size() || index.row() < 0)
    return QVariant();

  if((size_t) index.column() >= m_board.at(0).size() || index.column() < 0)
    return QVariant();

  return qVariantFromValue(get_state(index.row(), index.column()));
}

ma​​in.cpp

#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQmlContext>
#include <QQuickView>

#include <board.h>

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



  board game_board(10, 10);
  game_board.set_state(4, 9, board::state::snake);
  game_board.set_state(3, 10, board::state::fruit);

  QQuickView view;
  view.setResizeMode(QQuickView::SizeRootObjectToView);
  QQmlContext *ctxt = view.rootContext();
  ctxt->setContextProperty("myGameBoard", &game_board);

  QQmlApplicationEngine engine;
  engine.load(QUrl(QStringLiteral("qrc:/main.qml")));

  return app.exec();
}

ma​​in.qml

import QtQuick 2.6
import QtQuick.Window 2.2

Window {
  visible: true

  MouseArea {
    anchors.fill: parent
    onClicked: {
      Qt.quit();
    }
  }

  Text {
    text: qsTr("Hello World")
    anchors.centerIn: parent
  }

  GridView {
    model: myGameBoard
    delegate: Rectangle {
      width: 30
      height: 30
      color: blue
    }
  }
}

我敢肯定,我遗漏了很多东西,或者是大错特错。此外,我知道将 C++ 模型暴露给 QML 是经常被问及的,并且得到了很好的覆盖,但我仍然无法管理。

陈述几个更具体的问题:

  • 如何在 QML 中显示模型中的数据?我已经看到这是使用角色名称完成的,但我的行没有其列的角色名称
  • 我的模型是否正确实施?
  • 我需要使用什么 QML 组件来拥有棋盘/棋盘,如果我需要自定义组件,它需要哪些属性才能从模型中读取数据?

感谢观看!

【问题讨论】:

  • 不投反对票或接近投票,因为我对 QML 一无所知,这对那些了解 QML 的人来说可能完全有意义,但我无法从上述问题陈述中挑选出问题陈述。您可能想要扩展或明确说明您的目标是什么以及附加的代码应该如何帮助实现目标,但没有

标签: c++ qt qml qtquick2 qt-quick


【解决方案1】:
  1. QtQuick,主要使用 QML 的 UI 框架,基本上只处理列表模型,因此在例如使用TableView

  2. parent() 方法是错误的,因为它基本上再次返回相同的索引。这不会对您的情况造成任何问题,因为您有一张桌子而不是一棵树。

建议:如果你只需要一个表模型,从QAbstractTableModel派生,让它照顾index()parent()

  1. 没有可以处理表格模型的标准 QtQuick 元素,因此您必须自己构建或使用列表模型,只需将“列表项”排列在网格中。

如果是自定义项,您可以构建一个与您的模型一起使用的项目,甚至可以直接处理数据,而无需模型。

如果您使用模型并希望从 QML 实例化模型,那么您需要“指向模型类的指针”或“指向QAbstractItemModel 的指针”的属性。

如果您不想使用模型或不需要从 QML 实例化它,那么您根本不需要任何特定属性。

在任何一种情况下,您的自定义项目都可以使用以下方法之一:

  1. 它可以自己绘制所有内容,即瓷砖网格和瓷砖本身
  2. 只需提供网格并使用代表来绘制不同类型的图块,即像ListView 允许为列表元素、页眉、页脚等设置代表。

【讨论】:

  • 感谢您的回答。我之前尝试子类化QAbstractTableModel,但遇到了这个问题,我只能通过在TableView 中手动创建TableColumns 来找到使用它的方法。我也遇到了角色问题。我的目标是让 QML 显示我的二维向量具有的任何大小,即 10x10 的游戏板和 50x50 的游戏板。问题是,我不知道如何使用角色来显示更多单元格。我目前的理解是,表格使用行,每个行都有代表列的角色,但我不知道给单元格赋予什么角色。
  • 正如我所说,QtQuick 的元素不处理表格模型,它们只处理列表模型并为单个列表项的不同方面使用角色。 TableView 类型可以直观地将不同的方面映射到不同的列中。如果你想使用标准的 QtQuick 视图,我建议使用 Grid' with a Repeater` 和从 QAbstractListModel 派生的模型。该模型还需要一个属性来指示一行中有多少项目,然后您将在 QML 中使用此值来设置Gridcolumns 属性。 IE。一维数组,将块分组到列中
【解决方案2】:

我有一个适合我的解决方案。这是我所做的和一些代码:

  • 我使用 Q_ENUM 作为状态枚举,使其在 QML 中可用
  • 将 rootContext 连接到 QML 引擎
  • 编写了一个自定义 QML 组件,该组件由一个带有转发器的列组成,其中包含一个带有转发器的行
  • 保存外转发器和内转发器的索引,以编译访问数据的索引
  • 一些清理工作

代码

Board.qml

import QtQuick 2.0

Column {
  Repeater {
    model: myGameBoard.columnCount()

    Row {
      property int y_pos: index

      Repeater {
        id: repeatr
        model: myGameBoard.rowCount()

        Cell {
          x_cord: index
          y_cord: y_pos

          size: 20
        }
      } //Repeater
    } //Row
  } //Repeater
} //Column

Cell.qml

import QtQuick 2.0

Rectangle {
  id: cell
  property int x_cord: 0
  property int y_cord: 0
  property int size: 10

  width: size
  height: size

  color: getCorrectColor()

  Connections {
    target: myGameBoard
    onDataChanged: {
      cell.color = cell.getCorrectColor()
    }
  }

  function getCorrectColor() {
    switch(myGameBoard.data(myGameBoard.index(cell.x_cord, cell.y_cord)) + 0) {
    case 0 :
      return "honeydew"
    case 1 :
      return "black"
    case 2 :
      return "orangered"
    default:
      return "yellow"
    }
  }
}

C++ 方面基本保持不变,除了在board.h 的状态枚举上使用 Q_ENUM。

感谢转发器的帮助和提示!

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-03-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多