跳转至内容
  • 社区公告

    1 1
    1 主题
    1 帖子
    A
    为维护清朗网络空间,保障社区健康有序,依据《中华人民共和国网络安全法》《互联网信息服务管理办法》《网络信息内容生态治理规定》等法律法规,本社区严禁发布以下内容: 一、涉黄低俗 色情、淫秽、低俗、性暗示信息,以及任何涉及未成年人的不良内容。 二、危害国家安全 反对宪法确定的基本原则;危害国家安全、荣誉和利益;煽动颠覆国家政权、分裂国家、破坏民族团结;泄露国家秘密。 三、暴力恐怖 宣扬恐怖主义、极端主义;煽动民族仇恨、宗教极端与暴力行为。 四、违法犯罪 赌博、毒品、枪支弹药、管制刀具、危险化学品等违禁品的传播与交易;传授犯罪方法。 五、谣言与虚假信息 编造、传播虚假信息,扰乱社会秩序或社区秩序。 六、侵权与欺诈 造谣诽谤、侵犯他人隐私与名誉;诈骗、传销、非法集资。 七、违规广告导流 未经许可的商业广告、拉群导流、刷单返利、外链推广。 八、人身攻击 辱骂、歧视、骚扰、恶意引战。 处理办法 违反以上规定的,视情节轻重给予删帖、警告、禁言、封禁账号等处理;涉嫌违法犯罪的,依法向有关部门报告并移送司法机关。 本规范自发布之日起施行。
  • 莫谈国事

    1 0
    1 主题
    0 帖子
    没有新主题
  • 欢迎随时提问

    0 0
    0 主题
    0 帖子
    没有新主题
  • 0 主题
    0 帖子
    没有新主题
  • 5 主题
    13 帖子
    YOYOY
    @Bitwalker 可以了,感谢
  • 2 主题
    2 帖子
    A
    这篇是给刚拿到键鼠助手 4.0 的朋友准备的:第一次打开软件报缺 DLL、文档和视频去哪找、 读不出固件信息、硬件宏保存失败 —— 这几类情况基本都能在下面找到答案。装机前建议先扫一遍。 相关: 键鼠助手 4.0 资源汇总:主程序 / 宏编程文档 / SDK 与示例工程 / 常用工具下载 使用需知 首次打开提示「找不到 CH375DLL64.DLL」 第一次打开软件时,如果弹出“由于找不到CH375DLL64.DLL,无法继续执行代码。重新安装程序可能会解决”,请安装软件包中的程序 CH372_DRIVER.exe CH375DLL64.DLL 是沁恒 CH372 / CH375 系列 USB 芯片的动态库,缺它就说明驱动还没装,不是软件损坏,也不用重装主程序。 处理方式:运行 CH372_DRIVER.exe(以管理员身份)完成驱动安装,再重新打开主程序即可。 下载:CH372_DRIVER.exe(346 KiB,资源汇总帖里的「驱动程序」一项)。 这是个沁恒 CH372 / CH375 的驱动安装包:内含 CH375DLL.dll / CH375DLL64.dll 两个动态库与 CH375WDM 系列驱动文件,装完建议重启一次再打开主程序。 先查《键鼠助手--使用指南.chm》 《键鼠助手--使用指南.chm》(4.0的使用基本一样),有问题可以先查找一下,常见问题都可以找到答案,LUA相关接口使用请查看键鼠助手4系列视频教程。 遇到功能上的疑问,先翻这本 chm,绝大多数常见问题里面都有;它是按 3.0 系列写的,但 4.0 的用法基本一致,可以直接对照。 Lua 接口部分不在这本 chm 里,去看「键鼠助手 4 系列视频教程」。 本版块下载帖目前提供的是《键鼠助手软件宏编程文档.pdf》(Lua 函数说明,18 页),chm 尚未上传;需要的话可以补上来。 新手开箱视频 新手开箱视频:【键鼠助手4.0--新手使用指南及特性概要-哔哩哔哩】 https://bilibili.com/video/BV1CKqSYkEeo/ 直达链接:键鼠助手4.0--新手使用指南及特性概要(B 站,BV1CKqSYkEeo) 还没拆封的话,建议先跟着这一集过一遍流程,再回来动手。 读不出固件信息时,别强行追新版本 当使用最新软件无法查看固件信息时,请使用与固件配套的软件版本进行固件升级,升级完后请使用与固件配套的软件。 软件版本与固件版本之间有配套关系,不是越新越好 —— 新软件读不出老固件的固件信息属于版本不匹配,而非硬件故障。 正确顺序:换成与该固件配套的软件版本 → 升级固件 → 升级完继续用这一配套版本的软件,不要再跳到最新版。 硬件宏无法保存 无法保存硬件宏时,把程序文件夹放桌面,然后使用管理员权限运行程序 把整个程序文件夹挪到桌面(或任何普通用户有写权限的位置),再右键主程序选「以管理员身份运行」,然后重试保存。 常见诱因是程序放在受保护目录、C:\Program Files 下被 UAC 拦写,或软件本身没被授予写入权限。 无线版本接收器固件已开源 无线版本键鼠助手接收器开源地址:https://github.com/magicsunflower/kma_receiver.git 仓库地址:https://github.com/magicsunflower/kma_receiver.git 如果你用的是无线版,接收器部分的固件源码在这里,可自行查阅与编译。 需要 ssh 以外的协作方式(提交 Issue / PR)也走这个仓库。 还是没解决? 先确认用的软件版本与固件是否配套(见上面「读不出固件信息」那条); 再确认是否以管理员身份运行、程序目录是否有写权限(见「硬件宏无法保存」那条); 仍不行的话在本帖下回复,说明软件版本 + 固件版本 + 系统版本 + 具体报错,方便定位。
  • 博主的博文

    1 1
    1 主题
    1 帖子
    A
    「加个图形化编程页麻烦吗?」 这是 10 月 20 日中午我收到的一句话。当时的回答是:不麻烦,但得先看清手里有什么——因为图形化编辑器最容易做成的样子,是画得挺好看、生成出来的脚本跑不了。 两天之后,它长这样:块可以拖、可以拼、能撤销、能复制粘贴、能按 Ctrl 滚轮缩放,切回文本模式看到的就是块生成的规范化脚本,编译、校验、保存、烧录全部复用原来的链路。 [image: 键鼠助手 5.0 的图形化编程界面:左侧宏目录,右侧块画布] 成品:左边是宏目录,右边是块画布。中间那段 当 … 执行 是容器块,条件由值块拼成(count 小于 3 且 鼠标是否按下 1),下面跟着延时、赋值、移动、点击,最后一段是自定义函数 on_timer1 的定义块。 这篇文章记录这两天的实际过程:做了哪些决定、踩了哪些坑、时间花在哪。文末有一张按开发日志回填的时间账。 一、先别写代码,先盘点 动手之前先回答一个问题:这个「Lua 宏」到底是不是 Lua。 盘完的结论是——不是。它是一个封闭的小 DSL,解析器同时支持两套方言: Lua 风格(对外文档化的语法):local x = v、if..then..elseif..else..end、while..do..end、for i=a,b,step..do..end、function f(p)..end、do..end、return、break、-- 注释; 遗留 BASIC 风格(老脚本兼容):If/Then/EndIf、While/Wend、For/Next、Sub/End Sub、Dim、Goto、Rem、Call、Exit Sub。 内置能力面是两张静态表:15 条命令 + 9 个函数,每条都带分类、参数模式、参数种类、参数个数上下限、说明文本。 这个盘点值钱的地方在于它直接给出了方案:块调色板不用手写,可以从命令表派生;而唯一的缺口是 AST → 文本生成器(unparser),得新写。 盘点还顺手圈出一条红线:内置函数的编号是编译器按命令表声明顺序写进字节码的 uint16 操作数,只能在表末尾追加,不能插入或删除。所以图形化这一层绝不能碰编译链路,它只能是「块树 → 文本 → 原有 parser/compiler」的上面一层。 一个决定性的判断:图形化只是入口,不是新引擎。这个边界守住了,后面所有工作都只在一个 UI 模块里发生,设备端字节码、VM、函数编号零改动。 二、第二条决断:不塞 Web 引擎 做块编辑器最省事的路是塞一个 Web 引擎进去,跑现成的 Blockly。这条路被否掉了,理由很实际: 安装包会凭空涨几十兆,启动慢一截; 多一个运行时的版本问题(Qt WebEngine 的 Chromium 更新、显卡驱动兼容); 复制粘贴、字体渲染、高分屏缩放全部隔着两层,和原生控件的行为对不齐。 于是决定:原生 Qt 自绘,视觉和几何照 Blockly 移植。代价是每一根连接齿、每一段圆角都要自己算;收益是这套东西完全可控——后来做缩放时,「所有几何都在逻辑坐标里」这件事能成立,正是因为画布是我自己的。 三、布局和绘制必须分开 一开始是很朴素的一层:一个函数量尺寸、一个函数画。很快就撞墙了——插入线要落点、命中测试要知道点到哪个参数、内嵌的编辑框要贴到某个字段上,这些全都要「尺寸从哪来」。 所以最终分成三层: 层 职责 为什么不合并 block_render_info 纯几何与文案,算出一棵布局树 命中测试、插入线、内嵌控件几何全都复用它,不能只有绘制时才存在 block_drawer 只按布局树画,不改任何状态 绘制无副作用,缩放时才能只换一个变换矩阵 block_render_constants 齿高、圆角、行高、内衬这些形状常量 改一版视觉只动一处 撞墙的具体形态是「凹凸连接件不吻合」:上一版齿的曲率半径和缺口对不上,块叠起来有一条白缝。最后是对着参考实现的源码逐段校正几何参数才解决——这也是第一天晚上 20:25 那条日志的由来。 右侧的块目录直接复用了同一套绘制代码,所以「面板里长什么样」和「拖进画布后长什么样」不会有第二份实现: [image: 右侧块目录:每个条目就是一个缩小的块,带参数名与说明] 块目录的条目不是截图,也不是另画的示意图——它调用的是画布那套 block_render_info + block_drawer,只是画在一个小画布上。 四、真相只能有一个 命令表原本只有一个 displayName,那是文本模式里真实可键入的调用名,不能改。但块面上要显示中文名,右侧目录也要中文名。 一开始的写法是在块的渲染里硬编码一套中文。这套东西立刻就脱轨了:同一个命令,块面上叫「按下按键」,目录里叫「按下」,诊断里又叫「按键按下」。改一处,另两处不知道。 改成:命令表上加一个 chineseName,块面、目录、提示共用;文本模式继续用 displayName。一处声明,三处消费。 同样的思路后来用在表达式上,而且更极端: 槽里的文本是唯一真相,AST 是它的一次投影,槽文本变了才重建; 拖一个值块进来,是替换子树,不是往字符串里追加。 第二条是血的教训。最早为了省事,「把函数拖进条件槽」实现成往槽的文本里拼字符串,结果 a + 1 拖两次变成 a + 1 + 1,而且撤销回去再看,表达式树已经和文本对不上了。结构化之后这类问题整类消失:拖拽只发生在树的两颗子树之间,文本只是它的输出。 五、剪贴板:不要用文本当中间格式 Ctrl+C / Ctrl+V 的第一版想法很自然:把选中的块生成脚本文本,粘贴时再解析回来。这条路走不通,因为文本层丢了信息: 「可选参数槽未启用」和「槽里是空的」在文本里长得一样; if 分支的启用状态、被折叠的分支,文本表达不了; 往返一次,块的类型信息(哪一段是用户子程序调用、哪一段是自定义函数)就退化成函数名。 于是剪贴板走的是「整棵块树序列化」:自定义 MIME 类型 application/x-kma-blocks,内容是 JSON,带格式版本号。同时副带一份等价脚本文本——这样粘到记事本里也讲得清,从纯文本粘回来时走 parser 兜底,至少不会丢得莫名其妙。 一个可以复用的结论:块的复制粘贴不要走「块 → 文本 → 块」。文本是给人看的产物,不是传输格式。 六、那圈看不见的选中框 多选本身不难:按住 Ctrl 点一下切换选中,选中集合是个保序去重的数组,主选中恒等于第一个(粘贴位置、调色板新块都跟着它)。真正的坑在「看不见」。 选中框最初用的是主题的高亮色。逻辑上完全正确——主题换肤自动跟随。但在浅色主题下,那个高亮是淡蓝的 1px 细线,压在蓝色的块体上,肉眼几乎无法分辨。用户反馈是「按了 Ctrl 点,不知道有没有选中」。 最后改成固定配色:外圈琥珀 3px,内圈深棕 1px 做内衬,深浅主题下都压得住。教训是主题色不等于可见色,选中这种强反馈的视觉不要交给定主题去决定。 七、缩放:所有几何只能有一套坐标系 这是整个两天里最值得写的一段。 缩放最直觉的做法是「把所有尺寸都乘一下系数」。这么做会在三个地方同时崩:命中测试、拖拽落点、内嵌编辑框的位置。因为一旦有两套坐标,任何一处忘了换算,就是「看着点在 A 上、选中了 B」。 所以定了一条规矩:布局、命中、拖拽,全部在逻辑坐标里算;缩放只作用在两个入口——绘制的 QPainter::scale(),和把鼠标位置换算成逻辑坐标的那一个函数。这条规矩守住之后,缩放本身变得很简单。 真正难的是那些「看起来该跟着缩,但其实不能缩」的东西: 布局宽度取视口宽度,且与缩放无关。这条是「真缩放」和「只改字号」的分界线。如果布局宽度写成「视口宽 ÷ zoom」,那缩放只是在改字号,块永远不会横向溢出,也就永远不需要横向滚动; 内嵌的输入框和下拉框是子控件,不缩放。所以既要按缩放换算它们的位置和尺寸,又要按缩放调字号,两处都得乘; 滚动条的单位是逻辑像素。滚到一半改变缩放时,要先记住「当前视口顶端的逻辑坐标」,重排之后按这个锚点恢复,否则内容会突然跳走。 滚轮以光标为锚点缩放,也是同一个套路:算出光标下的逻辑点,缩放后反解出新的滚动偏移,让那个点停在原地。 再加两条写进代码注释的自我约束: 布局逻辑宽度有下限(560),窗口极窄时不至于把块挤成一条竖线; 缩放范围 40%~300%,一格 1.15 倍。连续滚动是乘方关系,所以是平滑的,一格一档也不会跳。 八、两个让我半夜还在查的 bug 1. 300% 缩回 100% 直接崩:0xC00000FD 现象是必崩:放大到 300% 再缩回 100%,进程退出码 0xC00000FD——栈溢出。 原因很朴素:refreshLayout() 末尾调了 resize(),而 resize() 会同步回调 resizeEvent,回调里又调 refreshLayout()。放大时内容尺寸单调增长,递归很快就耗尽了栈。 修法是加一个重入闸(refreshing_),进入布局就先置位、退出再复位,回调进来直接返回。 值得说的是为什么它没被测出来:早期的截图测试窗口尺寸是固定的,缩放也只在同一个尺寸内做,递归深度刚好没到阈值。是「在真实窗口里来回缩」这个动作把它逼出来的,不是断言。 2. 一个测试程序完全没有输出 另一个更折磨人:新写的缩放测试程序一启动就死,连 main 第一行的 printf 都没有。 排查绕了很久,最后是靠「第一行都没打印」这个线索反推的——不是逻辑崩,是进程在第一次输出之前就没了。罪魁是这一行: std::setvbuf(stdout, nullptr, _IOLBF, 0); 想把 stdout 改成行缓冲,size 传了 0。这在 MSVC 的 CRT 里会直接把进程打死。给一个真实缓冲区就好了。 记录下来是因为这条非常有欺骗性:现象是「程序什么都不做就退出」,看起来像链接错了、像静态初始化崩了、像 Qt 插件找不到,唯独不像一行日志相关的调用。 九、回归测试搬回仓库 这套东西的正确性没法靠眼睛看——块的几何、命中、嵌入、撤销,随便改一行都可能悄悄坏掉。之前这些验证散在构建目录下的临时程序里,改一次代码要手工找一遍入口。 这次把它们收进仓库,做成一件可重复的事: 一份用例清单是唯一真相(类型 | 可执行文件 | 模式 | 最少断言数 | 参数形式 | 说明),两个平台的脚本共读同一份; 三种类型:断言型(12 个,判定依据)、冒烟型(7 个,只要求不崩)、截图型(8 个,默认不跑,出图人工比对); 最少断言数是必需的防御:模式名写错会走到默认分支静默返回 0,看起来「通过」,其实是没跑; 两个平台各一个脚本,退出码统一:0 全通过 / 1 有用例失败 / 2 环境或构建失败。失败不中断,最后统一汇总。 跑通的结果是 19 通过 / 0 失败 / 8 跳过,截图模式 8/8 出了 18 张图。 跨平台脚本这部分,坑比功能代码还多: 坑 表现 处置 批处理文件存成 UTF-8 中文 Windows 上按系统代码页逐行解析,行边界错乱,报「xxx 不是内部或外部命令」 存成 ANSI/GBK 用 for /f 读 UTF-8 清单 中文重配对把分隔符吞掉,整行被静默跳过(用例凭空少三条) 先按 UTF-8 读、只取纯 ASCII 列,生成工作清单 用 find 计数 PATH 里装了 GNU find,/c 被当成 C 盘递归扫描,CPU 打满 计数交给专用脚本 shell 里 kill -0 判存活 僵尸进程仍然返回成功,每条用例都等满超时 改判进程状态里的 Z 十、148 处硬编码文案 最后一件收尾的事:英文界面下,块编辑器整片是中文。因为绘制层是命名空间里的自由函数,没有 QObject,拿不到 tr(),所以文案全是硬编码字面量。 处置分两步: 造一个只有 tr() 的壳类(Q_DECLARE_TR_FUNCTIONS),上下文固定,调用点统一写 BlockText::tr(...)。148 处字面量批量替换,只换函数名,跨行拼接的参数原样保留;替换前先确认这些字面量没有一处参与比较、持久化或生成脚本——否则翻译会让脚本跟着变语言。 重扫翻译文件、补齐译文、重新生成 .qm。结果是两边各 478 条、0 条未完成。 顺带发现两件事值得写下来: 旧翻译文件严重滞后,中文界面此前有一部分实际显示的是英文(表格列名、设备类型这些)。这次一并补上; 提取工具必须一次给全量源目录。只对单个目录跑,工具会把其余 478 条判成「已废弃」直接删掉——我被清空过两次,靠备份才恢复。这是那种「操作成功、结果正确、数据没了」的典型。 十一、时间账 下面这张表是按开发日志的推进节点回填的,不是秒表计时,误差大概在半小时量级。所谓「一轮」= 一次完整的改→编→验证→截图确认。 日期 时段 内容 轮数 10-20 12:35 起约 45 分钟 盘点 DSL、定边界、确认唯一缺口 — 10-20 下午约 4.5 小时 块模型 / 文档 / 画布 / 页面 + AST 转文本 1 10-20 傍晚约 2.5 小时 视觉移植:C 形嘴部、齿形吻合、块上直改 3 10-20 晚间约 2.5 小时 渲染层按参考源码逐段校正、拖动、撤销重做 3 10-21 上午约 2 小时 表达式结构化:值块、且/或/非、取负、拖放替换 5 10-21 中午约 3.5 小时 对齐、分隔条、右栏分类、形态开关、值块选中 5 10-21 下午约 2.5 小时 定位、右键分流、死按钮、图标定稿 4 10-21 晚间约 3.5 小时 剪贴板、多选、缩放 3 10-21 21:00–22:30 回归整合 + 两个平台的一键脚本 1 10-21 22:30–23:10 文案翻译收尾 1 合计 两个工作日,净投入约 24 小时 约 25 轮 产出的体量:33 个文件、+10617 / −539 行。其中纯功能代码大概占七成,剩下是测试、翻译资源和文档。 如果把时间按性质拆开,比例大概是这样: 类别 占比 具体是什么 写功能 ~55% 模型、布局、绘制、交互、表达式 排障 ~20% 栈溢出、隐式共享深拷贝、坐标没换算 构建与验证 ~15% 编译、跑回归、看截图、来回缩 测试与文案 ~10% 用例、脚本、翻译 十二、回头看 真正花时间的从来不是「把块画出来」。画块是第一天的下午就完成的事。 耗时间的是三件事: 坐标系。只要有两套坐标,就一定会有一处忘了换算。把它压到一套,缩放从「到处乘以系数」变成了两个入口的改动。 真相只有一个。块的文案、槽的文本、剪贴板的格式,每一处都有「省事的双份实现」诱惑,而每一处双份最后都脱轨了。 验证要能重复。改一行就可能悄悄坏掉的模块,靠眼睛看是靠不住的;把验证做成一条命令,才有勇气去动第二十五轮。 至于「两天」这个数字——它更像两天 × 大约十二小时,中间还有不少时间花在等编译和重跑回归上。真要说有什么经验,是那条最初的红线救了整件事:图形化只是入口,不碰编译链路。守住这条线,才敢在两天里迭代二十五轮。 本文涉及客户名称、内部路径、内网地址与版本标识的细节已做脱敏处理。