zhonghua

C++博客 首页 新随笔 联系 聚合 管理
  72 Posts :: 1 Stories :: 4 Comments :: 0 Trackbacks

#

Qt提供了一个绝妙的属性系统。跟那些由编译器提供的属性差不多。然而,作为一个独立于编译器和平台的库,Qt不依赖于非标准的编译特性,比如 __property 或[property]。Qt可以在任何平台上的标准编译器下编译。Qt属性系统基于元数据对象系统--就是那个提供了对象内置信号和槽通讯机制的家伙。


声明属性需要什么


要声明一个属性,需在继承自QObject的类中使用Q_PROPERTY()宏。
Q_PROPERTY(type name
   READ getFunction
   [WRITE setFunction]
   [RESET resetFunction]
   [NOTIFY notifySignal]
   [DESIGNABLE bool]
   [SCRIPTABLE bool]
   [STORED bool]
   [USER bool]
   [CONSTANT]
   [FINAL])

下面是一些典型的声明属性的示例:

  1. Q_PROPERTY(bool focus READ hasFocus)  
  2. Q_PROPERTY(bool enabled READ isEnabled WRITE setEnabled)  
  3. Q_PROPERTY(QCursor cursor READ cursor WRITE setCursor RESET unsetCursor)  
  • 一个属性的行为就像类的数据成员,但是它还具有附加的特性,这些特性可以被元数据对象系统操作。这些特性是:
    需要一个READ访问器函数。用于读属性的值。理想情况下,有一个不变的函数用于此目的,并且它必须返回属性的类型的值或指针或引用。例如,QWidget::focus是一个只读的属性,它对应一个读函数:QWidget::hasFocus()。
  • 一 个可选的WRITE访问器函数。它用于设置属性的值。它必须返回空并且至少具有一个参数,参数是属性类型的值或指针或引用。例 如:QWidget::enabled具有WRITE函数QWidget::setEnable()。只读属性不需要写函数。例 如,QWidget::focus没有对应的写函数。
  • 一个可选的RESET函数。用于设置属性的值到它的默认值。例 如:QWidget::cursor具有典型的READ和WRITE函数,QWidget::cursor()和 QWidget::setCursor(),并且它也具有一个RESET函数,QWidget::unsetCursor()。RESET函数必须返回 void并且不带有任何参数。
  • 一个可选的NOTIFY信号。如果被定义了,信号将在属性的值改变时发出。信号必须带有一个参数,这个参数的类型必须与属性相同;参数保存的是属性的新值。
  • 一个DESIGNABLE变量表明此属性是否在界面设计器的属性编辑器中出现。大多数属性是可见的,除了为这个变量传入true或false,你还可以指定一个bool型的成员函数。
  • SCRIPTABLE变量表明这个属性是否可以被一个脚本引擎操作(默认是true)。你也可以赋予它true或false或bool型函数。
  • STORED 变量表明了属性是否被认为是独立存在还是依赖于其它的值而存在。它也表明是否在保存对象状态时保存此属性的值。大多数属性都是需要保存的,但是,如 QWidget::minimumWidth()就是不被保存的,因为它的值是从另一个属性QWidget::minimumSize()得来的。
  • USER变量表明属性是否被设计为面向用户的或用户可修改的类属性。通常,每个类只有一个USER属性。例如,QAbstractButton::checked是按钮类的用户可修改属性。注意QItemDelegate获取和设置widget的USER属性。
  • CONSTANT的出现表明属性的值是不变的。对于一个object实例,常量属性的READ方法在每次被调用时必须返回相同的值。此常量值可能在不同的object实例中不相同。一个常量属性不能具有WRITE方法或NOYIFY信号。
  • FINAL变量的出现表明属性不能被派生类所重写。有些情况下,这可以用于效率优化,但不是被moc强制的。程序员必须永远注意不能重写一个FINAL属性。

READ,WRITE和RESET函数都可以被继承。它们也可以是虚函数。当它们在被多重继承中被继承时,它们必须出现在第一个被继承的类中。

属性的类型可以是被QVariant支持的所有类型,也可以是用户定义的类型。在下面的例子中,类QDate被当作用户自定义类型。
Q_PROPERTY(QDate data READ getDate WRITE setDate)
因为QDate是用户定义的,你必须包含<QDate>头文件。

对 于QMap,QList和QValueList属性,属性的值是一个QVariant,它包含整个list或map。注意Q_PROPERTY字符串不能 包含逗号,因为逗号会划分宏的参数。因此,你必须使用QMap作为属性的类型而不是QMap<QString,QVariant>。为了保持 一致性,也需要用QList和QValueList而不是QList<QVariant>和 QValueList<QVariant>。


通过元数据对象系统读写属性

一 个属性可以使用常规函数QObject::property()和QObject::setProperty()进行读写,不用知道属性所在类的任何细 节,除了属性的名字。在下面的小代码片段中,调用QAbstractButton::setDown()和QObject::setProperty() 都把属性设置为“down”。

  1. QPushButton *button = new QPushButton;  
  2. QObject *object = button;  
  3. button->setDown(true);  
  4. object->setProperty("down", true);  

通 过WRITE操作器来操作一个属性是上面两者中更好的,因为它快并且在编译时给于更好的诊断帮助,但是以这种方式设置属性要求你必须在编译时了解其类。通 过名字来操作属性使你可以操作在编译器你不了解的类。你可以在运行时发现一个类的属性们,通过查询它的QObject,QMetaObject和 QMetaProerties。

  1. QObject *object = ...  
  2. const QMetaObject *metaobject = object->metaObject();  
  3. int count = metaobject->propertyCount();  
  4. for (int i=0; i<count; ++i) {  
  5.     QMetaProperty metaproperty = metaobject->property(i);  
  6.     const char *name = metaproperty.name();  
  7.     QVariant value = object->property(name);  
  8.     ...  
  9. }  

在上面的代码片段中,QMetaObject::property()被用于获取未知类中的属性的metadata。从metadata中获取属性名然后传给QObject::property()来获取

一个简单例子


假 设我们有一个类MyClass,它从QObject派生并且在它的private区使用 了Q_OBJECT宏。我们想在MyClass类中声明一个属性来持续追踪一个Priorty值。属性的值叫做priority,并且它的类型是一个在类 MyClass中定义的叫做Priority的枚举。

我们在类的private区使用Q_PROPERTY()来声明属性。READ函数 叫做priority,并且我们包含一个WRITE函数叫做setPriority。枚举类型必须使用Q_ENUMS()注册到元数据对象系统中。注册一 个枚举类型使得枚举的名字可以在调用QObject::setProperty()时使用。我们还必须为READ和WRITE函数提供我们自己的声明。 MyClass的声明看起来应该是这样的:

  1. class MyClass : public QObject  
  2. {  
  3.     Q_OBJECT  
  4.     Q_PROPERTY(Priority priority READ priority WRITE setPriority)  
  5.     Q_ENUMS(Priority)  
  6. public:  
  7.     MyClass(QObject *parent = 0);  
  8.     ~MyClass();  
  9.     enum Priority { High, Low, VeryHigh, VeryLow };  
  10.     void setPriority(Priority priority);  
  11.     Priority priority() const;  
  12. };  

READ函数是const的并且返回属性的类型。WRITE函数返回void并且具有一个属性类型的参数。元数据对象编译器强制做这些事情。

在有了一个指向MyClass实例的指针时,我们有两种方法来设置priority属性:

  1. MyClass *myinstance = new MyClass;  
  2.  QObject *object = myinstance;  
  3.  myinstance->setPriority(MyClass::VeryHigh);  
  4.  object->setProperty("priority", "VeryHigh");  

在 此例子中,枚举类型在MyClass中声明并被使用Q_ENUMS()注册到元数据对象系统中。这使得枚举值可以在调用setProperty()时做为 字符串使用。如果枚举类型是在其它类中声明的,那么我们就需要用枚举的全名(如OtherClass::Priority),并且这个其它类也必须从 QObject中派生并且也要注册枚举类型。
另一个简单的Q_FLAGS()也是可用的。就像Q_ENUMS(),它注册一个枚举类型,但是它把 枚举类型作为一个flag的集合,也就是,值可以用OR操作来合并。一个I/O类可能具有枚举值Read和Write并且 QObject::setProperty()可以接受 Read|Write。此时应使用Q_FLAGS()来注册枚举值。

动态属性

Qobject::setProperty() 也可以用来在运行时向一个类的实例添加新的属性。当使用一个名字和值调用它时,如果一个对应的属性已经存在,并且如果值的类型与属性的类型兼容,那么值就 被存储到属性中,然后返回true。如果值类型不兼容,属性的值就不会发生改变,就会返回false。但是如果对应名字的属性不存在,那么一个新的属性就 诞生了,以传入的名字为名,以传入的值为值,但是依然会返回false。这表示返回值不能用于确定一个属性是否被设置值,除非你已经知道这个属性已经存在 于QObject中了。
注意动态属性被添加到单个实现的基础中,也就是,被添加到QObject,而不是QMetaObject。一个属性可以从 一个实例中删除,通过传入属性的名字和非法的QVariant值给QObject::setProperty()。默认的QVariant构造器构造一个 非法的QVariant。
动态属性可用QObject::property()来查询,就行使用Q_PROPERTY()声明的属性一样。

属性和自定义类型

被属性使用的自定义类型需要使用Q_DECLARE_METATYPE()宏注册,以使它们的值能被保存在QVariant对象中。这使得它们可以用于被Q_PROPERTY()声明的静态类型中,也可以被用于动态类型中。

posted @ 2013-06-05 09:53 米米 阅读(1006) | 评论 (0)编辑 收藏

QGraphicsItem类是视图框架的一部分,是在一个QGraphicsScene中最基本的图形类,它为绘制你
 
 
 
自己的item提供了一个轻量级的窗口,包括声明item的位置,碰撞检测,绘制重载和item之间的相
 
 
 
互作用通过事件处理
 
Qt提供了一系列标准的items对一些常见的图像,非常的方便,有下面这些:
 
QGraphicsEllipseItem  提供一个椭圆item
 
QGraphicsLineItem     提供一条线的item
 
QGraphicsPathItem     提供一个任意的路径item
 
QGraphicsPixmapItem   提供一个图形item
 
QGraphicsPolygonItem  提供一个多边形item
 
QGraphicsRectItem     提供一个矩形item
 
QGraphicsSimpleTextItem 提供一个简单的文本item
 
QGraphicsTextItem     提供一个文本浏览item
 
item 的所有几何位置信息都建立在本地坐标系统上,item的位置,使用pos()获得,是唯一一个不再本地item中实现的。它返回的是item在父亲坐标系 统中的位置,关于坐标系统可以详细参看 The Graphics View Coordinate System
 
 
 
通 过调用哪个setVisible(),可以设置item是否可见,隐藏一个item同时也隐藏了他的孩子,相似的,你可以通过调用 setEnabled()来是指item是否可用。如果禁用了item,那么它所有的孩子都不可用。默认的,items都是可见和可用的。来开关一个 item是否被选择,首先通过设置itemsSelectable flag来使能选择。然后调用setSelect(),通常,是否可被选择的开关时打开的
 
 
 
写自己的item图形,首先应该继承QGraphicsItem,然后重写他的两个纯虚公共函数,

boundingRect()和paint(),第一个函数返回绘制item大概的区域,第二个函数用来绘制item内容
 
 
 


boundingRect() 函数有很多用处,场景在boundingRect()来建立它的item的index,视图view使用它来剪切可见的item,在重新绘制item时 候,来决定相互重叠的部分,此外,item的碰撞检测机制也使用的boundingRect()来提供一个高效的定点,在 collidesWithItem()更好的碰撞算法建立在调用函数shape(),shape()函数以QpainterPath类型返回item的精 准的轮廓。
 
一般的,场景不希望item的boundingRect()和shape()变化,除非该item被通告,如果想通过一些方法改变item的形状,首先应该调用QgraphicsScene()来允许场景QgraphicsScene来刷新它的item记录。
 
 
 
碰撞检测可以通过下面两种方法来完成
 
1、重写shape()函数来返回item的精准轮廓,依靠默认的collidesWithItem()来做外形交集。如果item轮廓和复杂时候,这个消耗是很大的
 
2、重写collidesWithItem(),提供一个自己的item和轮廓碰撞的算法
 
 
 
Contains()函数可以调用,用来决定一个item是否包含一个点。这个函数也可以重写,contains()函数默认的方法是通过调用shape()来完成的。
 
Items中也可以包含其他的items,也可以被别的items包含,所有的items可以有一个父亲item和一串孩子items,除非一个item没有父亲,否则它的位置是在父亲坐标中,父亲items遗传他的位置和转换给孩子item
 
转换
 
QgraphicsItem 支持投射转换,有很多方法来改变item的转换,对于简单的转换,可以调用函数setRotation()或者setScale(),可以传递一个转换矩 阵给函数setTransform(),对于一些更复杂的转换,可以通过调用函数setTransformations()来设置一系列组合的转换。
 
Item 转换从父亲到孩子进行聚集,因此如果一个父亲和孩子item都旋转90度,那么孩子就旋转了180度,相似的,如果父亲item放大了2X倍,那么孩子 item就被方法4X倍,一个item的转换不影响他的外观,所有和外观有关的函数(例如contains(),update()和所有的映射 mapping函数)将会在本地坐标中操作,更方便的,QgraphicsItem提供函数sceneTransform(),将会返回item所有的转 换矩阵,scenePos()将会返回item在场景坐标中的位置,重新设置item的矩阵,调用函数resetTransform()
 
一 般的转换回产生一个不同的结果,这取决于转换应用的顺序,例如,如果你放大一个转换,然后再旋转它,可能和你先旋转它得到的结果不一样,你设置转换属性的 顺序并不影响转换的结果,(也就是仍旧会按照你的转换命令去转换,只是最后得到的图形不一样而已),QgraphicsItem经常应用一个合适的顺序如 下:
 


绘图painting
 
paint()函数被QgrapicsView类调用来绘制item的 内容,item默认是没有背景或者填充颜色的。在函数中没有被绘制的所有区域都将会发亮,可以调用update()来重绘item,可以选择传递需要重绘 的矩形区域(不是必须的)。取决于item在view中是否可见,item可能会也可能不会重绘,QgraphicsItem里面没有和 Qwidget::repaint()函数等价的
 
item通过view来绘制,从父items开始,然后是自items,以上升的栈的 顺序,可以通过调用setZValue()设置item的栈顺序,通过zValue()来测试,具有低z-values的item比具有高z-value 的item先绘制,栈顺序应用于兄弟items,父items总是比子items更早绘制。
 
 
 
排序sort
 
所有的items都按照一个已经声明的,稳定的顺序来绘制,这个顺序也决定了当你在场景中点击鼠标时候,哪个items最先接受鼠标的输入。一般的,你不需要担心排序问题,因为所有的items都按照一个在场景中声明的自然的顺序
 


在一个栈中,子item在父item的上面,兄弟item按照插入场景的顺序来入栈,如果你先添加了item A ,然后是item B,然后是item C ,这样栈中的顺序从下往上就是A,B,C
 
Drag and Drop Robot例子中展示了该robot的栈顺序,躯干顺序是根item(其他所有的item都是躯干item的子item或者后代item),然后是头 item被绘制,由于它是躯干item的子item列表中的第一个item,然后是左臂膀上面的那部分item,下面那个臂膀item是上面臂膀item 的子item,所以会在和3号item同等级的兄弟item绘制完后被绘制,接着就是右边上面的的臂膀item,就是5号item。
 
 。可以调用setZvalue()来设置一个item的相对亦另一个向上,向下或者兄弟栈顺序。默认的Z值是0,具有同样的Z值的item会按照插入的顺序来入栈。
 
。可以调用stackBefore()来备份孩子item的列表,这可以直接更正item的顺序。
 
。如果想让孩子item在父item的后面,也就是先绘制孩子item,然后在绘制父item,怎么办呢?可以设置ItemStacksBehindParent属性给这个item,利用函数setFlag();
 
两个兄弟item的顺序也决定了他们的子item 和后代item的顺序,如果一个父item的在两个父item的后面,那么他所有的孩子items都在另一个父item的孩子items后面。
 
 
 
事件Event
 
QgraphicsItem从场景中通过sceneEvent()函数来接受事件,这个函数通过一些方便的操作分散大部分事件
 
ContextMenuEvent()函数接受上下文菜单事件
 
FocusInEvent()和focusOutEvent()函数接受焦点进出事件
 
hoverEnterEvent(), hoverMoveEvent(), and hoverLeaveEvent() 接受鼠标悬浮 移动和离开事件
 
inputMethodEvent()函数处理输入法事件,
 
keyPressEvent() and keyReleaseEvent()事件处理键盘按下和松开事件
 
mousePressEvent(), mouseMoveEvent(), mouseReleaseEvent(), and mouseDoubleClickEvent()处理鼠标按下,移动,松开,双击事件
 
也 可以为一些item过滤一些事件通过安装过滤器,这个和QT一般的事件过滤器不一样,一般的过滤器只工作在Qobject和它的子类,通过调用 installSceneEventFilter()为item安装了过滤器后,被过滤的事件将会被虚函数sceneEventFilter().捕捉 到,可以通过调用函数removeSceneEventFilter().来去除掉事件过滤器
 
 
 
Custom Data数据
 
有 些时候为item注册一些数值很有用,做一个普通的item或者标准的item,可以调用setData()来为任一个item设置值,这个值使用 key-value对,(key是整形,value是变种数据Qvarient)来得到item的数据,通过调用data(),
 
 
 
 
 
 
 
 
 
 
 
1、QVariant QGraphicsItem::itemChange ( GraphicsItemChange change, const QVariant & value )   [virtual protected]
 
这个函数被QGraphicsItem调用用来标明items的一些状态改变了,通过重载这个函数,可以对自己定义事件响应,在一些情况下,可以做一些调整。
 
参数change是改变的那个item的改变状态参数,value是一个新的数据,他的类型取决于change,
 
 
 
change是QGraphicsItem::GraphicsItemChange的枚举变量
 
enum      GraphicsItemChange { ItemEnabledChange, ItemEnabledHasChanged,

 
 
ItemMatrixChange, ItemPositionChange, ..., ItemScenePositionHasChanged }
 
例如:
 
 QVariant Component::itemChange(GraphicsItemChange change, const QVariant &value)
 
 {
 
     if (change == ItemPositionChange && scene()) {
 
         // value is the new position.
 
         QPointF newPos = value.toPointF();
 
         QRectF rect = scene()->sceneRect();
 
         if (!rect.contains(newPos)) {
 
             // Keep the item inside the scene rect.
 
             newPos.setX(qMin(rect.right(), qMax(newPos.x(), rect.left())));
 
             newPos.setY(qMin(rect.bottom(), qMax(newPos.y(), rect.top())));
 
             return newPos;
 
         }
 
     }
 
     return QGraphicsItem::itemChange(change, value);
 
 }
 
默认的函数什么都不做,只返回value
 
注 意:在使用这个函数时候,在函数内部调用函数时候要小心,因为这可能导致一些意想不到的结果,例如:你不能再这个函数里面调用setPos()在 change是ItemPositionChange时候,由于setPos()函数将会再次调用 itemChange(ItemPositionChange),如此就一直循环下去了。
 
2、void QGraphicsItem::setFlag ( GraphicsItemFlag flag, bool enabled = true )
 
void QGraphicsItem::setFlags ( GraphicsItemFlags flags )
 
把flags设置为item的属性,如果item获得了光标,但是flags没有使能ItemsFocusable,这个item
 
 
 
将会丢失光标,同样的,当item被选择到,但是没有使能ItemsSelectable,这个item会自动的失去
 
 
 
选择。
 
默认的,所有的flags都是不可用的。(QGraphicsWidget 为了获得位置变化默认使能了
 
 
 
ItemSendsGeometryChanges)
 
相近的函数
 
GraphicsItemFlags QGraphicsItem::flags () const
 
返回item的所有使能的flags,例如,如果flags里面包含了ItemIsFocusable,这个item可以接受
 
 
 
输入光标
 
3、QPainterPath QGraphicsItem::shape () const   [virtual]
 
以QPainterPath返回item在local坐标中的形状,这个形状可以用来做很多事情,包括碰撞侦测,
 
 
 
打击测试,还有用来 QGraphicsScene::items() 函数
 
默认的函数调用boundingRect()返回一个简单的矩形形状,但是子类可以重载这个函数,为非矩形
 
 
 
的item返回一个更加精准的形状,例如一个圆形的item可以选择返回一个椭圆形,用来获得更好的
 
 
 
碰撞侦测效果。代码:
 
QPainterPath RoundItem::shape() const
 
 {
 
     QPainterPath path;
 
     path.addEllipse(boundingRect());
 
     return path;
 
 }
 
形状的轮廓线可以通过绘制时候的pen来变化
 
4、QRectF QGraphicsItem::boundingRect () const   [pure virtual]
 
这个纯虚函数用矩形声明了item的边界轮廓,所有的绘制都必须限定在item的矩形边框内。
 
 
 
QGraphicsView使用这个方法来决定item是否需要重绘
 
尽管item的形状可以是任意的,但是边框一直都是矩形,不影响items的变换
 
如果想改变items的边框,应该首先调用prepareGeometryChange(),这将通知场景scene即将发生的变化,这样场景可以刷新item的位置下标。否则,场景将不会察觉到item的变化,结果也未知。
 
如果要重绘item时候,重载这个函数来让QGraphicsView来决定item的边界区域,
 
注意:由于绘制边界时候的边界轮廓线,在这个矩形区域内包含画笔pen宽度的一半很重要,不需
 
 
 
要补偿画图走样  例如
 
 QRectF CircleItem::boundingRect() const
 
 {
 
     qreal penWidth = 1;
 
     return QRectF(-radius - penWidth / 2, -radius - penWidth / 2,
 
                   diameter + penWidth, diameter + penWidth);
 
 }
 
 
 
同样的一个返回item轮廓的函数
 
QRegion QGraphicsItem::boundingRegion ( const QTransform & itemToDeviceTransform ) const
 
返回该item的轮廓区域,返回的区域的坐标系统依赖于参数itemToDeviceTransform,如果你传递一个Qtransform对象作为参数,那么函数将返回本地坐标系统区域
 
返回的区域是item内容可见的一个大概的轮廓,尽管计算起来很浪费空间和时间,但是比boundingRect()更精准,而且当重绘时候,它还能避免不必要的重绘。对像线或者简单的多边形来说非常有效。
 
也可以调节轮廓区域的粒度通过调用setBoundingRegionGranularity(),默认的粒度是0,这时候item的区域和轮廓矩形一样的。
 
itemToDeviceTransform是从item坐标系统到设备坐标系统的一个转换。如果你想让这个函数返回一个场景坐标区域,可以用函数sceneTransform()作为参数。
 
 
 
相关函数qreal QGraphicsItem::boundingRegionGranularity () const
 
返回item的轮廓区域粒度
 
 
 
5、void QGraphicsItem::setPos ( const QPointF & pos )
 
在父对象坐标系统中设置item的位置为pos,对没有父对象的items,pos使用的场景的坐标系统,
 
6、QPointF QGraphicsItem::scenePos () const
 
返回该item在场景中的坐标点,等价于调用函数mapToScene(0, 0).
 
7、QPointF QGraphicsItem::pos () const
 
返回item在父坐标系中的位置,如果没有父坐标系,那么返回在场景坐标系中的位置,此时等价于调用scenePos (),可以直接调用scenePos ()来获取该item在场景中的位置,从而忽略其父对象。
 
 
 
8、enum QGraphicsItem::CacheMode
设置绘图的缓冲模式,默认是没有缓冲,如果使用缓冲区的话,在重绘时候,非常快
 
设置缓冲模式使用函数QGraphicsItem::setCacheMode().
 
 
 
9、bool QGraphicsItem::acceptDrops () const
 
如果item能够接受拖拽事件,就返回真,否则返回假,默认的不能接受拖拽事件
 
 同类函数
 
void QGraphicsItem::setAcceptDrops ( bool on )
 
如果On是真的话,就设置item可以接受拖拽事件,否则对拖拽事件是透明的,也就是不能响应拖拽,默认是不响应的。
 
10、bool QGraphicsItem::acceptHoverEvents () const
 
如果可以接受鼠标悬浮在他上面的事件,就返回真,否则返回假。默认也是不接受该事件的。
 
同样的用于设置是否接受该事件的函数
 
void QGraphicsItem::setAcceptHoverEvents ( bool enabled )
 
如果enabled为真,就接受,否则就不接受。
 
11、Qt::MouseButtons QGraphicsItem::acceptedMouseButtons () const
 
返回这个item所能接受的鼠标事件的鼠标键,默认的是所有的鼠标键事件都能接受。
 
同 样的使用函数void QGraphicsItem:: setAcceptedMouseButtons ( Qt::MouseButtons buttons )来设置接受的鼠标键事件,如果想忽略某个鼠标键事件,可以用setAcceptedMouseButtons(0)
 
12、void QGraphicsItem::advance ( int phase )   [virtual]
 
这 个虚函数被QGraphicsScene::advance()调用两次,第一次调用,所有的items都调用,使用参数phase=0,表示场景中所有 的items都将advance(前进),然后所有的items都将调用,使用参数phase=1,重写这个函数,可以来刷新你的item,如果你需要简 单的场景控制动画。
 
默认的该函数什么事都不做
 
如果想写动画,可以使用两种方法,一个使用 QGraphicsItemAnimation,,或多重继承Qobject和QgraphicsItem,然后使用 QObject::startTimer() 和 QObject::timerEvent().来使item成动画。
 
 
 
13、QList<QGraphicsItem *> QGraphicsItem::childItems () const
 
返回孩子item的一个链表,这些items按照栈顺序排列,考虑了item的插入顺序和Z-values
 
14、QRectF QGraphicsItem::childrenBoundingRect () const
 
返回这个item的所有子孙items的本地坐标系统轮廓矩形。这个矩形包括了这个item所有的子孙item,如果这个item不含有子孙item,这个函数将返回一个空的矩形。
 
返回的矩形不包括item本身的轮廓矩形,只返回子孙item的矩形轮廓,如果你想包含item本身的矩形区域,可以使用QRectF::operator|()把boundingRect()和hildrenBoundingRect()或起来。
 
这个函数很复杂,它决定了返回轮廓的大小通过重复所有子孙item
 
 
 
15、void QGraphicsItem::clearFocus ()
 
去除item的光标,如果原来有光标焦点,当焦点失去时候,事件focus out event会发送给这个item,提示他将会失去焦点。
 
Item只有设置了ItemsFocusable属性,或者widget设置了合适的焦点策略,才能接受键盘焦点。
 
 
 
 
 
16、 bool QGraphicsItem::collidesWithItem ( const QGraphicsItem * other, Qt::ItemSelectionMode mode = Qt::IntersectsItemShape ) const   [virtual]
 
