Bootstrap

【QGIS二次开发】

 1. 系统界面设计

设计的系统界面如下,很好还原了QGIS、ArcGIS等软件的系统界面,充分利用了QT中顶部工具栏、菜单栏、底部状态栏,实现了图层管理器、鹰眼图、工具箱三个工具面板。

菜单栏、工具栏、工具箱集成了系统中实现的全部功能,并进行了功能分区,让用户能够更便捷地进行操作,底部状态栏显示了标语和QGIS工程的坐标系更方便用户的查看。

图 1 界面设计

2. 地图显示与交互

2.1 地图显示

图层显示通过在工具栏中添加按钮为用户提供了打开矢量与栅格数据的方式,打开位置如下:

图 2 地图数据显示按钮

        打开矢量数据效果如下:

图 3 打开矢量数据效果

        打开栅格数据的操作步骤和显示效果如下:

图 4 打开栅格数据界面

图 5 打开栅格数据效果

        实现数据显示的代码如下,对于矢量数据处理on_actionAddVectorData_triggered函数中用户先通过文件对话框选择一个或多个Shapefile文件,然后针对每个选择的文件,创建一个QgsVectorLayer对象并检查其有效性。如果图层有效,将其添加到地图图层列表layerList中。最后,将所有矢量图层添加到QgsProject中,并刷新地图画布map canvas。

        对于栅格数据处理on_actionAddRasterData_triggered函数:用户通过文件对话框选择一个或多个栅格文件,然后为每个选择的文件创建一个QgsRasterLayer对象并检查其有效性。如果图层有效,将其添加到地图图层列表layerList中。最后,将所有栅格图层添加到QgsProject中,并刷新地图画布。

2.2 图层管理

图层管理通过QGIS目录树形式管理地图文档中的图层,通过添加添加组、展开和折叠所有组、移除图层/组四个按键,实现了控制图层的显示与隐藏等功能,图层树的实现界面如下,打开了多个矢量文件用于展示:

图 6 图层管理功能实现

void DataViewer::on_actionAddVectorData_triggered()    
{    
    QStringList layerPathList = QFileDialog::getOpenFileNames(this, "选择矢量数据", "", "shapefile (*.shp)");    
    QList<QgsMapLayer*> layerList;    
    for each (QString layerPath in layerPathList)    
    {    
        QFileInfo fi(layerPath);    
        if (!fi.exists()) { return; }    
        QString layerBaseName = fi.baseName(); // 图层名称    
    
        QgsVectorLayer* vecLayer = new QgsVectorLayer(layerPath, layerBaseName);    
        if (!vecLayer) { return; }    
        if (!vecLayer->isValid())    
        {    
            QMessageBox::information(0, "", "layer is invalid");    
            return;    
        }    
        layerList << vecLayer;    
    }    
        
    QgsProject::instance()->addMapLayers(layerList);    
    m_mapCanvas->refresh();    
}    
    
void DataViewer::on_actionAddRasterData_triggered()    
{    
    QStringList layerPathList = QFileDialog::getOpenFileNames(this, "选择栅格数据", "", "Image (*.img *.tif *.tiff)");    
    QList<QgsMapLayer*> layerList;    
    for each (QString layerPath in layerPathList)    
    {    
        QFileInfo fi(layerPath);    
        if (!fi.exists()) { return; }    
        QString layerBaseName = fi.baseName(); // 图层名称    
    
        QgsRasterLayer* rasterLayer = new QgsRasterLayer(layerPath, layerBaseName);    
        if (!rasterLayer) { return; }    
        if (!rasterLayer->isValid())    
        {    
            QMessageBox::information(0, "", "layer is invalid");    
            return;    
        }    
        layerList << rasterLayer;    
    }    
    
    QgsProject::instance()->addMapLayers(layerList);    
    m_mapCanvas->refresh();    
}    

通过在dataviewer类中实现成员函数initLayerTreeView用于初始化地图图层树视图。通过创建图层树模型,并与当前QGIS项目的图层树根节点相关联,实现了对图层的显示和管理。代码中设置了图层树模型的各项属性,包括节点的重新排序、重命名、可见性控制等。同时,通过建立图层树与地图画布之间的桥梁,保持了它们之间的同步。为了提供更多的操作功能,代码还添加了自定义的图层树操作,如添加组、展开和折叠所有组、移除图层/组等。这些操作被整合到一个工具栏中,通过设置工具栏的图标大小和连接相应的信号槽,提供了用户友好的操作界面。整体而言,这段代码实现了一个功能完备的地理信息系统图层树控制界面,为用户提供了方便的图层管理和操作功能。

2.3 图层透明度设置

