Qt是一个很灵活的框架,其优秀的跨平台特性、丰富的界面定制功能和简明的信号-槽大大的简化了编程工作,并让代码很简洁优雅,但是在实际项目中,还是会遇到一些问题
1. 无边框窗口的移动问题
如果只是为了“能用”,那么网上现成的这类代码很多,简单的参考一下就可以写出这样的代码:
MyWidget::MyWidget(QWidget *parent) : QWidget(parent) { //... this->setFixedSize(this->size()); this->setWindowFlags(Qt::FramelessWindowHint); this->m_isHold = false; //... } void MyWidget::mousePressEvent(QMouseEvent *pEvent) { if(pEvent->button() == Qt::LeftButton) { m_isHold = true; m_relativePoint = pEvent->globalPos() - this->pos(); } } void MyWidget::mouseReleaseEvent(QMouseEvent *) { m_isHold = false; } void MyWidget::mouseMoveEvent(QMouseEvent *pEvent) { if(m_isHold) { this->move(pEvent->globalPos() - m_relativePoint); } }
但是这种做法会导致一个很严重的问题,就是在鼠标拖动窗口的时候,可以把窗口拖到任务栏下面,联众游戏里面捆绑的联众助手就有这个问题。
有一种网上流传的土办法可以解决此问题:
this->setWindowFlags(Qt::FramelessWindowHint | Qt::WindowStaysOnTopHint);
这样做的话,窗口的确不会被拖到任务栏下了,但是总在最前的窗口也不能让人满意。
因为这个问题只会出现在Windows下,所以我们可以通过Win32 API来解决问题,MSDN里可以查到一个名叫ClipCursor的函数:
BOOL WINAPI ClipCursor(_In_opt_ const RECT *lpRect); /* Parameters: lpRect [in, optional] Type: const RECT* A pointer to the structure that contains the screen coordinates of the upper-left and lower-right corners of the confining rectangle. If this parameter is NULL, the cursor is free to move anywhere on the screen. Return value: Type: BOOL If the function succeeds, the return value is nonzero. */
这个函数接收一个RECT*类型的参数,限制鼠标指针的活动在这个RECT限制的范围内。
于是我们有了以下的解决方案:
//类声明: #include <windows.h> class MyWidget: public QWidget { Q_OBJECT //... private: RECT rtConfined; RECT rtDefault; //... } //构造函数里: { //... ::SystemParametersInfo(SPI_GETWORKAREA , 0 , &this->rtConfined , 0); ::GetWindowRect(::GetDesktopWindow() , &this->rtDefault); //... } //重写事件响应虚函数: void MyWidget::mousePressEvent(QMouseEvent *pEvent) { //... if(pEvent->button() == Qt::LeftButton) { m_isHold = true; m_relativePoint = pEvent->globalPos() - this->pos(); } ::ClipCursor(&rtConfined); //... } void MyWidget::mouseReleaseEvent(QMouseEvent *) { m_isHold = false; ::ClipCursor(&rtDefault); }
2. Qt4无边框窗口失去焦点的问题
在项目从Qt5迁移到Qt4的过程中,出现过一个问题:一个无边框透明窗体,在showMinimized之后,点击任务栏图标,不会触发窗体的自动重绘,窗口表现为失去焦点
设置窗体透明的代码如下:
this->setWindowOpacity(1); QPalette pal = palette(); pal.setColor(QPalette::Background, QColor(0x00,0xff,0x00,0x00));//Qt4里必须这样否则不透明 this->setPalette(pal); this->setWindowFlags(Qt::FramelessWindowHint|Qt::WindowMinimizeButtonHint|Qt::WindowSystemMenuHint); this->setAttribute(Qt::WA_TranslucentBackground, true);
在中文网路上查找了很久也没有找到解决方案,后来改去爆栈上搜,也没看出个所以然,但是初步怀疑是无边框窗口的缘故,最后用Google去“site:qt-project.org”里看到了相关资料,这是一个 Qt4的bug,可以参考官网的链接
Widget with Qt::FramelessWindowHint and Qt::WA_TranslucentBackground stops painting after minimize/restore
按照说明,得到了如下的workaround:
void MyWidget::showEvent(QShowEvent *pEvent) { QApplication::postEvent(this, new QEvent(QEvent::UpdateRequest), Qt::LowEventPriority); QWidget::showEvent(pEvent); }
是的,就是这样,问题就解决了。