2.Qt Quick-QML地图引擎之v4版本(新增多模型切换/3D模型欧拉角模拟)

在上个版本Qt quick-QML地图引擎之v4版本(新增3D模型/高德/谷歌在线/离线预览/多线程离线裁剪下载/区域查询/位置搜索/路径规划)_qt 高德地图离线_诺谦的博客-CSDN博客更新了很多小功能。经过朋友们一致需求建议,所以V4继续优化。

链接:

百度网盘 请输入提取码百度网盘为您提供文件的网络备份、同步和分享服务。空间大、速度快、安全稳固,支持教育网加速,支持手机端。注册使用百度网盘即可享受免费存储空间icon-default.png?t=O83Ahttps://pan.baidu.com/s/1CrI9B-KZFxZXiYWhiOadcA?pwd=44ky提取码:44ky

B站视频: Qt Quick-QML地图引擎之v4版本(新增3D模型/抗锯齿下载优化)_哔哩哔哩_bilibili

GIF如下所示: 


2024/10/17 - 新增模型各种模拟角度

csdn限制图片最多5MB以内,只能上传模糊动图:


 


1.多边形下载-抗锯齿

V3旧版本裁剪后的如下图所示:

V4版本裁剪后的如下图所示:

增加抗锯齿后,裁剪明显没有锯齿了。

2.3D模型

但是由于Qt地图常常用于嵌入式系统,或者就是无人机系统,需要实时展示物体/飞行器实时姿态和经纬度,由于市面上QT的大部分都是2D物体模拟,比如:

所以V4增加3D模型实现,这样就可以显示具体的姿态信息,这样更加地直观,欧拉角姿态如下图所示:

截图如下所示:

支持各种倾斜、旋转、纠正视角:

 支持跨平台、Linux/嵌入式/mac一键编译:

也可以支持显示四轴无人机、雷达等等各种3D模型、

由于本人没有实际设备获取,所以数据通过轨迹线模拟生成的,模拟代码如下所示:

    // 启动导航 list<coordinate>
    function startNavigator(pathData) {
        editEnable = false
        navigationLine.path = []  
        navigationTimer.path = pathData
        navigationTimer.currentIdx = 0
        navigationTimer.initializedData(true);
        navigationTimer.restart()
        navigatorHintPopup.open();      // 提示
    }

    Timer {
        id: navigationTimer
        interval: 200
        running: false
        repeat: true

        property var path           // 路径
        property int currentIdx     // 当前到哪个路径
        property real arc           // 弧点
        property var end            // 结束点
        property var begin          // 起始点
        property var current        // 当前点
        property int currentStepCnt: 0

        property real longStep      // 步数
        property real latStep

        property bool longAdd       // 增减关系
        property bool latAdd

        onTriggered: {

            currentStepCnt+=1

            current.latitude = currentStepCnt*latStep + navigationTimer.begin.latitude
            current.longitude = currentStepCnt*longStep + navigationTimer.begin.longitude
 

            let intervalY = latAdd ? current.latitude - end.latitude : end.latitude - current.latitude;
            let intervalX = longAdd ? current.longitude - end.longitude : end.longitude - current.longitude;
  
            if(intervalX>0 || intervalY>0) {
                target.center = end
                current = end

                navigation3DModel.nextCoordinate(current)
                navigationLine.replaceCoordinate(currentIdx,current)

                if((currentIdx+1) >= navigationTimer.path.length) {
                    navigationTimer.stop();
                    navigatorHintPopup.close();    
                    editEnable = true
                } else {
                    initializedData(); 
                    let less = Math.sqrt(Math.pow(intervalY,2) + Math.pow(intervalX,2))

                    navigationTimer.begin.longitude += less * Math.cos(navigationTimer.arc)
                    navigationTimer.begin.latitude += less * Math.sin(navigationTimer.arc)
                    navigation3DModel.nextCoordinate(navigationTimer.begin)

                }

            } else {
                target.center = current
                navigation3DModel.nextCoordinate(current)

                if(navigation3DModel.visible)
                  navigationLine.replaceCoordinate(currentIdx, navigation3DModel.coordinate)
            }
        }

        // 初始化数据
        function initializedData(first = false) {

            navigationTimer.begin = navigationTimer.path[currentIdx];
            let end = navigationTimer.path[currentIdx+1];

            let arc = Math.atan2((end.latitude - navigationTimer.begin.latitude), (end.longitude - navigationTimer.begin.longitude));

            navigationTimer.longStep = navigatorSpeed * Math.cos(arc)
            navigationTimer.latStep = navigatorSpeed * Math.sin(arc)

            navigationTimer.longAdd =  navigationTimer.longStep > 0 ? true : false
            navigationTimer.latAdd =  navigationTimer.latStep > 0 ? true : false

            navigationTimer.currentStepCnt = 0

            console.log("开始导航", currentIdx, end.longitude , end.latitude , begin.longitude, begin.latitude)

            navigationTimer.current = navigationTimer.begin
            navigationTimer.end = end
            navigationTimer.arc = arc
            if(currentIdx == 0) navigationLine.addCoordinate(navigationTimer.begin)          
            navigationLine.addCoordinate(navigationTimer.begin)

            navigation3DModel.initCoordinate(navigationTimer.begin, end, first)

            currentIdx += 1
            navigatorHintPopup.label = `路径点${currentIdx}->路径点${currentIdx+1} 正在导航中...`;      // 提示

        }
    }

