看一段操作:

上面这个动画是我自己花了一周实现CAD绘图系统,里面实现了类CAD的交互命令
交互式命令操作流程

通过在命令栏或者菜单触发命令来启动相关功能,在命令执行过程中,命令栏提示当前命令的输入状态,当用户根据命令提示中的输入信息完成相应的操作(如在地图中点击某个点或者输入某个字符串)之后继续命令的下一步操作,直到命令的结束。
这种操作方式在设计类、地图类软件中经常用到,如AutoCAD,ARCGIS软件中,实现方式也有很多种。也各有优缺点,本文主要介绍类CAD系统中的交互命令实现。

在AutoCAD的二次开发中,有类似的提示用户等待函数

acedGetString函数暂停程序执行,等待用户输入一个字符串,该函数定义为:
int acedGetString(int cronly, const char * prompt, char * result);
acedGetString函数暂停程序执行,等待用户输入一个坐标,该函数定义为:
int acedGetPoint(const char* prompt,AcDbPoint* result);
//命令函数,以下是伪代码;
void cmd_drawline()
{AcDbPoint result_point;auto es= acedGetPoint("请输入起点坐标",&result_point);//在这个函数中,中断了命令drawline的执行,在不卡住主界面的情况下让用户输入所需要的值;AcDbPolylinePtr line=AcDbPolyline::createObject();if(es==eOk){line->addPoint(result_point);}do {es= acedGetPoint("请输入下一个点坐标",&result_point);if(es==eNone){break;}line->addPoint(result_point);} while(1);
}

常用交互式系统的解决办法

基于消息循环实现(C++)

消息循环的方式在各种语言中的都有类似的方法,比较容易找到解决办法。
优点:比较容易实现,在windows下可跨语言交互,

  1. 使用Qt的消息循环