任务要求:支持用户选择一个图层,设置该图层的透明度。

        完成的逻辑是在QgsLayerTreeViewMenuProvider的实例类中添加一个editOpacity方法,实现修改透明度功能。

        代码逻辑如下:

  1. 首先获取当前选中的图层。如果没有选中的图层,就直接返回。
  2. 创建一个新的对话框,并设置其标题为"修改透明度"。
  3. 创建一个水平布局,并将其设置到对话框中。
  4. 创建一个滑动条和一个输入框,并将它们添加到布局中。滑动条的范围设置为0到100,初始值设置为100。输入框的初始值设置为1.0。
  5. 连接滑动条的valueChanged信号到一个槽函数。在这个槽函数中,首先将滑动条的值转换为0.0到1.0的透明度值,然后调用updateOpacity方法更新输入框的值,最后更新图层的透明度。
  6. 连接输入框的textChanged信号到一个槽函数。在这个槽函数中,首先将输入框的值转换为0到100的整数值,然后调用updateOpacity方法更新滑动条的值,最后更新图层的透明度。
  7. 显示对话框,并等待用户的操作。
  8. updateOpacity方法用于更新图层的透明度。首先检查图层的类型,如果图层是栅格图层,就获取图层的渲染器,然后设置渲染器的透明度,并触发图层的重绘。如果图层是矢量图层,就获取图层的渲染器,然后获取渲染器的所有符号,对每个符号设置透明度,并触发图层的重绘。

实现的效果是,在图层管理器中图层的右键菜单中添加修改透明度选项,点击修改透明度选项打开对话框,对话框内包含了一个滑块条和输入框,滑动滑块或修改输入框内的数字即可修改图层透明度。同时滑动滑块或修改输入框内的数字时另一项也随之改变。可以修改矢量图层和栅格图层的透明度。

核心代码如下:

void MenuProvider::editOpacity() {  
    QgsMapLayer* mapLayer = mView->currentLayer();  
    if (!mapLayer)  
        return;  
  
    // 创建一个新的对话框  
    QDialog dialog;  
    dialog.setWindowTitle("修改透明度");  
    // 创建一个水平布局  
    QHBoxLayout layout(&dialog);  
  
    // 创建一个滑动条  
    QSlider slider(Qt::Horizontal);  
    slider.setRange(0, 100);  
    layout.addWidget(&slider);  
    // 创建一个输入框  
    QLineEdit lineEdit;  
    layout.addWidget(&lineEdit);  
    slider.setValue(100);  
    lineEdit.setText(QString::number(1.0));  
    layout.addWidget(&slider, 3);  // 设置滑动条的拉伸因子为3  
    layout.addWidget(&lineEdit, 1);  // 设置输入框的拉伸因子为1  
    dialog.resize(300, 50);  
  
    // 连接滑动条的valueChanged信号到一个槽函数  
    QObject::connect(&slider, &QSlider::valueChanged, [&](int value) {  
        // 将滑动条的值转换为0.0到1.0的透明度值  
        double opacity = value / 100.0;  
  
        // 更新输入框的值  
        lineEdit.setText(QString::number(opacity));  
  
        // 更新图层的透明度  
        updateOpacity(mapLayer, opacity);  
        });  
  
    // 连接输入框的textChanged信号到一个槽函数  
    QObject::connect(&lineEdit, &QLineEdit::textChanged, [&](const QString& text) {  
        // 将输入框的值转换为0到100的整数值  
        int value = text.toDouble() * 100;  
  
        // 更新滑动条的值  
        slider.setValue(value);  
  
        // 更新图层的透明度  
        updateOpacity(mapLayer, text.toDouble());  
        });  
  
    // 显示对话框  
    dialog.exec();  
}  
  
void MenuProvider::updateOpacity(QgsMapLayer* mapLayer, double opacity) {  
    // 检查图层类型  
    QgsRasterLayer* rasterLayer = qobject_cast<QgsRasterLayer*>(mapLayer);  
    QgsVectorLayer* vectorLayer = qobject_cast<QgsVectorLayer*>(mapLayer);  
  
    // 调整栅格图层透明度  
    if (rasterLayer) {  
        QgsRasterRenderer* renderer = rasterLayer->renderer();  
        if (renderer) {  
            renderer->setOpacity(opacity);  
            rasterLayer->triggerRepaint();  
        }  
    }  
    // 调整矢量图层透明度  
    else if (vectorLayer) {  
        QgsFeatureRenderer* renderer = vectorLayer->renderer();  
        if (renderer) {  
            QgsRenderContext renderContext;  
            QgsSymbolList symbols = renderer->symbols(renderContext);  
            for (QgsSymbol* symbol : symbols) {  
                symbol->setOpacity(opacity);  
            }  
            vectorLayer->triggerRepaint();  
        }  
    }  
}  

实现的效果如下:

图 7 右键-透明度按键

图 8 设置透明度

        

        其余内容见下篇!!

;