Essential Qt 第十六章 元素/场景

来源:互联网 发布:java项目开发小 组长 编辑:程序博客网 时间:2024/06/05 18:39

        对于一般的窗体/图像的修改,可以使用前一章介绍的绘图以及绘图事件,但这种绘制通常用于比较少量且较为简单的绘制,对于需要大量并且复杂的图像绘制会往往会先的力不从心,下面是一张来自Qt自带的例子的截图


          这是Qt一个有关图表制作的例子,类似上面的图像比较复制,对于这样的情况使用绘图来直接绘制会非常的繁琐,有时候甚至会无法完成,对于这样的情况,Qt提供了另一种解决绘图的方案------元素/场景,上面的图片是来自于Qt用于制作图表的强大模块Qt Charts,该模块正是基于元素/场景的,有兴趣的可以在文档中查询Qt Charts模块的全部例子,从这些例子感受就可以感受下Qt Charts的强大,而这个强大模块正是基于元素/场景

          说明:Qt Charts模块在Qt5.7以前是需要付费使用的,直到5.7版本才成为免费的,另外Qt在安装是默认不安装Qt Charts模块,如果没有安装该模块,则在文档中就无法查询到相关的例子


          元素/场景结构最基本的是由3个类来构成,QGraphicsScen(地图),QGraphicsView(显示器),QGraphicsItem(元素),括号内的中文名是我起的,只是为了方便描述问题。首先看下这三个类的基本工作原理,QGraphicsScene通常被称作场景,有点类似游戏里的地图,其大小是无限的,他有个非常重要的函数setSceneRect(),该函数用于制定显示的区域,而QGraphicsView则通过setScene(QGraphicsScene×)来显示,对于有过游戏制作经验的人来说这样的设定很熟悉,但对于有些人可能会感到很陌生,所以我画了一张草图来显示这两者的关系


        上面的黑色网格便是QGraphicsScene,他最大的特点就是无限大,而蓝色的方框就是QGraphicsView,通过setscene()负责显示场景的某个部分,而这个部分则是QgraphicsScene通过setSceneRect()来决定的,而我们能看到的就是QGraphicsView显示的那个部分(蓝色虚线区域),然后需要做的是往场景上添加各种元素,比如可以想这个场景里添加一些带颜色的形状


              在上面添加了一个黑色三角,一个红色的L型区域和一个黄色的三角,程序里将会显示一个L型的红色区域和一个黑色区域(黑色三角的一部分),而黑色三角的另一部分和黄色的三角无法看见,应为他们不在view的显示范围内。喜欢玩游戏的话对于这样的设定不会感到陌生,QGraphicsScen相当于游戏的地图(当然大部分游戏的地图大小会是有限的),而QGraphicsView相当于显示器,显示器可以显示一个制定的区域,而QGraphicsItem则是地图上的各种元素,英雄,小兵,箭塔等等。

              总结下,QGraphicsView的继承自QWidget,一般看到的就是他,而QGraphicsScene继承自QObject所以配合QGraphicsView才能显示,而QGraphicsItem是一个虚基类,并且他没有继承自QObject,所以没有办法使用信号与槽,一般情况下我们都使用他的子类,只有提供的子类不能满足需要,才会直接继承QGraphicsItem类

              然后是一个使用项/视图的简单例子,这个例子如下图

    

              这个程序可以用于演示Qt的类之间的继承关系,程序有两个功能,第一,当鼠标点击程序一个位置时,就会提示你输入一段文字,输入完成后这段文字就会显示在鼠标点击的位置,第二,鼠标点击一段文字不放,移动到另一段文字在松开(鼠标的拖动),鼠标点击和松开的位置直接就会有条线,这样就可以把文字连接起来

            先看下主程序的构造函数

