diff --git a/InkJetPrinter.cpp b/InkJetPrinter.cpp index af1d383..3129a16 100644 --- a/InkJetPrinter.cpp +++ b/InkJetPrinter.cpp @@ -1054,7 +1054,7 @@ void InkJetPrinter::timerHandler(void){ qInfo() << "Self check done and send print para to FPGA"; sysPara->devOp->setHighVoltageAmplitudetoSTM32(); sysPara->devOp->writeFpgaCalandar(0); - sysPara->devOp->writeFpgaPrintPara(); + sysPara->devOp->writeFpgaPrintPara(true); if(sysPara->para.Reciclar) test_flag = 1; diff --git a/Printsetting.cpp b/Printsetting.cpp index 00de811..ae914e6 100644 --- a/Printsetting.cpp +++ b/Printsetting.cpp @@ -50,6 +50,9 @@ PrintSetting::PrintSetting(QWidget *parent) : ui->eleEyeEdgetTriggerInner->click(); ui->radioButtonSynTriggerInner->click(); + + ui->tabWidget->setCurrentIndex(0); + ui->tabWidget->removeTab(4); } PrintSetting::~PrintSetting() @@ -250,6 +253,18 @@ void PrintSetting::updateSysStatusToUI(){ void PrintSetting::on_pushButtonApplyPulseFix_clicked() { + float circle = ui->lineEditDiameter->text().toFloat() * 3.1415926; + float pulseNum = ui->lineEditPulsePerCycle->text().toFloat(); + if(circle != 0){ + auto pulseNumPerMM = 4*pulseNum/circle; //电路和Fpga做了4倍频处理,这里乘以4是为了匹配4倍频的处理进行正确的显示 + m_model.encoderPulsePerMm = pulseNumPerMM; + auto b = ui->lineEditTriggerPulsePerMm->blockSignals(true); + ui->lineEditTriggerPulsePerMm->setText(QString::number(pulseNumPerMM)); + ui->lineEditTriggerPulsePerMm->blockSignals(b); + } + + return; + if(!sysPara) return; @@ -385,12 +400,13 @@ void PrintSetting::setTriggerUI(int triggerMode){ if(triggerMode == PRINT_TRIGGER_SYNCHRONIZER) { ui->labelEleEyeTriggerPrintDelay->setText(tr("触发延时(毫米)")); + ui->labelEleEyeTriggerIntervalMs->setText(tr("重复间隔(毫米)")); } else { ui->labelEleEyeTriggerPrintDelay->setText(tr("触发延时(毫秒)")); + ui->labelEleEyeTriggerIntervalMs->setText(tr("重复间隔(毫秒)")); } - ui->labelEleEyeTriggerIntervalMs->setText(tr("重复间隔(毫秒)")); ui->labelEleMaskTime->setText(tr("封锁时间(毫秒)")); } if(triggerMode == PRINT_TRIGGER_ELEC_EYE_VOLTATE_LEVEL @@ -582,7 +598,13 @@ void PrintSetting::on_eleEyeEdgetTriggerExternal_clicked() m_model.eleEyeTriggerSynPrintEn = ui->radioButtonSynTriggerExternal->isChecked(); on_checkEyeTriggerSynPrintEn_clicked(m_model.eleEyeTriggerSynPrintEn); + if(ui->radioButtonEleEyeEdgetTrigger->isChecked()) + m_model.triggerMode = PRINT_TRIGGER_ELEC_EYE_EDGE; + else + m_model.triggerMode = PRINT_TRIGGER_ELEC_EYE_VOLTATE_LEVEL; + setEnableEleEyeSetting(true); + setTriggerUI(m_model.triggerMode); } void PrintSetting::setEnableSynTriggerSetting(bool flag){ @@ -666,9 +688,11 @@ void PrintSetting::on_radioButtonSynTriggerExternal_clicked() } m_model.eleEyeTriggerSynPrintEn = ui->eleEyeEdgetTriggerExternal->isChecked(); + on_checkEyeTriggerSynPrintEn_clicked(m_model.eleEyeTriggerSynPrintEn); setEnableSynTriggerSetting(true); setEnableEleEyeSetting(!ui->eleEyeEdgetTriggerInner->isChecked()); + setTriggerUI(m_model.triggerMode); } void PrintSetting::on_radioButtonEleEyeEdgetTrigger_clicked() diff --git a/Printsetting.ui b/Printsetting.ui index 5505108..5de6939 100644 --- a/Printsetting.ui +++ b/Printsetting.ui @@ -561,39 +561,13 @@ - 210 + 230 60 - 521 + 341 81 - - - - - 0 - 0 - - - - - 150 - 0 - - - - Qt::LeftToRight - - - 脉冲数修正 - - - - :/icon/pulse.png:/icon/pulse.png - - - @@ -629,7 +603,7 @@ - 150 + 170 37 @@ -638,7 +612,7 @@ - + @@ -839,6 +813,96 @@ Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + 400 + 220 + 161 + 31 + + + + Qt::ImhNone + + + + + + 220 + 220 + 171 + 31 + + + + 同步器直径(毫米): + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + 260 + 170 + 131 + 31 + + + + 脉冲数/转: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + 400 + 170 + 161 + 31 + + + + Qt::ImhNone + + + + + + 400 + 260 + 161 + 37 + + + + + 0 + 0 + + + + + 150 + 0 + + + + Qt::LeftToRight + + + 脉冲数修正 + + + + :/icon/pulse.png:/icon/pulse.png + + @@ -1301,7 +1365,7 @@ - + 调试设置 diff --git a/QtUI.pro b/QtUI.pro index 3392725..24e5318 100644 --- a/QtUI.pro +++ b/QtUI.pro @@ -32,8 +32,10 @@ greaterThan(QT_VER_MAJ, 5) | greaterThan(QT_VER_MIN, 9) QT += serialbus greaterThan(QT_MAJOR_VERSION, 4):QMAKE_CXXFLAGS += -fpermissive greaterThan(QT_MAJOR_VERSION, 4):CONFIG += c++11 +# 启用release下带调试信息,详见`VX5 Release编译发行.md`,如果不需要,可以注释掉,但请在软件绝对稳定时注释 CONFIG += qt warn_on debug_and_release QMAKE_CXXFLAGS += -g + #QMAKE_POST_LINK += ulimit -c unlimited LIBS += -ldl @@ -177,7 +179,8 @@ HEADERS += Public.h \ Log/logmessage.h \ Log/logglobal.h \ loginfoctrl.h \ - runtimectrl.h + runtimectrl.h \ + dump.h FORMS += \ @@ -232,6 +235,7 @@ SOURCES +=\ mainwidget.cpp \ testprintwidget.cpp\ main.cpp \ + dump.cpp \ InkJetPrinter.cpp \ MyInputMethod.cpp \ InputMethod.cpp \ @@ -382,16 +386,4 @@ if(contains(DEFINES,__2451WIFI__)) #SOURCES += /home/student/newDisk/2451_linux/wpa_supplicant_hostapd/wpa_supplicant_hostapd-0.8/src/common/wpa_ctrl.c #INCLUDEPATH += /home/student/newDisk/2451_linux/wpa_supplicant_hostapd/wpa_supplicant_hostapd-0.8/src #INCLUDEPATH += /home/student/newDisk/2451_linux/wpa_supplicant_hostapd/wpa_supplicant_hostapd-0.8/src/utils -} - - - - - - - - - - - - +} \ No newline at end of file diff --git a/SystemSetting.cpp b/SystemSetting.cpp index 0d05686..f2ecee9 100644 --- a/SystemSetting.cpp +++ b/SystemSetting.cpp @@ -199,7 +199,7 @@ void SystemSetting::applyUIPara(){ quint16 diaphramOffset = 5*(ui->comboBoxDiaphramOffset->currentIndex()+1); sysPara->para.diaphramOffset = diaphramOffset; //sysPara->para.mixValveisNewVersion = ui->radioButtonMixValveNewVersion->isChecked(); - if(sysPara->devOp->writeFpgaPrintPara() != -1){ + if(sysPara->devOp->writeFpgaPrintPara(true) != -1){ QMessageBox::information(this,tr("提示"),tr("应用成功")); } emit changeEditMode(sysPara->para.EditMode); diff --git a/doc/VX5 Release编译发行.md b/doc/VX5 Release编译发行.md new file mode 100644 index 0000000..44e106a --- /dev/null +++ b/doc/VX5 Release编译发行.md @@ -0,0 +1,144 @@ +## 带崩溃转储功能的VX5 Release 编译发行、dump收集、问题分析 +由于VX5相比VX4在稳定性上有些许差异,目前VX5在用户端仍然可能存在崩溃问题,作为兜底方案,我们引入了崩溃转储功能。 +崩溃转储功能是工程化的软件项目中几乎必有的一个功能,它可以在软件崩溃时,将当前程序的函数调用栈、寄存器状态、内存状态等保存到dump上,开发人员依据dump文件结合程序日志可以快速定位到崩溃的位置。 +带崩溃转储的发行版发布、dump收集、问题分析,步骤是:使用带-g编译标记编译release版本、生成带调试符号的可执行文件、分离调试符号生成符号调试表和不带调试符号的发行版可执行程序、将发行版可执行程序发布给用户上机运行、运行过程中触发崩溃、收集dump文件、将dump与之前分离的调试信息表以及发行版可执行程序放在一起,使用 addr2line 等工具封装为脚本将dump中的需要观察的调用栈地址、程序地址等转换为包含源码位置等的信息,最后进行源码分析并修复。 + +## 项目先决条件 +在进行此方式编译发行前,可能需要检查以下先决条件,虽然这些已经在最新 VX5_V6.7.1 和 韩国VX5 项目中默认配置。 +1. QtUI.pro 中需包含如下配置: +``` pro +CONFIG += qt warn_on debug_and_release # 启用警告和调试信息 +QMAKE_CXXFLAGS += -g # 启用 -g 编译标记 +#QMAKE_POST_LINK += ulimit -c unlimited +LIBS += -ldl # 链接 dl 库,用于获取当前程序的基址 + +# ... 其他内容 + +# 包含 dump转储功能的头文件和源文件 +HEADERS += dump.h +SOURCES += dump.cpp +``` +2. `main.cpp` 中需包含 `dump.h` 文件,并在 `QApplication app` 后第一时间调用 `installSignalHandler()` 函数,用于安装信号处理函数。 +```cpp + QApplication a(argc, argv); + gAppNameStr = QString::fromUtf8(argv[0]); + installSignalHandlers(); // 这里安装信号处理函数 +``` +3. `dump.cpp` `dump.h` 已添加至最新的标准VX5、韩国VX5项目中。 +4. 用于分离调试符号的脚本 `strip_debug.sh` 已添加至项目源码目录中。 +5. 用于分析dump文件的脚本 `resolve_dump.sh` 已添加至项目源码目录中。 + +## 编译发行 +1. 使用QtCreator IDE 一键release编译,或使用VScode 配置命令行,编译release版本的带调试符号的、满足项目需求的可执行文件,得到`QtUI`。 +```shell +inkjet@ubuntu:/mnt/hgfs/share/bSpec/800X600_New_SystemProject-_NewInterface$ ls ../arm_build/QtUI* -lh +-rwxrwxrwx 1 root root 55M 7月 1 10:22 ../arm_build/QtUI +``` +可以看到`QtUI`的大小为55M,说明包含了调试符号,但是因为携带了调试符号,运行时可能会占用更多内存,且不利于用户发行更新,同时还包含了源码信息,存在泄漏风险。为此我们需要将调试符号分离出来,生成符号调试表和不带调试符号的发行版可执行程序。 + +2. 使用之前编译`QtUI`时使用的交叉编译工具链中的 `objcopy` 工具,将`QtUI`的调试符号分离出来,对于 `InkjetUbuntu-T1` 虚拟机,已经包含了 `objcopy` 工具。 + + 使用命令行分离:进入QtUI所在目录,按如下命令进行操作: +```shell +inkjet@ubuntu:/mnt/hgfs/share/bSpec/arm_build$ aarch64-linux-gnu-objcopy --only-keep-debug QtUI QtUI.dbg # 分离调试符号生成符号调试表 +inkjet@ubuntu:/mnt/hgfs/share/bSpec/arm_build$ aarch64-linux-gnu-objcopy --strip-debug QtUI QtUI # 移除调试符号 +inkjet@ubuntu:/mnt/hgfs/share/bSpec/arm_build$ aarch64-linux-gnu-objcopy --add-gnu-debuglink=QtUI.dbg QtUI # 关联调试符号文件和发行程序 +inkjet@ubuntu:/mnt/hgfs/share/bSpec/arm_build$ ls ./QtUI* -lh +-rwxrwxrwx 1 root root 6.7M 7月 1 10:43 ./QtUI +-rwxrwxrwx 1 root root 48M 7月 1 10:43 ./QtUI.dbg +``` +注意输出结果,一个大小为6.7M的`QtUI`,该文件与平常不带`-g`编译出来的`QtUI`一致,用于用户发布;一个大小为48M的`QtUI.dbg`,存放调试符号信息,用于内部保留日后分析dump文件,两个文件进行了关联,因此两个文件在内部必须成对存放,不可缺一。 + +第二种方法:使用`strip_debug.sh`脚本分离调试符号 + 进入QtUI所在目录,按如下命令进行操作: +```shell +inkjet@ubuntu:/mnt/hgfs/share/bSpec/arm_build$ ./strip_debug.sh +``` + 或者指定QtUI路径: +```shell +inkjet@ubuntu:/mnt/hgfs/share/bSpec/arm_build$ ./strip_debug.sh QtUI +``` + +两种方式都可以生成`QtUI`和`QtUI.dbg`两个文件,此时将 `QtUI` 附上更新说明发布。将 `QtUI.dbg` `QtUI` 打包上传SVN仓库内部存放。 +```shell +inkjet@ubuntu:/mnt/hgfs/share/bSpec/arm_build$ ls ./QtUI_V6.7.1_韩国_2026年7月* -lh +-rwxrwxrwx 1 root root 23M 7月 1 11:11 ./QtUI_V6.7.1_韩国_2026年7月1日11_11_08.zip +``` +注意明确标准编译时间,避免与用户端软件编译时间不一致导致的dump文件时间错误。 +将携带`QtUI.dbg`的`QtUI_V6.7.1_韩国_2026年7月1日11_11_08.zip`上传至 `https://pc-20181215fqqf/svn/src/QTUI软件版本/VX5` 仓库。 + +然后仅将 `QtUI`与发行说明打包按正常流程发布。 + +## 收集dump文件 +当用户端或产线测试发生崩溃,dump文件会被收集到`/home/plg/loginfo`目录下。 +```shell +cat@lubancat:/home/plg/loginfo$ ls /home/plg/loginfo/dumpfile_2026_07_01_11_33_07.txt -lh +-rw-r--r-- 1 root root 3.1K 7月 1 11:33 /home/plg/loginfo/dumpfile_2026_07_01_11_33_07.txt +``` +使用 `QtUI` 的日志导出功能,可将所有 dump 文件导出至U盘,dump 文件格式为 `dumpfile_YYYY_MM_DD_HH_MM_SS.txt`,按照崩溃时间记录。 +拿到的dump文件可能如下 + +```txt +Stack trace info: +Base address: 0x557162e000 + +/home/plg/QtUI(+0x6b29c) [0x557169929c] + ??:? + +/home/plg/QtUI(+0x6b96c) [0x557169996c] + ??:? + +linux-vdso.so.1(__kernel_rt_sigreturn+0) [0x7f9d72458c] + ??:0 +...... 省略更多.... +``` +注意dump文件中缺失了源码信息,需要结合之前的`QtUI.dbg`文件进行分析。 + +## 分析dump文件 +由于之前对编译好的`QtUI`进行了调试符号分离,因此崩溃捕获到的dump信息与普通不带`-g`编译的`QtUI`崩溃捕获的dump一样,会缺少调试符号。 +因此我们将该dump文件与之前内部保存的 `QtUI` `QtUI.dbg` 放在一起,进行一点处理就可以获得完整的调试源码信息。 +执行脚本`resolve_dump.sh`,即可获得完整的dump信息。 +```shell +inkjet@ubuntu:/mnt/hgfs/share/bSpec/arm_build$ ./resolve_dump.sh ./dumpfile_2026_07_01_11_33_07.txt +Base address: 0x557162e000 +QtUI 路径: /mnt/hgfs/share/bSpec/arm_build/QtUI +QtUI.dbg: /mnt/hgfs/share/bSpec/arm_build/QtUI.dbg +输出文件: ./dumpfile_2026_07_01_11_33_07_resolved.txt + +正在解析 dump 文件: ./dumpfile_2026_07_01_11_33_07.txt +========================================= + +Stack trace info: + +Base address: 0x557162e000 + + +/home/plg/QtUI(+0x6b29c) [0x557169929c] + createStackTraceFile(char const*, char const*) at /mnt/hgfs/share/bSpec/800X600_New_SystemProject-_NewInterface/../arm_build/../800X600_New_SystemProject-_NewInterface/dump.cpp:54 + + +/home/plg/QtUI(+0x6b96c) [0x557169996c] + signalHandler(int) at /mnt/hgfs/share/bSpec/800X600_New_SystemProject-_NewInterface/../arm_build/../800X600_New_SystemProject-_NewInterface/dump.cpp:96 (discriminator 9) + + +linux-vdso.so.1(__kernel_rt_sigreturn+0) [0x7f9d72458c] + ??:0 + +/opt/Qt/lib/libQt5Widgets.so.5(_ZNK7QWidget11windowTitleEv+0xc) [0x7f9d1ad56c] + ??:0 + +/home/plg/QtUI(+0x189f30) [0x55717b7f30] + SetNumDlg::on_btn_confirm_clicked() at /mnt/hgfs/share/bSpec/800X600_New_SystemProject-_NewInterface/../arm_build/../800X600_New_SystemProject-_NewInterface/setnumdlg.cpp:39 + + +/home/plg/QtUI(+0x1b14d0) [0x55717df4d0] + SetNumDlg::qt_metacall(QMetaObject::Call, int, void**) at /mnt/hgfs/share/bSpec/800X600_New_SystemProject-_NewInterface/../arm_build/tmp/moc_setnumdlg.cpp:147 + +...... 省略更多.... +``` + +注意输出结果 +``` text + SetNumDlg::on_btn_confirm_clicked() at /mnt/hgfs/share/bSpec/800X600_New_SystemProject-_NewInterface/../arm_build/../800X600_New_SystemProject-_NewInterface/setnumdlg.cpp:39 +``` +说明崩溃位于`setnumdlg.cpp`文件的`39`行。同时`resolve_dump.sh`脚本会生成`dumpfile_2026_07_01_11_33_07_resolved.txt`文件。 \ No newline at end of file diff --git a/dump.cpp b/dump.cpp new file mode 100644 index 0000000..a167595 --- /dev/null +++ b/dump.cpp @@ -0,0 +1,106 @@ +#define _GNU_SOURCE +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include "dump.h" +#include + +extern QString gAppNameStr; + +static uintptr_t g_base_address = 0; + +static int find_base_address(struct dl_phdr_info *info, size_t size, void *data) +{ + (void)size; + (void)data; + if (info->dlpi_name == nullptr || info->dlpi_name[0] == '\0') { + g_base_address = info->dlpi_addr; + return 1; + } + return 0; +} + +static uintptr_t get_base_address() +{ + if (g_base_address == 0) { + dl_iterate_phdr(find_base_address, nullptr); + } + return g_base_address; +} + +void createStackTraceFile(const char *fileName, const char *appName) +{ + std::ofstream file(fileName); + if (!file.is_open()) { + std::cerr << "Error opening file: " << fileName << std::endl; + return; + } + + void *array[50]; + size_t size; + char **strings; + + size = backtrace(array, 50); + if (size == 0) { + std::cerr << "No stack frames obtained" << std::endl; + return; + } + + uintptr_t base_addr = get_base_address(); + + strings = backtrace_symbols(array, size); + file << "Stack trace info:\n"; + file << "Base address: 0x" << std::hex << base_addr << std::dec << "\n\n"; + + for (size_t i = 0; i < size; ++i) { + file << strings[i] << std::endl; + + uintptr_t addr = reinterpret_cast(array[i]); + uintptr_t offset = addr - base_addr; + + char cmd[512]; + snprintf(cmd, sizeof(cmd), "addr2line -e %s 0x%lx", appName, offset); + + FILE *fp = popen(cmd, "r"); + if (fp) { + char line[256]; + while (fgets(line, sizeof(line), fp)) { + file << " " << line; + } + pclose(fp); + } else { + std::cerr << "Failed to run addr2line command" << std::endl; + } + file << std::endl; + } + + free(strings); + file.close(); +} + +static void signalHandler(int signum) +{ + QString dumpFileName = QString("/home/plg/loginfo/dumpfile_%1.txt").arg(QDateTime::currentDateTime().toString("yyyy_MM_dd_hh_mm_ss")); + QString appName = gAppNameStr; + createStackTraceFile(dumpFileName.toUtf8().data(), appName.toUtf8().data()); + exit(signum); +} + +void installSignalHandlers() +{ + signal(SIGSEGV, signalHandler); + signal(SIGABRT, signalHandler); + signal(SIGILL, signalHandler); + signal(SIGFPE, signalHandler); +} \ No newline at end of file diff --git a/dump.h b/dump.h new file mode 100644 index 0000000..3e6dc57 --- /dev/null +++ b/dump.h @@ -0,0 +1,6 @@ +#ifndef DUMP_H +#define DUMP_H + +void installSignalHandlers(); + +#endif // DUMP_H \ No newline at end of file diff --git a/dumpfile_2026_07_01_11_33_07.txt b/dumpfile_2026_07_01_11_33_07.txt new file mode 100644 index 0000000..c5b20cd --- /dev/null +++ b/dumpfile_2026_07_01_11_33_07.txt @@ -0,0 +1,105 @@ +Stack trace info: +Base address: 0x557162e000 + +/home/plg/QtUI(+0x6b29c) [0x557169929c] + ??:? + +/home/plg/QtUI(+0x6b96c) [0x557169996c] + ??:? + +linux-vdso.so.1(__kernel_rt_sigreturn+0) [0x7f9d72458c] + ??:0 + +/opt/Qt/lib/libQt5Widgets.so.5(_ZNK7QWidget11windowTitleEv+0xc) [0x7f9d1ad56c] + ??:0 + +/home/plg/QtUI(+0x189f30) [0x55717b7f30] + ??:? + +/home/plg/QtUI(+0x1b14d0) [0x55717df4d0] + ??:? + +/opt/Qt/lib/libQt5Core.so.5(_ZN11QMetaObject8activateEP7QObjectiiPPv+0x6a8) [0x7f9c72b32c] + ??:0 + +/opt/Qt/lib/libQt5Widgets.so.5(_ZN15QAbstractButton7clickedEb+0x3c) [0x7f9d268e4c] + ??:0 + +/opt/Qt/lib/libQt5Widgets.so.5(+0x2890ec) [0x7f9d2690ec] + ??:0 + +/opt/Qt/lib/libQt5Widgets.so.5(+0x28ac5c) [0x7f9d26ac5c] + ??:0 + +/opt/Qt/lib/libQt5Widgets.so.5(_ZN15QAbstractButton17mouseReleaseEventEP11QMouseEvent+0x184) [0x7f9d26aee4] + ??:0 + +/opt/Qt/lib/libQt5Widgets.so.5(_ZN7QWidget5eventEP6QEvent+0x218) [0x7f9d1c5eb8] + ??:0 + +/opt/Qt/lib/libQt5Widgets.so.5(_ZN19QApplicationPrivate13notify_helperEP7QObjectP6QEvent+0x5c) [0x7f9d181aa0] + ??:0 + +/opt/Qt/lib/libQt5Widgets.so.5(_ZN12QApplication6notifyEP7QObjectP6QEvent+0x1038) [0x7f9d18a5fc] + ??:0 + +/opt/Qt/lib/libQt5Core.so.5(_ZN16QCoreApplication15notifyInternal2EP7QObjectP6QEvent+0x178) [0x7f9c6fd308] + ??:0 + +/opt/Qt/lib/libQt5Widgets.so.5(_ZN19QApplicationPrivate14sendMouseEventEP7QWidgetP11QMouseEventS1_S1_PS1_R8QPointerIS0_Ebb+0x194) [0x7f9d188c24] + ??:0 + +/opt/Qt/lib/libQt5Widgets.so.5(+0x200590) [0x7f9d1e0590] + ??:0 + +/opt/Qt/lib/libQt5Widgets.so.5(+0x202e9c) [0x7f9d1e2e9c] + ??:0 + +/opt/Qt/lib/libQt5Widgets.so.5(_ZN19QApplicationPrivate13notify_helperEP7QObjectP6QEvent+0x5c) [0x7f9d181aa0] + ??:0 + +/opt/Qt/lib/libQt5Core.so.5(_ZN16QCoreApplication15notifyInternal2EP7QObjectP6QEvent+0x178) [0x7f9c6fd308] + ??:0 + +/opt/Qt/lib/libQt5Gui.so.5(_ZN22QGuiApplicationPrivate17processMouseEventEPN29QWindowSystemInterfacePrivate10MouseEventE+0x68c) [0x7f9cb54e3c] + ??:0 + +/opt/Qt/lib/libQt5Gui.so.5(_ZN22QGuiApplicationPrivate17processTouchEventEPN29QWindowSystemInterfacePrivate10TouchEventE+0x17e8) [0x7f9cb53c18] + ??:0 + +/opt/Qt/lib/libQt5Gui.so.5(_ZN22QWindowSystemInterface22sendWindowSystemEventsE6QFlagsIN10QEventLoop17ProcessEventsFlagEE+0xe0) [0x7f9cb2ce54] + ??:0 + +/opt/Qt/lib/libQt5XcbQpa.so.5(+0x705e8) [0x7f98a005e8] + ??:0 + +/lib/aarch64-linux-gnu/libglib-2.0.so.0(g_main_context_dispatch+0x250) [0x7f9b625a70] + ??:0 + +/lib/aarch64-linux-gnu/libglib-2.0.so.0(+0xaaebc) [0x7f9b67aebc] + ??:0 + +/lib/aarch64-linux-gnu/libglib-2.0.so.0(g_main_context_iteration+0x34) [0x7f9b622f14] + ??:0 + +/opt/Qt/lib/libQt5Core.so.5(_ZN20QEventDispatcherGlib13processEventsE6QFlagsIN10QEventLoop17ProcessEventsFlagEE+0x54) [0x7f9c7584c4] + ??:0 + +/opt/Qt/lib/libQt5Core.so.5(_ZN10QEventLoop4execE6QFlagsINS_17ProcessEventsFlagEE+0xf4) [0x7f9c6fb994] + ??:0 + +/opt/Qt/lib/libQt5Core.so.5(_ZN16QCoreApplication4execEv+0x98) [0x7f9c7045a8] + ??:0 + +/home/plg/QtUI(+0x47350) [0x5571675350] + ??:? + +/lib/aarch64-linux-gnu/libc.so.6(+0x273fc) [0x7f9c0573fc] + ??:0 + +/lib/aarch64-linux-gnu/libc.so.6(__libc_start_main+0x98) [0x7f9c0574cc] + ??:0 + +/home/plg/QtUI(+0x480d0) [0x55716760d0] + ??:? + diff --git a/fileManage/FileManageWidget.cpp b/fileManage/FileManageWidget.cpp index e5b2e41..9fca997 100644 --- a/fileManage/FileManageWidget.cpp +++ b/fileManage/FileManageWidget.cpp @@ -36,7 +36,12 @@ FileManageWidget::FileManageWidget(QWidget *parent) : connect(ui->systemFileListView, &QListWidget::itemSelectionChanged, this, &FileManageWidget::refreshButtonsStatus); connect(ui->udiskFileListView, &QListWidget::itemSelectionChanged, this, &FileManageWidget::refreshButtonsStatus); - updateUDiskDevList(); + m_hasUDisk = checkUDiskPresent(); + onUDiskStateChanged(); + + m_udiskCheckTimer = new QTimer(this); + connect(m_udiskCheckTimer, &QTimer::timeout, this, &FileManageWidget::onUDiskCheckTimeout); + m_udiskCheckTimer->start(1000); m_focusedListView = ui->systemFileListView; m_focusedListView->setFocus(); @@ -47,6 +52,7 @@ FileManageWidget::FileManageWidget(QWidget *parent) : FileManageWidget::~FileManageWidget() { + m_udiskCheckTimer->stop(); ui->systemFileListView->removeEventFilter(this); ui->udiskFileListView->removeEventFilter(this); delete ui; @@ -94,20 +100,22 @@ void FileManageWidget::onFilterCBoxSelectChanged(int i) } m_currFilterType = i; - updateFileListView(ui->systemFileListView); - updateFileListView(ui->udiskFileListView); + refreshFileList(ui->systemFileListView); + if (m_hasUDisk) { + refreshFileList(ui->udiskFileListView); + } } void FileManageWidget::onExportBtnClicked() { copyFiles(ui->systemFileListView, ui->udiskFileListView); - updateFileListView(ui->udiskFileListView); + refreshFileList(ui->udiskFileListView); } void FileManageWidget::onImportBtnClicked() { copyFiles(ui->udiskFileListView, ui->systemFileListView); - updateFileListView(ui->systemFileListView); + refreshFileList(ui->systemFileListView); } void FileManageWidget::onSelectAllBtnClicked() @@ -138,37 +146,59 @@ void FileManageWidget::onDelSelectedBtnClicked() qInfo() << "Delete: " << filename << ", result: " << ret; } - updateFileListView(m_focusedListView); + refreshFileList(m_focusedListView); } -void FileManageWidget::updateUDiskDevList() +bool FileManageWidget::checkUDiskPresent() { - bool hasUDisk = false; QFile mountsFile("/proc/mounts"); if (mountsFile.open(QIODevice::ReadOnly | QIODevice::Text)) { - QTextStream in(&mountsFile); - while (!in.atEnd()) { - const QString line = in.readLine(); - if (line.contains("/udisk ")) { - hasUDisk = true; - break; - } - } + QString mountsContent = QString::fromLocal8Bit(mountsFile.readAll()); mountsFile.close(); + if (mountsContent.lastIndexOf("/udisk ") != -1) { + return true; + } } + return false; +} - if (!hasUDisk) { - QDir dir(UDISK_DOC_ROOT_DIR); - hasUDisk = dir.exists(); +bool FileManageWidget::ensureUDiskDocDir() +{ + QDir dir(UDISK_DOC_ROOT_DIR); + if (!dir.exists()) { + if (!dir.mkpath(UDISK_DOC_ROOT_DIR)) { + qCritical() << "Cannot create UDisk doc dir:" << UDISK_DOC_ROOT_DIR; + return false; + } + qInfo() << "Created UDisk doc dir:" << UDISK_DOC_ROOT_DIR; } + return true; +} +void FileManageWidget::onUDiskCheckTimeout() +{ + bool hasUDisk = checkUDiskPresent(); + if (hasUDisk == m_hasUDisk) { + return; + } + m_hasUDisk = hasUDisk; + onUDiskStateChanged(); +} + +void FileManageWidget::onUDiskStateChanged() +{ ui->udiskSelectBox->clear(); - if (hasUDisk) { + if (m_hasUDisk) { ui->udiskSelectBox->addItem(tr("U盘")); ui->udiskSelectBox->setCurrentIndex(0); + if (ensureUDiskDocDir()) { + refreshFileList(ui->udiskFileListView); + } else { + ui->udiskFileListView->clear(); + } + } else { + ui->udiskFileListView->clear(); } - - updateFileListView(ui->udiskFileListView); } void FileManageWidget::refreshButtonsStatus() @@ -187,15 +217,14 @@ void FileManageWidget::refreshButtonsStatus() ui->selectAllBtn->setEnabled(m_focusedListView->count() != selectedItems.size()); } -int FileManageWidget::updateFileListView(QListWidget* listView) +int FileManageWidget::refreshFileList(QListWidget* listView) { QString searchPath = listView->property("searchPath").toString(); - // 判断目录是否存在 QDir dir(searchPath); - if(!dir.exists()) { - qCritical() << "Cannot open path: " << searchPath; - return -1; + if (!dir.exists()) { + listView->clear(); + return 0; } // 根据过滤类型设置过滤器字符串 diff --git a/fileManage/FileManageWidget.h b/fileManage/FileManageWidget.h index 5641844..454e2bd 100644 --- a/fileManage/FileManageWidget.h +++ b/fileManage/FileManageWidget.h @@ -3,6 +3,7 @@ #include #include +#include namespace Ui { class FileManageWidget; @@ -33,13 +34,16 @@ private slots: void onSelectAllBtnClicked(); void onCancelAllBtnClicked(); void onDelSelectedBtnClicked(); - void updateUDiskDevList(); + void onUDiskCheckTimeout(); void refreshButtonsStatus(); private: - int updateFileListView(QListWidget* listView); + bool checkUDiskPresent(); + bool ensureUDiskDocDir(); + int refreshFileList(QListWidget* listView); + void onUDiskStateChanged(); void copyFiles(QListWidget* from, QListWidget* to); @@ -48,10 +52,12 @@ private: int m_currFilterType; QListWidget* m_focusedListView = nullptr; - // 用于文件显示列表中,存放每个文件名对应的绝对路径所用的数据键 static constexpr int AbsPathRole = Qt::UserRole + 1; + QTimer* m_udiskCheckTimer = nullptr; + bool m_hasUDisk = false; + void applyStyleSheet(); // 应用样式表 }; -#endif // FILEMANAGEWIDGET_H +#endif // FILEMANAGEWIDGET_H \ No newline at end of file diff --git a/main.cpp b/main.cpp index 1cc748b..bac4ba9 100644 --- a/main.cpp +++ b/main.cpp @@ -1,8 +1,3 @@ -#define _GNU_SOURCE // 必须放在所有头文件之前 -#include // dl_iterate_phdr 的声明 -#include // dladdr 等 -#include // backtrace - #include #include #include @@ -26,101 +21,9 @@ #include "InkJetPrinter.h" #include "Log/filelogger.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include +#include "dump.h" -QString gAppNameStr = "";//这是个全局变量,用于后续获取当前运行程序的绝对路径 - -// 全局变量保存基址 -static uintptr_t g_base_address = 0; - -// 回调函数:找到当前可执行文件的基址 -static int find_base_address(struct dl_phdr_info *info, size_t size, void *data) { - // 检查是否是主程序(第一个加载的通常是主程序) - if (info->dlpi_name == nullptr || info->dlpi_name[0] == '\0') { - // 获取第一个段的加载地址(通常是基址) - g_base_address = info->dlpi_addr; - return 1; // 停止遍历 - } - return 0; // 继续遍历 -} - -// 获取程序基址的函数 -uintptr_t get_base_address() { - if (g_base_address == 0) { - dl_iterate_phdr(find_base_address, nullptr); - } - return g_base_address; -} - -void createStackTraceFile(const char *fileName, const char *appName) -{ - std::ofstream file(fileName); - if (!file.is_open()) { - std::cerr << "Error opening file: " << fileName << std::endl; - return; - } - - void *array[100]; - size_t size; - char **strings; - - size = backtrace(array, 100); - if (size == 0) { - std::cerr << "No stack frames obtained" << std::endl; - return; - } - - // 获取程序基址(PIE需要) - uintptr_t base_addr = get_base_address(); - - strings = backtrace_symbols(array, size); - file << "Stack trace info:\n"; - file << "Base address: 0x" << std::hex << base_addr << std::dec << "\n\n"; - - for (size_t i = 0; i < size; ++i) { - file << strings[i] << std::endl; - - // 计算偏移地址:实际地址 - 基址 - uintptr_t addr = reinterpret_cast(array[i]); - uintptr_t offset = addr - base_addr; - - char cmd[512]; - // 使用偏移地址而不是实际地址 - snprintf(cmd, sizeof(cmd), "addr2line -e %s 0x%lx", appName, offset); - - FILE *fp = popen(cmd, "r"); - if (fp) { - char line[256]; - while (fgets(line, sizeof(line), fp)) { - file << " " << line; // 缩进显示更清晰 - } - pclose(fp); - } else { - std::cerr << "Failed to run addr2line command" << std::endl; - } - file << std::endl; - } - - free(strings); - file.close(); -} - -void signalHandler(int signum) -{ - // 直接指定固定的路径 - QString dumpFileName = "/home/plg/doc/dumpfile.txt"; - QString appName = gAppNameStr; - createStackTraceFile(dumpFileName.toUtf8().data(), appName.toUtf8().data()); - exit(signum); -} +QString gAppNameStr = ""; //#define SYS_CN @@ -175,10 +78,7 @@ int main(int argc, char *argv[]) QApplication a(argc, argv); gAppNameStr = QString::fromUtf8(argv[0]); - signal(SIGSEGV, signalHandler); - signal(SIGABRT, signalHandler); - signal(SIGILL, signalHandler); - signal(SIGFPE, signalHandler); + installSignalHandlers(); // 配置日志 QString configFileName = ":/conf/config.ini"; @@ -224,7 +124,6 @@ int main(int argc, char *argv[]) langurageFile= "./thai.qm"; f.setPointSize(12); f.setPixelSize(12); - } else if(langurageType=="Vietnamese") { f.setFamily("Arial");//f.setFamily("Zfull-GB"); //f.setFamily("WenQuanYiMH");//Cambria @@ -341,4 +240,4 @@ void loadApplicationFonts() { } } } -} +} \ No newline at end of file diff --git a/resolve_dump.sh b/resolve_dump.sh new file mode 100644 index 0000000..991f700 --- /dev/null +++ b/resolve_dump.sh @@ -0,0 +1,103 @@ +#!/bin/bash + +TOOLCHAIN_PREFIX="aarch64-linux-gnu-" +ADDR2LINE="${TOOLCHAIN_PREFIX}addr2line" + +if [ -z "$1" ]; then + echo "错误: 请指定 dump 文件路径" + echo "用法: $0 [QtUI所在目录]" + echo "" + echo "示例:" + echo " $0 ./dumpfile_2026_07_01_11_33_07.txt" + echo " $0 ./dumpfile_2026_07_01_11_33_07.txt /path/to/QtUI_dir" + exit 1 +fi + +DUMP_FILE="$1" + +if [ ! -f "$DUMP_FILE" ]; then + echo "错误: 找不到 dump 文件: $DUMP_FILE" + exit 1 +fi + +DUMP_DIR="$(dirname "$DUMP_FILE")" +DUMP_BASENAME="$(basename "$DUMP_FILE")" +DUMP_NAME="${DUMP_BASENAME%.*}" +DUMP_EXT="${DUMP_BASENAME##*.}" +RESOLVED_FILE="${DUMP_DIR}/${DUMP_NAME}_resolved.${DUMP_EXT}" + +if [ -n "$2" ]; then + QTUI_DIR="$2" +else + QTUI_DIR="$(cd "$(dirname "$0")" && pwd)" +fi + +QTUI_PATH="${QTUI_DIR}/QtUI" +QTUI_DBG="${QTUI_DIR}/QtUI.dbg" + +if [ ! -f "$QTUI_PATH" ]; then + echo "错误: 找不到 QtUI 文件: $QTUI_PATH" + echo "请确保 QtUI 和 QtUI.dbg 在同一目录下" + exit 1 +fi + +if [ ! -f "$QTUI_DBG" ]; then + echo "警告: 找不到 QtUI.dbg 文件: $QTUI_DBG" + echo "addr2line 将仅使用 QtUI 中的调试信息(如果有)" +fi + +if ! command -v "$ADDR2LINE" &> /dev/null; then + echo "错误: 找不到 $ADDR2LINE" + exit 1 +fi + +BASE_ADDR=$(grep -oP 'Base address:\s*\K0x[0-9a-fA-F]+' "$DUMP_FILE") + +if [ -z "$BASE_ADDR" ]; then + echo "错误: 无法从 dump 文件中解析 Base address" + exit 1 +fi + +echo "Base address: $BASE_ADDR" +echo "QtUI 路径: $QTUI_PATH" +echo "QtUI.dbg: $QTUI_DBG" +echo "输出文件: $RESOLVED_FILE" +echo "" + +echo "正在解析 dump 文件: $DUMP_FILE" +echo "=========================================" +echo "" + +ADDR_LINE_REGEX='^(.+/QtUI)\(\+0x([0-9a-fA-F]+)\)\s+\[(0x[0-9a-fA-F]+)\]' + +{ + while IFS= read -r line; do + if [[ "$line" =~ $ADDR_LINE_REGEX ]]; then + OFFSET="0x${BASH_REMATCH[2]}" + RUNTIME_ADDR="${BASH_REMATCH[3]}" + + read -r source_line + + RESOLVED=$($ADDR2LINE -e "$QTUI_PATH" -f -C -p "$OFFSET" 2>/dev/null) + + echo "$line" + if [ -n "$RESOLVED" ] && [ "$RESOLVED" != "?? ??:0" ] && [ "$RESOLVED" != "?? ??:?" ]; then + echo " $RESOLVED" + else + echo " $source_line" + fi + echo "" + elif [[ "$line" =~ ^Base\ address: ]]; then + echo "$line" + echo "" + elif [[ "$line" =~ ^Stack\ trace\ info: ]]; then + echo "$line" + echo "" + else + echo "$line" + fi + done < "$DUMP_FILE" + + echo "=========================================" + echo "解析完成" +} | tee "$RESOLVED_FILE" \ No newline at end of file diff --git a/strip_debug.sh b/strip_debug.sh new file mode 100644 index 0000000..8ff4f83 --- /dev/null +++ b/strip_debug.sh @@ -0,0 +1,61 @@ +#!/bin/bash + +TOOLCHAIN="aarch64-linux-gnu-objcopy" + +if [ -n "$1" ]; then + QTUI_PATH="$1" +else + SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" + QTUI_PATH="${SCRIPT_DIR}/QtUI" +fi + +if [ ! -f "$QTUI_PATH" ]; then + echo "错误: 找不到 QtUI 文件: $QTUI_PATH" + echo "用法: $0 [QtUI路径]" + exit 1 +fi + +if ! command -v "$TOOLCHAIN" &> /dev/null; then + echo "错误: 找不到工具链 $TOOLCHAIN" + exit 1 +fi + +QTUI_DIR="$(dirname "$QTUI_PATH")" +QTUI_NAME="$(basename "$QTUI_PATH")" +QTUI_DBG="${QTUI_DIR}/${QTUI_NAME}.dbg" + +echo "目标文件: $QTUI_PATH" +echo "" + +echo "步骤 1/3: 分离调试符号..." +$TOOLCHAIN --only-keep-debug "$QTUI_PATH" "$QTUI_DBG" +if [ $? -ne 0 ]; then + echo "错误: 分离调试符号失败" + exit 1 +fi +echo "完成: $QTUI_DBG" + +echo "步骤 2/3: 移除调试符号..." +$TOOLCHAIN --strip-debug "$QTUI_PATH" "$QTUI_PATH" +if [ $? -ne 0 ]; then + echo "错误: 移除调试符号失败" + exit 1 +fi +echo "完成" + +echo "步骤 3/3: 关联调试符号文件..." +$TOOLCHAIN --add-gnu-debuglink="$QTUI_DBG" "$QTUI_PATH" +if [ $? -ne 0 ]; then + echo "错误: 关联调试符号文件失败" + exit 1 +fi +echo "完成" + +echo "" +echo "========== 结果 ==========" +ls -lh "$QTUI_PATH" "$QTUI_DBG" +echo "" +echo "操作完成:" +echo " 发行程序: $QTUI_PATH" +echo " 调试符号: $QTUI_DBG" +echo " 注意: 两个文件已关联,必须成对存放" \ No newline at end of file diff --git a/testprintwidget.cpp b/testprintwidget.cpp index 2c91b7a..361c16d 100644 --- a/testprintwidget.cpp +++ b/testprintwidget.cpp @@ -44,8 +44,11 @@ TestPrintWidget::TestPrintWidget(QWidget* parent) // m_ButtonGroup->addButton(ui.btnList, 7); dialogSaveFile =new DialogSaveFile(0); + connect(dialogSaveFile,SIGNAL(returnMainWidget(int)),this,SIGNAL(sendButtonClickedSignal(int))); dialogFileOperation = new DialogFileOperation(0); + connect(dialogFileOperation,SIGNAL(openFile(QString,bool)),this,SLOT(onFileOpen(QString,bool))); + connect(dialogFileOperation,SIGNAL(createSceneImage(QString)),this,SLOT(onCreateSceneImage(QString))); m_CreateImageFlag = false; m_pSavePicScene = new QGraphicsScene(); @@ -81,11 +84,6 @@ TestPrintWidget::TestPrintWidget(QWidget* parent) getShowSelectItemText(); }); - connect(dialogSaveFile,SIGNAL(returnMainWidget(int)),this,SIGNAL(sendButtonClickedSignal(int))); - - connect(dialogFileOperation,SIGNAL(openFile(QString,bool)),this,SLOT(onFileOpen(QString,bool))); - connect(dialogFileOperation,SIGNAL(createSceneImage(QString)),this,SLOT(onCreateSceneImage(QString))); - // ========== 连接所有信号槽 ========== // 左侧工具栏按钮组 connect(m_ButtonGroup, QOverload::of(&QButtonGroup::buttonClicked), @@ -214,12 +212,18 @@ TestPrintWidget::~TestPrintWidget() if(m_pSavePicScene) { delete m_pSavePicScene; + m_pSavePicScene = nullptr; } if(m_TextEdit){ delete m_TextEdit; + m_TextEdit = nullptr; } + if (imageLoader) { delete imageLoader; + imageLoader = nullptr; +} + } QPixmap TestPrintWidget::loadIcon(const QString& iconName, const QSize& size) @@ -233,9 +237,11 @@ void TestPrintWidget::on_openfileButton_pressed() { pPicScene->clearSelection(); pPicScene->clearFocus(); + if (dialogFileOperation) { dialogFileOperation->refreshListView(editMode); dialogFileOperation->exec(); } +} void TestPrintWidget::addDateItem() { @@ -261,7 +267,9 @@ void TestPrintWidget::on_enlargeButton_pressed() // pViewer->scale(scaleFactor, scaleFactor); // TODO:: 保留,以后真的要用到放大缩小 - ((MyView*)pViewer)->ZoomOut(); +// ((MyView*)pViewer)->ZoomOut(); + auto myView = dynamic_cast(pViewer); + if (myView) myView->ZoomOut(); } void TestPrintWidget::on_addButton_pressed(const SourceType type) @@ -294,7 +302,9 @@ void TestPrintWidget::onEditItemDone() const QString content = m_LineEdit->text(); foreach (QGraphicsItem *item, pPicScene->selectedItems()) { - PicTextItem *pTextItem = (PicTextItem *)item; + PicTextItem* pTextItem = dynamic_cast(item); + if (pTextItem == nullptr) continue; + if(pTextItem->sourceType() == SourceType::TEXT) pTextItem->setSource(content); } @@ -302,8 +312,6 @@ void TestPrintWidget::onEditItemDone() void TestPrintWidget::getShowSelectItemText() { - //Scene Test - ui.m_LineEdit->setEnabled(false); ui.m_LineEdit->setText(""); @@ -314,6 +322,8 @@ void TestPrintWidget::getShowSelectItemText() PicTextItem* lastSelectedTxtItem = nullptr; for(auto item : selectedItems) { PicTextItem* txtItem = dynamic_cast(item); + if (txtItem == nullptr) continue; + if(txtItem->sourceType() != SourceType::IMAGE && txtItem->sourceType() != SourceType::CODE && txtItem->sourceType() != SourceType::UNKNOWN) { lastSelectedTxtItem = txtItem; } @@ -401,6 +411,8 @@ void TestPrintWidget::saveFile(){ int index=0; foreach (QGraphicsItem *item, items) { PicTextItem *picText = qgraphicsitem_cast(item); + if (picText == nullptr) continue; + writer.writeStartElement("VBfile"); // 开始子元素 writer.writeTextElement(QString::fromLocal8Bit("pointer_x"), QString::fromLocal8Bit("%1").arg(picText->pos().x())); writer.writeTextElement(QString::fromLocal8Bit("pointer_y"), QString::fromLocal8Bit("%1").arg(picText->pos().y())); @@ -431,9 +443,12 @@ void TestPrintWidget::saveFile(){ writer.writeEndElement(); // 结束子元素 } + + if (m_printSetting) { writer.writeEmptyElement("PrintSettings"); if(m_printSetting) PrintSettingModel::saveToXml(m_printSetting->model(), writer); + } writer.writeEndElement(); // 结束根元素 writer.writeEndDocument(); // 结束文档 @@ -468,14 +483,17 @@ void TestPrintWidget::onFileOpen(QString filePath,bool reviewFlag){ QFile file(filePath); if (file.open(QIODevice::ReadOnly | QIODevice::Text)) { // 以只写模式打开文件 - xmlfileName=filePath; + + if(reviewFlag) + xmlfileName=filePath; + QXmlStreamReader reader(&file); if(!m_CreateImageFlag) { pPicScene->clear(); pViewer->viewport()->update(); unsigned char SpecialVarEnable = 0; - sysPara->devOp->fpgaWr(0xa4b0,(unsigned char *)&SpecialVarEnable,1); + if (sysPara) sysPara->devOp->fpgaWr(0xa4b0,(unsigned char *)&SpecialVarEnable,1); } // 解析 XML,直到结束 while (!reader.atEnd()) { @@ -683,7 +701,7 @@ void TestPrintWidget::parseVbfile(QXmlStreamReader &reader, bool loadPrintSettin // Condition= false ; // CatchValue = -1; //LogInfo::setRunStatusText(tr("接收文件解析::2")); - if(!sysPara->status.forTemplateUse || m_CreateImageFlag) + if(sysPara!=nullptr && !sysPara->status.forTemplateUse || m_CreateImageFlag) { pPictText->setHtml(strCDATA); } @@ -967,6 +985,7 @@ void TestPrintWidget::on_delButton_pressed() foreach (QGraphicsItem *item, pPicScene->selectedItems()) { pPicScene->removeItem(item); delete item; + break; } } @@ -1040,9 +1059,12 @@ void TestPrintWidget::onBtnItemConfigClicked() return; } - auto item = dynamic_cast(selectedItems[0]); + PicTextItem* item = dynamic_cast(selectedItems[0]); + if (item == nullptr) return; + if(item->sourceType() == SourceType::COUNTER) { auto counterItem = dynamic_cast(item); + if (counterItem) { CounterViewModel vm; vm.counterType = counterItem->counterType(); vm.digitalNum = counterItem->digitalNum(); @@ -1050,6 +1072,7 @@ void TestPrintWidget::onBtnItemConfigClicked() vm.enbCustomCharWidth = false; vm.charWidth = counterItem->charWidth(); emit sig_showEditCounter(vm); + } return; } @@ -1067,12 +1090,14 @@ void TestPrintWidget::onBtnItemConfigClicked() if(item->sourceType() == SourceType::DATETIME) { // @see TestPrintWidget::addDateTimeItem auto dateTimeItem = dynamic_cast(item); + if (dateTimeItem) emit sig_showEditDateTime(dateTimeItem->model(), dateTimeItem->textItemModel()); return; } if(item->sourceType() == SourceType::SCHEDULE_SHIFT) { auto scheduleItem = dynamic_cast(item); + if (scheduleItem) emit sig_showScheduleShiftsEditor(scheduleItem->schedules(), scheduleItem->textItemModel()); return; } @@ -1097,6 +1122,8 @@ void TestPrintWidget::onBtnItemConfigClicked() void TestPrintWidget::onItemEditorAccepted() { + if(!itemEditor || !itemEditor->currentItem()) return; + if(itemEditor->currentItem()->source() == SourceType::UNKNOWN) { // Nothing.... } @@ -1448,4 +1475,4 @@ void TestPrintWidget::on_btn_copy_clicked() void TestPrintWidget::on_btn_paste_clicked() { pasteCopiedLayer(); -} \ No newline at end of file +}