Qt 4.8 文档翻译 :异形钟表示例,Shaped Clock Example
异形钟表 示例,展示了如何向一个顶级部件应用一个部件掩码,以产生一个异形窗口。
部件掩码,用来对顶级部件的形状进行自定义,具体原理就是限制可进行绘制的区域。在某些窗口系统中,设置了特定了窗口标志位之后,就会导致窗口装饰(标题栏、窗口边框、按钮)被禁用,于是就可以创造出特殊形状的窗口了。在本示例中,我们利用这个特性来创建一个包含着一个模拟时钟的圆形窗口。
由于这个示例中的窗口不提供 文件菜单及关闭按钮,所以,我们提供了一个上下文菜单,其中带有一个 退出菜单项,这样,这个示例程序就可以被用户关闭了。在窗口上单击右键就会打开此菜单。
ShapedClock 类是以 模拟时钟 示例中定义的 AnalogClock 类为基础的。下面展示 出整个类的定义代码:
class ShapedClock : public QWidget
{
Q_OBJECT
public :
ShapedClock( QWidget * parent = 0 );
QSize sizeHint() const ;
protected :
void mouseMoveEvent( QMouseEvent * event);
void mousePressEvent( QMouseEvent * event);
void paintEvent( QPaintEvent * event);
void resizeEvent( QResizeEvent * event);
private :
QPoint dragPosition;
};
paintEvent() 的实现与 AnalogClock 类中的实现是相同的。 我们实现了 sizeHint() ,这样就不需要显式地改变该部件的尺寸了。 我们还提供了一个事件处理函数,用于处理尺寸变更事件。 这样,我们就可以在尺寸发生改变时更新掩码。
由于包含 着此时钟部件的窗口没有标题栏,所以,我们实现了 mouseMoveEvent() 和 mousePressEvent() , 以使得用户可将此时钟在屏幕上到处拖动。 dragPosition 变量用来跟踪用户 上次是在部件的哪个位置点击鼠标的。
ShapedClock 构造函数 中 所做的事情与 AnalogClock 构造函数中相同。 我们启动了一个定时器,并且 将它连接到此部件的update()信号槽上:
ShapedClock :: ShapedClock( QWidget * parent)
: QWidget (parent , Qt :: FramelessWindowHint | Qt :: WindowSystemMenuHint)
{
QTimer * timer = new QTimer ( this );
connect(timer , SIGNAL(timeout()) , this , SLOT(update()));
timer -> start( 1000 );
QAction * quitAction = new QAction (tr( "E&xit" ) , this );
quitAction -> setShortcut(tr( "Ctrl+Q" ));
connect(quitAction , SIGNAL(triggered()) , qApp , SLOT(quit()));
addAction(quitAction);
setContextMenuPolicy( Qt :: ActionsContextMenu);
setToolTip(tr( "Drag the clock with the left mouse button.\n"
"Use the right mouse button to open a context menu." ));
setWindowTitle(tr( "Shaped Analog Clock" ));
}
我们为此部件设置了 Qt::FramelessWindowHint 标志位,以告知窗口管理器,此部件不需要窗口边框作为装饰。由此带来 的后果是,我们需要提供一种方式,让用户来将此时钟在屏幕上到处移动。
鼠标按键的事件会被传递给 mousePressEvent() 这个处理函数:
void ShapedClock :: mousePressEvent( QMouseEvent * event)
{
if (event -> button() == Qt :: LeftButton) {
dragPosition = event -> globalPos() - frameGeometry() . topLeft();
event -> accept();
}
}
当用户在此部件上按下鼠标左键时, 我们记录鼠标点击事件发生处的全局(屏幕)坐标与此部件的边框(当然 ,此时是隐藏状态 )的左上角处的全局坐标之间的偏移值。 当用户接下来按住左键并且移动鼠标时, 我们会用上这个偏移值。由于 我们对该事件作出了响应,因此,我们调用它的 accept() 函数来接受它。
当鼠标在此部件上移动时, mouseMoveEvent() 处理函数会被调用。
void ShapedClock :: mouseMoveEvent( QMouseEvent * event)
{
if (event -> buttons() & Qt :: LeftButton) {
move(event -> globalPos() - dragPosition);
event -> accept();
}
}
当用户按住鼠标左键并且移动鼠标时, 我们就会将当前鼠标位置 的全局坐标减去 dragPosition ,再将此部件的左上角移动到该结果位置去。 当我们处理拖动操作时,我们也要接受该事件。
paintEvent() 函数 是为了代码的完整而展示出来的。参考 模拟时钟 示例 , 以了解用于渲染此时钟的全过程。
void ShapedClock :: paintEvent( QPaintEvent * )
{
static const QPoint hourHand [ 3 ] = {
QPoint ( 7 , 8 ) ,
QPoint ( - 7 , 8 ) ,
QPoint ( 0 , - 40 )
};
static const QPoint minuteHand [ 3 ] = {
QPoint ( 7 , 8 ) ,
QPoint ( - 7 , 8 ) ,
QPoint ( 0 , - 70 )
};
QColor hourColor( 127 , 0 , 127 );
QColor minuteColor( 0 , 127 , 127 , 191 );
int side = qMin (width() , height());
QTime time = QTime :: currentTime();
QPainter painter( this );
painter . setRenderHint( QPainter :: Antialiasing);
painter . translate(width() / 2 , height() / 2 );
painter . scale(side / 200.0 , side / 200.0 );
painter . setPen( Qt :: NoPen);
painter . setBrush(hourColor);
painter . save();
painter . rotate( 30.0 * ((time . hour() + time . minute() / 60.0 )));
painter . drawConvexPolygon(hourHand , 3 );
painter . restore();
painter . setPen(hourColor);
for ( int i = 0 ; i < 12 ; ++ i) {
painter . drawLine( 88 , 0 , 96 , 0 );
painter . rotate( 30.0 );
}
painter . setPen( Qt :: NoPen);
painter . setBrush(minuteColor);
painter . save();
painter . rotate( 6.0 * (time . minute() + time . second() / 60.0 ));
painter . drawConvexPolygon(minuteHand , 3 );
painter . restore();
painter . setPen(minuteColor);
for ( int j = 0 ; j < 60 ; ++ j) {
if ((j % 5 ) != 0 )
painter . drawLine( 92 , 0 , 96 , 0 );
painter . rotate( 6.0 );
}
}
在 resizeEvent() 处理函数 中, 我们重用了 paintEvent() 中的某些代码,用来确定此部件中哪个区域对于用户是可见的:
void ShapedClock :: resizeEvent( QResizeEvent * /* event */ )
{
int side = qMin (width() , height());
QRegion maskedRegion(width() / 2 - side / 2 , height() / 2 - side / 2 , side ,
side , QRegion :: Ellipse);
setMask(maskedRegion);
}
由于时钟的表面就是在此部件的中心处绘制的圆圈,所以,我们就用此区域来作为掩码。
由于缺少窗口边框 ,所以,在某些平台上,用户狠难去改变此部件的大小,但是尺寸 并不是完全不可能改变的。 resizeEvent() 函数 会确保, 当此部件的尺寸发生改变时, 它的掩码也会对应地正确变化,并且 还确保了, 当此部件第一次显示出来时,掩码 也会被正确设置。
最后 ,我们实现了 sizeHint() ,这样,当此部件第一次被显示时,会被给予一个合理的默认尺寸:
QSize ShapedClock :: sizeHint() const
{
return QSize ( 100 , 100 );
}
由于 QRegion 可以创建任意复杂的区域,所以 , 可使用部件掩码来制造出任意奇特形状 的窗口,甚至 还可以制造出中间带着洞洞的部件。
还可以利用位图(pixmap)的内容来构造出部件掩码,以定义部件中的透明部分。对于 一个带有透明通道的位图, 可使用 QPixmap::mask ()来获取一个对应的掩码。
张澜澜
Your opinionsHxLauncher: Launch Android applications by voice commands