我正在使用 wxWidgets 用 C++ 开发一个小型桌面应用程序,它是关于可视化不同类型的排序。为了在排序发生并在屏幕上绘制时保持 GUI 响应,我尝试在不同的线程上做一些工作,但我知道 GUI 更新必须从主线程进行 - 所以我找到了一个教程使用
CallAfter
以便从主线程更新 GUI。然而,我得到的结果是,表格几乎立即被排序(通过工作线程)并显示在屏幕上,但 GUI 冻结了一段时间并更新了很多(屏幕上没有任何变化,因为表格在开始)。
我正在谈论的一些代码:
#include "GUIMyFrame1.h"
GUIMyFrame1::GUIMyFrame1(wxWindow* parent)
:
MyFrame1(parent), _maxElemValue{ 25 }
{
UpdateTabSize();
Draw();
}
void GUIMyFrame1::drawPanelOnSize(wxSizeEvent& event)
{
Draw();
}
void GUIMyFrame1::m_slider_Num_of_ElemOnScroll(wxScrollEvent& event)
{
_maxElemValue = _tab.size();
UpdateTabSize();
Draw();
}
void GUIMyFrame1::m_button_SortOnButtonClick(wxCommandEvent& event)
{
auto f = [this]() {
for (int i = 0; i < _tab.size() - 1; i++)
for (int j = 0; j < _tab.size() - i - 1; j++) {
wxGetApp().CallAfter([this, j] {
Draw();
});
if (_tab[j + 1] < _tab[j])
std::swap(_tab[j + 1], _tab[j]);
}
wxGetApp().CallAfter([this] {
Draw();
});
};
std::thread worker(f);
worker.detach();
}
void GUIMyFrame1::m_button_ShuffleOnButtonClick(wxCommandEvent& event)
{
UpdateTabSize();
Draw();
}
void GUIMyFrame1::Draw()
{
wxClientDC dc1(drawPanel);
wxBufferedDC dc(&dc1);
dc.Clear();
int w, h;
drawPanel->GetSize(&w, &h);
int tempWidth = w / _tab.size();
double unitHeight = (double)h / _maxElemValue;
int shift = GetShift(w);
for (int i = 0; i < _tab.size(); i++) {
wxBrush tempBrush(_tab[i]._color);
wxPen tempPen(_tab[i]._color, 0);
dc.SetBrush(tempBrush);
dc.SetPen(tempPen);
dc.DrawRectangle(
wxRect(
shift + i * tempWidth, h - (int)(unitHeight * _tab[i]._value) + 2,
tempWidth, (int)(unitHeight * _tab[i]._value) - 4
));
}
}
void GUIMyFrame1::UpdateTabSize()
{
_tab.clear();
for (int i = 0; i < m_slider_Num_of_Elem->GetValue(); i++)
_tab.push_back(getNewRandomElement(_maxElemValue));
}
int GUIMyFrame1::GetShift(int w) {
int AllElemWidth = _tab.size() * int(w / _tab.size());
return (w - AllElemWidth) / 2;
}
我的问题是如何解决这个问题?我猜整个绘图过程比排序中的每次比较花费的时间要长得多,但是为什么 GUI 更新在表排序后不立即停止?我确实尝试过
std::mutex
,因为我认为同步可能会有所帮助,因为 _tab
可能会在排序和绘制到屏幕时同时更改和读取,但它没有帮助。请不要对我太严厉,因为我经验不足:/
为了使其看起来有吸引力,您可以使用condition_variable,其中工作人员将在每个
wait
之后对其进行callafter
,并且主线程将完成绘制然后notify_one
,以便工作人员可以继续排序。
每次修改您都会得到一帧,请随意在工作人员中添加额外的sleep,直到结果足够吸引人。