您的位置:首页 > 编程语言 > Delphi

Delphi2010组件/控件安装方法和注意事项

2013-01-22 17:39 615 查看


QTBUG8107

在QMainWindow中,我们可以通过拖动中心窗体和停靠窗体之间的分割线(Sepearator)来改变中心窗口的大小。
QTBUG8107描述这样一个问题:
当通过拖动Sepearator改变了停靠窗体的大小后,一旦我们改变整个QMainWindow窗口的大小时,停靠窗体的大小会自动跳到原来的大小。

这是Qt4.6.2(包括)之前Bug,现已修复,但由于它的修复代码造成了其他bug,故尔,还是有必要看看


原因

当我们改变 QMainWindow 窗口大小时,其内部的QLayout要重新计算所有widget的大小

首先:QMainWindow 的矩形框
Rect0
菜单栏 和 状态栏简单,高度不变,直接减掉即可
得到一个Rect1
顶部和底部、左侧和右侧的工具栏区域,分别从矩形区域中除
得到一个Rect2
接下来:在Rect2 中,如何得到合理的中心窗体和停靠窗体的大小?
这个东西很让人头痛,因为它已经导致了很多了Bug


解决

当QMainWindow改变时,我们期待中心窗体相应地放大或缩小,而让停靠窗体的宽度或高度保持不变


QTBUG15689

QTBUG15689
QTBUG8107 的一个副作用:
如果一开始,QMainWindow没有设置中心窗体,而是等界面显示以后,我们再设置一个中心窗体。此时,由于原有的停靠窗体倾向于保持自己的大小不变,那么中心窗体只能占据很小的一点点的空间。


原因?

思考的核心是,当布局改变时,我们的停靠窗体是应该
倾向于使用自己当前的大小

还是
倾向于使用自己的sizeHint()

这是由QDockAreaLayout中的
bool fallbackToSizeHints; //determines if we should use the sizehint for the dock areas (true until the layout is restored or the central widget is set)


所控制的。
注意看其中的注释,它的意图应该是:当QMainWindow被设置了中心窗体
QMainWindow::setCentralWidget()


或者恢复到先前保存的状态
QMainWindow::restoreState()


后,每次都倾向于使用停靠窗口的当前大小。
但是,伴随着QTBUG-8107的修复,已经改变了这个行为(尽管此处的注释没有改动)


解决?

如何解决QTBUG15689而不影响QTBUG8107?
重新思考前面的问题:当布局改变时,我们的停靠窗体是应该
倾向于使用自己当前的大小

还是
倾向于使用自己的sizeHint()

恩,似乎应该这样
当且仅当调用了
QMainWindow::restoreState()


或者
手动通过分隔线调整了停靠窗口和中心窗口的大小


后,才让停靠窗体倾向于使用自己的大小
具体修复起来比较简单,一两行代码即可。


QTBUG15080

QTBUG15080描述这样一个问题:
用QMainWindow::saveState() 保存当前状态,退出程序,然后再次启动并使用QMainWindow::restoreState()恢复到先前布局状态。结果QDockWidget记不住先前的大小。


原因

前面的描述是一个表象,很难让人联想到QWidget::setStylesheet()这种看似完全无害的东西。
MainWindow::MainWindow()
{
...
QSettings settings("MyCompany", "MyApp");
restoreGeometry(settings.value("myWidget/geometry").toByteArray());
restoreState(settings.value("myWidget/windowState").toByteArray());

ui->lineEdit.setStylesheet("background-color: lime");
}


恩,这个bug的直接原因就在于此。
当我们调用setStylesheet时,会触发QEvent::StyleChange事件,进而到了:

bool QMainWindow::event(QEvent *event)
{
Q_D(QMainWindow);
switch (event->type()) {
case QEvent::StyleChange:
#ifndef QT_NO_DOCKWIDGET
d->layout->layoutState.dockAreaLayout.styleChangedEvent();
#endif


然后:
void QDockAreaLayout::styleChangedEvent()
{
sep = mainWindow->style()->pixelMetric(QStyle::PM_DockWidgetSeparatorExtent, 0, mainWindow);
fitLayout();
}


此处的fitLayout()负责调整DockArea内各个区域的大小。
在讨论它为何会造成问题之前,似乎我们可以先看看如何解决它...


如何避免?

该Bug后面的其他人的commit给出了各种避免的方法。比如使用QTimer将restoreState()的调用推后。其实直接将restoreState语句和setStylesheet交换位置也可以。
考虑QLayout如何起起作用的...
调用QWidget::show()[即QWidget::setVisible()]时,在显示之前,通过调用

QLayout::activate()


来激活布局(如果有的话)
改变窗体的大小时,

void QLayout::widgetEvent(QEvent *e)
{
...
switch (e->type()) {
case QEvent::Resize:
if (d->activated) {
QResizeEvent *r = (QResizeEvent *)e;
d->doResize(r->size());
} else {
activate();
}
break;


某个QWidget内的子Widget的大小自身变化,需要通知布局进行重新计算

QWidget::updateGeometry()


如果parent有layout布局,直接让布局无效(强制Layout重新计算大小)。而如果parent没有布局,它给父widget发送LayoutRequest事件
QDockAreaLayout::fitLayout()如何被调用?
QLayout起作用时,
QMainWindowLayout::setGeometry()


会重新计算各个widget的尺寸,而停靠区域的计算也在这儿被调用。


原因(续)

接前面的
void QDockAreaLayout::styleChangedEvent()
{
sep = mainWindow->style()->pixelMetric(QStyle::PM_DockWidgetSeparatorExtent, 0, mainWindow);
fitLayout();
}


这段代码是为了修复QTBUG2774而引入的,当样式改变时,直接调用fitLayout()来重新计算中心窗体和停靠窗体的大小。由于在窗口显示之前,fitLayout()这个东西还会被调用多次,此处看起来也似乎没什么危害
但是,此时,我们已经调用了
QMainWindow::restoreState()


来试图恢复状态,而在QLayout尚未activate()之前,QDockAreaLayout::fitLayout()的调用,破坏了我们试图要恢复的状态。


解决

似乎只要加半条语句即可:

void QDockAreaLayout::styleChangedEvent()
{
sep = mainWindow->style()->pixelMetric(QStyle::PM_DockWidgetSeparatorExtent, 0, mainWindow);
if (mainWindow->isVisible())
fitLayout();
}



参考

https://bugreports.qt.nokia.com/browse/QTBUG-8107

https://bugreports.qt.nokia.com/browse/QTBUG-15689

https://bugreports.qt.nokia.com/browse/QTBUG-2774

https://bugreports.qt.nokia.com/browse/QTBUG-15080
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: