STTPage.h 不只包含页面类,还包含面板、贴磁视图、定时器、串口、打印机、键盘、扫码枪、HTTP 和异步任务等辅助类。本文依据当前头文件及实现说明实际行为。
stt 命名空间。rt 是运行时,par 是父控件/父对象;父对象必须比子对象活得久。SttXxx 是相应类的指针别名,例如 SttPage 等价于 STTPage*。void handler(uint16_t code, LvEvent e),注册目标对象必须在回调解除前有效。STTPanel* panel = new STTPanel(getRuntime(), this, "content");
panel->setMainArg(10, 10, 460, 240, 0);
panel->setMainStyle(4, 0xffffff, 0xf0f0f0, 1, 255, 1, 0x999999);
panel->setBgImg("res/background.png");
setMainArg(x, y, w, h, type):设置位置尺寸。type=0 面板,1 图片,2 页面;面板和图片关闭滚动,页面可自动滚动。setMainStyle(...):设置圆角、背景渐变、透明度、边框及可选背景图。setBgImg(path, part):设置背景图。当前实现也会识别 GIF,并创建 GIF 子对象,不能仅按头文件“仅 STTPage 可设置 GIF”的说明理解。setSize(w, h):先清空图片、使缓存失效、调整尺寸,再恢复图片,适合带背景图的面板。注意:GIF 对象及图片资源由框架管理;不要在外部释放其底层 LVGL 对象。频繁切换大图会触发缓存失效,避免在高频定时器中调用。
STTTileView* tabs = new STTTileView(getRuntime(), this, "pages", 3, 1);
tabs->setMainArg(0, 0, 480, 260);
tabs->setMainStyle(0, 0xffffff, 0xffffff, 0, 255, 0, 0);
STTTile* first = tabs->getTile(0);
STTLabel* title = new STTLabel(getRuntime(), first, "title");
title->setText("第一页");
tabs->setTileIndex(1, true);
STTTile* active = tabs->getActTile();
构造函数接收 cols、rows。当前实现只校验乘积在 1..12,不单独校验每个参数 <10;非法时退回 3 x 1。tile 在首次 getTile、setTileIndex 或 getActTile 时延迟创建。
getTile(index):返回指定 tile。setTileIndex(index, anim):切换 tile;值改变后可写入绑定变量。getTileIndex() / getActTile():获取当前序号/对象。build():内部延迟构建函数,业务代码通常不调用。onDataChanged(...):绑定整数变量变化时切换页。严重注意:实现只检查 index < size,没有检查 index >= 0。index 是 int8_t,负数会越界;调用前必须自行保证 0 <= index < cols*rows。
页面可由父控件创建,也可直接指定 layer:
STTPage* page = new STTPage(getRuntime(), parent, "Page01", "ext");
// 或:new STTPage(getRuntime(), 0, "Page01", "ext");
page->setMainArg(0, 0, 480, 272, 2);
page->setMainStyle(0, 0xffffff, 0xffffff, 0, 255, 0, 0);
构造后可直接使用公开管理器 pm、dm、vm、fm 和运行时指针 p_rt。
page->getEncoderGroup()->push_back(btn);
page->setLastFocused(btn);
STTWidget* last = page->getLastFocused();
page->display();
page->close();
getEncoderGroup() 返回页面内部 vector 指针,不要 delete;页面销毁后失效。setLastFocused/getLastFocused 保存返回页面后的编码器焦点。display() 刷新页面及子控件;close() 请求页面管理器关闭当前页面。page->setBgImg("res/idle.gif");
vector<STTWidget*> overlay = { statusLabel };
page->snapshotToPng("/customer/usr/snapshot.png", false,
video1, nullptr, nullptr, nullptr, &overlay);
snapshotToPng 是同步函数。带视频时每路可能等待约 2 秒,并借助临时 JPG 合成画面;实现没有向调用者返回失败状态,也没有检查每次视频截图是否成功。vecWidget 中对象会被移动到顶层参与合成,原始层级索引不会恢复。不要在高频 UI 回调中使用,也不要传入即将销毁的控件。
当前 setBgImg 每次都会新建一个 STTPanel 并覆盖内部指针,重复调用可能留下旧面板;建议页面初始化阶段只调用一次。
page->addResideVar("PLC.Counter");
std::function<void()> refreshFn = [this]() { refreshValues(); };
page->addVarMsg(refreshFn);
addResideVar 会把查找到的 IVariable* 直接强转为 IDvcVariable*,调用者必须确保该变量确实是设备变量。addVarMsg 保存对传入 std::function 的引用包装,所以上例中的 refreshFn 必须比页面活得更久;不要传局部临时对象。
timer = new STTTimer(getRuntime(), this, "poll", false);
timer->set(1000, 10);
timer->onEventHandler(Event::READY, this,
(EHandler)&Frm01::onPollTimer);
timer->start();
void Frm01::onPollTimer(uint16_t code, LvEvent e) {
// 定时处理
}
set(period, count, cron):周期小于 100 ms 会被钳制为 100 ms;负 count 变为 0,count < 1 表示不限次数。start()、pause()、resume()、stop() 返回状态是否成功转换。status() 返回是否运行;isBackground() 返回是否后台定时器;getPeriod() 返回毫秒周期。sendTick(tick) 由调度系统送入时间戳;type() 固定用于标识定时器;event()、sendEvent() 对业务无实际用途。处于暂停状态时直接调用 stop() 当前会返回 false,且不复位计数;如需停止,可先 resume() 再 stop()。sendTick 内部把时间差转为 int32_t,超长运行/时钟回绕存在风险。
com = new STTCom(getRuntime(), this, "COM1", false);
if (com->set("baud=115200;data=8;stop=1;parity=0") && com->open()) {
com->onEventHandler(Event::READY, this,
(EHandler)&Frm01::onComData);
com->writeData({0x01, 0x03, 0x00, 0x00, 0x00, 0x02});
}
set(settings) 配置底层 UART;具体字符串字段由当前 UART 驱动决定。open()、pause()、resume()、close() 控制端口。writeData(bytes) 发送字节。readData(out, waittime) 阻塞等候数据,同时泵送当前 looper;等待期间 UI 仍可能明显卡顿。clean(t) 清缓冲。当前实现忽略 t,总是调用无参数 clean(),不能可靠地区分读/写缓冲。onEventHandler 注册接收回调。readData 的内部超时字段为 int16_t,不要传大于约 32767 ms 的值;推荐短超时或事件驱动接收。
STTBarcodePrinter* printer =
new STTBarcodePrinter(getRuntime(), this, "TSC");
if (printer->set("baud=9600") && printer->open()) {
ILabelPrinter* label = printer->getLabelPrinter();
// 使用 ILabelPrinter 接口排版并打印
}
构造 id 当前只安全支持 "TSC"(标签打印机)或 "POS"(票据/条码打印机)。其他字符串可能使类型和内部指针未初始化。
set/open/pause/resume/close 转发到底层打印设备。getLabelPrinter() 仅标签类型返回非空。getBarcodePrinter() 仅条码类型返回非空。onEventHandler(...) 当前实现为空,不要依赖打印事件回调。STTProgressWorker* worker = new STTProgressWorker(app, this, "export", 30);
worker->invoke([this]() {
exportLargeFile();
})->onFinished([this]() {
vm->setChar("提示", "导出完成");
});
worker->start();
invoke 设置后台函数,onFinished 设置结束函数,start 启动并显示进度框。timeout 小于 0 时改为 5。
updateProgress(rate, msg) 的当前实现存在错误:当 rate 在 1..100 时,它把 msg 写入进度率变量,而不是写入 rate。在修复库前不要依赖百分比显示。每次 start() 都会连接系统变量,析构中未看到对应断开;同一对象不要反复启动。
scanner = new STTBarcodeScanner(getRuntime(), this, "scanner");
scanner->onEventHandler(Event::READY, this,
(EHandler)&Frm01::onScan);
void Frm01::onScan(uint16_t code, LvEvent e) {
const char* text = static_cast<const char*>(e.user_data);
string value = text ? text : ""; // 必须在回调内立即复制
}
pause()/resume() 控制是否派发输入。STTKeyboard 的 m_type=0 表示普通键盘,STTBarcodeScanner 固定为扫码类型。
回调中的 e.user_data 指向函数局部 vector 的缓冲区,只在本次同步回调期间有效。必须立即复制,严禁保存指针或交给异步线程。
http = new STTHttp(getRuntime(), this, "api");
http->setHeader("Content-Type: application/json");
http->setOption(CURLOPT_TIMEOUT, int64_t(10));
http->onEventHandler(this, (EHandler)&Frm01::onHttpDone);
int sid = http->post("http://192.168.1.10/api/status", "{\"run\":true}");
void Frm01::onHttpDone(uint16_t code, LvEvent e) {
// code 固定为 Event::LEAVE;请求 sid 放在 e.eventCode 中
if (http->mRetCode == 0 && http->mResponseCode >= 200 &&
http->mResponseCode < 300) {
string body = http->mResponse;
}
}
setHeader(line) 添加并去重请求头;clearHeader() 清空。setOption 重载直接转发 CURL option,返回 CURL 结果。字符串参数的底层生命周期要符合对应 option 要求。get(url) / post(url, data) 当前同步执行,可能阻塞调用线程;成功启动返回 10001..19999 的 sid,负数是前置失败。upload(url, file) / download(url, path) 使用 AsynWorker,完成后回调。mRetCode 是 CURL 执行结果,mResponseCode 是 HTTP 状态码,mResponse 是响应体(下载写文件时通常为空)。前置失败码包括:空参数、运行时不可用、已有请求占用等。是否业务成功必须同时判断 mRetCode 和 mResponseCode,不能只看 sid。一个实例同一时间只允许一个请求。异步请求结束前不得销毁 STTHttp;否则后台 lambda 会访问悬空的 this。
asyncWorker = new STTAsyncWorker(getRuntime(), this, "pollWorker");
asyncWorker->setPeriod(500);
asyncWorker->onActionHandler(this, (EHandler)&Frm01::onBackgroundTick);
asyncWorker->onFinishHandler(this, (EHandler)&Frm01::onBackgroundFinished);
asyncWorker->start();
setPeriod(ms) 仅接受大于 1 的值;默认 10 ms。start() 仅在状态为停止且无 worker 时成功。pause():1 -> 2;resume():2 -> 1;stop():1/2 -> 0。status():0 停止、1 运行、2 暂停。onActionHandler 设置周期任务,onFinishHandler 设置停止后的完成回调。Action 在后台 worker 循环中调用,必须避免直接操作 UI,且任务时长会叠加到周期。析构前应 stop() 并确保后台循环退出;实现缺少显式同步保护,回调与 UI 状态共享数据时需要应用层互斥。
头文件在 STTAsyncWorker 后错误地再次声明 typedef STTTimer* SttTimer;,并未提供 SttAsyncWorker 别名,应直接使用 STTAsyncWorker*。
STTFx* fx = new STTFx(getRuntime(), this, "PLC.Speed", [this]() {
refreshSpeed();
});
变量每次变化都会同步调用 mFuc()。构造和析构均直接查询变量后解引用,没有空指针检查,因此变量名必须存在,变量对象也必须活到 STTFx 析构之后;回调函数不能为空。
onwDataChanged 是信号槽入口,业务代码不应直接调用。类似地,refrChild、calc、doPerform、onTriHandler、onAct、onFinish 和各 onDataReceiveHandler 都是框架内部方法,应通过对应的 display、定时器、HTTP、异步任务或设备公开接口间接触发。
STTTimer、STTAsyncWorker、串口和异步 HTTP 请求。STTTileView 的负索引。