Qt信息隐藏(Q_D/Q_Q)介绍

2023-11-04

目录:

        1:基本介绍与二进制兼容

        2:二进制兼容的设计原则        

        3:常见c++/qt信息隐藏

        4Q_Q,Q_D介绍

        5:定制可编辑treewidget与如何访问基类的Private

        6:总结


1:基本介绍与二进制兼容

        作者虽然一直在linux做开发工作,        对于window平台下,软件的开发模式与稳定性及质量一直不太满意,但是对于window系统在系统兼容方面还是十分佩服的,最近拿着win95下的一款软件在win7上安装了一下,依然运行的很好,这就涉及到了程序兼容的感念,一般可以分为二进制兼容(Binarycompatible),与源代码兼容(sourcecompatible)window的开发接口winapi/mfc在这两方面做的非常好,而linux平台下的大部分软件在这方面表现很差劲,当然主要原因在于linux下的很多基本库属于不同的组织,个人或者基金会,这方面的问题已经是linux系统在桌面的普及的一大障碍,面对将近几百个发行版,而且每一个发行版一年内至少更新一次,对于普通用户来说实在是个艰难的选择。可喜的是linux的社区已经开始注意这方面的问题。

        那么什么是二进制兼容呢,二进制兼容是针对动态链接库而言,如果动态库的更新,可以兼容以来此库以前版本的程序的话,我们就说这个库是二进制兼容的(微软提供的大部分library,都在这个级别上),如果程序必须在动态库上重新编译链接的话,那么就是源代码兼容(linux平台大部分库都需要重新编译),如果源代码重新编译不了的话,...,那...太失败了,glibc/gtk++/qt/python在主要版本发生变化时都是这种情况,这里可能引起歧义,程序设计语言可以分为编译型和脚本型,下文限定到编译型语言,你可以直接理解为c/c++.

: m* Q: X& H1 {  @

2:二进制兼容的设计原则        

        此段是从kde社区的一篇文档,针对每一条,笔者并没有去测试(内容主要讲一些基本原则,那些操作可以保持二进制兼容,那些操作是禁止,库设计人员需要遵循的准则等等),如果有兴趣的话,可以从编译链接的角度来解释为什么会这样,欢迎讨论啊...

        http://techbase.kde.org/index.php?title=Policies/Binary_Compatibility_Issues_With_C%2B%2B

        基本原则:

        程序设计中,开发正角度看到的库包括头文件(.h)和lib(window下文.lib,linux下为.so)我们知道编译器链接之后呢,访问类中的元素是通过偏移量来访问,所以抽象出我们的一条原则:

        a: 不可以改变类的大小和类成员的顺序

        如果类中有虚函数的话,虚函数通过虚拟地址表来访问虚函数,这就抽象出第二条原则:

        b:如果类中没有虚函数,那么不能添加虚函数,其实也是影响类的大小的变化。

1 N) S5 ^. B9 @$ v

3:常见c++/qt信息隐藏

        做为软件开发者来说,二进制兼容会经常碰到,在c/c++中如果头文件的变化,按照make的时间戳机制,凡是原来这个头文件的源码文件都会重新编译,而依赖此库的别的库都需要重新编译。而库的实现方法改进,修正bug等等,当然会引起库的不断更新,那么如何继续保持二进制兼容呢.

        我们先来看一个基本的C++类的定义,我们以一个可编辑的treewidget为基础,一步一步介绍基本的设计原则与隐藏信息隐藏方法,最终这个treewidget要支持undo/redo/add/del/copy/paste/drag等等所有想到的功能:

        #第一个版本v1如下(下载:v1.tar.gz)假设我们需要一个支持可添加子节点,兄弟节点的treewidget

        代码如下:

        treeop.h

#ifndef _treeop_h_

6 j% W( c9 u. ?* K: d! g# Z

#define _treeop_h_

0 S( Y- B- C( g9 {* R" ~

#include <QTreeWidget>


#include <QTreeWidgetItem>

9 u; y( G! ?9 j: k( W* G) t0 j; q* p# `

#include <QAction>

2 g2 p. g2 I  A$ g

#include <QAction>


class TreeOpPrivate;


class TreeOp : publicQTreeWidget


{


    Q_OBJECT

' S  s9 J" r# L" O8 B6 s

public:


    TreeOp(QWidget *parent =NULL);


    virtual ~TreeOp();

5 _3 W) s7 _8 L" ~

    virtual QTreeWidgetItem*newTreeItem();


public slots:

: t# V( K4 e# D* N0 B/ H) @) y

    void addChild();

4 E8 c2 o  x& J9 w, `0 D% |& m1 |! I

    void addNextSlibing();


    void addPrevSlibing();


private:


    QAction *m_addChild;

6 T2 \) N% {$ l- B% {

    QAction*m_addPrevSlibing;


    QAction*m_addNextSlibing;


};


' A: ^" G  r3 ~) R

#endif

% _: y6 c0 Y$ @" W! m
源文件请下载:

+ O1 ]; m. u4 c  p/ r+ ~$ A
测试代码为main.cpp,请从附件中下载编译方法qmake&&make,下面的例子不再赘述

1 F6 d/ u; F' I7 R. V: S  Y( h
添加子节点被绑定为Alt+a,添加上一个兄弟节点为Alt+p,下一个兄弟节点为Alt+n。工作的非常好,打包发布,喝咖啡...


第二天有了反馈,用户希望再添加一个删除的功能,好,在TreeOp类中添加一个新的接口,简单,包含treeop.h的文件重新编译一次即可...等等,用户还想再添加支持节点可以moveup/movedown/moveright/moveleft的功能,OMG...,等等TreeOp类中QAction的几个成员名字起的不好,改改名字,每次都要重新编译,如果您依赖TreeOp,您也得重新编译...崩溃5分钟...,有没有好的办法呢。我们可以设计一个私有类,来保存所有的变量和操作


第二个版本:(下载v2.tar.gz )

        treeop.h代码:

#ifndef_treeop_h_

) H$ N5 Q- n% j

#define_treeop_h_


#include<QTreeWidget>


#include<QTreeWidgetItem>


#include<QAction>

0 h0 ?5 J+ W) u: L  K- j( A/ `

#include<QAction>

" y0 }  F! y. Y- m! c

classTreeOpPrivate;


classTreeOp : public QTreeWidget

$ ^# ^  z# ^! ~, c

{

1 T  {& C: r1 C2 y$ f/ K- W

    Q_OBJECT

8 ?' Q! O" B  @) ~. H) G$ V$ `

public:

, o2 m0 N9 U# v: x( c; P: @

    TreeOp(QWidget*parent = NULL);

& W5 W: u5 \# Q/ W/ {% d

    virtual~TreeOp();


    virtualQTreeWidgetItem *newTreeItem();

. A. v7 L! B2 b, w0 h+ r% c9 j3 s

private:


    TreeOpPrivate*m_p;

8 r; h/ c& F8 M; |6 V) f; }( e

};



0 y: L3 l5 f* j+ Y9 T. f

#endif

6 k  P  m3 x: N" j
从代码中可以看到,treeop.h文件已经完全简化了,我们新添加了一个类,TreeOpPrivate;

把所有可变化的量全部写道treeop.cpp文件中去,TreeOpPrivate继承了QObject类,所以可以添加slots,因为在cpp中,所以呢在treeop.cpp的文件末尾我们#include“treeop.moc”,来让Qttreeop.cpp也进行预处理。

' I1 {, M$ K# {# }' n+ i
好了现在不管用户想添加什么功能,我们都可以在private类中实现,无论如何修改,需要编译的文件只有自己,别的即使依赖treeop.h的文件也无须重新编译。世界安静了吗?...等等,再看看程序,在源程序中可以看到很多m_owner->m_p->为什么呢,m_owner->TreeOpPrivate类访问TreeOp的接口,m_p->TreeOp类访问TreeOpPrivate的接口,好麻烦,有别的方法吗,让我们看起来美观又直接,好下面我们进化到第三个版本,看看Qt中的Q_D/Q_Q

4Q_Q,Q_D介绍

假 如整个类的继承体系有点深怎么办,想用那一层的东西还要一点点的爬着去找,真麻烦! 没关系,有共享d指针呢,只需要在上一层里加个d指针就可以省去这么多麻烦了。它可以访问任意一层的私有数据(其实这样就不是private啦,叫 protected还差不多,有点不像OO),还可以获取父层的访问器。 
共享d指针实现方法如下: 
1、在基类中定义一个protected权限的d_ptr指针; 3 _2 k6 W5 v2 L1 }
2、在每个派生类中定义d_func(),获取基类d_ptr,并将其转换为当前私有类指针(派生自基类d_ptr); 6 @: r/ a/ {% q
3、在函数中使用Q_D,这样就可以使用d了; . G3 h) _! m9 C1 I7 V) e' t' t
4、在私有数据继承体系中,不要忘记将析构函数定义为虚函数,基类析构函数中释放d_ptr,以防内存泄露!!! n
5、类的派生,加上protected 构造函数,调用父类构造函数,将私有数据类传参; 
第三个版本:(下载v3.tar.gz)

        treeop.h的代码:

#ifndef_treeop_h_


#define_treeop_h_

* {1 b9 M) Y2 R7 E

#include<QTreeWidget>


#include<QTreeWidgetItem>


#include<QAction>

7 a8 o# _: z7 s- X. X: a0 j

#include<QAction>


classTreeOpPrivate;


classTreeOp : public QTreeWidget


{


    Q_OBJECT

! E% N* W" H+ m* `

public:


    TreeOp(QWidget*parent = NULL);

3 W  W% X3 C* o2 q: D& M4 F7 x

    virtual~TreeOp();

( T4 ^0 M+ `5 S4 W* ]2 C3 r

    virtualQTreeWidgetItem *newTreeItem();

: |) k# q! v5 q' V/ L* \

protected:

. Z) _  s! m) h( k7 I$ `

    TreeOpPrivate*const d_ptr;


private:


   Q_DECLARE_PRIVATE(TreeOp);


};

1 K- E5 X! F; e, E" e7 d& M0 n2 p+ E6 e

( I' ^" u4 r9 J, v

#endif        

/ O% X- {" b/ z4 P6 l3 J4 Y
我们可以发现和第二个版本相比,在类TreeOp中多了这几行

protected:

# u$ G& C, b5 _" ?( m! G5 A

    TreeOpPrivate*const d_ptr;


private:

+ A3 V" t0 X/ G0 d" Y$ V

   Q_DECLARE_PRIVATE(TreeOp);


3 j6 i1 ?$ R. \/ r( g
在类TreeOpPrivate中,多了以下几行:

    TreeOp* const q_ptr;


   Q_DECLARE_PUBLIC(TreeOp);


' E( X3 \/ c4 x1 T4 K9 p, k
想要了解一下,把这个宏拆开好了:

#defineQ_DECLARE_PRIVATE(Class) \

        inlineClass##Private* d_func() { return reinterpret_cast<Class##Private*>(d_ptr); } \- s2 F  ?/ Y+ M  i* M; F' a
        inline const Class##Private* d_func() const {return reinterpret_cast<const Class##Private *>(d_ptr); }\
        friend class Class##Private;        

这其中”##”表示的连接的意思,例如a##b,经过编译器预处理后,就成了ab.


从宏的定义可以看出,声明了两个内联的函数d_funcconstd_func(),用于返回对应的d_ptr.并且把Class##Private声明为Class的友元.

* y1 w7 b" G  Z5 w# E$ g
我们再来看看Q_D宏的展开:


#defineQ_D(Class) Class##Private * const d = d_func()

        假如我们传入TreeOp会翻译成 TreeOpPrivate *const d =d_func(); 从而就可以得到了d_ptr的指针。在来看看TreeOp的初始化:

TreeOp::TreeOp(QWidget*parent) : QTreeWidget(parent),d_ptr(new TreeOpPrivate(this))


{


    setHeaderHidden(true);

( H. ]. G7 Z' s# {0 C" _6 H$ |

}

        我们在构造函数中把d_ptr进行初始化,有的同学可能会问,这里写成这样子如何?

TreeOp::TreeOp(QWidget*parent) : QTreeWidget(parent)

{

* z7 |2 s7 V" \: {' r

            d_ptr = newTreeOpPrivate(this);

            setHeaderHidden(true);

9 q% E. a" c5 P

}         h  O" M' Y4 Z

实际编译一下就知道了,应为我们定义的d_ptrconst的,所以这样是无法编译通过的.

再来看看Q_DECLARE_PUBLIC的展开

#defineQ_DECLARE_PUBLIC(Class)  \
inlineClass* q_func() { return static_cast<Class *>(q_ptr); } \
inlineconst Class* q_func() const { return static_cast<const Class*>(q_ptr); } \

friendclass Class;7 m0 Q' Y& n/ I; X8 g
#defineQ_Q(Class) Class * const q = q_func()


可以看出和Q_DECLARE_PRIVATE差不多,可以得到q_ptr的指针。

/ K8 M, M$ ]  F/ a
同时在看看Q_Q

#defineQ_Q(Class) Class * const q = q_func()

好了差不多,经过这次处理,我们的代码看起来整洁多了,结束了吗?可能你已经想结束了,可是本教程还没呢,如果你还有兴趣,我们还要将到如何定制这个可编辑的treewidget,如何把它用到书签,目录中,如何实现engine/gui的分离,好吗,你们不是来听我罗嗦的,不过确实下面才是重点啊。

9 T2 A  a  t; E% ]4 {
第四个版本:(下载v4.tar.gz)

+ E* O/ L3 B$ N! Q4 K
在完成了上面的功能之后,我们来考虑一下,冲上一杯咖啡,点燃一根香烟,哦算了,照顾下女同志

        a:        从功能角度讲,距离我们预期的目标还很远,我们还缺少copy/paste/cut,drag/drop,undo

/redo/edit等功能

        b:        从用户角度看,基本这个widget什么都没有,实现的功能也都是快捷键,我鼠标点击那里阿,右键菜单呢,怎么保存我修改好的呢,这是谁写的阿,怎么差劲...

        c:        从使用这个widget的开发者来说,这是啥玩意阿,我要自定义QTreeWidgetItem,我中间有目录,有文件,我还要添加别的功能,我的这个item不能支持删除,也不能再添加子节点了,omg,这是谁写的垃圾阿...



哥是从来不会写出垃圾的,不过这个这个...越看越像小学生的哦...好,我们继续...

2 I% ?# M2 Z( j- u# Z7 N
我们先从使用这个widget的开发人员来看,我希望有一个完全可编辑的的treewidget,我还要可以自定义,我要可以美化这个widget,我想把它用到浏览器的书签功能中,我要做个书籍的分类,我要安排一个日程表...,我要它来记录一个公司组织架构图...

" u6 q! [% H) O. p8 U1 U
神啊,软件工程,设计模式,用户行为...,下班了,哦,不行,不实现这些功能都不准下班。


好,我们提供一个基本的treeop,这个用来处理所有的逻辑操作,但是不包含任何用户行为

我们在构造一个TreeOpWidget类,让它负责跟用户交互,如果RD不满意这个类,让他们自己写好了。


再来看看新版的treeop.h的代码:(因为里面涉及copy/paste/undo/redo/drag/drop)笔者就不详细介绍了,有不满意的地方想联系作者,看文章开头:

#ifndef_treeop_h_

$ r9 ^9 M; M: K( y1 S4 f* o

#define_treeop_h_

$ Y( N% V2 [3 Q

#include<QTreeWidget>

8 v  m  Z4 @1 g* ^

#include<QTreeWidgetItem>


#include<QAction>

7 C  x( L3 E9 X' l9 O9 H2 S8 e

#include<QAction>


classTreeOpPrivate;


classTreeOp : public QTreeWidget


{


    Q_OBJECT


public:


    TreeOp(QWidget*parent = NULL);


    virtual~TreeOp();


    voidclearUndoStack();

//清空undo

    voidundo();

//undo操作

    voidredo();

//redo操作

    voidsetEditable(bool);


    boolisEditable();


    voidsetCopyable(bool);

3 ?+ u  H' ?4 }, O# a8 l

    boolisCopyable();


    voidsetDragable(bool);


    boolisDragable();

! f3 B& \; w1 P4 N7 i

    voidsetDelable(bool);


    boolisDelable();


    voidsetUndoable(bool);


    boolisUndoable();


    boolisModified();

//设置是否可编辑,可拷贝,可拖拉,可编辑,可undo,可删除等

    voidcopy();

/ E, ~7 Y# U( X

    voidpaste();

# r! Y7 a% d# g7 X# y: t

    voidcut();

( i* @7 F5 T; Q5 J

    voidmoveUp();


    voidmoveDown();

- [3 `) Y; H% H; x2 S1 }% K

    voidmoveLeft();

7 X# w  k' @3 N3 e5 K6 g! B

    voidmoveRight();

6 `/ l$ o  k1 l0 m5 M0 X3 ]

    voiddel();

//实际操作的接口

    voidsort(bool sub);

//无视

    virtualbool canCopy(QTreeWidgetItem *item);


    virtualbool canPaste(QTreeWidgetItem *item);

. Q+ l$ |# u: T9 Y& `# w: i8 o

    virtualbool canCut(QTreeWidgetItem *item);


    virtualbool canMoveUp(QTreeWidgetItem *item);


    virtualbool canMoveDown(QTreeWidgetItem *item);

" V. L. m# V( S4 w2 u. q

    virtualbool canMoveLeft(QTreeWidgetItem *item);


    virtualbool canMoveRight(QTreeWidgetItem *item);

2 s: a% m8 K2 m3 R

    virtualbool canAddChild(QTreeWidgetItem *item);


    virtualbool canAddNextSlibing(QTreeWidgetItem *item);


    virtualbool canAddPrevSlibing(QTreeWidgetItem *item);

2 i* A+ o8 C! ^

    virtualbool canDel(QTreeWidgetItem *item);

//询问子QTreeWidgetItem,可进行这些操作吗?

    virtualQTreeWidgetItem *newTreeItem();

//创建一个新item

    virtualQTreeWidgetItem *treeItemFrom(const QString &str);


    virtualQString treeItemTo(QTreeWidgetItem *item);

//用于支持copy/paste操作,子QTreeWidgetItem需要自己实现这些功能,产生一个str,并且从strnew一个item出来

    virtualQString mimeType();

//copy/pastemimeType

    virtualvoid updateItem(QTreeWidgetItem *item,int col);

//Item被更新

    voidclearMenu();


    voidaddAction(QAction*);


    voidaddSeaparator();

//做右键菜单

    voidaddChild();


    voidaddNextSlibing();

( Y; K4 z7 u8 b

    voidaddPrevSlibing();

//添加节点

    voidrefreshSignals();

//重新刷一边使能信号

protected:


    voiddragEnterEvent(QDragEnterEvent *event);

: ?$ _5 N$ ]2 H* Q% d7 e

    voiddropEvent(QDropEvent *event);


    voidcontextMenuEvent(QContextMenuEvent *event);


signals:


    voidsigUndoEnabled(bool);

/ J1 M6 `. F' G/ N" a: X" |

    voidsigRedoEnabled(bool);


    voidsigCopyEnabled(bool);


    voidsigPasteEnabled(bool);


    voidsigCutEnabled(bool);


    voidsigDelEnabled(bool);


    voidsigMoveUpEnabled(bool);


    voidsigMoveDownEnabled(bool);

5 n  R- }0 y9 q& w! J$ P

    voidsigMoveLeftEnabled(bool);

# `1 z- ~0 p/ N" {* o8 N. _3 R

    voidsigMoveRightEnabled(bool);

+ O$ |, \4 b# g0 L3 c* r( u' t

    voidsigAddChildEnabled(bool);

; v/ u7 M- e# f7 A5 |) g: ]: a& t6 b$ d

    voidsigAddNextSlibingEnabled(bool);

8 {. y. [  y/ V" s; P$ _

    voidsigAddPrevSlibingEnabled(bool);

//操作的使能信号

protected:

# \' C# n, J+ Q

    TreeOpPrivate* const d_ptr;

8 t5 F; Z4 ^* y( G

private:

  u* I+ [5 a& I

   Q_DECLARE_PRIVATE(TreeOp);


};

; G  N& Z2 @) w' {7 Q
1 s2 M, v1 [% f5 O

#endif        

! r7 H& x3 h/ D7 E% v! ]


代码的旁边有简单的注视,我们可以把接口分为几类,

2 A# s- J% B$ s5 \) F. w
一类是使能信号,通过signal送出,表明这时候可以进行这项操作吗?例如voidsigAddChildEnabled(bool);

表示是否可进行添加子节点的操作。

; ?: W# P4 b& x
一类是虚拟类,如virtual boolcanCopy(QTreeWidgetItem *item);

用来判断这个item是否可进行copy

# S$ U7 D! g/ E0 ]% o. |3 `
一类是实际操作类,voidcopy(),就是进行copy这个动作。

5 @' f% s1 W$ p, I8 c( q3 @2 Z


我们有新添加了一个文件叫做treeopwidget.h从名字可以看出这个文件会输出一个可编辑的widget。来看看treeopwidget.h的代码

#ifndef_treeopwidget_h_


#define_treeopwidget_h_


#include<QWidget>

+ Z2 W4 ]( A# Y+ o4 K

#include<QTreeWidget>

1 g, K4 ~# k6 c( ?4 h0 h

classTreeOpWidgetPrivate;

5 _- `$ S9 w3 P: p

classTreeOp;

# O4 E/ Z3 J' u. r2 Z8 q. B1 g% r' q

classTreeOpWidget : public QWidget


{

3 O3 E8 Y9 y$ t+ [5 e7 ?

    Q_OBJECT

2 R% Z2 g- p6 u3 z+ `0 L( ?

public:

# W+ z6 `1 M/ Y6 {+ J+ i

    TreeOpWidget(TreeOp*tree = NULL,QWidget *parent = NULL);

( R+ W, r0 p. T( |+ _( v* u

    virtual~TreeOpWidget();

4 {4 T4 H% X- F

    voidshowToolbar(bool);


    TreeOp*treeWidget();


protected:

6 e& L8 i! x' e; i2 t

    TreeOpWidgetPrivate* const d_ptr;


private:


   Q_DECLARE_PRIVATE(TreeOpWidget);


};

8 J8 R3 Z& H6 }* T


#endif

8 C4 P/ N6 n% y2 K- W
; ~% \1 F$ w9 N' X  b; ^5 M

# P/ W: }5 j/ D
同样我们定义TreeOpWidgetPrivate,TreeOpWidget

+ @) r; k3 e6 |) a2 @
看构造函数:

        TreeOpWidget(TreeOp*tree = NULL,QWidget *parent = NULL);


5 P& P4 {) q, |- B: f
如果用户没有继承TreeOp的话,tree为空,那么cpp中就自己new一个出来。在treeopwidget的实现文件中,我们定义了所有的操作,和action,请参看代码:

( U6 x/ Y& _; u9 @$ D' C
2 c) L, L& X/ s3 s2 {2 E8 s4 K4 o

' u7 g& R) {# c
到这里第一步功能总算完成了,一个支持所有操作的TreeOp类,一个可以把这个导出为一个WidgetTreeOpWidget类,而且我们对TreeOpWidget的定制不会影响二进制兼容,yeah


嗯,总算有点样子了。可是这个widget好丑,那个up/down/left/rightaction我不想要怎么办,而且放在上面也难看了,一个RD想做个bookmark管理器,要求好多,不过没办法,谁让我们追求完美呢,我们进化到下一个版本


5:定制可编辑treewidget与如何访问基类的Private  (下载v5.tar.gz)

        bookmark的开发者说,那个TreeOpWidget的类挺好的,想完全抛弃它,能不能在上面改改来完成我的功能呢,比如,我想添加一个open/save的菜单到上面,用来打开和保存编辑内容,你能帮我想想办法吗,
哥哥总是心太软,心里面想你为啥不自己写一个TreeOpWidget呢,可是嘴里说没问题,我来看看,小妹妹阿...


她的需求一句话,虽然她在不同项目组中,但是她希望可以访问到TreeOpWidgetPrivate类,哦,等等,我的TreeOpWidgetPrivate类是在cpp中定义的阿,这这...,把它暴露出来,影响二进制兼容啊,不暴露,怎么使用呢?怎么办,怎么办...

/ I" a% D3 @2 O" O* K& G2 t' _% y
看看QT的源代码吧,为什么存在怎么多_p.h文件呢?...

2 @) ~! G  Q- y; h* K  y( L
既然bookmark想用到TreeOpWidgetPrivate类,那么我们就把它输出出来,但是不能暴露给别的不使用这个类的模块,好,开始...


我们有一个bookmarks.xml文件,内容如下:

<bookmarksversion="0.1">

0 A/ D% U. G% o) h# {4 E- e

    <categoryname="linux">

/ Z. G5 z8 T0 o8 m, D$ j8 W6 c

        <dataname="linxu program"/>

' T& j  Z# J; t4 e" R1 M

        <dataname="linux program"/>


        <dataname="linux program"/>

$ S# _: N  S/ l

    </category>

4 k$ _, ~4 E0 t# x8 d- k5 b

</bookmarks>

        category表示这是一个目录,可编辑,data表示这是一项数据,不可编辑。


我们现在把TreeOpWidgetPrivate的定义放到一个新建文件中,treeopwidget_p.h然后在treeopwidget.cppinclude这个文件,treeopwidget.h定义如下:

#ifndef_treeopwidget_h_

; y3 e: d+ ~$ b" G

#define_treeopwidget_h_


#include<QWidget>

" t  d" x. V$ e9 J& H, N* Q

#include<QTreeWidget>

& J) |! z7 X* }- S8 N* @

classTreeOpWidgetPrivate;


classTreeOp;

* S  y  P( {( _" Z5 J- Q5 D2 V- k

classTreeOpWidget : public QWidget

6 ?' L( _; W" {9 A

{


    Q_OBJECT

6 i) U- h. R  `( l  N, g

public:


    TreeOpWidget(TreeOp*tree = NULL,QWidget *parent = NULL);


    virtual~TreeOpWidget();

+ c. Q4 `; D. N4 K+ M! }% L

    voidshowToolbar(bool);


    TreeOp*treeWidget();

, E3 G0 w& b4 t' M$ u

protected:

' L) b4 E3 ?) b

    TreeOpWidgetPrivate* const d_ptr;


   TreeOpWidget(TreeOpWidgetPrivate&dd,QWidget *parent = NULL);

2 L* i, y- p) n, P8 }7 S0 ~

private:

1 A4 u# g& W, b3 S8 I! F

   Q_DECLARE_PRIVATE(TreeOpWidget);

$ y$ [, q& b5 W6 X

};


. y# z6 W4 g7 u, U4 Y+ m) }
& ], W8 z6 z/ [3 m4 k8 O

#endif



可以看到我们能新添加了一个接口TreeOpWidget(TreeOpWidgetPrivate&dd,QWidget *parent = NULL);

一个Protected构造接口,方便来bookmark类构造父类.

9 Y' \$ E9 A8 T% F
bookmark定义,在book/目录下bmwidget.h

#ifndef_bmwidget_h_


#define_bmwidget_h_


#include<QWidget>

4 K$ M% `6 f( L% D0 ^

#include"treeopwidget.h"

, c( }0 n& W! T! J" W7 h! Q

classBookmarksTreeItem;


classBookmarkWidgetPrivate;


classTreeOpWidget;


classBookmarkWidget : public TreeOpWidget


{


    Q_OBJECT


public:


    BookmarkWidget(QWidget*parent = NULL);


    virtual~BookmarkWidget();

protected:
 BookmarkWidgetPrivate* const d_ptr;

private:

,  Q_DECLARE_PRIVATE(BookmarkWidget);

};

* l% S$ D( P$ |7 V: M$ @4 u- i

#endif

        BookmarkWidget继承了TreeOpWidget,BookmarkWidgetPrivatebmwidget.cpp中,它继承了TreeopWidgetPrivate类,看看BookmakrWidget的构造:

BookmarkWidget::BookmarkWidget(QWidget*parent) : d_ptr(newBookmarkWidgetPrivate(this)),TreeOpWidget(*d_ptr,parent){

- K3 o( y! |8 P' \2 S. w

}

通过TreeOpWidget(TreeOpWidgetPrivate&dd,QWidget *parent = NULL);

来初始化了父类。

运行效果如图:

在最前添加了load,save接口。


6:总结

         在此教程中我们从二进制兼容开始,讲到了信息隐藏的技术,实际构建了一个可编辑的treewidget ,介绍了Q_D,Q_Q 等等,其实在window 系统我们经常会用到这种技术,最最典型的的就是processMsg(inttype,long *lParam,long*wParm), 通过lParam,wParam 这两个指针可以传递任何数据类型,在这个接口面前,本文介绍的都是浮云, 不管如何,希望你能有点体会,如果想跟作者交流.
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Qt信息隐藏(Q_D/Q_Q)介绍 的相关文章

  • QGraphicsView 和 eventFilter

    这个问题已经困扰我两天多了 所以我想我应该问一下 我在Win7上使用Qt 4 5 3 用VC2008编译 我有 MyGraphicsView 继承 QGraphicsView 和 MyFilter 继承 QObject 类 当我将 MyFi
  • QDesktopServices::openUrl 在资源管理器中选择指定文件

    在大多数编码程序中 您可以右键单击该项目 然后单击 在资源管理器中显示 它会在资源管理器中显示选定项目的文件 在 Qt 中使用 QDesktopServices 如何做到这一点 或在 QT 中执行此操作的任何方式 您可以使用此方法在 Win
  • QT/QML Android App,点击通知栏时打开应用程序

    我为 Android 应用程序制作了一个 QT 当我单击平板电脑中上面看到的按钮通知栏时 但是 如果单击通知 我的应用程序将打开 聚焦 不知道 并显示黑色窗口 简单来说怎么做呢 我的代码是 package org ays operation
  • Qt mouseReleaseEvent() 未触发?

    我有一个显示图片的库 我们称之为 PictureGLWidget 其中 class PictureGLWidget public QGLWidget 所以 PictureGLWidget 扩展了 QGLWidget 在PictureGlWi
  • QtCreator 调试暂停停在代码而不是汇编处

    如何配置 QtCreator 以便在调试并按下暂停时它会显示当前正在处理的代码 现在显示汇编 无法在任何地方找到有关此问题的答案 我使用的是 Windows 7 我在 Ubuntu 16 04 中使用 Qt Creator 4 2 2 时遇
  • 如何将图像显示为缩略图

    我有一个QTreeView显示硬盘驱动器和目录 我也有一个QListView显示图像文件如下 但我想将图像显示为缩略图 如下所示 My code mainWidget mainWidget QWidget parent QWidget pa
  • QToolButton:更改菜单位置

    使用菜单时QToolButton菜单显示在按钮的正下方 有没有办法在按钮的左侧 右侧显示菜单 我知道这个问题不久前已得到回答 但我想为此问题添加新答案 因为接受的答案不再有效 实际上 更改 QToolButton 上的菜单位置非常容易 您需
  • 更改 Qt OpenGL 窗口示例以使用 OpenGL 3.3

    我正在尝试更改 Qt OpenGL 示例以使用更现代的 opengl 版本 330 似乎合适 所以我做了 在 main cpp 上设置版本和配置文件 设置着色器版本 更改着色器以使用统一 它现在构建没有任何错误 但我只看到一个空白窗口 我错
  • 如何在 QTabWidget Qt 中展开选项卡

    我有一个QTabWidget像这个 但我想展开选项卡以 填充 整个小部件宽度 如下所示 我怎样才能做到这一点 我在用Qt 5 3 2 and Qt 创建者 3 2 1 Update 我尝试使用setExpanding功能 ui gt myT
  • 使用 QtWebEngine 将 C++ 对象暴露给 Qt 中的 Javascript

    使用 QtWebkit 可以通过以下方式将 C 对象公开给 JavascriptQWebFrame addToJavaScriptWindowObject如中所述https stackoverflow com a 20685002 5959
  • Qt 布局,在小部件大小更改后调整到最小大小

    基本上我有一个QGridLayout里面有一些小部件 最重要的是 2 个标签 我用它们将图像绘制到屏幕上 好吧 如果用户愿意 他可以更改传入图像的分辨率 从而强制标签调整大小 我们假设标签的初始大小是320x240 用户将 VideoMod
  • 在 MacOS 终端上运行 ffmpeg [关闭]

    Closed 这个问题是无关 help closed questions 目前不接受答案 我对 MacOS 相当陌生 我发现使用终端来获取信息并不容易ffmpeg和我在 Window 上一样正常运行 我有 ffmpeg 二进制文件ffmpe
  • Qt - 设置不可编辑的QComboBox的显示文本

    我想将 QComboBox 的文本设置为某些自定义文本 不在 QComboBox 的列表中 而不将此文本添加为 QComboBox 的项目 此行为可以在可编辑的 QComboBox 上实现QComboBox setEditText cons
  • 如何检测QTableView中的双击

    我正在使用 PyQt 创建 GUI 应用程序 在继承自 QTableView 的视图中 需要检测用户双击行时选择的行 该表可以排序 但不能编辑 我该怎么做 注意 尝试了 doubleClicked int 信号 它是由鼠标按钮发出的 而不是
  • 同时从多个流中捕获、最佳方法以及如何减少 CPU 使用率

    我目前正在编写一个应用程序 该应用程序将捕获大量 RTSP 流 在我的例子中为 12 个 并将其显示在 QT 小部件上 当我超过大约 6 7 个流时 问题就会出现 CPU 使用率激增并且出现明显的卡顿 我认为它不是 QT 绘制函数的原因是因
  • Qt 是否已经有了自己的 new 和 delete 运算符?

    我正在使用一个QGraphicsScene小部件并在其上显示一些点QGraphicsRectItem 这意味着要拨打很多电话new addItem 当出现时 并且removeItem delete摆脱未使用的点 当然 对于性能问题 我已经实
  • QML 连接:不推荐使用连接中隐式定义的 onFoo 属性

    升级到 Qt 5 15 时收到以下错误消息 QML Connections Implicitly defined onFoo properties in Connections are deprecated Use this syntax
  • Mac 上的 Qt — 如何访问帮助菜单中的“搜索”

    我正在将我的 Qt 应用程序移植到 Mac OS X 在 Windows Linux 上 我使用 QLineEdit 在帮助菜单中提供搜索功能 In Mac OS X I saw something like this is built i
  • 静态变量中的 qt tr()

    我在 qt 中的翻译方面遇到问题 我的项目中的所有翻译都工作正常 但有一个翻译位于类的静态变量中 相应部分代码如下 头文件类似于这样 typedef struct int type QString problematicString inf
  • 是否可以在 Qt Creator 中将 Qt 样式表与升级的小部件一起使用?

    我正在尝试使用 Qt 样式表对标准小部件进行一些重大的重新设计 因此 在为不同的小部件手动完成大部分工作之后 objectName选择器 我决定以某种方式对类似的小部件进行分组 例如我有多个QFrames其作用类似于内部表单中的标题 我希望

随机推荐

  • 【Linux】RPM软件包和Yum软件仓库、apt

    RPM 简介 RPM Package Manager RPM 包管理器 由红帽公司提出 Redhat SUSE 等系列采用 建立集中数据库 记录软件包安装 卸载等变化信息 分析软件包依赖关系 RPM 包 文件名特征 软件名 版本信息 操作系
  • 10种简单的Java性能优化学习

    10种简单的Java性能优化学习 你是否正打算优化hashCode 方法 是否想要绕开正则表达式 Lukas Eder介绍了很多简单方便的性能优化小贴士以及扩展程序性能的技巧 最近 全网域 Web Scale 一词被炒得火热 人们也正在通过
  • 多层目录编译的makefile文件编写

    一直想自己做一个目录结构稍复杂一点的makefile 看make的manual上写的有点不好理解 再从网上搜搜也没有发现比较好的教程 我是个搞工程的 懂点计算 也没有受过专业编程训练 对于一些技术不是很懂 有时想使用也会不知到如何下手 找不
  • Mysql 管理

    目录 0 课程视频 1 系统数据库 gt 安装完mysql gt 自带四个数据库 2 常用工具 gt 写脚本用 2 1 mysql 客户端工具 2 2 mysqladmin 2 3 mysqlbinlog gt 二进制日志 gt 运维讲解
  • UI架构相关

  • 锐捷网管系统漏洞利用分析

    利用此漏洞可获取后台密码 现在复现 1 fofa搜索 title RG UAC登录页面 body admin 2 现在 查看源代码 向下拉看到这个 就是密码 3 MD5解密 登录成功
  • SpringBoot配置双数据源(一个项目同时连接操作两台数据库)

    前言 近期公司要上线3 0版本 需要将2 0的数据迁移到3 0中继续沿用 由于3 0的数据库相比2 0 的数据库改动很大 最主要的是2 0数据库的主键为自然数自增主键 而3 0数据库的主键为UUID2 所以只能使用程序动态迁移 声明 本教程
  • ROS中的Navigation

    ROS中的Navigation 1 Navigation Stack的核心就是move base 2 move base的三个功能 全局规划 静态 局部规划 动态 处理异常行为 有三个接口 3 costmap 代价地图 是move base
  • 第三次C语言课程设计作业

    上节课我们学习了文件型指针的运用 这节课我们则学习了变量型指针和链表的学习的使用 还了解了free函数 malloc函数的应用 指向结构体变量的指针变量的定义形式与一般指针变量的定义形式相同 只是将其指向类型定义为结构体类型即可 例如 st
  • Eclipse 常用快捷键

    常用的 编辑 Ctrl 1 快速修复 解决很多问题 比如import类 try catch包围等 Ctrl Shift F 格式化当前代码 Ctrl Shift M 添加类的import导入 Ctrl Shift O 组织类的import导
  • 时间序列分析之ARIMA模型预测__R篇

    相关文章 时间序列分析之ARIMA模型预测 SAS篇 之前一直用SAS做ARIMA模型预测 今天尝试用了一下R 发现灵活度更高 结果输出也更直观 现在记录一下如何用R分析ARIMA模型 1 处理数据 1 1 导入forecast包 fore
  • 使用Hutool的流方式读取Excel大文件

    官网介绍 在标准的ExcelReader中 如果数据量较大 读取Excel会非常缓慢 并有可能造成内存溢出 因此针对大数据量的Excel Hutool封装了Sax模式的读取方式 Excel07SaxReader支持Excel2007格式的S
  • Unity Notes之控制粒子系统的最大粒子数量

    Unity中的粒子系统使用起来还是比较方便的 不过在实际过程中遇到这样的一个问题 需要动态的来控制一个粒子系统组件所能产生的最大粒子数 看doc上说是有maxParticles来控制 但实际上却没有这个开放的参数 只能通过其它的方式来实现
  • STM32F103RBT6型号说明

    STM32系列专为要求高性能 低成本 低功耗的嵌入式应用设计的ARMCortex M0 M0 M3 M4和M7内核 主流产品 STM32F0 STM32F1 STM32F3 超低功耗产品 STM32L0 STM32L1 STM32L4 ST
  • 在Idea中使用控制台,显示文件

    在Idea中使用控制台 显示文件 前言 在IDEA创建项目时 IDEA会自动帮你生成一个存放 class文件的地方 就是在out目录下 而可执行的代码都在src目录下 可以在src目录下创建packet 包 可以把Java作业放在一个 Pr
  • Zookeeper学习笔记四之持久节点和临时节点

    持久节点和临时节点 znode节点可以是持久 persistent 节点 还可以是临时 ephemeral 节点 持久节点node 如 path 只能通过delete命令进行删除 而临时节点相反 当创建临时节点的客户端崩溃或者关闭了与Zoo
  • CAD导出DXF再导入PADS出现尺寸大小不一致的解决办法?

    如下错误 1 用CAD打开板框 删除板框多余部分 只留需要的部分 注意 从左边拖动鼠标选中 再删除 如果从右边拖动鼠标选中无法全选 2 设置原点 选中整个板框 输入命令m 点击最左下脚 输入坐标0 0设置为原点 此时板框完全不见了 双击滚轮
  • 制作Centos7自动安装镜像(三)

    文章总览 制作Centos7自动安装镜像 在这里说明一下 我们制作自动化安装镜像用的是linux的kickstart技术 这个技术的核心是制作一个ks cfg文件 将所有需要自动化安装的内容写成一个脚本 放置在镜像中 并在安装菜单中指定这个
  • 文件I/O和标准I/O的区别

    文件I O 文件I O是操作系统提供的操作文件的API 例如Linux中的open 和write 等 这些函数可以完成文件的操作 但是效率不一定很高 标准I O 标准I O是应用层提供的库函数 例如C语言提供的文件操作函数fopen fcl
  • Qt信息隐藏(Q_D/Q_Q)介绍

    目录 1 基本介绍与二进制兼容 2 二进制兼容的设计原则 3 常见c qt信息隐藏 4 Q Q Q D介绍 5 定制可编辑treewidget与如何访问基类的Private 6 总结 1 基本介绍与二进制兼容 作者虽然一直在linux做开发