2023/06/06 - 离线地图加载- 增加支持多个离线地图厂家

 支持加密和非加密,并且加密和非加密加载速度一样的,如下图所示,使用加密后的离线文件数据:

2024/02/29 - 绘制工具新增抗锯齿

之前版本:

优化后:

2024/10/17 - 新增多模型加载

下个版本实现:

3.Qt Quick-QML地图引擎之v4.3版本(新增动态轨迹线/海图/天地图街道/天地图卫星)

<think>好的,我现在需要帮助用户在QML的TableView中显示自定义Python模型的表头。用户提到参考文档中的columnAtIndex方法,但根据之前的对话,用户可能更关注如何将Python模型QMLTableView集成,特别是表头的显示。首先,我需要回忆QtQML与Python模型的交互方式。Qt通常使用QAbstractItemModel或其子类作为数据模型,而QML的TableView需要模型提供特定的角色数据,包括表头信息。用户可能已经创建了一个自定义的Python模型,需要将其暴露给QML,并正确显示表头。接下来,查阅用户提供的引用内容。引用[1]提到使用QStandardItemModel处理表头排序和上下文菜单,引用[3]解释了QML的基本概念,如元素、属性和信号槽机制。引用[4]讨论了图表组件的应用,可能与当前问题无关,但需要注意用户可能是在工业控制系统中应用,因此需要代码的实时性和可靠性。 用户的问题涉及如何在QML中显示Python模型的表头,所以需要确保Python模型正确实现了headerData方法。在Qt中,表头数据通过headerData角色提供,包括水平(列)和垂直(行)表头。因此,自定义Python模型需要重写headerData方法,返回相应的表头文本。 然后,考虑如何将Python模型暴露给QMLQtQML引擎可以通过setContextProperty或注册QML类型的方式将Python模型导入QML环境。用户需要确保模型实例在QML中可用。接下来,QML的TableView需要设置delegate来显示表头和单元格内容。对于表头,可能需要使用TableView的rowDelegate或columnDelegate,或者单独使用HeaderView组件。但根据Qt6文档,TableView本身不直接支持表头,可能需要结合其他组件,如HorizontalHeaderView和VerticalHeaderView。查看用户提供的文档链接,columnAtIndex方法返回指定列的列对象,但不确定是否直接用于表头显示。可能需要使用其他方法或属性来设置表头。因此,解决方案的大致步骤是:1.在Python模型中实现headerData方法,提供表头文本。2.将Python模型注册到QML上下文中。3.QML中使用TableView和HeaderView组件,绑定到模型的headerData。需要验证这些步骤是否正确,特别是HeaderView的使用方式。同时,考虑到用户可能使用的是Qt6,需要确保引用的组件和方法在Qt6中仍然有效。另外,用户之前的回答中提到columnWidthProvider用于动态列宽,可能需要结合该功能,但当前问题主要关注表头的显示。需要分步骤说明如何实现表头,并确保代码示例正确无误。 最后,确保回答结构清晰,分步骤说明,并提供完整的示例代码,同时检查是否有遗漏的细节,如模型角色定义、QML的导入语句等。</think>### 在QML TableView中显示自定义Python模型的表头实现方法 #### 一、Python模型的核心实现要求 1. **必须继承`QAbstractTableModel`**: ```python from PySide6.QtCore import QAbstractTableModel, Qt class CustomModel(QAbstractTableModel): def __init__(self, data, headers, parent=None): super().__init__(parent) self._data = data # 二维数据数组 self._headers = headers # 表头数组 ``` 2. **实现`headerData`方法**: ```python def headerData(self, section: int, orientation: Qt.Orientation, role: int): if role == Qt.DisplayRole: if orientation == Qt.Horizontal: return self._headers[section] # 返回水平表头文本 elif orientation == Qt.Vertical: return str(section + 1) # 返回垂直行号 return None ``` 3. **必须定义`rowCount`和`columnCount`**: ```python def rowCount(self, parent=QModelIndex()): return len(self._data) def columnCount(self, parent=QModelIndex()): return len(self._headers) if self._data else 0 ``` #### 二、QML端集成步骤 1. **注册模型QML上下文**: ```python # main.py from PySide6.QtQml import QQmlApplicationEngine engine = QQmlApplicationEngine() model = CustomModel(data=[["A1","B1"],["A2","B2"]], headers=["Header1","Header2"]) engine.rootContext().setContextProperty("customModel", model) ``` 2. **使用`TableView`与`HorizontalHeaderView`组件**: ```qml import QtQuick 2.15 import QtQuick.Controls 2.15 ApplicationWindow { width: 400 height: 300 visible: true // 水平表头组件 HorizontalHeaderView { id: header anchors.top: parent.top syncView: tableView model: customModel delegate: Text { text: model.headerData ?? "" font.bold: true padding: 8 } } // 表格主体 TableView { id: tableView anchors.top: header.bottom anchors.bottom: parent.bottom width: parent.width model: customModel delegate: Text { text: model.display padding: 8 } } } ``` #### 三、关键技术细节 1. **模型角色映射**: - 在Python模型中实现`data`方法时,需明确返回`Qt.DisplayRole`数据 ```python def data(self, index, role=Qt.DisplayRole): if role == Qt.DisplayRole: return self._data[index.row()][index.column()] return None ``` 2. **动态更新处理**: -模型数据变化时发送信号: ```python def update_data(self, new_data): self.beginResetModel() self._data = new_data self.endResetModel() ``` 3. **性能优化公式**: 当处理大型数据集时,可采用视口渲染优化: $$ \text{可见行数} = \lceil \frac{\text{TableView高度}}{\text{行高}} \rceil + 2 $$ 通过`cacheBuffer`属性预加载额外数据[^3] #### 四、完整示例代码 ##### Python端: ```python # model.py from PySide6.QtCore import Qt, QAbstractTableModel class CustomTableModel(QAbstractTableModel): def __init__(self, data, headers): super().__init__() self._data = data self._headers = headers def headerData(self, section, orientation, role=Qt.DisplayRole): if role == Qt.DisplayRole: if orientation == Qt.Horizontal: return self._headers[section] return str(section + 1) return None def rowCount(self, parent=None): return len(self._data) def columnCount(self, parent=None): return len(self._headers) def data(self, index, role=Qt.DisplayRole): if role == Qt.DisplayRole: return self._data[index.row()][index.column()] return None ``` ##### QML端: ```qml // main.qml import QtQuick import QtQuick.Controls import QtQuick.Layouts ApplicationWindow { visible: true width: 640 height: 480 ColumnLayout { anchors.fill: parent // 水平表头 HorizontalHeaderView { id: horizontalHeader Layout.fillWidth: true syncView: tableView model: customModel delegate: Rectangle { color: "#f0f0f0" border.width: 1 Text { text: model.headerData anchors.centerIn: parent } implicitWidth: 120 implicitHeight: 40 } } // 表格主体 TableView { id: tableView Layout.fillWidth: true Layout.fillHeight: true model: customModel delegate: Rectangle { border.width: 1 color: "white" Text { text: model.display anchors.centerIn: parent } implicitWidth: 120 implicitHeight: 40 } } } } ``` #### 五、调试技巧 1. **验证模型数据流**: ```qml Component.onCompleted: { console.log("模型行数:", tableView.model.rowCount()) console.log("第一列表头:", tableView.model.headerData(0, Qt.Horizontal)) } ``` 2. **动态更新测试**: ```python # 在Python中更新数据 model.update_data([["NewA1","NewB1"],["NewA2","NewB2"]]) ``` 3. **性能监控方法**: ```qml Timer { interval: 1000 running: true repeat: true onTriggered: console.log("渲染帧率:", 1.0 / (frameTime / 1000)) } ``` ---
评论 13
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

诺谦

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值