嵌入式单片机实验室头像
关注
【CStackGUI 实战】画板 drawpad:鼠标拖拽作图、撤销 / 重做、导出 PNG封面图

【CStackGUI 实战】画板 drawpad:鼠标拖拽作图、撤销 / 重做、导出 PNG

在这里插入图片描述

一、它演示什么

画板上的图形不是像素,而是被录制的绘制命令:每画一笔记一条命令,控件重绘(被盖住又露出、改大小)时按序回放 —— 所以没有"擦掉某一笔"的接口。示例把每条命令另存进一张固定数组,它才是可撤销的"真相来源":撤销 = 丢掉最后一条 → 清空记录 → 重放剩下的。

鼠标事件挂在画板自己(mousedown / mousemove / mouseup),按下时运行时自动捕获鼠标,拖出边界也不断笔;直线 / 矩形 / 椭圆在拖动中有橡皮筋预览,松手才提交。

下面是「录制-回放」模型的核心流程:

是

松手提交

用户按下鼠标

捕获鼠标并记录起点

拖动中

写入临时命令 + 整表重放
(橡皮筋预览)

把命令追加进命令表
(真相来源)

重放整张画布

状态栏更新命令条数

撤销:丢最后一条 → 清空 → 重放

二、怎么用

  1. 在项目根编译:build-app.bat drawpad,双击 examples\drawpad\drawpad.exe。
  2. 顶部「工具」下拉选一种(直线 / 矩形 / 椭圆 / 文字 / 自由笔);点「颜色」色块弹系统取色器;「笔宽」数值框调 1…12。
  3. 在白色画板上按下拖动:直线 / 矩形 / 椭圆拖动中就有预览、松手提交;文字工具单击一次落一个字;自由笔一路划过去逐段提交。
  4. 「撤销」退掉最后一笔,「重做」放回来,「清空」全部丢弃;状态栏三段分别是当前工具、命令条数、最近一次操作。
  5. 「导出 PNG」把画板截图存进用户数据目录(文件名带时间戳);底部终端会记下每一步作图 / 撤销 / 导出。
  6. 命令行:drawpad.exe(自动找同名 .cgui)、drawpad.exe xxx.cgui、--selftest。

三、一屏总览

控件 id类型干什么
cbo_toolcombo工具:0 直线 / 1 矩形 / 2 椭圆 / 3 文字 / 4 自由笔
cp_inkcolorpicker当前色(value = 0xRRGGBB),点它弹系统取色器
edt_penedit(spin)笔宽数值框,1…12
btn_undo / btn_redo / btn_clearbutton撤销 / 重做 / 清空,共用一个回调
btn_exportbutton导出 PNG
cv_boardcanvas画板本身,挂 mousedown / mousemove / mouseup
term_logterminal每一步操作记一行
sb_mainstatusbar三段:当前工具 / 命令条数 / 最近操作

四、关键接口

  • 画板绘制:cg_canvas_clear、cg_canvas_set_color、cg_canvas_set_line_width、cg_canvas_line、cg_canvas_rect、cg_canvas_ellipse、cg_canvas_text
  • 鼠标坐标:cg_event_x、cg_event_y(就是画板内的逻辑像素)
  • 截图与落盘:cg_widget_screenshot、cg_app_data_dir、cg_path_join
  • 记录与状态:cg_term_write、cg_status_set、cg_status_setf

五、录制-回放 vs 像素擦除

维度录制-回放(本示例)像素擦除(位图)结论
撤销粒度按「命令」撤销,可精确回退到任意一笔只能整块擦除像素,无法还原被覆盖的笔画录制-回放更精细,支持逐笔撤销
内存占用只存命令参数(类型 + 坐标 + 颜色 + 笔宽),固定数组上限 kMaxOps = 4096每帧都要存整张位图,画布越大内存越高录制-回放更省内存,且占用可预估
缩放质量重放时按当前尺寸重新绘制,任意缩放都清晰位图缩放会模糊 / 锯齿,需额外重采样录制-回放缩放无损,质量更高
实现复杂度需维护命令表 + 重放逻辑,预览要「临时命令 + 整表重放」直接改像素,实现直观但撤销 / 缩放难做录制-回放略复杂,但换来前三个维度的收益

