问题描述
我最近编写了一个可以选定视频区域的程序。用户可以通过鼠标在视频上自由绘制长方形区域。这个程序不仅可以实时跟踪并更新选中的区域,还允许用户直接操作这个长方形,比如放大、缩小或移动。所有的信息,包括选中的长方形的左上角的坐标,以及其宽度和高度,都会实时显示在编辑框中,方便用户查看和修改。
实现
使用了Qt的信号与槽机制。
当Qt中的长方形坐标改变时,会发出一个信号,并且会同步更新与之关联的编辑框的内容。相反,如果编辑框的内容发生了变化,可以直接驱动长方形进行重绘,实现两者之间的同步更新。值得注意的是,QSpinBox
的valueChanged
信号具有两种重载形式,一种是接受int型参数,另一种是接受QString
型参数。因此,在连接信号和槽函数时,必须使用QOverload
模板来明确指定信号的参数类型,以避免出现连接错误。
代码如下:
初始化信号与槽
void Dialog::init()
{
//绑定界面控件信号
connect(ui->spb_x, QOverload<int>::of(&QSpinBox::valueChanged), this, &Dialog::onValueChanged);
connect(ui->spb_y, QOverload<int>::of(&QSpinBox::valueChanged), this, &Dialog::onValueChanged);
connect(ui->spb_w, QOverload<int>::of(&QSpinBox::valueChanged), this, &Dialog::onValueChanged);
connect(ui->spb_h, QOverload<int>::of(&QSpinBox::valueChanged), this, &Dialog::onValueChanged);
}
//新建透明窗口
void Dialog::creatScerrn(bool flag, int x, int y, int w, int h)
{
QPoint p = ui->video->mapToGlobal(QPoint(0, 0));
QSize size = ui->video->size();
if(m_screen == NULL)
{
m_screen = new Screen(0,p.x(),p.y(),size.width(),size.height());
//连接信号与槽
connect(m_screen, &Screen::rectChanged, this, &Dialog::onRectChanged);
}
m_screen->init(flag,x,y,w,h);
m_screen->move(p.x(),p.y());
m_screen->show();
}
//长方形变化槽函数
void Dialog::onRectChanged(int x, int y, int w, int h)
{
//同步修改编辑框内容
ui->spb_x->setValue(x);
ui->spb_y->setValue(y);
ui->spb_w->setValue(w);
ui->spb_h->setValue(h);
}
//QSpinBox数值变化槽函数
void Dialog::onValueChanged()
{
if(m_screen == NULL)
{
return;
}
int x,y,w,h;
x = ui->spb_x->value();
y = ui->spb_y->value();
w = ui->spb_w->value();
h = ui->spb_h->value();
//将变化发送给透明窗口去重绘长方形
m_screen->init(false,x,y,w,h);
}
我发现,QSpinBox::setValue()
方法在执行结束后会触发QSpinBox::valueChanged
信号的发送,而且每当其长方形被设置参数时也会进行参数计算,进一步触发信号以更新编辑框的值。这导致了信号与槽的持续触发,形成了闭合回路,导致界面出现卡顿,操作也不流畅。
为了解决这个问题,找到了bool QObject::blockSignals(bool block)
方法。
看一下官方解释吧:
QObject::blockSignals()
方法可以阻止一个 QObject 类的信号连接到相应的槽函数,这意味着即使信号被发射,也不会触发任何槽函数。
所以我们只需要对上面的槽函数进行改造,当对目标进行操作时,阻塞目标的信号即可打破这个闭合回路。
修改后槽函数如下:
void Dialog::onRectChanged(int x, int y, int w, int h)
{
//阻塞信号发送
ui->spb_x->blockSignals(true);
ui->spb_y->blockSignals(true);
ui->spb_w->blockSignals(true);
ui->spb_h->blockSignals(true);
//同步修改编辑框内容
ui->spb_x->setValue(x);
ui->spb_y->setValue(y);
ui->spb_w->setValue(w);
ui->spb_h->setValue(h);
//恢复信号发送
ui->spb_x->blockSignals(false);
ui->spb_y->blockSignals(false);
ui->spb_w->blockSignals(false);
ui->spb_h->blockSignals(false);
}
void Dialog::onValueChanged()
{
if(m_screen == NULL)
{
return;
}
int x,y,w,h;
x = ui->spb_x->value();
y = ui->spb_y->value();
w = ui->spb_w->value();
h = ui->spb_h->value();
m_screen->blockSignals(true);
m_screen->init(false,x,y,w,h);
m_screen->blockSignals(false);
}
结果
完美实现了我们的需求。
大概效果如下: