Qt编程核心技术-5

Qt编程核心技术-5

7.3 QScrollView类

QScrollView窗口部件提供了在需要时可以出现滚动条的滚动区域。QScrollView是一个很大的画布,这个画布可它下面的窗口支持的坐标大得多。例如:一个很大的网页,长度超过了窗口限制,必须使用滚动条拖动才能阅读超出窗口的内容。

QScrollView类重新实现了drawContents()函数,并且可以使用resizeContents()设置查看区域的大小。使用addChild()和moveChild()在视图中增加和移动子窗口部件。

QScrollView有四个孩子窗口部件,它们分别是:viewport()、verticalScrollBar()、horizontalScrollBar()和cornerWidget()。viewport()是视图在窗口中的可见部分。通过滚动条的滚动,从viewport()上可按部分依次查看整个视图的内容。它们在窗口上的位置如图15。

QScrollView类有三种风格的窗口结构:单个较大的子窗口部件,带有一些窗口部件的平面区域和带有许多窗口部件的平面区域。下面对这三种风格的窗口分别进行说明:

(1) 单个较大的子窗口部件

QScrollView的最简单的用法是在一个不超过4000像素的滚动区域(这是X11服务器的最大可靠尺寸),放入一个大的子窗口部件在QScrollView中。这个子窗口部件是滚动视图的viewport()的孩子,它通过addChild()被添加。在视图中添加一个子窗口部件的方法如下:

QScrollView* sv =new QScrollView(...);//创建视图//给视图的viewport()类添加单个子窗口部件 QVBox* big_box =new QVBox(sv->viewport()); sv->addChild(big_box);//给单个子窗口部件添加孩子


还可继续在滚动视图中单一孩子中添加任意子窗口部件,方法如下:

QLabel* child1 = new QLabel("CHILD", big_box); QLabel* child2 = new QLabel("CHILD", big_box); QLabel* child3 = new QLabel("CHILD", big_box);


这样,在视图中,viewport()有一个孩子——大的QVBox。这个QVBox有三个QLabel对象作为子窗口部件。当视图被滚动,QVBox被移动,从viewport()窗口可以依次按部分查看QVBox及它的孩子,就好像几个子窗口部件被移动了一样。QScrollView类的viewport()子类有单个较大的子窗口部件的情况如图15。

文件:Linux Qt core introduction 09.png

图15 QScrollView类的viewport()子类有单个较大的子窗口部件

(2) 带有一些窗口部件的平面区域

QScrollView有非常大的滚动区域,在这个区域上少量窗口部件的高度或宽度超过4000像素。这时,你需要调用resizeContents()来设置区域的大小,并且重新实现drawContents()来绘制内容。你可以使用addChild()给viewport()添加孩子,而不是给viewport()的一个子类添加孩子。方法如下:

QScrollView* sv =new QScrollView(...); QLabel* child1 =new QLabel("CHILD", sv->viewport()); sv->addChild(child1); QLabel* child2 =new QLabel("CHILD", sv->viewport()); sv->addChild(child2); QLabel* child3 =new QLabel("CHILD", sv->viewport()); sv->addChild(child3);


这样,viewport()有三个QLabel对象作为子窗口部件。当视图被滚动,viewport()会分别看到子窗口部件。QScrollView带有一些窗口部件的平面区域的情况如图17。

图17 QScrollView带有一些窗口部件的平面区域的情况

(3) 带有许多窗口部件的平面区域

QScrollView是非常大的滚动区域,在这个滚动区域中,有许多窗口部件的高度或宽度超过4000像素。这时,你需要调用resizeContents()设置区域的大小并重新实现drawContents()来绘制内容。然后你调用函数enableClipper(TRUE)把viewport()设置为比窗口大的区域,再向viewport()的添加孩子。方法如下:

QScrollView* sv =new QScrollView(...); sv->enableClipper(TRUE); QLabel* child1 =new Qlabel(“CHILD”, sv->viewport()); sv->addChild(child1); Qlabel* child2 =new Qlabel(“CHILD”, sv->viewport()); sv->addChild(child2); Qlabel* child3 =new Qlabel(“CHILD”, sv->viewport()); sv->addChild(child3);