如 果这个item和另一个item碰撞就返回真,mode是应用于其他item的,默认是Qt::IntersectsItemShape,如果其他的 item和这个item相互影响或者包含,被包含于这个item的形状(详见Qt::ItemSelectionMode)。
 
默认的函 数是就基于外形相互影响,它调用两个item的shape()函数,这将会花费很大时间,你可以在QgraphicsItem子类中重写这个函数,提供一 个简单的算法。这样你可以使用自己item的一些参数,这样可以提高碰撞侦测的效果。例如,两个完全没有转换的圆item的碰撞可以通过对比他们的圆心位 置和半径
 
 
 
17、bool QGraphicsItem::collidesWithPath ( const QPainterPath & path, Qt::ItemSelectionMode mode = Qt::IntersectsItemShape ) const   [virtual]
 
如果item按照path碰撞就返回真,
 
 
 
18、QList<QGraphicsItem *> QGraphicsItem::collidingItems ( Qt::ItemSelectionMode mode = Qt::IntersectsItemShape ) const
 
返回和这个item相互碰撞的item的链表
 
碰撞侦测模式取决于mode,
 
 
 
19\ QGraphicsItem * QGraphicsItem::commonAncestorItem ( const QGraphicsItem * other ) const
 
返回item最近的祖先item,如果other为0,或者没有祖先item,就返回0
 
 
 
20、bool QGraphicsItem::contains ( const QPointF & point ) const   [virtual]
 
如果item包含了点point就返回TRUE,否则返回FALSE,最常用的是在QgraphicsView中调用,来判断这个item是否在光标下面,如果要重写这个函数,那就尽可能的简单。默认的这个函数调用的shape()函数
 
 
 
21、void QGraphicsItem::contextMenuEvent ( QGraphicsSceneContextMenuEvent * event )   [virtual protected]
 
右键菜单事件,可以在子类中重写这个函数,event里面包含了事件要具体处理的数据。
 
如果忽略这个事件,调用函数QEvent::ignore(),事件event将会被传播到任何在该item之下的items,如果没有item响应这个事件,那么场景就忽略这个事件,传递给view视图
 
通常是响应该事件打开一个右键菜单,例如
 
