看完 .cpp 的完整源码后,导致“拖动界面调整窗口时,右侧数据消失”的核心根源终于全找到了!
结合之前的 .ui 布局,这个问题是 UI 控件约束不足 与 C++ 信号刷新机制 共同造成的。主要有以下 2 个硬伤:
🐛 原因一:.ui 缺乏 Splitter 伸缩因子与右侧最小宽度(最主因)
在 .cpp 的构造函数中:
PatchDialog::PatchDialog(QWidget* parent) :
QDialog(parent),
ui(new Ui::PatchDialog)
{
ui->setupUi(this);
// ...
// 此处缺少了对 ui->splitter 的 stretchFactor 和 collapsible 设置
}
- 问题解析:Qt 中的
QSplitter 默认情况下,如果代码里没有用 setStretchFactor() 显式分配左右比例,且 .ui 里没有为右侧容器设置 minimumSize,当你拉伸窗口或拖拽分割条时,Qt 会优先压缩右侧 layoutWidget_1(因为右侧控件较多,计算出来的 Flex 空间易被抢占)。
- 当右侧宽度被压缩到小于其按钮和列表所需的极限尺寸时,由于右侧采用了嵌套的
QVBoxLayout + QHBoxLayout,布局系统无法响应,整个右侧 layoutWidget_1 会直接被挤压成 0 宽度,视觉效果就是右侧整个列表和所有按钮“轰的一声消失了”!
🐛 原因二:拖动窗口触发 resizeEvent 与 Bridge 的重绘信号连锁(代码逻辑因)
在 .cpp 构造函数第 16 行:
connect(Bridge::getBridge(), SIGNAL(updatePatches()), this, SLOT(updatePatches()));
在 updatePatches() 方法中:
void PatchDialog::updatePatches()
{
if(this->isVisible())
mGroupSelector->reject();
mIsWorking = true;
ui->listModules->clear();
ui->listPatches->clear(); // <-- 直接清空列表!
mPatches.clear();
// ...
- 问题解析:某些版本的 x64dbg GUI 框架在主窗口/子对话框发生
resizeEvent(拖动调整大小时),底层会向全局 Bridge 广播 updatePatches 信号。
- 当你拖动窗口时,
updatePatches() 被触发,首先直接执行了 ui->listPatches->clear() 清空右侧列表!
- 紧接着如果此时
DbgFunctions()->PatchEnum 获取补丁失败,或者因为某种原因直接 return 了,listPatches 就留在一个被清空的状态,造成“一拖动,右侧数据就全消失了”的假象!
🛠️ 终极修复方案
只要修改 PatchDialog.cpp 的构造函数和 updatePatches() 两个地方,即可彻底解决该问题:
1. 修改构造函数 (PatchDialog.cpp)
在构造函数最后加入 布局伸缩保护 与 右侧最小宽度 代码:
PatchDialog::PatchDialog(QWidget* parent) :
QDialog(parent),
ui(new Ui::PatchDialog)
{
ui->setupUi(this);
setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
setModal(false);
// ================== 【新增:彻底解决拖动消失的布局保护】 ==================
// 1. 给右侧容器设置硬性最小宽度,保证右侧 5 个按钮和列表绝不会被挤压成 0 像素
ui->layoutWidget_1->setMinimumWidth(300);
ui->layoutWidget->setMinimumWidth(150);
// 2. 禁止 QSplitter 将左右两侧折叠(彻底防止拖死)
ui->splitter->setCollapsible(0, false);
ui->splitter->setCollapsible(1, false);
// 3. 设置 Splitter 比例:左侧 1,右侧 2(优先拉伸右侧)
ui->splitter->setStretchFactor(0, 1);
ui->splitter->setStretchFactor(1, 2);
// 4. 为右侧列表开启横向自动滚动条(防止长注释导致渲染异常)
ui->listPatches->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded);
// =========================================================================
connect(Bridge::getBridge(), SIGNAL(updatePatches()), this, SLOT(updatePatches()));
connect(Bridge::getBridge(), SIGNAL(dbgStateChanged(DBGSTATE)), this, SLOT(dbgStateChanged(DBGSTATE)));
mGroupSelector = new PatchDialogGroupSelector(parent);
mGroupSelector->setGroup(0);
connect(mGroupSelector, SIGNAL(rejected()), this, SLOT(showNormal()));
connect(mGroupSelector, SIGNAL(rejected()), this, SLOT(setFocus()));
connect(mGroupSelector, SIGNAL(groupToggle()), this, SLOT(groupToggle()));
connect(mGroupSelector, SIGNAL(groupPrevious()), this, SLOT(groupPrevious()));
connect(mGroupSelector, SIGNAL(groupNext()), this, SLOT(groupNext()));
connect(ui->listPatches, SIGNAL(itemDoubleClicked(QListWidgetItem*)), this, SLOT(on_listPatches_itemDoubleClicked(QListWidgetItem*)));
mIsWorking = false;
}
2. 优化 updatePatches() 防止误清空 (PatchDialog.cpp)
不要一进函数就 clear(),先枚举数据,确认有数据再清空 UI,避免拖动刷新时数据丢失:
void PatchDialog::updatePatches()
{
if(this->isVisible())
mGroupSelector->reject();
size_t cbsize;
if(!DbgFunctions()->PatchEnum || !DbgFunctions()->PatchEnum(0, &cbsize))
return;
int numPatches = (int)cbsize / sizeof(DBGPATCHINFO);
if(!numPatches)
{
// 如果没有补丁,清空 UI 并退出
ui->listModules->clear();
ui->listPatches->clear();
mPatches.clear();
return;
}
DBGPATCHINFO* patches = new DBGPATCHINFO[numPatches];
memset(patches, 0, numPatches * sizeof(DBGPATCHINFO));
if(!DbgFunctions()->PatchEnum(patches, 0))
{
delete [] patches;
return;
}
// 只有成功获取到新数据后,才清空 UI 重新填充
mIsWorking = true;
ui->listModules->clear();
ui->listPatches->clear();
mPatches.clear();
STATUSINFO defaultStatus;
defaultStatus.group = 0;
defaultStatus.checked = true;
defaultStatus.comment = "";
for(int i = 0; i < numPatches; i++)
{
if(!patches[i].addr || !*patches[i].mod) continue;
QString mod = patches[i].mod;
if(mPatches.contains(mod))
mPatches[mod].append(PatchPair(patches[i], defaultStatus));
else
{
PatchInfoList patchList;
patchList.append(PatchPair(patches[i], defaultStatus));
mPatches.insert(mod, patchList);
}
}
delete [] patches;
for(PatchMap::iterator i = mPatches.begin(); i != mPatches.end(); ++i)
{
qSort(i.value().begin(), i.value().end(), PatchInfoLess);
PatchInfoList & curPatchList = i.value();
for(int j = 0, group = 0; j < curPatchList.size(); j++)
{
if(!isPartOfPreviousGroup(curPatchList, j)) group++;
curPatchList[j].status.group = group;
unsigned char byte;
if(DbgMemRead(curPatchList[j].patch.addr, &byte, sizeof(byte)))
curPatchList[j].status.checked = byte == curPatchList[j].patch.newbyte;
}
ui->listModules->addItem(i.key());
}
if(mPatches.size()) ui->listModules->item(0)->setSelected(true);
mIsWorking = false;
}
修改这两处后,重新编译你的插件/调试器,无论怎么拖动缩放 PatchDialog,右侧数据和控件都会保持完好、不会再消失了!