QtNote::QtNote(QWidget *parent)    : QDialog(parent){    map_GraphicsScene = new NoteScene;  //这个NoteScene是自定义的类,继承自QgraphicsScene    view_GraphicsView = new QGraphicsView;    map_GraphicsScene->setSceneRect(0,0,1200,1200);   //设置显示区域的大小    view_GraphicsView->setScene(map_GraphicsScene);    QHBoxLayout* main_Layout = new QHBoxLayout;    main_Layout->addWidget(view_GraphicsView);    setLayout(main_Layout);    main_Layout->setSizeConstraint(QLayout::SetFixedSize);    connect(map_GraphicsScene,SIGNAL(clickMap(QPointF)),this,SLOT(addNewText(QPointF)));  // 实现程序的两个功能的函数    connect(map_GraphicsScene,SIGNAL(makeLine(QPointF,QPointF)),this,SLOT(addNewLine(QPointF,QPointF)));}
          从代码上看,当鼠标点击程序某个位置是,以及鼠标移动的时候,自定义的类NoteScene都会发射信号,clickMap(QPointF)信号在鼠标点击时候发射,参数是鼠标点击的坐标,makeLine(QPointF,QPointF)则是鼠标拖动后发射,参数是鼠标拖动的起始-终止坐标;然后是两个对应函数的实现,首先是添加文字

void QtNote::addNewText(QPointF p){    QString txt = QInputDialog::getText(this,tr("New Name"),tr("Please Input New Name"));    if(txt.isEmpty())   //如果没有输入,则该函数什么都不做        return;    QGraphicsTextItem* newItem = new QGraphicsTextItem(txt);    QFont ft;    ft.setPointSize(20);    newItem->setFont(ft);    newItem->setDefaultTextColor(QColor(Qt::green));  //通过设置QPen来设置字体的颜色和大小    map_GraphicsScene->addItem(newItem);   //把元素添加到场景中    newItem->setPos(p);  //把元素移动到鼠标点击的位置}
        这里使用了QGraphicsTextItem来作为元素,他完全能满足程序的需要,所以没必要继承QGraphicsIem来自定义一个类了,然后是添加线条的函数

void QtNote::addNewLine(QPointF headP , QPointF tailP){    QGraphicsLineItem* newLine = new QGraphicsLineItem(headP.x(),headP.y(),tailP.x(),tailP.y());    map_GraphicsScene->addItem(newLine);    QPen p;    p.setColor(Qt::yellow);    p.setWidth(3);    newLine->setPen(p);    newLine->setPos(0,0);  //这里坐标设为0,0}
         这里需要说明的是坐标的变换,参数是鼠标的起始-终止位置的坐标,这个坐标是 QGraphicsScene的坐标,假如鼠标起始坐标是(5,5),终止是(20,30),为了在这两点之间画一条线,可以使用QGraphicsLineItem(5,5,20,20),然后把这条线移动到场景的(0,0)位置,这样这条线就会出现在(5,5)到(20,30)的位置上
         接下来就是自定义的 NoteScene类了,这个类继承自QGraphicsScene,他的作用就是在鼠标点击和拖动是正确的发射信号,而这两个信号是在他的鼠标点击以及松开事件中完成的,首先看下他的鼠标点击事件函数

void NoteScene::mousePressEvent(QGraphicsSceneMouseEvent* event){    QPointF p = event->scenePos();    if(itemAt(p,QTransform()) == 0)  //判断是不是点击到文本了        emit clickMap(p);    lastPoint_PointF = p;  //保存本次点击的坐标,    QGraphicsScene::mousePressEvent(event);}
          这个程序有一点需要注意的是当点击到文本的时候就不会弹出文本输入对话,框即不会发射信 号,因为一方面同一个位置只能显示一个文本放置重叠显示,另外防止和文本之间拖动时候的鼠标点击相冲突,这个类有个类成员lasPoint_Point用于保存本次点击的坐标
          然后是鼠标松开事件函数

void NoteScene::mouseReleaseEvent(QGraphicsSceneMouseEvent* event){    QPointF lp = lastPoint_PointF;    QPointF p = event->scenePos();    if(abs(lp.x()-p.x()) >= 10 || abs(lp.y()-p.y())》=10)  //发射前先确定下,鼠标松开的坐标和鼠标点击的坐标之间的距离必须足够大,否则认为ibenci操作是无效操作        emit makeLine(lp,p);    QGraphicsScene::mouseReleaseEvent(event);}




原创粉丝点击