void CustomItem::contextMenuEvent(QGraphicsSceneContextMenuEvent *event)  {      QMenu menu;      QAction *removeAction = menu.addAction("Remove");      QAction *markAction = menu.addAction("Mark");      QAction *selectedAction = menu.exec(event->screenPos());      // ...  }
 
 
默认是忽略该事件。
 
 
 
 
 
22、QVariant QGraphicsItem::data ( int key ) const
 
以Qvariant的格式返回item中的数据,当前的item数据对存储任意数据很有用,例如
 
static const int ObjectName = 0;    QGraphicsItem *item = scene.itemAt(100, 50);  if (item->data(ObjectName).toString().isEmpty()) {      if (qgraphicsitem_cast<ButtonItem *>(item))          item->setData(ObjectName, "Button");  }
void QGraphicsItem::setData ( int key, const QVariant & value )
 
用来设置item的数据
 
23、QTransform QGraphicsItem::deviceTransform ( const QTransform & viewportTransform ) const
 
返回item设备的转换矩阵,使用viewportTransform来从场景到设备影射坐标,这个矩阵可以用来从这个item本地坐标到视图口坐标系统映射坐标或者几何图形,如果要映射视图口坐标到本地坐标,首先应该转换返回的这个矩阵。
 
QGraphicsRectItem rect;  rect.setPos(100, 100);    rect.deviceTransform(view->viewportTransform()).map(QPointF(0, 0));  // returns the item's (0, 0) point in view's viewport coordinates    rect.deviceTransform(view->viewportTransform()).inverted().map(QPointF(100, 100));  // returns view's viewport's (100, 100) coordinate in item coordinates
这个函数是结合了到场景的转换和到视图的转换,设备转换可以用来对那些没有进行转换坐标的item计算碰撞。
 
24、拖拽类函数
 
void QGraphicsItem::dragEnterEvent ( QGraphicsSceneDragDropEvent * event )   [virtual protected]
 
void QGraphicsItem::dragLeaveEvent ( QGraphicsSceneDragDropEvent * event )   [virtual protected]
 
void QGraphicsItem::dragMoveEvent ( QGraphicsSceneDragDropEvent * event )   [virtual protected]
 
void QGraphicsItem::dropEvent ( QGraphicsSceneDragDropEvent * event )   [virtual protected]
 
 
 
25、qreal QGraphicsItem::effectiveOpacity () const
 
返回item的有效透明度,这个数值在0.0~~1.0之间
 
同样的可以用函数void QGraphicsItem::setOpacity ( qreal opacity )来设置透明度,
 
默认的子item继承父item的透明度
 
同样可以设置item的属性QGraphicsItem::ItemIgnoresParentOpacity 来忽略父item的透明度对自己的影响
 
设置子item的属性QGraphicsItem::ItemIgnoresTransformations来忽略视图转换(放大,缩小)等操作对自己的影响,在显示文本时候很有用。
 
 
 
26、void QGraphicsItem::hoverEnterEvent ( QGraphicsSceneHoverEvent * event )   [virtual protected]
 
void QGraphicsItem::hoverLeaveEvent ( QGraphicsSceneHoverEvent * event )   [virtual protected]
 
void QGraphicsItem::hoverMoveEvent ( QGraphicsSceneHoverEvent * event )   [virtual protected]
 
鼠标悬浮事件
 
 
 
27、void QGraphicsItem::mouseDoubleClickEvent ( QGraphicsSceneMouseEvent * event )   [virtual protected]
 
void QGraphicsItem::mouseMoveEvent ( QGraphicsSceneMouseEvent * event )   [virtual protected]
 
void QGraphicsItem::mousePressEvent ( QGraphicsSceneMouseEvent * event )   [virtual protected]
 
void QGraphicsItem::mouseReleaseEvent ( QGraphicsSceneMouseEvent * event )   [virtual protected]
 
鼠标双击,移动,释放的事件
 
 
 
 
 
 
 
28、QPointF QGraphicsItem::mapFromItem ( const QGraphicsItem * item, const QPointF & point ) const
 
QPolygonF QGraphicsItem::mapFromItem ( const QGraphicsItem * item, const QRectF & rect ) const
 
QPainterPath QGraphicsItem::mapFromItem ( const QGraphicsItem * item, const QPainterPath & path ) const
 
QPolygonF QGraphicsItem::mapFromItem ( const QGraphicsItem * item, qreal x, qreal y, qreal w, qreal h ) const
 
QPointF QGraphicsItem::mapFromItem ( const QGraphicsItem * item, qreal x, qreal y ) const
 
QPointF QGraphicsItem::mapFromParent ( const QPointF & point ) const
 
QPolygonF QGraphicsItem::mapFromParent ( const QRectF & rect ) const
 
QPolygonF QGraphicsItem::mapFromParent ( const QPolygonF & polygon ) const
 
QPainterPath QGraphicsItem::mapFromParent ( const QPainterPath & path ) const
 
QPolygonF QGraphicsItem::mapFromParent ( qreal x, qreal y, qreal w, qreal h ) const
 
QPointF QGraphicsItem::mapFromParent ( qreal x, qreal y ) const
 
QPointF QGraphicsItem::mapFromScene ( const QPointF & point ) const
 
QPolygonF QGraphicsItem::mapFromScene ( const QRectF & rect ) const
 
QPolygonF QGraphicsItem::mapFromScene ( const QPolygonF & polygon ) const
 
QPainterPath QGraphicsItem::mapFromScene ( const QPainterPath & path ) const
 
QPolygonF QGraphicsItem::mapFromScene ( qreal x, qreal y, qreal w, qreal h ) const
 
QPointF QGraphicsItem::mapFromScene ( qreal x, qreal y ) const
 
QRectF QGraphicsItem::mapRectFromItem ( const QGraphicsItem * item, const QRectF & rect ) const
 
QRectF QGraphicsItem::mapRectFromItem ( const QGraphicsItem * item, qreal x, qreal y, qreal w, qreal h ) const
 
。。。。。。。。。。太多了
 
转换矩阵函数
 
 
 
 
 
29、void QGraphicsItem::moveBy ( qreal dx, qreal dy )
 
向x,y方向上移动dx,dy的距离,等价于调用setPos(pos()+QpointF(dx,dy))
 
 
 
 
 
30\最重要的一个函数
 
void QGraphicsItem::paint ( QPainter * painter, const QStyleOptionGraphicsItem * option, QWidget * widget = 0 )   [pure virtual]
 
这个函数通常被view调用,然后再本地坐标系统中绘制item的内容
 
 
 
31、qreal QGraphicsItem::rotation () const
 
返回item的旋转度,
 
设置旋转度函数使用setRotation(),
 
 
 
32、qreal QGraphicsItem::scale () const
 
返回缩放系数
 
设置缩放使用setScale()
 
 
 
33、bool QGraphicsItem::sceneEvent ( QEvent * event )   [virtual protected]
 
截获事件,在事件没有分发给各个具体的事件处理函数之前。
 
相当于bool QObject::event ( QEvent * e )   [virtual]
 
 
 
34、void QGraphicsItem::scroll ( qreal dx, qreal dy, const QRectF & rect = QRectF() )
 
滚动item
 
 
 
35、在使item能响应一些事件之前,需要先使能这个功能,例如
 
void QGraphicsItem::setAcceptDrops ( bool on ) 

如果on为真,那么就使能拖拽,否则将会忽略拖拽事件。默认是不接受的
 
void QGraphicsItem::setAcceptHoverEvents ( bool enabled )
 
如果on为真,使能鼠标悬浮事件。默认不接受该事件。
 
void QGraphicsItem::setAcceptTouchEvents ( bool enabled )
 
在一些支持触摸的设备上,这个函数将使能触摸事件。
 
 
 
36、void QGraphicsItem::setEnabled ( bool enabled )
 
使该item有效,如果enabled为假,则该item不能用了
 
 
 
37、void QGraphicsItem::setGraphicsEffect ( QGraphicsEffect * effect )、
 
设置item的效果,可以用模糊效果,印象效果,给定颜色绘制效果,透明效果等
 
详细的可以参见QgraphicsEffect类
 
 
 
38、const int QGraphicsItem::Type
 
返回该item的类型值,这个值是自己通过定义Type的值,然后使用type()虚函数来返回的,看例子
 
class QGraphicsPathItem : public QAbstractGraphicsShapeItem  {   public:    enum { Type = 2 };      int type() const { return Type; }    ...  };
 
 
39、T qgraphicsitem_cast ( QGraphicsItem * item )
 
如果item是T类型的,把item转换成T类型的item,如果失败,则返回0
 
如果想让他正确的工作,需要重新实现函数type()来进行判断

posted @ 2013-05-24 11:08 米米 阅读(4128) | 评论 (0)编辑 收藏

一:qt plugin 介绍

      Qt Plugin和其他类型的插件一样,是一种计算机应用程序,它和主应用程序(host application)互相交互,以提供特定的功能。应用程序支持Plugin有许多原因,一些主要原因包括:使得第三方开发者有能力扩展应用程序,以提供无法先期预料的特色;减小应用程序的大小;由于软件版权之间的不兼容性将源代码和应用程序分享。Qt Plugin 分动态插件和静态插件两种。

二:qt plugin 创建和使用方法

Qt有两种与插件有关的API。一种用来扩展Qt本身的功能,如自定义数据库驱动,图像格式,文本编解码,自定义分格,等等,称为Higher-Level API。另一种用于应用程序的功能扩展,称为Lower-Level API。前一种是建立在后一种的基础之上的。这里讨论的是后一种,即用来扩展应用程序的Lower-level API

让应用程序支持插件扩展的步骤:

 1. 定义一个接口集(只有纯虚函数的类),用来与插件交流。

 2. 用宏Q_DECLARE_INTERFACE()将该接口告诉Qt元对象系统。

 Q_DECLARE_INTERFACE(BrushInterface,"com.trolltech.PlugAndPaint.BrushInterface/1.0")

 3. 应用程序中用QPluginLoader来装载插件。

 4. 用宏qobject_cast()来确定一个插件是否实现了接口。

 QObject *obj = new QTimer; 

 QTimer *timer = qobject_cast<QTimer *>(obj);

写一个插件的步骤:

 1. 声明插件类,该类从QObject和该插件希望实现的接口继承而来。

 2. 用宏Q_INTERFACES()将该接口告诉Qt元对象系统。

  class BasicToolsPlugin : public QObject,

                            public BrushInterface,

                            public ShapeInterface,

                            public FilterInterface

  {

       Q_OBJECT

       Q_INTERFACES(BrushInterface ShapeInterface FilterInterface)

  public:

       ...

   };

 3. 用宏Q_EXPORT_PLUGIN2()导出插件。

  Q_EXPORT_PLUGIN2 ( PluginName, ClassName )

 4. 用适当的.pro文件构建插件。

下面的代码声明了一个接口类:

 

class FilterInterface

 {

 public:

        virtual ~FilterInterface() {}

        virtual QStringList filters() const = 0;

        virtual QImage filterImage(const QString &filter, const QImage &image, QWidget* parent)=0;

  };

Q_DECLARE_INTERFACE(FilterInterface, "com.trolltech.PlugAndPaint.FilterInterface/1.0")

这里是实现该接口的插件类的定义:

 

 #include <QObject>

 #include <QStringList>

 #include <QImage>

 #include <plugandpaint/interfaces.h>

 class ExtraFiltersPlugin : public QObject, public FilterInterface

 {

 Q_OBJECT

 Q_INTERFACES(FilterInterface)

 public:

     QStringList filters() const;

     QImage filterImage(const QString &filter, const QImage &image,

     QWidget *parent);

 };

根据插件的类型不同,pro文件中配置上有不同。下面是pro文件分析:

TEMPLATE      = lib                                  // 声明为lib,动态和静态插件一样。

CONFIG       += plugin static                // 声明为plugin,带static表面为静态,否则为动态。

INCLUDEPATH += ../..

HEADERS       = basictoolsplugin.h

SOURCES       = basictoolsplugin.cpp

TARGET        = $$qtLibraryTarget(pnp_basictools)           // 指明插件的名称

DESTDIR       = ../../plugandpaint/plugins

加载插件的主应用程序默认在当前目录下的plugins文件夹中寻找可用插件,如果是动态插件,则直接放在plugins文件夹中便可,如果是静态,则需要在主应用程序的main函数的开始的地方用宏:Q_IMPORT_PLUGIN(pluginname(和pro文件中声明的一致))声明需要加载的插件并在工程配置中指明插件的lib位置。

三:基于qt plugin 技术的框架结构设想

1.      愿景

由于我们目前系统功能多,模块多,缺乏系统的整体性。我们想借助Qt Plugin技术,把各个独立的功能模块实现为一个个插件,统一在主体框架中,并能根据不同地方的用户的不同需求,在主框架中加载不同的功能模块,以实现整个系统的功能集中,体现出系统的整体性。

2.      plugin 接口

通过技术验证得出,目前我们采用动态插件,各个功能的插件实现定义的统一接口,具体功能放在插件界面中实现,此部分就像开发独立的应用程序,只是需要注意的是:

功能部分的主界面需要继承至插件界面基类:PluginWidget,插件接口中用具体的实现类指针给插件界面基类指针赋值,在加载插件的主框架中通过插件接口中定义的基类指针统一调用,利用C++动态技术动态识别具体指向的实现类。

插件界面类必须实现基类的虚函数:CreateActions()用于创建Action

创建Action需要使用基类的方法newAction创建,在此函数中加入了保存创建的Action功能。

插件接口定义如下:

class QPluginInterface

{

public:

    // 析构函数

    virtual ~QPluginInterface() {}

   

    // 插件名称

    virtual    QString    PluginName() = 0;

   

    // 插件显示在主框架中的图标文件路径

    virtual    QString PluginIconurl() = 0;

   

    // 插件提供的对外操作接口集

    virtual QList<QAction*>* Actions() = 0;

 

    // 创建插件提供的操作方法

    virtual    void CreateActions()=0;

 

    // 插件的主界面

    virtual QWidget* Widget() = 0;

protected:

    // 插件的主界面基类

    PluginWidget *pluginWidget;

};

插件界面基类定义如下:

class PluginWidget :public QMainWindow

{

    Q_OBJECT

public:

    PluginWidget(QWidget*parent=0);

    ~PluginWidget();

    QList<QAction*>* Actions();

    virtual void      CreateActions(){}

    QActionnewAction(const QIcon &icon,const QString &text,QObject*parent);

    QAction *         newAction(const QString &text,QObject*parent);

    void       AppendAction(QAction*act);

protected:

    // action链表

    QList<QAction*> *m_actlist;

};

下图是一个实现案例中各类之间的关系图:

基于QT Plugin框架结构  - yleesun - 与青春有关的日子...

3.      插件调用

插件在主框架中动态加载,目前考虑主框架基本结构是继承至QMainWindow,工具栏上显示当前加载的插件的功能键,并留有返回键可以回退到上一级。主工作区是一个QStackWidget,保存插件的界面,并把插件序号和插件对应的界面建立映射,保存在QMap<int,QWidget>中。通过序号到QStackWidget中切换界面。

下图是把DBManager做成插件加载到主框架的运行界面:

基于QT Plugin框架结构  - yleesun - 与青春有关的日子...

下图是把一个简单的绘图程序做成了插件,加载到主框架的运行界面:

基于QT Plugin框架结构  - yleesun - 与青春有关的日子...

四:总结

       目前只是通过实现两个动态插件在主框架中运行,基本算是功能性的验证,离具体实施还有很多工作需要进一步的研究,比如主框架的风格,插件的管理等等。由于本人的能力有限,可能有很多认识不够的地方,请指正。

posted @ 2013-02-22 15:10 米米 阅读(728) | 评论 (0)编辑 收藏

概念

名称 含义
View 视图 视觉化一个场景
Scene 场景 用于管理图形项
Item 图形项 具体的图形对象

 

QT的图形视图架构采用Model-View模型,Scence里面包含多个Item,可以用多个View以不同的方式观察一个Scence

功能
Scene(QGraphicsScene)

  • 添加,删除,查询图项

  • 事件分发

  • 管理图项的选择状态和焦点

View (QGraphicsView)

  • 为Scene提供一个视图接口
  • 接收鼠标键盘事件,转换后发给Scene
  • 负责View和Scence之间的坐标变换 

可以为View设置不同的ViewPort控件,比如QGLWidget来支持OpenGL,Read More!

Item (QGraphicsItem)

 

图形项的基类是QGraphicItem

基础的Feature包括:

  • 处理鼠标键盘事件
  • 拖放
  • 分组
  • 碰撞检测

每个图形项都有自己的本例坐标系统,并提供它和场景坐标的转换函数。所以可以单独旋转或缩放某个一图形项


除了pos等很少的几个函数,图形项的多数函数所处理的坐标都是它的本地坐标(例如bounding rect)

坐标系统

如上所述视图体系采用三种坐标系统:

  • 视口坐标: 对应物理坐标/设备坐标
  • 场景坐标: 对应逻辑坐标
  • 项坐标: 本地坐标

window-viewport概念

window指定了逻辑坐标的矩形范围,viewport指定了设备坐标的矩形范围,和WorldTransform(世界转换矩阵)一起,决定了逻辑坐标和设备坐标之间的转换

 

可以使用QGraphicsItemAnimation来动画一个Item,使用QTimeLine来控制时间进度

 

其它特性

Animation动画

一个Item只能有效的和一个Animation对象关联(by QGraphicsItemAnimation::setItem),因为Animation对象设置图项Item的转换矩阵时,会替换对象现有的矩阵。 所以你不能通过绑定多个Animation对象来做多重动画,但是你可以在一个Animation对象中设置多种变换效果(或者用分组的方法,分级控 制?)


QGraphicsItemAnimation直接继承自QObject,和普通的QAbstractAnimation没有关系,所以没法只用用QAnimationGroup来组装,然后串行/并行使用多个动画。

 

图形控件和布局管理

QGraphicsWidget的目的是在图形对象的功能基础上提供类似QWidget的功能。(从QGraphicsObject和 QGraphicsLayoutItem继承来),从而可以使用完整的Widget控件属性,设置字体,Style,提供size hints,以及信号槽机制等。

 

QGraphicsLayout是专门用来布局QGraphicsWidget的布局控件。你可以通过多重继承QGraphicsLayoutItem来定制QGraphicsItem使其能被QGraphicsLayout所管理

嵌入普通控件

你可以通过QGraphicsScene::addWidget来将一个普通QWidget嵌入到视图体系中来使用,本质是通过创建一个代理控件 (QGraphicsProxyWidget)来在QWidget和QGraphicsItem之间传递事件。需要注意的是,这只是一个特殊的实现方式, 对性能有要求的程序不宜使用这个机制。(没有太看出哪里对性能有很大影响)

 

QGraphicsProxyWidget能够处理复杂的控件嵌套,甚至能自动对被嵌套控件的子Popup Window控件创建代理控件。
 

QGraphicsView本身也是一个普通QWidget控件,所以可以被添加到Scene中,创建复杂的嵌套视图体系

posted @ 2013-02-06 10:38 米米 阅读(1981) | 评论 (0)编辑 收藏

 QRect rectWorkArea = qApp->desktop()->availableGeometry();
 QPoint ptLeftTop = geometry().topRight() + QPoint(2, 0);
 QSize sizeSettingsWindow = m_pSettingsWindow->size();
 if(ptLeftTop.x() + sizeSettingsWindow.width() > rectWorkArea.right())
  ptLeftTop.setX(rectWorkArea.right() - sizeSettingsWindow.width());
 m_pSettingsWindow->move(ptLeftTop);
posted @ 2013-02-06 10:00 米米 阅读(317) | 评论 (0)编辑 收藏

看《C++必知必会》比看《C++编程思想》编程思想有趣多了,《C++编程思想》是为C程序员写的,而我基本上一来就直接学的C++,因而《C++编程思想》并不适合我。而《C++必知必会》是直接从实际出发,直接点出来在编程中遇到的各种问题,因而很是实用,深得我心。  
1.数据结构 
摘要:为类型选择一个描述性名字。如果难以为这个类型命名,那就说明你还不知道你想要实现什么。 
评:很是精辟,每当我想到了这个类的名字,我就知道了如何去做和做些什么。摘要:列举类型所能执行的操作。......要避免在实现时简单地为数据成员提供一串get/set操作——那不叫做数据抽象而是懒惰且缺乏想象力的表现。 
评:不太明白,在我看来,C++中的类型仅仅是把数据按照一定的规律(类型)进行封装,其目的就是为了进行数据之间的交换。我的确不仅仅是用了一串get/set操作,但不是的操作也仅仅比get/set有一点延伸,比如按照某个条件来get/set一个或一组数据。 
 2.多态 
摘要:基类可以不知道除自身以外的任何事物。 
评:很是恰当,基类可以看成派生类的接口,派生类可以看成是基类的实现。 
 3.设计模式 
摘要:一旦设计完成后,甚至你的经理都能够理解完整的设计方案,只要他具备一些必需的模式方面的知识。 
评:以前我一直没有看过关于设计模式方面的书,也不理解什么是设计模式和为什么需要设计模式。现在,通俗的话说设计模式是程序员之间的“黑话”,它与使用的平台、语言毫无关系,它指的是你在制作程序时的一种构想或方法,而这些构造方法都是大家熟知的、经过验证的、有效的、高效率的。  
4.STL  
摘要:STL包含三大组件:容器、算法和迭代器。......STL的优秀思想体现在:容器与在容器上执行的算法之间无需彼此了解,这种戏法是通过迭代器实现的。 
评:上面已经很好的解释了什么是STL了,而且还道出了C++中的精髓,无需彼此了解,不是侵入式的设计,还象那个基类、派生类。 
 5.引用 
评:我只会把引用用在函数形参上,一般还给它加上一个const,其他的地方么,我一般使用指针。 
 6.数组形参 
摘要:数组在传入时,实质上只传入指向其首元素的指针。 
评:要小心!书中用一个准确的词“退化“来描述这种状况。不过自从我使用了vector后也就找不出什么理由使用数组了。我很高兴是这样的结果。什么,你说你一定要用多维数组,那么我说,你去死吧,我一般用一维代替多维,或者使用vector进行镶套。  
7.产量指针和指向产量的指针 
评:我背下了一个简单的方法来区别这两个。我把*和它前面的作为一组,*后面的作为一组,如const T *ptr,读后面的部分,ptr是指针,指向const T,又如T *const ptr,读为一个const指针,指向T类型。不过话说回来,我一直没有用过,也还没有想到使用的理由。  
8.指向指针的指针 
评:对这一点我一直提不起兴趣,讨厌这么复杂的冬冬。不过在实际中渐渐明白。如 vector my_circle; vector::iterator p=my_circle.begin(); for(;p!=my_circle.end();p++) {(*p)->draw();}  
9.新式转型操作符 
摘要:更丑陋,更难用,并且威力较小。 
评:看到了这一章,我心里面咯噔了一下。我的程序里面还有那么野蛮的、不讲理的转换。我得尽快用这种更丑陋的方式去改过来,以减少错误的可能性。要注意static_const和dynamic_cast;  
10.常量成员函数含义 
摘要: 
class X{  
Public: void modify_buffer(int index,int val) const //不德!!!!!! { Buffer[index] = val;} Private: int *buffer; };  
评:的确是不道德的,const的函数偷偷的改了一个数据,没有发现因为没有修改class X中的对象。我得小心了,不干这种事情。 
 11.编译器会在类中放东西 
摘要:如果一个类声明了一个或多个虚函数,那么编译器将会为该类的每一个对象插入一个指向虚函数表的指针。 
摘要:如果使用了虚拟继承,对象将会通过嵌入的指针、嵌入的偏移或其他的信息来保持对其虚基类子对象位置的跟踪。 
摘要:一个POD(“plain old data”)非常重要。比如int、double、C struct、union都是POD。摘要:如果希望复制一个类对象,那么永远都不要使用memcpy这样的标准内存块复制函数。......相反,应该使用对象的初始化或者赋值操作。 
评:看到最后这条我才明白为何有这章。看来对于高层的东西要使用高层的操作。  
12.赋值和初始化并不相同 
摘要:直截了当的说,赋值发生于当你赋值时,除此之外遇到的所有其他的复制情况均为初始化,包括声明,函数返回,参数传递以及捕俘描述异常中的初始化。 
评:突然想起鼻祖书中的话,赋值是对一个结构良好的存储区去做,而初始化是对一个未定义的存储区去做。如果要赋值,那么就确立一下赋值的对象是否还是磁盘上的荒芜地带。  
13.复制操作  
摘要:复制构造和复制赋值是两种不同的操作。  
14.函数指针 
摘要:将一个函数的地址初始化或赋值给一个指向函数的指针时,无需显示的取得函数地址。 
例子:void (*fp)(int);  
extern void h(int);  
fp = h; //OK  
fp = &h; //OK  
摘要:为了调用函数指针所指向的函数,而对指针进行解引用操作也是不必要的。 
例子:(*fp)(12); //显示调用  
fp(12); //隐式调用 
摘要:函数指针的一个传统用途是实现回调(callback)。 
评:一个很好的例子就是windows中对左右手的变化,如果改变了就只需要交换一下函数指针就OK了。  
15.指向类成员的指针并非指针 
摘要:“指向类成员的指针”即不包含地址,行为也不像指针。通常看作一个偏移量。 
摘要:指向数据成员的指针对于描述“逆变性”的概念很方便。 
摘要:存在指向基类成员的指针到指向公有派生类成员的指针隐式转换,反之不行。 
评:明白了它的作用却不知道哪里有用,完全可以换个方式使用,即使是为了上一章的回调。如果要得到类中的成员,完全可以用类的对象或指向类对象的指针获得,这个可能是为了C程序员吧。 
 tip:不能想着指向static成员,它们并不是存在于所有类对象的一个偏移量。使用的方式和普通的数据一样。  
16.指向成员函数的指针并非指针  
摘要:NULL  
17.处理函数和数组声明 
摘要: 
int *f1(); //一个返回值为int *的函数 
 int (*fp1)(); //一个指针,指向一个返回值为int的函数  
int *a1[N]; //一个具有N个int *元素的数组  
int (*ap1)[N]; //一个指针,指向一个具有N个int元素的数组 
评:够复杂吧,不过看了下面的就要。。。。。。  
摘要: int(*af2[N])(); //一个具有N个元素的数组  
//其元素类型指向返回值为int的函数指针 
评:够吓人的,虽然有解决法子可我没在意。因为老早就决定不用数组了。 
 18.函数对象 
摘要:函数对象也是一个普通的类对象,通过重载函数调用操作符()来创建类似于函数指针的东西。 
评:这可是个好东西,在泛型算法中作为谓词。 
 19.Command模式与好莱坞法则 
摘要:好莱坞法则即“不要call我们,我们会call你”。 
摘要:将一个函数对象于好莱坞法则相结合,即为Command模式的一个实例。 
摘要:好处是,函数对象可以封装数据,另一个好处是函数对象可以通过虚拟成员表现出动态行为,第三个好处是处理类层次结构而不是较为原始的,缺乏灵活性的结构(例如函数指针)。  
20.STL函数参数 
 摘要:NULL 
 21.重载与重写并不相同 
摘要:重载发生于同一个作用域内有两个或更多个函数具有相同的名字但签名不同时。 
摘要:重写发生于派生类函数和基类有相同的的名字和签名时。  
22.Template Method 模式 
摘要: Template Method(模板方式)模式和C++模板一点关系都没有。实际上,它是基类设计者为派生类设计者提供清晰指示的一种方式,这个指示就是“应该如何实现基类所规定的契约”。 
摘要:一个基类的成员函数是否应该为非虚拟的、虚拟的或纯虚拟的,这样的决策主要是基于该函数的行为如何被派生类定制。 
摘要:如果基类成员是非虚拟的,那么基类设计者就是以该基类为所确立的层次结构指明了一个不变式。派生类不应该用同名的派生类成员去隐藏基类非虚函数。 
摘要:虚函数和纯虚函数指定的操作,其实现可以由派生类通过重写机制定制。一个非纯虚函数提供了一个默认实现,并不强迫派生类一定要重写它,而一个纯虚函数则必须在具体派生类中进行重写。 
评:这一章对于我们如何定义基类有了一个很好的说明。函数是具体的实现,而我们确定如何具体的实现函数。 
 Tip:另外有一种派生类作为基类接口的形式,那里的法则就不太一样了。基类是实现,派生类是为了给其他用户的接口。  
23.名字空间  
摘要:本质上,名字空间是对全局作用域的细分。 
摘要:许多C++程序员建议将using指令放在全局作用域中,这是个馊注意。 
评:一定要很好的区分什么是using指令和using声明。  
Using namespace namespace_name //是指令  
Using anamespace_name::名字空间声明的的东西 //是声明 
如果在全局作用域中使用using指令那等于去掉了名字空间的作用域。这里一般指的是程序员自己定义的名字空间,不是默认的std。  
24.成员函数查找 
摘要:调用一个成员函数时,涉及三个步骤:第一步,编译器查找函数的名字;第二步,从可用候选者中选取最佳匹配函数;第三步,检查是否具有访问该匹配函数是权限; 
评:这里面隐含的说了一个冬冬,如果编译器找到了函数的名字,它是不会再去找的了。如果无权限范围该函数,那么就会在第三步出现编译错误。  
25.实参相依的查找 
摘要:ADL(实参相依的查找)指的是,当查找一个函数调用表达式中的函数名字时,编译器也会到“包含函数调用实参的类型”的名字空间查找。 
评:我却好像记得不止是名字空间,也包括class,因为class其实也是一种特殊的名字空间。回头还得再看一遍鼻祖的书,那里面其实都有,只是没有重点标出来而已。  
26.操作符函数查找 
摘要:当使用函数调用语法时,应用的是普通的查找规则(ADL)。而对重载操作符的中缀调用的处理机制不同。 
例子: 
class X{ X operator%(const X&)const;};  
X x,y;  
x % y; //中缀调用 
 x.operator%(y); //成员函数调用 
摘要:对于中缀操作符调用来说,编译器不仅会考虑成员操作符,也会考虑非成员操作符。 
评:说的有点让人迷糊。我想主要注意使用.operator时,记得有个默认的*this实参。  
27.能力查询 
摘要:能力查询只是偶尔需要,但它们往往被过渡使用。它们通常是糟糕设计的“指示器”。最好避免对一个对象的能力进行运行期查询。 
评:能力查询指的是对一个类对象进行dynamic_cast,来知道它是否是另外的类型,通常,是横向转换而不是普通的向上或者向下。  
28.指针比较的含义 
摘要:指针比较不是关于地址的问题,而是关于对象同一性的问题。 
摘要:一个非常重要的经验,处理指向对象的引用或指针时,必须小心避免丢失类型信息(如把指针赋值给void*指针)。 
评:一个基类的指针是与其派生类的指针==的,并不是因为地址相同而是类型相同,因为派生类就是基类,就像班长就是学生一样。这里比较的是基类的类型。  
29.虚构造函数与Prototype模式 
评:构造函数是不能虚的,而这里指的是具有这样功能的函数。如在一个类中,我们使用一个成员函数clone来调用复制构造函数new X(*this),我的经验无法告诉我为何需要,不过有一点是很明确的,这个例子证明了软件设计的“不知情”模式。  
30.Factory Method模式 
评:一个沉重的打击,我在28、29看见了什么是比较高级的构架,也让我想起了别来call我,需要时我来call你的好莱坞模式。每个类都明白自己做什么,而你只是在问它们一个很大众的问题,而不是很私人的问题。重点推荐,我得好好看看,认真感受。如果能很明确的使用,我的认识将能够上一个档次。  
31.协变返回类型 
摘要:协变的优势在于,总是可以在适当程度的抽象层工作。如果我们是处理Shape,获得一个抽象的ShapeEditor;如果在处理某种具体的形状类型,比如Circle,我们就可以直接获得CircleEditor。协变机制使得我们可以不使用类型转换操作来“重新”提供类型信息,而这种信息是一开始就不应该丢掉的。  
32.禁止复制 
摘要:访问修饰符(public、protect、private)可以用于表达和执行高级约束技术,指明一个类可以被怎样使用。这些技术中最常见的一种是不接受对象的复制操作,这是通过将其复制操作声明为private同时不为之提供定义而做到的。 
评:有时候我们应该把所有不想给其他人使用的函数全部放进private。 
 (因为后面的比较深,暂时作罢。勉强看也只能懂个浮浅的东西。未完待续)  
posted @ 2013-01-21 17:22 米米 阅读(298) | 评论 (0)编辑 收藏

一个正则表达式就是由普通字符(例如字符 a 到 z)以及特殊字符(称为元字符)组成的文字模式。该模式描述在查找文字主体时待匹配的一个或多个字符串。正则表达式作为一个模板,将某个字符模式与所搜索的字符串进行匹配。

这里有一些可能会遇到的正则表达式示例:

Visual Basic Scripting Edition VBScript 匹配
/^\[ \t]*$/ "^\[ \t]*$" 匹配一个空白行。
/\d{2}-\d{5}/ "\d{2}-\d{5}" 验证一个ID 号码是否由一个2位数字,一个连字符以及一个5位数字组成。
/<(.*)>.*<\/\1>/ "<(.*)>.*<\/\1>" 匹配一个 HTML 标记。

下表是元字符及其在正则表达式上下文中的行为的一个完整列表:

字符 描述
\ 将下一个字符标记为一个特殊字符、或一个原义字符、或一个 后向引用、或一个八进制转义符。例如,'n' 匹配字符 "n"。'\n' 匹配一个换行符。序列 '\\' 匹配 "\" 而 "\(" 则匹配 "("。
^ 匹配输入字符串的开始位置。如果设置了 RegExp 对象的 Multiline 属性,^ 也匹配 '\n' 或 '\r' 之后的位置。
$ 匹配输入字符串的结束位置。如果设置了RegExp 对象的 Multiline 属性,$ 也匹配 '\n' 或 '\r' 之前的位置。
* 匹配前面的子表达式零次或多次。例如,zo* 能匹配 "z" 以及 "zoo"。 * 等价于{0,}。
+ 匹配前面的子表达式一次或多次。例如,'zo+' 能匹配 "zo" 以及 "zoo",但不能匹配 "z"。+ 等价于 {1,}。
? 匹配前面的子表达式零次或一次。例如,"do(es)?" 可以匹配 "do" 或 "does" 中的"do" 。? 等价于 {0,1}。
{n} n 是一个非负整数。匹配确定的 n 次。例如,'o{2}' 不能匹配 "Bob" 中的 'o',但是能匹配 "food" 中的两个 o。
{n,} n 是一个非负整数。至少匹配n 次。例如,'o{2,}' 不能匹配 "Bob" 中的 'o',但能匹配 "foooood" 中的所有 o。'o{1,}' 等价于 'o+'。'o{0,}' 则等价于 'o*'。
{n,m} mn 均为非负整数,其中n <= m。最少匹配 n 次且最多匹配 m 次。刘, "o{1,3}" 将匹配 "fooooood" 中的前三个 o。'o{0,1}' 等价于 'o?'。请注意在逗号和两个数之间不能有空格。
? 当该字符紧跟在任何一个其他限制符 (*, +, ?, {n}, {n,}, {n,m}) 后面时,匹配模式是非贪婪的。非贪婪模式尽可能少的匹配所搜索的字符串,而默认的贪婪模式则尽可能多的匹配所搜索的字符串。例如,对于字符串 "oooo",'o+?' 将匹配单个 "o",而 'o+' 将匹配所有 'o'。
. 匹配除 "\n" 之外的任何单个字符。要匹配包括 '\n' 在内的任何字符,请使用象 '[.\n]' 的模式。
(pattern) 匹配pattern 并获取这一匹配。所获取的匹配可以从产生的 Matches 集合得到,在VBScript 中使用 SubMatches 集合,在Visual Basic Scripting Edition 中则使用 $0$9 属性。要匹配圆括号字符,请使用 '\(' 或 '\)'。
(?:pattern) 匹配 pattern 但不获取匹配结果,也就是说这是一个非获取匹配,不进行存储供以后使用。这在使用 "或" 字符 (|) 来组合一个模式的各个部分是很有用。例如, 'industr(?:y|ies) 就是一个比 'industry|industries' 更简略的表达式。
(?=pattern) 正向预查,在任何匹配 pattern 的字符串开始处匹配查找字符串。这是一个非获取匹配,也就是说,该匹配不需要获取供以后使用。例如, 'Windows (?=95|98|NT|2000)' 能匹配 "Windows 2000" 中的 "Windows" ,但不能匹配 "Windows 3.1" 中的 "Windows"。预查不消耗字符,也就是说,在一个匹配发生后,在最后一次匹配之后立即开始下一次匹配的搜索,而不是从包含预查的字符之后开始。
(?!pattern) 负向预查,在任何不匹配Negative lookahead matches the search string at any point where a string not matching pattern 的字符串开始处匹配查找字符串。这是一个非获取匹配,也就是说,该匹配不需要获取供以后使用。例如'Windows (?!95|98|NT|2000)' 能匹配 "Windows 3.1" 中的 "Windows",但不能匹配 "Windows 2000" 中的 "Windows"。预查不消耗字符,也就是说,在一个匹配发生后,在最后一次匹配之后立即开始下一次匹配的搜索,而不是从包含预查的字符之后开始
x|y 匹配 xy。例如,'z|food' 能匹配 "z" 或 "food"。'(z|f)ood' 则匹配 "zood" 或 "food"。
[xyz] 字符集合。匹配所包含的任意一个字符。例如, '[abc]' 可以匹配 "plain" 中的 'a'。
[^xyz] 负值字符集合。匹配未包含的任意字符。例如, '[^abc]' 可以匹配 "plain" 中的'p'。
[a-z] 字符范围。匹配指定范围内的任意字符。例如,'[a-z]' 可以匹配 'a' 到 'z' 范围内的任意小写字母字符。
[^a-z] 负值字符范围。匹配任何不在指定范围内的任意字符。例如,'[^a-z]' 可以匹配任何不在 'a' 到 'z' 范围内的任意字符。
\b 匹配一个单词边界,也就是指单词和空格间的位置。例如, 'er\b' 可以匹配"never" 中的 'er',但不能匹配 "verb" 中的 'er'。
\B 匹配非单词边界。'er\B' 能匹配 "verb" 中的 'er',但不能匹配 "never" 中的 'er'。
\cx 匹配由x指明的控制字符。例如, \cM 匹配一个 Control-M 或回车符。 x 的值必须为 A-Z 或 a-z 之一。否则,将 c 视为一个原义的 'c' 字符。
\d 匹配一个数字字符。等价于 [0-9]。
\D 匹配一个非数字字符。等价于 [^0-9]。
\f 匹配一个换页符。等价于 \x0c 和 \cL。
\n 匹配一个换行符。等价于 \x0a 和 \cJ。
\r 匹配一个回车符。等价于 \x0d 和 \cM。
\s 匹配任何空白字符,包括空格、制表符、换页符等等。等价于 [ \f\n\r\t\v]。
\S 匹配任何非空白字符。等价于 [^ \f\n\r\t\v]。
\t 匹配一个制表符。等价于 \x09 和 \cI。
\v 匹配一个垂直制表符。等价于 \x0b 和 \cK。
\w 匹配包括下划线的任何单词字符。等价于'[A-Za-z0-9_]'。
\W 匹配任何非单词字符。等价于 '[^A-Za-z0-9_]'。
\xn 匹配 n,其中 n 为十六进制转义值。十六进制转义值必须为确定的两个数字长。例如, '\x41' 匹配 "A"。'\x041' 则等价于 '\x04' & "1"。正则表达式中可以使用 ASCII 编码。.
\num 匹配 num,其中 num 是一个正整数。对所获取的匹配的引用。例如,'(.)\1' 匹配两个连续的相同字符。
\n 标识一个八进制转义值或一个后向引用。如果 \n 之前至少 n 个获取的子表达式,则 n 为后向引用。否则,如果 n 为八进制数字 (0-7),则 n 为一个八进制转义值。
\nm 标识一个八进制转义值或一个后向引用。如果 \nm 之前至少有is preceded by at least nm 个获取得子表达式,则 nm 为后向引用。如果 \nm 之前至少有 n 个获取,则 n 为一个后跟文字 m 的后向引用。如果前面的条件都不满足,若  nm 均为八进制数字 (0-7),则 \nm 将匹配八进制转义值 nm
\nml 如果 n 为八进制数字 (0-3),且 ml 均为八进制数字 (0-7),则匹配八进制转义值 nml。
\un 匹配 n,其中 n 是一个用四个十六进制数字表示的 Unicode 字符。例如, \u00A9 匹配版权符号 (?)。
posted @ 2012-10-23 11:03 米米 阅读(284) | 评论 (0)编辑 收藏

this->tableView->horizontalHeader()->setStyleSheet("QHeaderView::section {background-color:darkcyan;"
                                                       "color: black;padding-left: 4px;border: 1px solid #6c6c6c;}");


this->tableView->verticalHeader()->setStyleSheet("QHeaderView::section {background-color:darkcyan;"
                                                     "color: black;padding-left: 4px;border: 1px solid #6c6c6c;}");

 

以下好看的样式来自于http://blog.csdn.net/zenwanxin/article/details/6524559

tableView->horizontalHeader()->setStyleSheet

("QHeaderView::section {background-color: qlineargradient(x1:0, y1:0, x2:0, y2:1,"

"stop:0 #616161, stop: 0.5 yellow,stop: 0.6 green, stop:1 cyan);"
"color: white;padding-left: 4px;border: 1px solid #6c6c6c;}"
"QHeaderView::section:checked{background-color: red;}"
"QHeaderView::down-arrow {image: url(down_arrow.png);}"
"QHeaderView::up-arrow {image: url(up_arrow.png);}");

posted @ 2012-09-05 21:36 米米 阅读(5556) | 评论 (0)编辑 收藏

     摘要: 一、基础 1、说明:创建数据库 CREATE DATABASE database-name 2、说明:删除数据库 drop database dbname 3、说明:备份sql server --- 创建 备份数据的 device USE master EXEC sp_addumpdevice 'disk', 'testBack', 'c:\mssql7backup\MyNwind_1.dat...  阅读全文
posted @ 2012-08-30 09:43 米米 阅读(293) | 评论 (0)编辑 收藏

方案一:使用paintEvent
C/C++ code
void Widget::paintEvent(QPaintEvent *e)
{ QPainter painter(this);
 painter.drawPixmap(0,0,this->width(),this->height(),QPixmap(":/xxx.png"));
 }


方案二:使用setStyleSheet
C/C++ code
widget->setStyleSheet("border-image: url(:/xxx.png)");
posted @ 2012-08-28 10:56 米米 阅读(1752) | 评论 (0)编辑 收藏

仅列出标题
共8页: 1 2 3 4 5 6 7 8