StupidBeauty
Read times:1600Posted at: - no title specified
  • Qt 4.8  文档翻译 :异形钟表示例,Shaped Clock Example

内容目录

异形钟表示例

ShapedClock Class Definition

ShapedClock Class Implementation

Notes on Widget Masks

异形钟表示例

文件

异形钟表 示例,展示了如何向一个顶级部件应用一个部件掩码,以产生一个异形窗口。

部件掩码,用来对顶级部件的形状进行自定义,具体原理就是限制可进行绘制的区域。在某些窗口系统中,设置了特定了窗口标志位之后,就会导致窗口装饰(标题栏、窗口边框、按钮)被禁用,于是就可以创造出特殊形状的窗口了。在本示例中,我们利用这个特性来创建一个包含着一个模拟时钟的圆形窗口。

由于这个示例中的窗口不提供 文件菜单及关闭按钮,所以,我们提供了一个上下文菜单,其中带有一个 退出菜单项,这样,这个示例程序就可以被用户关闭了。在窗口上单击右键就会打开此菜单。

ShapedClock类定义

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类的实现

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 opinions

Your name:Email:Website url:Opinion content:
- no title specified

HxLauncher: Launch Android applications by voice commands