两种方案的取舍可以这样看:

需要逐笔撤销

只整块擦除即可

画板方案选型

撤销粒度要求

录制-回放
(本示例)

像素擦除
(位图)

优点:逐笔撤销 / 省内存 / 缩放无损

代价:命令表 + 重放逻辑较复杂

优点:实现直观

代价:撤销难 / 内存高 / 缩放模糊

结论:追求精细撤销与质量 → 选录制-回放

结论:简单场景够用 → 选像素擦除

六、容易踩的坑

  • 鼠标事件必须挂在画板这个子控件上,不能挂窗口级 app.events —— 按下 / 移动 / 抬起落在画板上时消息只送给画板,父窗口收不到(早先挂窗口的写法在真机上"按下拖动毫无反应")。
  • 画板事件里的坐标就是画板内像素,不用再减 cg_widget_x / cg_widget_y。
  • 画板的绘制是"录制 → 回放"模型,没有"画一笔但不作数"的接口,所以拖动预览只能"把临时命令写进命令表空槽 + 整表重放"(见 preview_op)。
  • 命令表是固定数组(上限 kMaxOps = 4096):满了只报一次提示并拒绝起笔;自由笔贴着上一个点 2px 以内的移动不记,免得几秒见底。
  • 画板重绘走双缓冲,所以"每步重放整张画布"的预览不会闪。

七、常见问题排查

Q1:画板无响应,按下拖动毫无反应?

  • 原因:鼠标事件挂错了对象,挂到了窗口级 app.events 而不是画板子控件 cv_board 上。
  • 解决:把 mousedown / mousemove / mouseup 全部挂到 cv_board 上;按下时运行时自动捕获鼠标,拖出边界也不断笔。

Q2:拖动预览闪烁?

  • 原因:画板重绘没有走双缓冲,或预览时直接改像素导致整块重绘。
  • 解决:确认画板重绘走双缓冲;预览用「临时命令写进命令表空槽 + 整表重放」的方式,避免逐像素直接改。

Q3:命令表满了,画不进去?

  • 原因:命令表是固定数组,上限 kMaxOps = 4096,画满后拒绝起笔。
  • 解决:先「清空」或「撤销」释放空间;自由笔贴着上一个点 2px 以内的移动不记,可有效延缓命令表见底。

Q4:导出 PNG 失败?

  • 原因:用户数据目录不可写,或文件名带非法字符。
  • 解决:确认 cg_app_data_dir 返回的目录存在且可写;导出文件名带时间戳,若仍失败请检查磁盘空间与目录权限。

Q5:文字工具单击落字位置偏移?

  • 原因:文字工具单击落字时,坐标换算用了窗口坐标而不是画板内坐标,或没考虑画板在窗口内的偏移。
  • 解决:确认落字坐标直接用 cg_event_x / cg_event_y(就是画板内逻辑像素),不要再减 cg_widget_x / cg_widget_y。

问题速查表

问题现象原因解决方案
Q1 画板无响应按下拖动毫无反应鼠标事件挂到了窗口级 app.events,而非画板子控件 cv_board把 mousedown / mousemove / mouseup 全部挂到 cv_board 上
Q2 预览闪烁拖动中画布闪烁画板重绘未走双缓冲,或预览时直接改像素重绘走双缓冲;预览用「临时命令 + 整表重放」
Q3 命令表满画不进去,拒绝起笔命令表固定数组上限 kMaxOps = 4096 已满先「清空」或「撤销」释放空间;自由笔 2px 内移动不记
Q4 导出 PNG 失败导出报错或文件未生成用户数据目录不可写,或文件名带非法字符确认 cg_app_data_dir 目录存在且可写;检查磁盘空间与权限
Q5 文字落字偏移单击落字位置不对坐标用了窗口坐标,或没考虑画板偏移直接用 cg_event_x / cg_event_y,不再减 cg_widget_x / cg_widget_y

转载自 CSDN-专业IT技术社区

原文链接:https://blog.csdn.net/qq_29246181/article/details/167226799

文章来源转载

评论

赞0

评论列表

微信小程序
QQ小程序

关于作者

点赞数:0
关注数:0
粉丝:0
文章:0
关注标签:0
加入于:--