//伪代码;
//用户输入类型;
enum InputType
{inputNothing,inputPoint,inputString,
};
struct CommandStatus
{//表示已经准备好输入;bool _has_read_get;char* _command_name;//输入结果;QVariant _result_value;//输入类型;InputType _inut_type=inputNothing;
};//等待用户输入函数;
int acedGetPoint(const char* prompt,AcDbPoint* result)
{//获取当前的命令状态;CommandStatus&  command_status=getCurrentCommandStatus();//标记状态;command_status._inut_type=inputString;command_status._has_read_get=false;do {//最为关键的一步,Qt中防止阻塞的办法; qApp->processEvents();if(command_status._has_read_get){break;}} while(true);//获取结果数据;result=command_status._value.toPoint();
}//地图中鼠标按下或者命令栏中的输入完成事件void Canvas::mousePress(QMouseEvent* event)
{if(event->inputType()==InputPoint){//当这个事件结束之后就会退出acedGetPoint函数中的死循环;CommandStatus&  command_status=getCurrentCommandStatus();auto point=toScreen(event->pos());//将像素坐标转为地理坐标;command_status._has_read_get=true;command_status._result_value=point;}
}
  1. 使用Qt中的QEventLoop

QEventLoop类为我们提供了一种进入和退出一个事件循环的方法,模态对话框就是类似的实现方式(也可以使用windows的消息循环,但是因为不能跨平台,windows的消息应用范围不高;

//等待用户输入函数;
QEventLoop _loop;
int acedGetPoint(const char* prompt,AcDbPoint* result)
{//获取当前的命令状态;CommandStatus&  command_status=getCurrentCommandStatus();//标记状态;command_status._inut_type=inputString;command_status._has_read_get=false;//开启独立的事件循环;_loop.exec();
}//地图中鼠标按下或者命令栏中的输入完成事件void Canvas::mousePress(QMouseEvent* event)
{if(event->inputType()==InputPoint){//当这个事件结束之后就会退出acedGetPoint函数中的死循环;CommandStatus&  command_status=getCurrentCommandStatus();auto point=toScreen(event->pos());//将像素坐标转为地理坐标; command_status._result_value=point;//退出循环;_loop.quit();}
}
  1. 在WPF中使用消息循环

当主程不是Qt程序时,上面的方法将会无效(当然也可以将Qt做成组件提供给C#中使用,具体方法在以前的文章<<C#的winform中嵌入Qt界面库>>。主要用用到了WPF的这个类 DispatcherFrame,这个类具体有什么作用,可以自行百度
ps:这个方法找了好久,才发现WPF有这样一个东西;

//其他的代码与C++的类似,最主要的时进入循环和退出循环中
class MessageCall
{private Stack<DispatcherFrame> _dispathcer_frame_list = new Stack<DispatcherFrame>();//挂起,进入消息循环,在public void yeild(){ComponentDispatcher.PushModal();var dispathcer_frame = new DispatcherFrame(true);_dispathcer_frame_list.Push(dispathcer_frame);Dispatcher.PushFrame(dispathcer_frame);}//退出消息循环;public void resume(){var dis_frame = _dispathcer_frame_list.Pop();if (dis_frame != null){dis_frame.Continue = false;}ComponentDispatcher.PopModal();}
}MessageCall message_call=new MessageCall();
int acedGetPoint(String prompt,ref AcDbPoint result)
{//获取当前的命令状态;CommandStatus  command_status=getCurrentCommandStatus();//标记状态;command_status._inut_type=inputString;command_status._has_read_get=false;//挂起,等待其他地方调用了resume才进行往下执行;message_call.yeild();
}//地图中鼠标按下或者命令栏中的输入完成事件void Canvas_mousePress(MouseEvent event)
{if(event->inputType()==InputPoint){//当这个事件结束之后就会退出acedGetPoint函数中的死循环;CommandStatus  command_status=getCurrentCommandStatus();auto point=toScreen(event->pos());//将像素坐标转为地理坐标; command_status._result_value=point;message_call.resume();}
}

使用有栈协程解决

是一种比线程更加轻量级的存在,正如一个进程可以拥有多个线程一样,一个线程可以拥有多个协程.很多语言如go自带了协程的支持,C++20支持无栈协程,在网络通信中应用比较多。

常用的C++开源协程有 libgolibcoboost coroutine2fibersC++20、windows纤程等 ,对于做命令驱动程序而言,一般只能用有栈协程, 无栈协程一般情况下协程函数一般情况下使用的是关键字来处理,有特定的返回值,也是通过语言语法糖来实现,局限性比较大(C++20的协程就是无栈协程)。

1. boost coroutine2

  1. 需要编译依赖boost的context模块,这里面没用到里面的fiber模块,fiber模块支持协程的调度。
  2. 封装到C#测试过,也支持协程的调用(某些协程跨语言调用就会出问题,如windows fiber在net2.0及以下版本可以用),也支持协程的调试。在net3.5版本中调试打断点会报调试异常(C#不支持协程调试)
  3. 这个库的使用相对来说还是不太友好,应用于交互系统中还是要写很多额外的代码。
#include "boost/coroutine2/all.hpp"
#include <stdio.h>
#include <iostream>
using namespace boost::coroutines2;
TCallback* _callback;
void cooperative(coroutine<void>::push_type &sink,int num)
{std::cout << "Hello";//之所以能够执行是因为重载了操作符()//返回main()函数继续运行//执行挂起操作;如果在交互系统中,这个sink是需要有成员变量接收的,//可以保留为指针,在命令中调用用户等待函数的时候在挂起;sink();  std::cout << "world";//执行完毕,返回main继续执行
}void Test::testone()
{coroutine<void>::pull_type source{ std::bind(cooperative,std::placeholders::_1, 1)};std::cout << ", ";_callback->showMessage();//返回cooperative函数继续执行source();std::cout << "!";std::cout << "\n";
}
输出结果是: hello,world!

2. 简单的协程库

https://github.com/tonbit/coroutine 是一个单文件协程库,在windows下使用的fiber实现,linux下是用的ucontext函数来实现的。非常简单易用.

thread_local static Ordinator ordinator
//coroutine.h文件中的101有一个这样的定义,需要去掉thread_local,否则多处调用很容易崩溃.
void cmd_drawline() //命令;
{AcDbPoint result_point;auto es= acedGetPoint("请输入起点坐标",&result_point);//在这个函数中,中断了命令drawline的执行,在不卡住主界面的情况下让用户输入所需要的值;AcDbPolylinePtr line=AcDbPolyline::createObject();if(es==eOk){line->addPoint(result_point);}do {es= acedGetPoint("请输入下一个点坐标",&result_point);if(es==eNone){break;}line->addPoint(result_point);} while(1);
}enum InputType
{inputNothing,inputPoint,inputString,
};
struct CommandStatus
{//协程ID;int _coroutine_id;char* _command_name;//输入结果;QVariant _result_value;//输入类型;InputType _inut_type=inputNothing;
};int acedGetPoint(const char* prompt,AcDbPoint* result)
{//获取当前的命令状态;CommandStatus&  command_status=getCurrentCommandStatus();//标记状态;command_status._inut_type=inputString;//挂起协程;coroutine::yield();
}void Canvas::mousePress(QMouseEvent* event)
{if(event->inputType()==InputPoint){//当这个事件结束之后就会退出acedGetPoint函数中的死循环;CommandStatus&  command_status=getCurrentCommandStatus();auto point=toScreen(event->pos());//将像素坐标转为地理坐标; command_status._result_value=point;//恢复命令,coroutine_id是我们创建的协程ID;coroutine::resume(command_status._coroutine_id); }
}void execCommand(const char* command_name)
{//根据命令名,找到命令的回调函数执行;auto call_fun=CommandManager::instance().findCommand(command_name);call_fun();
}//执行命令的主函数,非入口函数,这只是举个例子;
void main()
{auto rt1 = coroutine::create(std::bind(&execCommand, command_name));CommandStatus&  command_status=getCurrentCommandStatus();command_status._coroutine_id=rt1;
}

在C#中使用task await解决

在AutoCAD 2015版本之后协程的使用方式被放弃了,主要的原因是在C#中对协程的支持不好,不可调试,我自己使用是很容易崩溃。AutoCAD.Net使用的是Task await的C#关键字来实现,网上有一个C#实现的CAD系统就是这种方式,有兴趣的可以学习下.

simpleCAD https://github.com/oozcitak/SimpleCad

实现类CAD的交互式命令系统相关推荐

  1. 清华大学出版社计算机绘谱,清华大学出版社-图书详情-《土木与建筑类CAD技能一级(二维计算机绘图)AutoCAD培训教程》...

    计算机辅助设计(computer aided design,CAD)已经成为现代土木建筑工程设计的高效率表达工具,并广泛应用于科学技术的各个领域,形成了独具特色的计算机绘图技术和三维数字建模技术.熟练 ...

  2. 5大类CAD快捷键一览表!快速学会CAD不是问题!照单全收!

    想要提高CAD制图速度?你说CAD快捷键重要不重要?5大类CAD快捷键一览表!快速玩转CAD! 一:CAD常用快捷键命令 二:绘图命令 三:修改命令 四:视图缩放命令 五:尺寸标注命令 六:对象特征命 ...

  3. cad标注样式快捷键_制图大神最常用的六大类CAD快捷命令,学会CAD就是这么简单...

    现在有不少小伙伴的工作都需要和CAD打交道,大家也知道想要学会CAD是一件多么难的事情,有很多学习CAD的小白在记CAD快捷命令的时候,就已经感到不行了,毕竟快捷命令那么多,想记住也不是一件简单的事情 ...

  4. CDR类CAD制作室内装修平面图

    本文中我们用Coreldraw软件参照已有的CAD室内设计结构图,绘制一张室内装修彩色平面图.更多平面设计教程可登陆e良师益友网学习. 首先看一下CAD的平面布局图. 1.打开Coreldraw软件, ...

  5. 无锡设计类——CAD设计在机械制图中的优势

    1.CAD软件的操作比较简单容易使用 在制图中,设计了很多新零件,CAD技术经常被用于新零件的制造过程中.在机械制图中,AutoCAD软件不仅可以直接执行新零件的设计,还可以通过采用现有零件作为标准来 ...

  6. 开源交互式自动标注工具EISeg

    在人工智能行业有这么一句话:"深度学习有多智能.背后就有多少人工".这句话直接说出了深度学习从业者心中的痛处,毕竟模型的好坏数据占着很大的因素,但是数据的标注成本却让很多从业者感到 ...

  7. 业界首个高性能交互式自动标注工具EISeg正式开源,跨越式提升你的标注体验...

    在人工智能行业有这么一句话:"深度学习有多智能.背后就有多少人工".这句话直接说出了深度学习从业者心中的痛处,毕竟模型的好坏数据占着很大的因素,但是数据的标注成本却让很多从业者感到 ...

  8. cad计算机快捷键设置,小U讲解完整版电脑CAD快捷键大全

    有没有完整版电脑CAD快捷键大全,经常有小伙伴留言给小编索取电脑CAD快捷键大全,这也是在学习CAD软件的过程中必经之路,能够熟练使用CAD快捷键有助于我们更快的提高CAD软件的设计能力,那么下面小编 ...

  9. C#进行CAD二次开发学习笔记-01

    一些基础知识 需要引用CAD的库文件 常用接口和类 与C++ ---- ObjectArx库的一些区别 需要引用CAD的库文件 accoremad.dll acdbmgd.dll acmgd.dll ...

  10. UCanCode发布领先的大型组态建模仿真CAD与GIS开源套件2019版本

    2019年. 成都 UCanCode发布升级E-Form++可视化源码组件库2019全新版 ! --- 全面性能提升,UCanCode有史以来最强大的版本发布! E-Form++可视化源码组件库企业版 ...

最新文章

  1. Android消息机制Handler用法
  2. .htm .html .shtml的区别
  3. CentOS各个版本国内镜像下载地址,下载速度10M+
  4. c语言求5个数最小公倍数,C语言,求从键盘输入的五个自然数的最小公倍数
  5. Android Bitmap开发之旅--基本操作
  6. Go语言fmt.Printf使用指南(占位符总结)
  7. sqlserver学习3---sql函数
  8. .NET Conf 2021 正在进行中,带你看一看微软带来了什么内容
  9. [蓝桥杯][算法提高VIP]摆花-多重背包计数问题
  10. 避免常见的6种HTML5错误用法,如何避免常见的6种HTML5错误用法
  11. 阿里带火的中台,究竟是个啥?
  12. Redis底部的几种存储结构(sds、dict、ziplist、intset、skiplist)
  13. layui表格——table.render(options)(转)
  14. 哈希存储:字符串存储、数字存储
  15. protues仿真控制舵机
  16. 编写一个程序,新建一个文件:d:\abc.txt,从键盘输入abc.txt内容(不超过100个字符)。然后新建另外一个文件:d:\def.txt,将abc.txt的前10个字符复制到def.txt上
  17. 中文主播也能海外带货!同声传译助直播类应用开拓海外市场
  18. Packet Tracer安装包及安装教程(8.0版本)
  19. 【分析】魔兽争霸3的MPQ文件及模型格式分析
  20. 图解Pandas,数据结构介绍 | 图文第1篇

热门文章

  1. TVS和ESD那些事儿
  2. 风险资产的最优组合公式证明
  3. 【转载】排列组合公式原理
  4. Python有限状态机FMS结合测试用例
  5. /etc/shadow可以破解吗?
  6. coreldraw的线条怎么变成圆头_CDR的一些技巧
  7. CTF_ctfshow_meng新_web1-web24
  8. stm32定时器3产生1us延时的函数
  9. .webp是什么文件?怎么打开这种文件
  10. python自动轨迹绘制七边形_斜抛运动的数学模型