WPF实战学习笔记25-首页汇总
注意:本实现与视频不一致。本实现中单独做了汇总接口,而视频中则合并到国todo接口当中了。
- 添加汇总webapi接口
- 添加汇总数据客户端接口
- 总数据客户端接口对接3
- 首页数据模型
添加数据汇总字段类
新建文件MyToDo.Share.Models.SummaryDto
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;namespace MyToDo.Share.Models
{public class SummaryDto:BaseDto{private int sum;public int Sum{get { return sum; }set { sum = value; OnPropertyChanged(); }}private int compeleteCnt;public int CompeleteCnt{get { return compeleteCnt; }set { compeleteCnt = value; OnPropertyChanged(); }}private int memoCnt;public int MemoCnt{get { return memoCnt; }set { memoCnt = value; OnPropertyChanged(); }}private string? compeleteRatio;public string? CompeleteRatio{get { return compeleteRatio; }set { compeleteRatio = value; OnPropertyChanged(); }}private ObservableCollection<ToDoDto> todoList;public ObservableCollection<ToDoDto> TodoList{get { return todoList; }set { todoList = value; }}/// <summary>/// MemoList/// </summary>private ObservableCollection<MemoDto> memoList;public ObservableCollection<MemoDto> MemoList{get { return memoList; }set { memoList = value; }}}
}
添加汇总webapi接口
添加汇总接口
添加文件:MyToDo.Api.Service.ISummary
using MyToDo.Share.Parameters;namespace MyToDo.Api.Service
{public interface ISummary{Task<ApiReponse> GetAllInfo(SummaryParameter parameter);}
}
实现汇总接口
添加文件:MyToDo.Api.Service.Summary
using Arch.EntityFrameworkCore.UnitOfWork;
using AutoMapper;
using MyToDo.Api.Context;
using MyToDo.Share.Models;
using MyToDo.Share.Parameters;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.Collections.ObjectModel;namespace MyToDo.Api.Service
{public class Summary : ISummary{private readonly IUnitOfWork work;private readonly IMapper mapper;public Summary(IUnitOfWork work,IMapper mapper){this.work = work;this.mapper = mapper;}public async Task<ApiReponse> GetAllInfo(SummaryParameter parameter){try{SummaryDto sumdto = new SummaryDto();//获取所有todo信息var Pagetodos = await work.GetRepository<Todo>().GetPagedListAsync(pageIndex: parameter.PageIndex, pageSize: parameter.PageSize, orderBy: source => source.OrderByDescending(t => t.CreateDate));//获取所有memo信息var Pagememos = await work.GetRepository<Memo>().GetPagedListAsync(pageIndex: parameter.PageIndex, pageSize: parameter.PageSize, orderBy: source => source.OrderByDescending(t => t.CreateDate));//汇总待办数量sumdto.Sum = Pagetodos.TotalCount;//统计完成数量var todos = Pagetodos.Items;sumdto.CompeleteCnt = todos.Where(t => t.Status == 1).Count();//计算已完成比率sumdto.CompeleteRatio = (sumdto.CompeleteCnt / (double)sumdto.Sum).ToString("0%");//统计备忘录数量var memos = Pagememos.Items;sumdto.MemoCnt=Pagememos.TotalCount;//获取todos项目与memos项目集合sumdto.TodoList = new ObservableCollection<ToDoDto>(mapper.Map<List<ToDoDto>>(todos.Where(t => t.Status == 0)));sumdto.MemoList = new ObservableCollection<MemoDto>(mapper.Map<List<MemoDto>>(memos));return new ApiReponse(true, sumdto);}catch (Exception ex){return new ApiReponse(false, ex);}}}
}
添加控制器
添加文件MyToDo.Api.Controllers.SummaryController
using Microsoft.AspNetCore.Mvc;
using Microsoft.VisualBasic;
using MyToDo.Api.Service;
using MyToDo.Share.Models;
using MyToDo.Share.Parameters;namespace MyToDo.Api.Controllers
{[ApiController][Route("api/[controller]/[action]")]public class SummaryController:ControllerBase{private readonly ISummary service;public SummaryController(ISummary tService){this.service = tService;}[HttpGet]public async Task<ApiReponse> GetAllInfo([FromQuery] SummaryParameter parameter)=>await service.GetAllInfo(parameter);}
}
依赖注入
文件:webapi.Program.cs
添加:
builder.Services.AddTransient<ISummary, Summary>();
添加客户端数据接口
添加接口
新建文件:Mytodo.Service.ISummeryService.cs
using MyToDo.Share;
using MyToDo.Share.Contact;
using MyToDo.Share.Models;
using MyToDo.Share.Parameters;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;namespace Mytodo.Service
{public interface ISummeryService{//public async Task<ApiResponse<SummaryDto>> GetAllInfo(SummaryParameter parameter)Task<ApiResponse<SummaryDto>> GetAllInfo(SummaryParameter parameter);}
}
实现接口
添加文件:Mytodo.Service.SummeryService
using MyToDo.Share;
using MyToDo.Share.Contact;
using MyToDo.Share.Models;
using MyToDo.Share.Parameters;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;namespace Mytodo.Service
{public class SummeryService : ISummeryService{private readonly HttpRestClient client;private readonly string ServiceName="Summary";public SummeryService(HttpRestClient client){this.client = client;}public async Task<ApiResponse<SummaryDto>> GetAllInfo(SummaryParameter parameter){BaseRequest request = new BaseRequest();request.Method = RestSharp.Method.GET;//如果查询字段为空request.Route = $"api/{ServiceName}/GetAllInfo?PageIndex={parameter.PageIndex}" + $"&PageSize={parameter.PageSize}";//request.Route = $"api/{ServiceName}/GetAll?PageIndex={parameter.PageIndex}" + $"&PageSize={parameter.PageSize}" + $"&search={parameter.Search}";return await client.ExecuteAsync<SummaryDto>(request);}}
}
依赖注入
修改文件App.xaml.cs 添加内容
containerRegistry.Register<ISummeryService, SummeryService>();
首页对接接口
修改UI层绑定
修改文件:Mytodo.Views.IndexView.xaml
由
ItemsSource="{Binding TodoDtos}"
ItemsSource="{Binding MemoDtos}"
修改为
ItemsSource="{Binding Summary.TodoList}"
ItemsSource="{Binding Summary.MemoList}"
新建summary实例,并初始化
修改文件:Mytodo.ViewModels.IndexViewModel.cs
public SummaryDto Summary
{get { return summary; }set { summary = value; RaisePropertyChanged(); }
}
private SummaryDto summary;
新建summary服务实例,并初始化
private readonly ISummeryService summService;
public IndexViewModel(IContainerProvider provider,IDialogHostService dialog) : base(provider)
{//实例化接口this.toDoService= provider.Resolve<ITodoService>();this.memoService = provider.Resolve<IMemoService>();this.summService= provider.Resolve<ISummeryService>();//初始化命令EditMemoCmd = new DelegateCommand<MemoDto>(Addmemo);EditTodoCmd = new DelegateCommand<ToDoDto>(Addtodo);ToDoCompltedCommand = new DelegateCommand<ToDoDto>(Compete);ExecuteCommand = new DelegateCommand<string>(Execute);this.dialog = dialog;CreatBars();
}
添加首页数据初始化函数
/// <summary>
/// 更新首页所有信息
/// </summary>
private async void UpdateData()
{UpdateLoding(true);var summaryResult = await summService.GetAllInfo(new SummaryParameter() { PageIndex = 0, PageSize = 1000 });if (summaryResult.Status){Summary = summaryResult.Result;Refresh();}UpdateLoding(false);
}
public override async void OnNavigatedTo(NavigationContext navigationContext)
{UpdateData();base.OnNavigatedTo(navigationContext);
}void Refresh()
{TaskBars[0].Content = summary.Sum.ToString();TaskBars[1].Content = summary.CompeleteCnt.ToString();TaskBars[2].Content = summary.CompeleteRatio;TaskBars[3].Content = summary.MemoCnt.ToString();
}
用summary的字段替换掉TodoDtos与MemoDtos
更新添加、编辑、完成命
修改后的代码为:
using Mytodo.Common.Models;
using MyToDo.Share.Parameters;
using Prism.Mvvm;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Reflection.Metadata;
using System.Text;
using System.Threading.Tasks;
using MyToDo.Share.Models;
using Prism.Commands;
using Prism.Services.Dialogs;
using Mytodo.Dialog;
using Mytodo.ViewModels;
using Mytodo.Service;
using Prism.Ioc;
using System.Diagnostics;
using Microsoft.VisualBasic;
using ImTools;
using DryIoc;
using MyToDo.Share;
using System.Windows;
using Prism.Regions;namespace Mytodo.ViewModels
{public class IndexViewModel:NavigationViewModel{#region 定义命令/// <summary>/// Todo完成命令/// </summary>public DelegateCommand<ToDoDto> ToDoCompltedCommand { get; set; }public DelegateCommand<string> ExecuteCommand { get; set; }/// <summary>/// 命令:编辑备忘/// </summary>public DelegateCommand<MemoDto> EditMemoCmd { get;private set; }/// <summary>/// 命令:编辑待办/// </summary>public DelegateCommand<ToDoDto> EditTodoCmd { get; private set; }#endregion#region 定义属性public SummaryDto Summary{get { return summary; }set { summary = value; RaisePropertyChanged(); }}public string Title { get; set; }/// <summary>/// 首页任务条/// </summary>public ObservableCollection<TaskBar> TaskBars{get { return taskBars; }set { taskBars = value; RaisePropertyChanged(); }}#endregion#region 定义重要命令#endregion#region 定义重要字段private readonly IDialogHostService dialog;private readonly ITodoService toDoService;private readonly ISummeryService summService;private readonly IMemoService memoService;#endregion#region 定义普通字段private SummaryDto summary;private ObservableCollection<TaskBar> taskBars;#endregion#region 命令相关方法/// <summary>/// togglebutoon 的命令/// </summary>/// <param name="dto"></param>/// <exception cref="NotImplementedException"></exception>async private void Compete(ToDoDto dto){if (dto == null || string.IsNullOrEmpty(dto.Title) || (string.IsNullOrEmpty(dto.Content)))return;var updres = await toDoService.UpdateAsync(dto);if (updres.Status){var todo = Summary.TodoList.FirstOrDefault(x => x.Id.Equals(dto.Id));//更新信息todo.Status = dto.Status;}// 从数据库更新信息UpdateData();}/// <summary>/// 选择执行命令/// </summary>/// <param name="obj"></param>void Execute(string obj){switch (obj){case "新增待办": Addtodo(null); break;case "新增备忘": Addmemo(null); break;}}/// <summary>/// 添加待办事项/// </summary>async void Addtodo(ToDoDto model){DialogParameters param = new DialogParameters();if (model != null)param.Add("Value", model);var dialogres = await dialog.ShowDialog("AddTodoView", param);var newtodo = dialogres.Parameters.GetValue<ToDoDto>("Value");if (newtodo == null || string.IsNullOrEmpty(newtodo.Title) || (string.IsNullOrEmpty(newtodo.Content)))return;if (dialogres.Result == ButtonResult.OK){try{if (newtodo.Id > 0){var updres = await toDoService.UpdateAsync(newtodo);if (updres.Status){var todo = Summary.TodoList.FirstOrDefault(x=>x.Id.Equals(newtodo.Id));//更新信息todo.Content = newtodo.Content;todo.Title = newtodo.Title;todo.Status = newtodo.Status;}}else{//添加内容 //更新数据库数据var addres = await toDoService.AddAsync(newtodo);//更新UI数据if (addres.Status){Summary.TodoList.Add(addres.Result);}}}catch {}finally{UpdateLoding(false);}}// 从数据库更新信息UpdateData();}/// <summary>/// 添加备忘录/// </summary>async void Addmemo(MemoDto model){DialogParameters param = new DialogParameters();if (model != null)param.Add("Value", model);var dialogres = await dialog.ShowDialog("AddMemoView", param);if (dialogres.Result == ButtonResult.OK){try{var newmemo = dialogres.Parameters.GetValue<MemoDto>("Value");if (newmemo != null && string.IsNullOrWhiteSpace(newmemo.Content) && string.IsNullOrWhiteSpace(newmemo.Title))return;if (newmemo.Id > 0){var updres = await memoService.UpdateAsync(newmemo);if (updres.Status){//var memo = MemoDtos.FindFirst(predicate: x => x.Id == newmemo.Id);var memo = Summary.MemoList.FirstOrDefault( x => x.Id.Equals( newmemo.Id));//更新信息memo.Content = newmemo.Content;memo.Title = newmemo.Title;}}else{//添加内容var addres = await memoService.AddAsync(newmemo);//更新UI数据if (addres.Status){Summary.MemoList.Add(addres.Result);}}}catch{}finally{UpdateLoding(false);}}// 从数据库更新信息UpdateData();}#endregion#region 其它方法/// <summary>/// 更新首页所有信息/// </summary>private async void UpdateData(){UpdateLoding(true);var summaryResult = await summService.GetAllInfo(new SummaryParameter() { PageIndex = 0, PageSize = 1000 });if (summaryResult.Status){Summary = summaryResult.Result;Refresh();}UpdateLoding(false);}#endregion#region 启动项相关void CreatBars(){Title = "您好,2022";TaskBars = new ObservableCollection<TaskBar>();TaskBars.Add(new TaskBar { Icon = "CalendarBlankOutline", Title = "汇总", Color = "#FF00FF00", Content = "27", Target = "" });TaskBars.Add(new TaskBar { Icon = "CalendarMultipleCheck", Title = "已完成", Color = "#6B238E", Content = "24", Target = "" });TaskBars.Add(new TaskBar { Icon = "ChartLine", Title = "完成比例", Color = "#32CD99", Content = "100%", Target = "" });TaskBars.Add(new TaskBar { Icon = "CheckboxMarked", Title = "备忘录", Color = "#5959AB", Content = "13", Target = "" });}public override async void OnNavigatedTo(NavigationContext navigationContext){UpdateData();base.OnNavigatedTo(navigationContext);}void Refresh(){TaskBars[0].Content = summary.Sum.ToString();TaskBars[1].Content = summary.CompeleteCnt.ToString();TaskBars[2].Content = summary.CompeleteRatio;TaskBars[3].Content = summary.MemoCnt.ToString();}#endregionpublic IndexViewModel(IContainerProvider provider,IDialogHostService dialog) : base(provider){//实例化接口this.toDoService= provider.Resolve<ITodoService>();this.memoService = provider.Resolve<IMemoService>();this.summService= provider.Resolve<ISummeryService>();//初始化命令EditMemoCmd = new DelegateCommand<MemoDto>(Addmemo);EditTodoCmd = new DelegateCommand<ToDoDto>(Addtodo);ToDoCompltedCommand = new DelegateCommand<ToDoDto>(Compete);ExecuteCommand = new DelegateCommand<string>(Execute);this.dialog = dialog;CreatBars();}}
}
相关文章:
WPF实战学习笔记25-首页汇总
注意:本实现与视频不一致。本实现中单独做了汇总接口,而视频中则合并到国todo接口当中了。 添加汇总webapi接口添加汇总数据客户端接口总数据客户端接口对接3首页数据模型 添加数据汇总字段类 新建文件MyToDo.Share.Models.SummaryDto using System;…...
FreeRTOS源码分析-7 消息队列
目录 1 消息队列的概念和作用 2 应用 2.1功能需求 2.2接口函数API 2.3 功能实现 3 消息队列源码分析 3.1消息队列控制块 3.2消息队列创建 3.3消息队列删除 3.4消息队列在任务中发送 3.5消息队列在中断中发送 3.6消息队列在任务中接收 3.7消息队列在中断中接收 1 消…...
机器学习深度学习——权重衰减
👨🎓作者简介:一位即将上大四,正专攻机器学习的保研er 🌌上期文章:机器学习&&深度学习——模型选择、欠拟合和过拟合 📚订阅专栏:机器学习&&深度学习 希望文章对你…...
【Linux】线程互斥 -- 互斥锁 | 死锁 | 线程安全
引入互斥初识锁互斥量mutex锁原理解析 可重入VS线程安全STL中的容器是否是线程安全的? 死锁 引入 我们写一个多线程同时访问一个全局变量的情况(抢票系统),看看会出什么bug: // 共享资源, 火车票 int tickets 10000; //新线程执行方法 vo…...
【vue-pdf】PDF文件预览插件
1 插件安装 npm install vue-pdf vue-pdf GitHub:https://github.com/FranckFreiburger/vue-pdf#readme 参考文档:https://www.cnblogs.com/steamed-twisted-roll/p/9648255.html catch报错:vue-pdf组件报错vue-pdf Cannot read properti…...
Flink集群运行模式--Standalone运行模式
Flink集群运行模式--Standalone运行模式 一、实验目的二、实验内容三、实验原理四、实验环境五、实验步骤5.1 部署模式5.1.1 会话模式(Session Mode)5.1.2 单作业模式(Per-Job Mode)5.1.3 应用模式(Application Mode&a…...
Spring整合JUnit实现单元测试
Spring整合JUnit实现单元测试 一、引言 在软件开发过程中,单元测试是保证代码质量和稳定性的重要手段。JUnit是一个流行的Java单元测试框架,而Spring是一个广泛应用于Java企业级开发的框架。本文将介绍如何使用Spring整合JUnit实现单元测试,…...
Spring Boot学习路线1
Spring Boot是什么? Spring Boot是基于Spring Framework构建应用程序的框架,Spring Framework是一个广泛使用的用于构建基于Java的企业应用程序的开源框架。Spring Boot旨在使创建独立的、生产级别的Spring应用程序变得容易,您可以"只是…...
管理类联考——写作——论说文——实战篇——标题篇
角度3——4种材料类型、4个立意对象、5种写作态度 经过审题立意后,我们要根据我们的立意,确定一个主题,这个主题必须通过文章的标题直接表达出来。 标题的基本要求 主题清晰,态度明确 第一,阅卷人看到一篇论说文的标…...
idea中设置maven本地仓库和自动下载依赖jar包
1.下载maven 地址:maven3.6.3 解压缩在D:\apache-maven-3.6.3-bin\apache-maven-3.6.3\目录下新建文件夹repository打开apache-maven-3.6.3-bin\apache-maven-3.6.3\conf文件中的settings.xml编辑:新增本地仓库路径 <localRepository>D:\apache-…...
前缀和差分
前缀和 前缀和:一段序列里的前n项和 给出n个数,在给出q次问询,每次问询给出L、R,快速求出每组数组中一段L至R区间的和 给出一段数组,每次问询为求出l到r区间的和 普通方法:L到R进行遍历,那么…...
Golang GORM 模型定义
模型定义 参考文档:https://gorm.io/zh_CN/docs/models.html 模型一般都是普通的 Golang 的结构体,Go的基本数据类型,或者指针。 模型是标准的struct,由Go的基本数据类型、实现了Scanner和Valuer接口的自定义类型及其指针或别名组成&#x…...
微服务的各种边界在架构演进中的作用
演进式架构 在微服务设计和实施的过程中,很多人认为:“将单体拆分成多少个微服务,是微服务的设计重点。”可事实真的是这样吗?其实并非如此! Martin Fowler 在提出微服务时,他提到了微服务的一个重要特征—…...
使用 docker-compose 一键部署多个 redis 实例
目录 1. 前期准备 2. 导入镜像 3. 部署redis master脚本 4. 部署redis slave脚本 5. 模板文件 6. 部署redis 7. 基本维护 1. 前期准备 新部署前可以从仓库(repository)下载 redis 镜像,或者从已有部署中的镜像生成文件: …...
14-测试分类
1.按照测试对象划分 ①界面测试 软件只是一种工具,软件与人的信息交流是通过界面来进行的,界面是软件与用户交流的最直接的一层,界面的设计决定了用户对设计的软件的第一印象。界面如同人的面孔,具有吸引用户的直接优势…...
打开域名跳转其他网站,官网被黑解决方案(Linux)
某天打开网站,发现进入首页,马上挑战到其他赌博网站。 事不宜迟,不能让客户发现,得马上解决 我的网站跳转到这个域名了 例如网站跳转到 k77.cc 就在你们部署的代码的当前文件夹下面,执行下如下命令 find -type …...
redis总结
1.redis redis高性能的key-value数据库,支持持久化,不仅仅支持简单的key-value,还提供了list,set,zset,hash等数据结构的存储,支持数据的备份(master-slave模式) redis&…...
现代C++中的从头开始深度学习:激活函数
一、说明 让我们通过在C中实现激活函数来获得乐趣。人工神经网络是生物启发模型的一个例子。在人工神经网络中,称为神经元的处理单元被分组在计算层中,通常用于执行模式识别任务。 在这个模型中,我们通常更喜欢控制每一层的输出以服从一些约束…...
python怎么实现tcp和udp连接
目录 什么是tcp连接 什么是udp连接 python怎么实现tcp和udp连接 什么是tcp连接 TCP(Transmission Control Protocol)连接是一种网络连接,它提供了可靠的、面向连接的数据传输服务。 在TCP连接中,通信的两端(客户端和…...
java设计模式-观察者模式(jdk内置)
上一篇我们学习了 观察者模式。 观察者和被观察者接口都是我们自己定义的,整个设计模式我们从无到有都是自己设计的,其实,java已经内置了这个设计模式,我们只需要定义实现类即可。 下面我们不多说明,直接示例代码&am…...
从零实现富文本编辑器#5-编辑器选区模型的状态结构表达
先前我们总结了浏览器选区模型的交互策略,并且实现了基本的选区操作,还调研了自绘选区的实现。那么相对的,我们还需要设计编辑器的选区表达,也可以称为模型选区。编辑器中应用变更时的操作范围,就是以模型选区为基准来…...
相机从app启动流程
一、流程框架图 二、具体流程分析 1、得到cameralist和对应的静态信息 目录如下: 重点代码分析: 启动相机前,先要通过getCameraIdList获取camera的个数以及id,然后可以通过getCameraCharacteristics获取对应id camera的capabilities(静态信息)进行一些openCamera前的…...
基于Java+MySQL实现(GUI)客户管理系统
客户资料管理系统的设计与实现 第一章 需求分析 1.1 需求总体介绍 本项目为了方便维护客户信息为了方便维护客户信息,对客户进行统一管理,可以把所有客户信息录入系统,进行维护和统计功能。可通过文件的方式保存相关录入数据,对…...
C++:多态机制详解
目录 一. 多态的概念 1.静态多态(编译时多态) 二.动态多态的定义及实现 1.多态的构成条件 2.虚函数 3.虚函数的重写/覆盖 4.虚函数重写的一些其他问题 1).协变 2).析构函数的重写 5.override 和 final关键字 1&#…...
C#中的CLR属性、依赖属性与附加属性
CLR属性的主要特征 封装性: 隐藏字段的实现细节 提供对字段的受控访问 访问控制: 可单独设置get/set访问器的可见性 可创建只读或只写属性 计算属性: 可以在getter中执行计算逻辑 不需要直接对应一个字段 验证逻辑: 可以…...
【JVM面试篇】高频八股汇总——类加载和类加载器
目录 1. 讲一下类加载过程? 2. Java创建对象的过程? 3. 对象的生命周期? 4. 类加载器有哪些? 5. 双亲委派模型的作用(好处)? 6. 讲一下类的加载和双亲委派原则? 7. 双亲委派模…...
【Linux】自动化构建-Make/Makefile
前言 上文我们讲到了Linux中的编译器gcc/g 【Linux】编译器gcc/g及其库的详细介绍-CSDN博客 本来我们将一个对于编译来说很重要的工具:make/makfile 1.背景 在一个工程中源文件不计其数,其按类型、功能、模块分别放在若干个目录中,mak…...
轻量级Docker管理工具Docker Switchboard
简介 什么是 Docker Switchboard ? Docker Switchboard 是一个轻量级的 Web 应用程序,用于管理 Docker 容器。它提供了一个干净、用户友好的界面来启动、停止和监控主机上运行的容器,使其成为本地开发、家庭实验室或小型服务器设置的理想选择…...
CTF show 数学不及格
拿到题目先查一下壳,看一下信息 发现是一个ELF文件,64位的 用IDA Pro 64 打开这个文件 然后点击F5进行伪代码转换 可以看到有五个if判断,第一个argc ! 5这个判断并没有起太大作用,主要是下面四个if判断 根据题目…...
关于 ffmpeg设置摄像头报错“Could not set video options” 的解决方法
若该文为原创文章,转载请注明原文出处 本文章博客地址:https://hpzwl.blog.csdn.net/article/details/148515355 长沙红胖子Qt(长沙创微智科)博文大全:开发技术集合(包含Qt实用技术、树莓派、三维、OpenCV…...
