2024-08-07 18:18:37 +08:00
|
|
|
#include "main.h"
|
2023-06-06 12:13:33 +08:00
|
|
|
#include "mainwindow.h"
|
2024-08-07 18:18:37 +08:00
|
|
|
#include "deviceitem.h"
|
|
|
|
#include "gutil/qnetwork.h"
|
2022-01-04 18:11:48 +08:00
|
|
|
#include <QApplication>
|
2023-09-19 11:49:20 +08:00
|
|
|
#include <QFile>
|
2024-08-07 18:18:37 +08:00
|
|
|
#include <QDir>
|
2022-08-25 18:37:24 +08:00
|
|
|
#include <QMessageBox>
|
2022-01-04 18:11:48 +08:00
|
|
|
#include <QSplashScreen>
|
2024-08-07 18:18:37 +08:00
|
|
|
#include <QFileInfo>
|
2023-04-18 14:14:46 +08:00
|
|
|
#include <QStandardPaths>
|
2024-08-07 18:18:37 +08:00
|
|
|
|
2023-10-23 11:44:22 +08:00
|
|
|
#if(QT_VERSION_MAJOR > 5)
|
|
|
|
#include <QImageReader>
|
|
|
|
#endif
|
2022-01-04 18:11:48 +08:00
|
|
|
|
2024-08-07 18:18:37 +08:00
|
|
|
const QString UpdVerUrl = "https://www.ledok.cn/download/LedOK Express Updates.json";
|
|
|
|
|
|
|
|
QString programsDir() {
|
|
|
|
static auto rtn = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/programs";
|
|
|
|
return rtn;
|
|
|
|
}
|
|
|
|
|
2022-08-25 18:37:24 +08:00
|
|
|
#ifdef _MSC_VER //MSVC编译器
|
|
|
|
#include <Windows.h>
|
|
|
|
#include <DbgHelp.h>
|
|
|
|
LONG WINAPI handleException(_EXCEPTION_POINTERS *excep) {
|
2023-04-24 16:35:58 +08:00
|
|
|
auto errCode = QString::number(excep->ExceptionRecord->ExceptionCode, 16);
|
|
|
|
auto errAddr = QString::number((uint)excep->ExceptionRecord->ExceptionAddress, 16);
|
2023-08-01 11:42:41 +08:00
|
|
|
auto hDumpFile = CreateFile(L"ledok-crash.dmp", GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
|
2022-08-25 18:37:24 +08:00
|
|
|
if(hDumpFile == INVALID_HANDLE_VALUE) {
|
2023-08-01 11:42:41 +08:00
|
|
|
qCritical()<<"CreateFile ledok-crash.dmp Failed! ExceptionCode"<<errCode<<"ExceptionAddress"<<errAddr;
|
2022-08-25 18:37:24 +08:00
|
|
|
return EXCEPTION_CONTINUE_SEARCH;
|
2022-01-04 18:11:48 +08:00
|
|
|
}
|
2022-08-25 18:37:24 +08:00
|
|
|
MINIDUMP_EXCEPTION_INFORMATION dumpInfo{GetCurrentThreadId(), excep, TRUE};
|
|
|
|
MiniDumpWriteDump(GetCurrentProcess(), GetCurrentProcessId(), hDumpFile, MiniDumpNormal, &dumpInfo, NULL, NULL);//写入Dump文件内容
|
|
|
|
CloseHandle(hDumpFile);
|
2023-08-01 11:42:41 +08:00
|
|
|
QMessageBox::critical(0, "程序出错 (V" APP_VERSION" - " __DATE__", Code: "+errCode+")", "<b>程序出错!</b><br/>请将安装目录下的 ledok-crash.dmp 文件发送到 gangphon@qq.com 邮箱, 研发人员会尽快处理.");
|
2022-01-20 10:08:17 +08:00
|
|
|
return EXCEPTION_EXECUTE_HANDLER;
|
2022-08-25 18:37:24 +08:00
|
|
|
// EXCEPTION_EXECUTE_HANDLER 已处理异常, 让 windows 正常结束
|
|
|
|
// EXCEPTION_CONTINUE_SEARCH 未处理异常, 让 windows 弹出错误框并结束 (Qt会卡死一段时间)
|
|
|
|
// EXCEPTION_CONTINUE_EXECUTION 已修复错误, 让 windows 从异常发生处继续执行
|
2022-01-04 18:11:48 +08:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2022-08-25 18:37:24 +08:00
|
|
|
int main(int argc, char *argv[]) {
|
2023-10-23 11:44:22 +08:00
|
|
|
#if(QT_VERSION_MAJOR > 5)
|
|
|
|
QImageReader::setAllocationLimit(0);
|
|
|
|
#else
|
2022-01-20 10:08:17 +08:00
|
|
|
QApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
|
|
|
|
QApplication::setAttribute(Qt::AA_UseHighDpiPixmaps);
|
2022-08-25 18:37:24 +08:00
|
|
|
QApplication::setHighDpiScaleFactorRoundingPolicy(Qt::HighDpiScaleFactorRoundingPolicy::PassThrough);
|
2023-10-23 11:44:22 +08:00
|
|
|
#endif
|
2024-02-21 18:08:50 +08:00
|
|
|
QApplication::setOrganizationName("Sysolution");
|
2024-06-20 10:44:08 +08:00
|
|
|
QApplication::setOrganizationDomain("ledok.cn");
|
2022-08-25 18:37:24 +08:00
|
|
|
QApplication::setApplicationName("LedOK Express");
|
2023-09-19 11:49:20 +08:00
|
|
|
QApplication::setStyle("Fusion");
|
2022-01-04 18:11:48 +08:00
|
|
|
QApplication a(argc, argv);
|
2023-09-19 11:49:20 +08:00
|
|
|
|
|
|
|
QSplashScreen splash(QPixmap(":/res/splash.png"));
|
|
|
|
splash.show();
|
|
|
|
splash.showMessage(QObject::tr("Setting up the LedOK Express..."), Qt::AlignRight | Qt::AlignTop, Qt::white);
|
|
|
|
|
2023-04-18 14:14:46 +08:00
|
|
|
QFile file(":/css.css");
|
|
|
|
if(file.exists() && file.open(QFile::ReadOnly)) {
|
2023-10-23 11:44:22 +08:00
|
|
|
a.setStyleSheet(file.readAll());
|
2023-04-18 14:14:46 +08:00
|
|
|
file.close();
|
|
|
|
}
|
2022-08-25 18:37:24 +08:00
|
|
|
QFont font;
|
2023-08-01 11:42:41 +08:00
|
|
|
font.setFamilies(QStringList{"Arial","PingFang SC","Hiragino Sans GB","STHeiti","Microsoft YaHei","WenQuanYi Micro Hei","sans-serif"});
|
2022-08-25 18:37:24 +08:00
|
|
|
a.setFont(font);
|
2023-09-19 11:49:20 +08:00
|
|
|
auto plt = a.palette();
|
|
|
|
plt.setBrush(QPalette::AlternateBase, plt.brush(QPalette::Active, QPalette::Window));
|
|
|
|
plt.setBrush(QPalette::Inactive, QPalette::Highlight, plt.brush(QPalette::Active, QPalette::Highlight));
|
|
|
|
plt.setBrush(QPalette::Inactive, QPalette::HighlightedText, plt.brush(QPalette::Active, QPalette::HighlightedText));
|
|
|
|
a.setPalette(plt);
|
|
|
|
|
|
|
|
QTranslator qtTrans;
|
|
|
|
if(qtTrans.load(QLocale(), "qt", "_", "translations")) QCoreApplication::installTranslator(&qtTrans);
|
2022-08-25 18:37:24 +08:00
|
|
|
|
2023-04-18 14:14:46 +08:00
|
|
|
gFileHome = QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation);
|
2023-09-19 11:49:20 +08:00
|
|
|
|
2022-08-25 18:37:24 +08:00
|
|
|
#ifdef _MSC_VER
|
|
|
|
SetUnhandledExceptionFilter(handleException);
|
|
|
|
#endif
|
2022-01-04 18:11:48 +08:00
|
|
|
MainWindow w;
|
|
|
|
w.show();
|
2022-08-25 18:37:24 +08:00
|
|
|
splash.finish(&w);
|
2022-01-04 18:11:48 +08:00
|
|
|
|
|
|
|
return a.exec();
|
|
|
|
}
|
2024-08-07 18:18:37 +08:00
|
|
|
|
|
|
|
|
|
|
|
QString gFileHome;
|
|
|
|
QList<LedCard> gSelCards;
|
|
|
|
|
|
|
|
DeviceItem *findItem(QString id) {
|
|
|
|
int cnt = gDevicePanel->mDeviceTable->topLevelItemCount();
|
|
|
|
for(int i=0; i<cnt; ++i) {
|
|
|
|
auto item = (DeviceItem*) gDevicePanel->mDeviceTable->topLevelItem(i);
|
|
|
|
if(item==0) continue;
|
|
|
|
if(item->mCard.id==id) return item;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
QString checkReply(QNetworkReply *reply, QJsonDocument *outJson) {
|
|
|
|
auto err = errStr(reply);
|
|
|
|
if(! err.isEmpty()) {
|
|
|
|
auto data = reply->readAll();
|
|
|
|
if(! data.isEmpty()) err = err+"\n"+QCoreApplication::translate("Def","Device replied")+": "+data;
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
if(outJson) {
|
|
|
|
auto data = reply->readAll();
|
|
|
|
QJsonParseError jsonErr;
|
|
|
|
*outJson = QJsonDocument::fromJson(data, &jsonErr);
|
|
|
|
if(jsonErr.error != QJsonParseError::NoError) return "Json error: "+jsonErr.errorString()+"\n"+QCoreApplication::translate("Def","Device replied")+": "+data;
|
|
|
|
}
|
|
|
|
return "";
|
|
|
|
}
|
|
|
|
QString errStrWithJson(QNetworkReply *reply, JValue *outJson, QByteArray *outData) {
|
|
|
|
auto err = errStr(reply);
|
|
|
|
auto data = reply->readAll();
|
|
|
|
if(outData) *outData = data;
|
|
|
|
if(! err.isEmpty()) {
|
|
|
|
if(! data.isEmpty()) err = err+"\n"+QCoreApplication::translate("Def","Device replied")+": "+data;
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
QString error;
|
|
|
|
auto json = JFrom(data, &error);
|
|
|
|
if(! error.isEmpty()) return "JSON Error: "+error+"\n"+QCoreApplication::translate("Def","Device replied")+": "+data;
|
|
|
|
if(! json["success"].toBool()) return QCoreApplication::translate("Def","Fail")+". "+QCoreApplication::translate("Def","Device replied")+": "+data;
|
|
|
|
if(outJson) *outJson = json;
|
|
|
|
return "";
|
|
|
|
}
|
|
|
|
QString checkReplyForJson(QNetworkReply *reply, QJsonDocument *outJson, QByteArray *outData) {
|
|
|
|
auto err = errStr(reply);
|
|
|
|
auto data = reply->readAll();
|
|
|
|
if(outData) *outData = data;
|
|
|
|
if(! err.isEmpty()) {
|
|
|
|
if(! data.isEmpty()) err = err+"\n"+QCoreApplication::translate("Def","Device replied")+": "+data;
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
QJsonParseError jsonErr;
|
|
|
|
QJsonDocument json = QJsonDocument::fromJson(data, &jsonErr);
|
|
|
|
if(jsonErr.error != QJsonParseError::NoError) return "Json error: "+jsonErr.errorString()+"\n"+QCoreApplication::translate("Def","Device replied")+": "+data;
|
|
|
|
if(! json["success"].toBool()) return QCoreApplication::translate("Def","Fail")+". "+QCoreApplication::translate("Def","Device replied")+": "+data;
|
|
|
|
if(outJson) outJson->swap(json);
|
|
|
|
return "";
|
|
|
|
}
|
|
|
|
QString checkReplyForJson(QNetworkReply *reply, QString errField) {
|
|
|
|
auto err = errStr(reply);
|
|
|
|
auto data = reply->readAll();
|
|
|
|
if(! err.isEmpty()) {
|
|
|
|
if(! data.isEmpty()) err = err+"\n"+QCoreApplication::translate("Def","Device replied")+": "+data;
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
QJsonParseError jsonErr;
|
|
|
|
QJsonDocument json = QJsonDocument::fromJson(data, &jsonErr);
|
|
|
|
if(jsonErr.error != QJsonParseError::NoError) return "Json error: "+jsonErr.errorString()+"\n"+QCoreApplication::translate("Def","Device replied")+": "+data;
|
|
|
|
if(! json["success"].toBool()) {
|
|
|
|
auto errStr = json[errField].toString();
|
|
|
|
return QCoreApplication::translate("Def","Fail")+". "+QCoreApplication::translate("Def","Device replied")+": "+(errStr.isEmpty() ? data : errStr);
|
|
|
|
}
|
|
|
|
return "";
|
|
|
|
}
|
|
|
|
|
|
|
|
quint64 dirFileSize(const QString &path) {
|
|
|
|
QDir dir(path);
|
|
|
|
quint64 size = 0;
|
|
|
|
auto infos = dir.entryInfoList(QDir::Files);
|
|
|
|
foreach(QFileInfo fileInfo, infos) size += fileInfo.size();
|
|
|
|
auto subDirs = dir.entryList(QDir::Dirs | QDir::NoDotAndDotDot);
|
|
|
|
foreach(QString subDir, subDirs) size += dirFileSize(path + QDir::separator() + subDir);
|
|
|
|
return size;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool copyDir(const QString &source, const QString &destination, bool override) {
|
|
|
|
QDir directory(source);
|
|
|
|
if(!directory.exists()) return false;
|
|
|
|
QString srcPath = QDir::toNativeSeparators(source);
|
|
|
|
if(!srcPath.endsWith(QDir::separator())) srcPath += QDir::separator();
|
|
|
|
QString dstPath = QDir::toNativeSeparators(destination);
|
|
|
|
if (!dstPath.endsWith(QDir::separator())) dstPath += QDir::separator();
|
|
|
|
|
|
|
|
bool error = false;
|
|
|
|
QStringList fileNames = directory.entryList(QDir::AllEntries | QDir::NoDotAndDotDot | QDir::Hidden);
|
|
|
|
for (QStringList::size_type i=0; i != fileNames.size(); ++i)
|
|
|
|
{
|
|
|
|
QString fileName = fileNames.at(i);
|
|
|
|
QString srcFilePath = srcPath + fileName;
|
|
|
|
QString dstFilePath = dstPath + fileName;
|
|
|
|
QFileInfo fileInfo(srcFilePath);
|
|
|
|
if (fileInfo.isFile() || fileInfo.isSymLink())
|
|
|
|
{
|
|
|
|
if (override)
|
|
|
|
{
|
|
|
|
QFile::setPermissions(dstFilePath, QFile::WriteOwner);
|
|
|
|
}
|
|
|
|
QFile::copy(srcFilePath, dstFilePath);
|
|
|
|
}
|
|
|
|
else if (fileInfo.isDir())
|
|
|
|
{
|
|
|
|
QDir dstDir(dstFilePath);
|
|
|
|
dstDir.mkpath(dstFilePath);
|
|
|
|
if (!copyDir(srcFilePath, dstFilePath, override))
|
|
|
|
{
|
|
|
|
error = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return !error;
|
|
|
|
}
|
|
|
|
unsigned char GetCheckCodeIn8(unsigned char *str, unsigned int size) {
|
|
|
|
unsigned char checkCode = 0;
|
|
|
|
for(int i=0; i<size; i++) checkCode += str[i];
|
|
|
|
return (~checkCode) & 0xff;
|
|
|
|
}
|
|
|
|
|
|
|
|
void MergeFmt(QTextEdit *textEdit, const QTextCharFormat &fmt) {
|
|
|
|
QTextCursor cursor = textEdit->textCursor();
|
|
|
|
if(! cursor.hasSelection()) cursor.select(QTextCursor::WordUnderCursor);
|
|
|
|
cursor.mergeCharFormat(fmt);
|
|
|
|
}
|
|
|
|
|
|
|
|
Tick::Tick(QWidget *parent) : QOpenGLWidget(parent) {
|
|
|
|
setGeometry(0, 0, 1, 1);
|
|
|
|
connect(this, &QOpenGLWidget::frameSwapped, this, &Tick::doFrame);
|
|
|
|
}
|
|
|
|
|
|
|
|
void Tick::doFrame() {
|
|
|
|
auto cur = QDateTime::currentDateTime();
|
|
|
|
auto secs = cur.toSecsSinceEpoch();
|
|
|
|
if(secs != Sec) {
|
|
|
|
Sec = secs;
|
|
|
|
emit secChanged(cur);
|
|
|
|
}
|
|
|
|
update();
|
|
|
|
}
|