这样,clipper()有一个孩子:viewport()。viewport()有同样的三个标签作为子窗口部件。当视图被滚动时,viewport()被移动,它的孩子就像通常的子窗口部件那样被移动。QScrollView带有许多窗口部件的平面区域的情况如图18。

图18 QScrollView带有许多窗口部件的平面区域的情况

你可以使用上面所述三种方法中的一种来在视图中使用子窗口部件。你在滚动区域中看到的窗口部件是viewport()窗口部件,而不是QScrollView本身。因此你只有使用viewport()->setMouseTracking(TRUE)才能打开鼠标跟踪。

鼠标播放事件被传送给父对象,为了使拖放生效,你可以在QScrollView上设置setAcceptDrops(TRUE)。对于视图来说,常需要将鼠标在viewport()中的屏幕相对位置座标换算到视图的绝对座标,这需要使用viewportToContents()函数。

为了在滚动区域中处理鼠标事件,请重新实现contentsMousePressEvent(),而不是mousePressEvent(),因为contentsMousePressEvent()提供了滚动视图坐标系统中的转换事件。如果你重新实现mousePressEvent(),那么,只有当你QScrollView的cornerWidget()部件被点击时,mousePressEvent()才能得到调用。。

8 Qt风格(style)机制

Qt的风格机制实现了可以通过插件或风格类来改变的图形用户接口(GUI)的外观,例如Unix平台上通常使用Motif、CDE风格。在嵌入设备上常有主题(theme)或外观设置应用程序,它可以给嵌入设备设置不同风格的GUI图形界面。这个应用程序使用的就是Style(风格)机制。

8.1 风格类

QStyle是风格类的基类,为了设置窗口部件的UI界面风格,QStyle类定义了大量的枚举类型和十几个函数。枚举类型表示界面上的不同元素(如组合框中的按钮,按钮的边框等);函数控制图形用户界面的绘制,QStyle大多数成员函数只有声明而没有函数实现,他们的实现是通过在QCommonStyle、QWindowStyle、QMotifStyle及其子类中进行重载来实现的。风格类的继承关系图如图1。QStyle类实现了3个函数函数说明如下:

drawItem(): 负责绘制文本和象素图。

itemRect(): 返回文本或图像所占的区域。

visualRect(): 返回逻辑坐标,这个函数使Qt实现right-to-left风格(阿文、维文传统是文本从右向左显示,因此控件布局也是从右向左)。

图1显示了Qt中与风格相关的类的继承关系。

图1 风格相关类的继承关系图

按照对GUI图形界面的显示效果的影响,风格被分成不同枚举类型,每个枚举类型按照Qt的各种基本图形界面元素被分成不同的枚举项。表13对风格枚举类型进行说明:

表13 风格枚举类型说明 风格枚举类型 枚举类型说明 QStyle::ComplexControl 代表了一个复杂控制类型(如:SpinWidget、ComboBox、TitleBar等),它依赖于用户的点击或按键操作有不同的行为。 QStyle::ContentsType 代表了一个容积类型,它被用来计算各种widgets的容积尺寸。 QStyle::ControlElement 代表了一个控制元素,它是执行一些动作或显示信息给用户的widget的一部分。如:按钮的标签。 QStyle::PixelMetric 代表了一个像素点距离,它是风格依赖的尺寸,如:PM_ButtonMargin表示按钮标签与框架之间的空白区域的大小。 QStyle::PrimitiveElement 代表了一个风格的基本图形界面元素。它是一个通常的GUI元素,如:QStyle::PE_ArrowUp表示向上的箭头。 QStyle::StyleFlags 代表了一个基本图形界面元素的绘画标志。如:QStyle::Style_Enabled表示widget被激活时的风格。 QStyle::StyleHint 代表了一个StyleHint,它是一个常规的外观。如:QStyle::SH_ScrollBar_BackgroundMode表示一个QScrollBar的背景模式。 QStyle::StylePixmap 代表了一个风格位图。它是遵循一些存在的GUI风格的位图。如:QStyle::SP_MessageBoxWarning 表示警告信息的图标。 QStyle::SubControl 代表了在一个复杂控制的子控制。 QStyle::SubRect 代表了一个widget的子区域。Style使用这些区域画widget的不同部分。

如果用户需要修改界面风格的某一部分,就必须重载QStyle中描述这部分的相关绘制函数。下面只简单地说明这几个绘制函数,详细的说明可参考Qt文档:

drawPrimitive(......) 这个函数在指定区域绘制基本图形元素,如QSpinBox中的带箭头的按钮等。

drawComplexControl(......) 这个函数在指定区域用指定的颜色绘制复杂窗口部件,如SpinWidget,comboBox,slider,listView等。

drawComplexControlMask(...)这个函数在指定区域用指定的颜色以位掩码的方式绘制复杂窗口部件。

drawItem (......) 这个函数绘制指定区域中的文本或位图。

创建Style的方法是先选择一个风格基类,在用户的风格类中重载基类的函数。再从你的应用程序中使用这个新的风格类,或用一个插件来使用这个风格类。选择基类时,你可以选择QStyle或其它的风格派生类作为父类,如:QWindowsStyle或QMotifStyle。

示例:创建一个用户风格类CustomStyle

在这个例子中,我们首先改变在QWindowsStyle风格下的标准箭头的外观。箭头是PrimitiveElements类型(即GUI基本元素),使用函数drawPrimitive()绘画。在这个简单例子中,我们选择了QWindowsStyle作为父类。

CustomStyle类声明列出如下:

#include <qwindowsstyle.h> class CustomStyle :public QWindowsStyle { Q_OBJECTpublic: CustomStyle(); ~CustomStyle();  void drawPrimitive( PrimitiveElement pe, //绘制基本图形元素 Qpainter *p, const Qrect & r, //表示一个矩形区域/*表示一个部件(widget)的颜色组,color group含有部件绘制自己时使用的各种颜色,如前景色、背景色等*/const QcolorGroup & cg, //控制如何绘制图形界面元素的标志 Sflags flags = Style_Default, /*绘制不同的部件(widget)时会需要不同的参数,如绘制面板(panel)可能需要线宽作为额外参数。*/const QstyleOption &= QstyleOption::Default)const; private://取消了拷贝构造函数和‘=’操作符。 CustomStyle(const CustomStyle &); CustomStyle& operator=(const CustomStyle &);};


下面是用户风格类CustomStyle的实现代码:

CustomStyle::CustomStyle(){} CustomStyle::~CustomStyle(){} void CustomStyle::drawPrimitive( PrimitiveElement pe, QPainter * p, const QRect & r, const QColorGroup & cg, SFlags flags, const QStyleOption & opt )const{// 我们仅对箭头感兴趣if(pe >= PE_ArrowUp && pe <= PE_ArrowLeft){ QPointArray pa(3);// 使箭头覆盖它所支持的绘画区域的一半。int x = r.x();int y = r.y();int w = r.width()/2;int h = r.height()/2; x +=(r.width()- w)/2; y +=(r.height()- h)/2;  switch( pe ){case PE_ArrowDown: pa.setPoint(0, x, y ); pa.setPoint(1, x + w, y ); pa.setPoint(2, x + w /2, y + h );break;case PE_ArrowUp: pa.setPoint(0, x, y + h ); pa.setPoint(1, x + w, y + h ); pa.setPoint(2, x + w /2, y );break;case PE_ArrowLeft: pa.setPoint(0, x + w, y ); pa.setPoint(1, x + w, y + h ); pa.setPoint(2, x, y + h /2);break;case PE_ArrowRight: pa.setPoint(0, x, y ); pa.setPoint(1, x, y + h ); pa.setPoint(2, x + w, y + h /2);break;default:break;  }  //使用不同的颜色指示箭头enabled/disabled状态。if( flags & Style_Enabled ){ p->setPen( cg.mid()); p->setBrush( cg.brush( QColorGroup::ButtonText));}else{ p->setPen( cg.buttonText()); p->setBrush( cg.brush( QColorGroup::Mid));} p->drawPolygon( pa ); //画箭头}else{// 让基类处理其它的基本元素 QWindowsStyle::drawPrimitive( pe, p, r, cg, flags, data );}}


在Qt应用程序中有几种方法使用用户风格。最简单的方法是在应用程序的main()函数中加入下面的行:

int main(int argc, char** argv ){ QApplication::setStyle(new CustomStyle());//创建QApplication对象等其它通常的例程  ......}


注意你还必须在你的项目文件中包括customstyle.h和customstyle.cpp 文件。

你还可以以插件的形式创建stly,这时style作为共享对象在运行时被装载。编译你的style插件,并把它放在$QTDIR/plugins/styles目录里。对你已存在的应用程序可以使用下面的命令在运行时自动装载style插件:

./application -style custom

8.2 窗口系统如何更新风格

函数Global::applyStyle()是窗口系统风格更新的顶层函数,它通过系统信道"QPE/System"发送消息"applyStyle()"到各个应用程序,应用程序会根据这个消息调用函数applyStyle设置它的窗口风格。函数Global::applyStyle()调用层次图如图21。

 

Linux Qt core introduction 03.gif图21 函数Global::applyStyle()调用层次图

函数Global::applyStyle()列出如下:

void Global::applyStyle(){#ifndef QT_NO_COP QCopChannel::send("QPE/System", "applyStyle()");#else((QPEApplication *)qApp)->applyStyle();// apply without needing QCop for floppy version#endif}  void QPEApplication::systemMessage(const QCString &msg, const QByteArray &data){ ...... elseif( msg =="applyStyle()"){ applyStyle();} ......}


函数applyStyle应用风格,设置调色板及字体。还设置应用程序的装饰风格类。函数applyStyle读取配置文件中的风格类名、字体名、颜色值及位图,对应用程序的各个窗口设置风格,方法是得到应用程序的窗口部件链表,遍历各个窗口部件,调用风格类函数polish函数润饰窗口,设置窗口部件的调色板颜色值以及窗口部件的字体。

函数applyStyle分析如下:

void QPEApplication::applyStyle(){#ifdef Q_WS_QWS QString styleName;//读取qpe.conf配置文件中Appearance组中Style选项 Config config("qpe"); config.setGroup("Appearance"); styleName = config.readEntry("Style", "Qtopia");  //设置风格到窗口部件 internalSetStyle( styleName );  //从配置文件中读取颜色值,设置调色板 setPaletteEntry( tempPal, config, Button, "#F0F0F0"); setPaletteEntry( tempPal, config, Background, "#EEEEEE"); ...... setPalette( pal, TRUE ); //设置应用程序的缺省调色板为pal  color = config.readEntry("AlternateBase", "#EEF5D6");if( color[0]=='#')//存在颜色值,使用颜色值 style().setExtendedBrush(QStyle::AlternateBase, QColor(color));else{//不存在颜色值,则是位图名,装载位图 QPixmap pix; pix = Resource::loadPixmap(color); style().setExtendedBrush(QStyle::AlternateBase, QBrush(QColor("#EEF5D6"),pix));} ...... //读取窗口装饰配置,设置装饰对象,并重绘制窗口 QString dec = config.readEntry("Decoration", "Qtopia"); QString decTheme = config.readEntry("DecorationTheme", "");if( dec != d->decorationName ||!decTheme.isEmpty()){ qwsSetDecoration(new QPEDecoration( dec ));// 设置装饰对象,并重绘制窗口 d->decorationName = dec; d->decorationTheme = decTheme;}  // 读取字体配置 QString ff = config.readEntry("FontFamily", font().family());int fs = config.readNumEntry("FontSize", font().pointSize()); QFont fn(ff,fs);  //根据字体的大小算出图标高度,并将高度值存入配置文件#ifndef QPE_FONT_HEIGHT_TO_ICONSIZE#define QPE_FONT_HEIGHT_TO_ICONSIZE(x) (x+1)#endifint is = config.readNumEntry("IconSize", -1);if( is <0){ QFontMetrics fm(fn); config.writeEntry("IconSize", QPE_FONT_HEIGHT_TO_ICONSIZE(fm.height())); config.write();//写入图标高度(根据字体大小计算得到)}  setFont( fn, TRUE );//设置字体 #endif}


函数internalSetStyle创建风格对象,并设置风格。函数列出如下:

void QPEApplication::internalSetStyle(const QString &style ){  ...... QStyle*newStyle =0;if( style =="QPE"|| style =="Qtopia"){ newStyle =new QPEStyle;}else{ newStyle = QStyleFactory::create(style);}  if(!newStyle ){ newStyle =new QPEStyle; d->styleName ="QPE";}else{ d->styleName = style;}  if(&qApp->style()!= newStyle) setStyle( newStyle ); //设置风格 }


函数setStyle设置应用程序的GUI风格到参数style。风格对象的拥有关系被传递给QApplication,这样在QApplication在删除或一个新风格被设置时,QApplication将删除style对象。

在切换风格时,颜色调色板将被设置回初始的或系统缺省状态,这是必须的,因为某一风格必须采用完全与风格相适应的颜色调色板。

函数setStyle分析如下:

void QApplication::setStyle( QStyle *style ){ QStyle* old = app_style; app_style = style;#ifdef Q_WS_X11 qt_explicit_app_style = TRUE;#endif // Q_WS_X11  //如果启动失败,即一个QApplication对象还没被创建,删除旧的风格对象if( startingUp()){delete old;return;}  // 清除旧的风格if(old){if( is_app_running &&!is_app_closing ){ QWidgetIntDictIt it(*((QWidgetIntDict*)QWidget::mapper));register QWidget *w;while((w=it.current())){//遍历所有的窗口部件++it;if(!w->testWFlags(WType_Desktop)&&//除了Qtopia desktop外 w->testWState(WState_Polished)){// 已被润饰(Polished) old->unPolish(w);//在窗口部件动态润饰之前,必须取消前一次的润饰参数}}} old->unPolish( qApp );}  /*在润饰应用程序之前关注某一GUI风格的可能的调色板需求。因为style可能通过函数QApplication::setStyle()调用它自己。*/if(!qt_std_pal ) qt_create_std_palette();//创建标准调色板对象qt_std_pal QPalette tmpPal =*qt_std_pal; setPalette( tmpPal, TRUE ); //设置缺省的应用程序调色板app_pal = tmpPal  // 使用新的风格初始化应用程序 app_style->polish( qApp );  //如果必要,重新润饰存在的窗口部件if(old){if( is_app_running &&!is_app_closing ){ QWidgetIntDictIt it(*((QWidgetIntDict*)QWidget::mapper));register QWidget *w;while((w=it.current())){//遍历所有的窗口部件++it;if(!w->testWFlags(WType_Desktop)){//除了Qtopia desktop外if( w->testWState(WState_Polished)) app_style->polish(w);//重新润饰 w->styleChange(*old );if( w->isVisible()){ w->update();}}}}delete old;}}


窗口部件的润饰函数polish是用户的风格类中的重载函数,在这个函数中用户可以设置窗口部件的各种参数。每次窗口部件动态润饰之前,必须调用函数unPolish取消前一次的润饰参数。下面列出一个用户定义的FreshStyle风格的polish函数及unpolisy函数:

void FreshStyle::polish( QWidget *w ){if( w->inherits("QListBox")||   //是从QListBox继承的窗口部件 w->inherits("QListView")|| w->inherits("QPopupMenu")|| w->inherits("QSpinBox")){ QFrame *f =(QFrame *)w;//设置线条宽度 f->setFrameShape( QFrame::StyledPanel);//设置框架的样式 f->setLineWidth(1);}  //设置窗口部件w窗口状态为 WState_GlobalBrushOrigin,即画刷所在窗口 QStyleHackWidget::setGlobalBrushOrigin(w);} void FreshStyle::unPolish( QWidget *w ){if( w->inherits("QListBox")|| w->inherits("QListView")|| w->inherits("QPopupMenu")|| w->inherits("QSpinBox")){ QFrame *f =(QFrame *)w; f->setFrameShape( QFrame::StyledPanel); //设置框架的样式 f->setLineWidth(2); //设置线条宽度}   QStyleHackWidget::clearGlobalBrushOrigin(w);}


函数qwsSetDecoration设置QWSDecoration派生类对象并重新绘制窗口,这个方法仅在Qt/Embedded中使用。函数分析如下:

void QApplication::qwsSetDecoration( QWSDecoration *d ){if( d ){delete qws_decoration; qws_decoration = d; //得到QWSDecoration派生类对象,用于QApplication内部 QWidgetList *widgets = topLevelWidgets();//得到所有窗口部件的链表 QWidgetListIt it(*widgets ); QWidget *w;while((w=it.current())!=0){++it;if( w->isVisible()&& w != desktop()){((QETWidget *)w)->updateRegion(); //更新窗口各区域的大小       //发出重画事件重新绘制desktop()->rect()区域((QETWidget *)w)->repaintDecoration(desktop()->rect(), FALSE);if( w->isMaximized()) //如果已是最大化,就最大化显示 w->showMaximized();}}delete widgets;}}

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 黑马qt公开课是一套为期5天的课件,主要介绍了Qt框架的基础知识和应用。Qt框架是一个跨平台的开发工具,可以方便地进行应用程序的设计、开发和调试,被广泛应用于图形界面开发、嵌入式系统和移动应用等领域。 在5天的课程中,学习者将对Qt框架的整体架构有一个全面的认识,包括Qt及其常用库的概念、功能和用法,也学会了如何使用Qt Designer进行界面设计和基于信号与槽的事件编程。 此外,课程还将介绍Qt中常用的编程模式和技术,如MVC架构、文件操作、网络编程等,并通过实例让学习者深入理解和应用这些概念和技术。 5天的课件中还提供了大量的实践操作,让学习者通过编写实际案例,深入理解所学知识,并更好地掌握Qt框架的基础和应用,为以后的工作打下坚实的基础。 总之,如果你想快速入门Qt框架的基础知识和应用,那么黑马qt公开课—5天的课件,将是一个非常好的选择。 ### 回答2: 黑马qt公开课的课件共分为5天,内容涵盖了Qt的基础知识、UI设计、绘图系统、多线程编程和网络编程等方面。通过这5天的学习,学员可以全面掌握Qt的开发技能和应用场景,具备开发Qt应用程序的能力。 第一天课程主要介绍了Qt的基础知识,包括Qt窗口和控件、信号与槽机制、事件处理、布局和样式等内容。通过这些基础知识的学习,学员可以了解Qt的基本工作原理和操作方法。 第二天的课程主要讲解了Qt的UI设计,包括UI设计器的使用、自定义控件和样式等内容。学员可以从中学习到如何设计美观、直观的用户界面。 第三天的课程则主题为Qt的绘图系统,包括2D和3D绘图、动画效果和图形转换等内容。在这一天的课程中,学员可以学习到如何使用Qt进行图形绘制和界面效果的优化。 第四天的课程主要介绍了Qt的多线程编程,包括线程的创建和管理、互斥锁和信号量等内容。学员可以从中学习到如何在Qt中实现多线程应用程序。 第五天的课程则主题为Qt的网络编程,包括socket编程、HTTP协议和Web服务等内容。学员可以从中学习到如何使用Qt进行网络编程,实现客户端和服务器的互通。 总体来说,黑马qt公开课的5天课程涵盖了Qt的核心知识点,让学员能够全面掌握Qt的开发技能和应用场景。通过这些课程的学习,学员可以成为一名合格的Qt开发工程师。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值