新增代码检索功能、提升业务转码效率等

This commit is contained in:
taocong
2026-07-21 17:08:23 +08:00
parent b86fc071c2
commit 389c6eeecd
32 changed files with 2858 additions and 241 deletions
+2 -2
View File
@@ -12,7 +12,7 @@
AboutDialog::AboutDialog(QWidget* parent) : InterDialog(parent)
{
setFixedSize(354, 180);
setFixedSize(360, 210);
setWindowTitle("关于");
setStandardButtons(QDialogButtonBox::Ok);
@@ -34,7 +34,7 @@ AboutDialog::AboutDialog(QWidget* parent) : InterDialog(parent)
QFont nameTextFont = nameText->font();
nameTextFont.setWeight(QFont::Bold);
nameText->setFont(nameTextFont);
nameText->setTextPixelSize(17);
nameText->setTextPixelSize(15);
nameText->setText("UFT30 ChangeCode");
auto* versionText = new ElaText(this);
+8 -5
View File
@@ -151,12 +151,15 @@ void BatchConvertPage::initUI()
void BatchConvertPage::onProgressUpdate(int progress, const QString& status)
{
m_progressBar->setValue(progress);
QString displayStatus = status;
if (displayStatus.length() > 80) {
displayStatus = displayStatus.left(80) + "...";
// 仅当传入有效状态文字时才更新标签,避免无意义的日志行覆盖当前处理状态
if (!status.isEmpty()) {
QString displayStatus = status;
if (displayStatus.length() > 80) {
displayStatus = displayStatus.left(80) + "...";
}
m_progressLabel->setText(displayStatus);
}
m_progressLabel->setText(displayStatus);
}
bool BatchConvertPage::checkFunctionExists(const QString &funcName)
File diff suppressed because it is too large Load Diff
+127
View File
@@ -0,0 +1,127 @@
#ifndef CODESEARCHPAGE_H
#define CODESEARCHPAGE_H
#include <QWidget>
#include <QLabel>
#include <QMouseEvent>
#include <QPixmap>
#include <QStandardItemModel>
#include "ElaTableView.h"
#include "ElaText.h"
#include "ElaLineEdit.h"
#include "ElaPushButton.h"
#include "ElaTreeView.h"
#include "ElaComboBox.h"
#include "ElaPivot.h"
#include "utils/callchainanalyzer.h"
// 图标按钮(基于 QLabel):显示图标 + 悬停提示,鼠标点击触发信号
class IconButton : public QLabel
{
Q_OBJECT
public:
explicit IconButton(const QString &pixmapPath, const QString &tip,
QWidget *parent = nullptr)
: QLabel(parent)
{
const QPixmap pm(pixmapPath);
setPixmap(pm.scaled(22, 22, Qt::KeepAspectRatio, Qt::SmoothTransformation));
setToolTip(tip);
setCursor(Qt::ArrowCursor);
setFixedSize(28, 28);
setAlignment(Qt::AlignCenter);
}
signals:
void clicked();
protected:
void mousePressEvent(QMouseEvent *event) override
{
emit clicked();
QLabel::mousePressEvent(event);
}
};
class CodeSearchPage : public QWidget
{
Q_OBJECT
public:
enum Mode { ConvertMode, SearchMode };
explicit CodeSearchPage(QWidget *parent = nullptr);
~CodeSearchPage();
/// 静默刷新索引(无弹窗,转码完成后自动调用)
void refreshIndexSilent();
/// 转码后静默异步刷新:依次(后台线程)刷新各模式索引,全部完成后清除 m_indexing
void buildSilentChain(const QList<int> &modes, int idx);
protected:
void showEvent(QShowEvent *event) override;
private slots:
void onBrowsePath();
void onRefreshIndex();
void onFunctionDoubleClicked(const QModelIndex &index);
void onExpandAll();
void onCollapseAll();
void onImportChainFiles();
void onExportChain();
void onQueryByInput();
void savePathsToConfig();
void onPivotClicked(int index);
private:
void initUI();
void loadFunctionList();
void populateTree(const QList<CallChainAnalyzer::TreeNode> &rootNodes);
void autoResizeColumns(ElaTreeView *tree);
void addTreeItem(QStandardItem *parentItem, const CallChainAnalyzer::TreeNode &node);
QString resolveChineseName(const QString &displayName) const;
QString lookupUFT3FunctionNo(const QString &funcName);
void collectChainFiles(const CallChainAnalyzer::TreeNode &node, QSet<QString> &out);
static void collectChainLines(const CallChainAnalyzer::TreeNode &node, int depth, QStringList &lines);
static QString sanitizeDirName(const QString &name);
void runQuery(const QString &displayName);
void autoBuildIndexIfNeeded();
void applyMode();
CallChainAnalyzer *currentAnalyzer() const;
/// 后台线程异步构建索引(避免界面卡顿),构建期间显示进度框;完成后回调 onDone(ok, message)
void runIndexBuild(CallChainAnalyzer *ca, const QString &title,
std::function<void(bool, const QString &)> onDone);
Mode m_mode = ConvertMode;
ElaLineEdit *m_pathEdit;
ElaPushButton *m_browseBtn;
ElaPushButton *m_refreshBtn;
ElaPivot *m_modePivot;
IconButton *m_expandBtn;
IconButton *m_collapseBtn;
ElaPushButton *m_importBtn;
ElaPushButton *m_exportChainBtn;
ElaComboBox *m_queryCombo;
ElaLineEdit *m_queryEdit;
ElaPushButton *m_queryBtn;
ElaTableView *m_funcTable;
QWidget *m_leftPanel;
ElaTreeView *m_callTree[2];
QStandardItemModel *m_funcModel;
QStandardItemModel *m_treeModel[2];
QLabel *m_bottomTip; // 底部静态提示(仿 UF20 功能查询页)
CallChainAnalyzer *m_analyzers[2]; // 两种模式各自的分析器(独立索引)
QString m_pathByMode[2]; // 两种模式各自的代码检索路径
QString m_indexedPathByMode[2]; // 两种模式各自已构建索引的路径
bool m_userReSelectedByMode[2] = {false, false}; // 各自改路径后需手动刷新
QStringList m_currentChainFilePaths; // 当前调用链路中所有非"原代码已支持"的文件
QList<CallChainAnalyzer::TreeNode> m_currentChainRoots; // 当前调用链路森林(支持往前查),供导出文本用
QString m_searchRootPath; // 当前索引根路径(导出基准)
QString m_currentQueryName; // 当前查询的函数名称(导出目录名)
QString m_highlightName; // 当前查询的函数/功能名(调用链树中对应行蓝色高亮)
bool m_indexing = false; // 后台索引构建进行中,防止重复触发
};
#endif // CODESEARCHPAGE_H
+135 -56
View File
@@ -1,5 +1,6 @@
#include "metadatupdatepage.h"
#include "utils/Constants.h"
#include "utils/logmanager.h"
#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QGridLayout>
@@ -8,6 +9,7 @@
#include <QCoreApplication>
#include <QDir>
#include <QTimer>
#include <QtConcurrent>
const int UPDATE_ITEM_SHOW_COLUMN_COUNT = 3;
@@ -17,13 +19,18 @@ MetadataUpdatePage::MetadataUpdatePage(QWidget *parent)
initUI();
_processor = new MetadataProcessor(this);
_processor->initConfig(Constants::toolDir());
_watcher = new QFutureWatcher<UpdateResult>(this);
connect(_watcher, &QFutureWatcher<UpdateResult>::finished,
this, &MetadataUpdatePage::onWorkerFinished);
connect(this, &MetadataUpdatePage::sigProgress,
this, &MetadataUpdatePage::onUpdateProgress, Qt::QueuedConnection);
}
MetadataUpdatePage::~MetadataUpdatePage()
{
if (_workerThread && _workerThread->isRunning()) {
_workerThread->quit();
_workerThread->wait();
if (_watcher && _watcher->isRunning()) {
_watcher->waitForFinished();
}
}
@@ -72,6 +79,13 @@ void MetadataUpdatePage::initUI()
gridLayout->addWidget(_uft3CancelBtn, 1, 3, 1, 1);
_uft3UpdateBtn = new ElaPushButton("更新UFT30", contentWidget);
_uft3UpdateBtn->setMinimumWidth(140);
_uft3UpdateBtn->setMinimumHeight(38);
{
QFont f = _uft3UpdateBtn->font();
f.setPointSize(11);
_uft3UpdateBtn->setFont(f);
}
connect(_uft3UpdateBtn, &QPushButton::clicked, this, &MetadataUpdatePage::onUpdateUFT3);
gridLayout->addWidget(_uft3UpdateBtn, 1, 4, 1, 1);
@@ -105,17 +119,42 @@ void MetadataUpdatePage::initUI()
gridLayout->addWidget(_uf2CancelBtn, 1, 8, 1, 1);
_uf2UpdateBtn = new ElaPushButton("更新UF20", contentWidget);
_uf2UpdateBtn->setMinimumWidth(140);
_uf2UpdateBtn->setMinimumHeight(38);
{
QFont f = _uf2UpdateBtn->font();
f.setPointSize(11);
_uf2UpdateBtn->setFont(f);
}
connect(_uf2UpdateBtn, &QPushButton::clicked, this, &MetadataUpdatePage::onUpdateUF20);
gridLayout->addWidget(_uf2UpdateBtn, 1, 9, 1, 1);
gridLayout->addItem(new QSpacerItem(5, 5, QSizePolicy::Policy::Expanding, QSizePolicy::Policy::Minimum), 0, 10, 2, 1);
_forceFullCheckBox = new ElaCheckBox("强制全量更新", contentWidget);
_forceFullCheckBox->setToolTip("勾选后本次将重新扫描并解析全部接口文件、重建增量缓存;不勾选时仅处理有变化的文件");
connect(_forceFullCheckBox, &QCheckBox::toggled, this, [this](bool checked){
if (checked) {
auto ret = QMessageBox::question(this, "提示",
"全量更新接口耗时较久,请确认是否继续?",
QMessageBox::Yes | QMessageBox::No, QMessageBox::No);
if (ret != QMessageBox::Yes) {
_forceFullCheckBox->setChecked(false);
}
}
});
centerLayout->addWidget(contentWidget);
centerLayout->addItem(new QSpacerItem(5, 5, QSizePolicy::Policy::Minimum, QSizePolicy::Policy::Expanding));
// 进度标签行:左侧显示状态文字,右侧放置强制全量更新复选框
QHBoxLayout* processHeaderLayout = new QHBoxLayout();
_processLabel = new ElaText(this);
centerLayout->addWidget(_processLabel);
_processLabel->setTextPixelSize(14);
processHeaderLayout->addWidget(_processLabel, 1);
processHeaderLayout->addWidget(_forceFullCheckBox);
centerLayout->addLayout(processHeaderLayout);
_progressBar = new ElaProgressBar(this);
_progressBar->setValue(0);
@@ -209,8 +248,9 @@ void MetadataUpdatePage::onUpdateUF20()
_uf2AllBtn->setEnabled(false);
_uf2CancelBtn->setEnabled(false);
_progressBar->setValue(0);
updateUf2Interfaces(selectedItems);
_processor->setForceFull(_forceFullCheckBox->isChecked());
startUpdate(selectedItems, "UF20");
}
void MetadataUpdatePage::onUFT3SelectAll()
@@ -245,22 +285,22 @@ void MetadataUpdatePage::onUpdateUFT3()
_uft3AllBtn->setEnabled(false);
_uft3CancelBtn->setEnabled(false);
_progressBar->setValue(0);
updateUft3Interfaces(selectedItems);
_processor->setForceFull(_forceFullCheckBox->isChecked());
startUpdate(selectedItems, "UFT30");
}
void MetadataUpdatePage::updateUf2Interfaces(const QStringList& items)
UpdateResult MetadataUpdatePage::runUpdateUf2(const QStringList& items)
{
int itemCount = items.size();
bool success = true;
bool interfaceUpdated = false;
foreach (const QString& item, items) {
_processLabel->setText(QString("正在更新UF20 - %1").arg(item));
int currentIndex = items.indexOf(item);
int progress = (itemCount == 1) ? 0 : (currentIndex * 100 / itemCount);
_progressBar->setValue(progress);
QCoreApplication::processEvents();
int itemCount = items.size();
int idx = 0;
for (const QString& item : items) {
emit sigProgress((itemCount == 1) ? 0 : (idx * 100 / itemCount),
QString("正在更新UF20 - %1").arg(item));
if (item == "标准字段") {
processUf2StdFields();
} else if (item == "数据类型") {
@@ -268,35 +308,29 @@ void MetadataUpdatePage::updateUf2Interfaces(const QStringList& items)
} else if (item == "标准错误号") {
processUf2ErrNumber();
} else if (item == "接口数据") {
processUf2Interface(Constants::toolDir());
if (!processUf2Interface(Constants::toolDir())) {
success = false;
}
interfaceUpdated = true;
}
++idx;
}
if (interfaceUpdated) {
_processLabel->setText("正在更新数据缓存...");
QCoreApplication::processEvents();
UF2ConfigReader::reloadUF20Config();
}
_progressBar->setValue(100);
_processLabel->setText("UF20更新完成");
onUpdateFinished(true, "UF20更新完成");
// 数据缓存重载放到主线程执行(DataCache 单例非线程安全),见 onWorkerFinished
return UpdateResult{success, interfaceUpdated};
}
void MetadataUpdatePage::updateUft3Interfaces(const QStringList& items)
UpdateResult MetadataUpdatePage::runUpdateUft3(const QStringList& items)
{
int itemCount = items.size();
bool success = true;
bool interfaceUpdated = false;
foreach (const QString& item, items) {
_processLabel->setText(QString("正在更新UFT30 - %1").arg(item));
int currentIndex = items.indexOf(item);
int progress = (itemCount == 1) ? 0 : (currentIndex * 100 / itemCount);
_progressBar->setValue(progress);
QCoreApplication::processEvents();
int itemCount = items.size();
int idx = 0;
for (const QString& item : items) {
emit sigProgress((itemCount == 1) ? 0 : (idx * 100 / itemCount),
QString("正在更新UFT30 - %1").arg(item));
if (item == "标准字段") {
processUft3StdFields();
} else if (item == "数据类型") {
@@ -304,21 +338,15 @@ void MetadataUpdatePage::updateUft3Interfaces(const QStringList& items)
} else if (item == "表结构") {
processUft3Table();
} else if (item == "接口数据") {
processUft3Interface(Constants::toolDir());
if (!processUft3Interface(Constants::toolDir())) {
success = false;
}
interfaceUpdated = true;
}
++idx;
}
if (interfaceUpdated) {
_processLabel->setText("正在更新数据缓存...");
QCoreApplication::processEvents();
UFT3ConfigReader::reloadUFT3Config();
}
_progressBar->setValue(100);
_processLabel->setText("UFT30更新完成");
onUpdateFinished(true, "UFT30更新完成");
return UpdateResult{success, interfaceUpdated};
}
void MetadataUpdatePage::processUf2StdFields()
@@ -336,9 +364,9 @@ void MetadataUpdatePage::processUf2ErrNumber()
_processor->processUf2ErrNumber();
}
void MetadataUpdatePage::processUf2Interface(const QString& basePath)
bool MetadataUpdatePage::processUf2Interface(const QString& basePath)
{
_processor->processUf2Interfaces(basePath);
return _processor->processUf2Interfaces(basePath);
}
void MetadataUpdatePage::processUft3StdFields()
@@ -356,9 +384,60 @@ void MetadataUpdatePage::processUft3Table()
_processor->processUft3Table();
}
void MetadataUpdatePage::processUft3Interface(const QString& basePath)
bool MetadataUpdatePage::processUft3Interface(const QString& basePath)
{
_processor->processUft3Interfaces(basePath);
return _processor->processUft3Interfaces(basePath);
}
void MetadataUpdatePage::startUpdate(const QStringList& items, const QString& taskName)
{
m_currentTaskName = taskName;
_updateTimer.start();
_progressBar->setValue(0);
_processLabel->setText(QString("正在准备更新%1...").arg(taskName));
// 防御:若上一个任务仍在运行则不再启动(按钮已禁用,正常情况下不会触发)
if (_watcher->isRunning()) {
return;
}
if (taskName == "UF20") {
_watcher->setFuture(QtConcurrent::run([this, items]() {
return runUpdateUf2(items);
}));
} else {
_watcher->setFuture(QtConcurrent::run([this, items]() {
return runUpdateUft3(items);
}));
}
}
void MetadataUpdatePage::onWorkerFinished()
{
UpdateResult r = _watcher->result();
// 计算总耗时
qint64 elapsedMs = _updateTimer.elapsed();
QString elapsedStr;
if (elapsedMs < 1000) {
elapsedStr = QString::number(elapsedMs) + "ms";
} else {
elapsedStr = QString("%1s (%2ms)").arg(elapsedMs / 1000.0, 0, 'f', 1).arg(elapsedMs);
}
LogManager::instance()->log(QString("%1 元数据更新总耗时: %2").arg(m_currentTaskName, elapsedStr));
// 数据缓存重载必须在主线程执行(DataCache 单例与静态 cnameCache 非线程安全)
if (r.needReload) {
_processLabel->setText("正在更新数据缓存...");
if (m_currentTaskName == "UF20") {
UF2ConfigReader::reloadUF20Config();
} else {
UFT3ConfigReader::reloadUFT3Config();
}
}
onUpdateFinished(r.success, QString("%1更新完成").arg(m_currentTaskName));
_processLabel->setText(QString("%1更新完成(总耗时: %2)").arg(m_currentTaskName, elapsedStr));
}
void MetadataUpdatePage::onUpdateProgress(int progress, const QString& message)
+28 -9
View File
@@ -12,6 +12,14 @@
#include <QGridLayout>
#include <QMessageBox>
#include <QThread>
#include <QFutureWatcher>
#include <QElapsedTimer>
// 后台线程执行结果:success 表示整体是否成功,needReload 表示是否需要重新加载数据缓存
struct UpdateResult {
bool success;
bool needReload;
};
class MetadataUpdatePage : public ElaScrollPage
{
@@ -35,22 +43,30 @@ private slots:
private slots:
void onUpdateProgress(int progress, const QString& message);
void onUpdateFinished(bool success, const QString& message);
void onWorkerFinished(); // QFutureWatcher::finished 的回调(运行在主线程)
signals:
void sigProgress(int progress, const QString& message); // 后台线程 -> 主线程回传进度
private:
void initUI();
void showUpdateItems();
void updateUf2Interfaces(const QStringList& items);
void updateUft3Interfaces(const QStringList& items);
// 后台线程执行体(由 QtConcurrent 调用,禁止直接操作 UI)
UpdateResult runUpdateUf2(const QStringList& items);
UpdateResult runUpdateUft3(const QStringList& items);
// 在主线程启动后台任务
void startUpdate(const QStringList& items, const QString& taskName);
void processUf2StdFields();
void processUf2DataType();
void processUf2ErrNumber();
void processUf2Interface(const QString& basePath);
bool processUf2Interface(const QString& basePath);
void processUft3StdFields();
void processUft3DataType();
void processUft3Table();
void processUft3Interface(const QString& basePath);
bool processUft3Interface(const QString& basePath);
QGridLayout* _updateGridLayout{nullptr};
QGridLayout* _updateUf2GridLayout{nullptr};
@@ -60,14 +76,17 @@ private:
ElaPushButton* _uft3CancelBtn{nullptr};
ElaPushButton* _uf2AllBtn{nullptr};
ElaPushButton* _uft3UpdateBtn{nullptr};
ElaCheckBox* _forceFullCheckBox{nullptr};
ElaText* _processLabel{nullptr};
ElaProgressBar* _progressBar{nullptr};
QList<ElaCheckBox*> uft3CheckBoxes;
QList<ElaCheckBox*> uf20CheckBoxes;
MetadataProcessor* _processor{nullptr};
QThread* _workerThread{nullptr};
QFutureWatcher<UpdateResult>* _watcher{nullptr};
QElapsedTimer _updateTimer;
QString m_currentTaskName; // "UF20" / "UFT30",用于区分当前任务
};
#endif
#endif