时间:2016年9月5日 天气:小雨:umbrella:
Author:冬之晓:angry:
Email: 347916416@qq.com
MyAppearance:
今天又是周一了,下午快下班的时候,妈妈跟我聊天,说生活很没有意思,我回答 曰:确实没意思……无语了,终于明白为啥我自己比较消极了,原来我和我妈的想法都差 不多……
今天在进行Qt编程的时候,学习了一下如何自定义ui的样式,有时候,你在修改别人程序的时候,可能会发现别人程序中的某个控件ui设计是直接拖上去的。这种情况下如果你想增加该控件的事件处理,比如拖拽功能。不太好办,今天在书上找到一个方法,就是使用Qt设计师里面的提升,将需要增加事件响应的那个控件提升到一个类里面,然后自己实现里面相关的事件响应函数,今天我成功实现了一个QTreeWidget的提升,然后把里面的数据实现了拖拽功能,代码如下:
#ifndef NEW_QTREEWIDGET_H
#define NEW_QTREEWIDGET_H
#include <QTreeWidget>
#include <QDragEnterEvent>
class new_QTreeWidget:public QTreeWidget
{
Q_OBJECT
public:
new_QTreeWidget(QWidget *parent = 0);
protected:
void dragEnterEvent(QDragEnterEvent *e);
void dragMoveEvent(QDragMoveEvent *e);
void dropEvent(QDropEvent *e);
void mousePressEvent(QMouseEvent *e);
void mouseMoveEvent(QMouseEvent *e);
private:
QPoint _startPos;
void performDrag();
};
#endif // NEW_QTREEWIDGET_H
#include "new_qtreewidget.h"
#include <QMimeData>
#include <QDragEnterEvent>
#include <QDragMoveEvent>
#include <QDropEvent>
#include <QMouseEvent>
#include <QApplication>
#include <QDrag>
#include <QDebug>
new_QTreeWidget::new_QTreeWidget(QWidget *parent):QTreeWidget(parent)
{
setAcceptDrops(true);
}
void new_QTreeWidget::dragEnterEvent(QDragEnterEvent *e)
{
new_QTreeWidget *source = qobject_cast<new_QTreeWidget *>(e->source());
if(source)
{
e->setDropAction(Qt::MoveAction);
e->accept();
}
}
void new_QTreeWidget::dragMoveEvent(QDragMoveEvent *e)
{
new_QTreeWidget *source = qobject_cast<new_QTreeWidget *>(e->source());
if(source)
{
e->setDropAction(Qt::MoveAction);
e->accept();
}
}
void new_QTreeWidget::dropEvent(QDropEvent *e)
{
new_QTreeWidget *source = qobject_cast<new_QTreeWidget *>(e->source());
if(source)
{
new_QTreeWidget *source = qobject_cast<new_QTreeWidget *>(e->source());
if(source)
{
QTreeWidgetItem *item = this->itemAt(e->pos()); //当前位置的item
if( item == nullptr) //如果放下的位置没有item,则退出,没有这句话死机!
return;
if( -1 == this->indexOfTopLevelItem(item) && (-1 == this->indexOfTopLevelItem(currentItem())) ||
-1 != this->indexOfTopLevelItem(item) && (-1 != this->indexOfTopLevelItem(currentItem()))
) //如果“放下位置的item是顶层item,且原来的是顶层”或者“放下的不是顶层,且原来也不是顶层”
{
qDebug()<<"放下的文本是:"<<e->mimeData()->text();
item->setText(currentColumn(),e->mimeData()->text());
e->setDropAction(Qt::MoveAction);
e->accept();
}
}
}
}
void new_QTreeWidget::mousePressEvent(QMouseEvent *e)
{
if(e->button() == Qt::LeftButton)
{
_startPos = e->pos();
}
QTreeWidget::mousePressEvent(e);
}
void new_QTreeWidget::mouseMoveEvent(QMouseEvent *e)
{
if(e->buttons() & Qt::LeftButton)
{
int distance = (e->pos() - _startPos).manhattanLength();
if(distance >= QApplication::startDragDistance()) //当拖动距离大于一个推荐抖动距离时,表示同意已经拖动操作了
performDrag();
}
QTreeWidget::mouseMoveEvent(e);
}
void new_QTreeWidget::performDrag()
{
QTreeWidgetItem *item = currentItem();
int column = currentColumn();
if(item) //必须是非顶层item才可以移动数据
// if(item)
{
QMimeData *mineData = new QMimeData;
if(column != 4) //只有第四列才可以移动数据
return;
mineData->setText(item->text(column));
qDebug()<<item->text(column);
QDrag *drag = new QDrag(this);
drag->setMimeData(mineData);
drag->exec(Qt::MoveAction);
}
}
这样就解决了当界面已经设置好后如何添加功能的难题!
条款22 :将成员变量声明为private
为什么不采用public成员变量
首先,语法一致性考虑,客户唯一能访问对象的方法就是通过成员函数,客户不必考虑是否该记住使用小括号()。
其次,使用函数可以让你对成员变量的处理有更精确的控制。如果成员变量是public,每个人都可以读写它,但是如果你也函数。取得或设定其值,你就可以实现“不准访问”、“只读访问”,以及”读写访问”甚至”唯写访问“:
class AccessLevels
{
public:
int getReadOnlay() const {return readOnly;}
void setReadWrite(int value){readWrite = value;}
int getReadWrite()const {return readWrite;}
void setWriteOnly(int value){writeOnly = value;}
protected:
private:
int noAccess;
int readOnly;
int readWrite;
int writeOnly;
};
如此细微的访问控制颇为必要,因为许多成员变量应该被隐藏起来。
最后,还有封装性。如果通过函数访问成员变量,日后可改以某个计算替换这个成员变量,客户不会知道class内部实现已经起了变化。 如下面的测速程序,汽车通过,其速度被计算并填入一个速度收集器内:
class SpeedDataCollection
{
public:
void addValue(int speed); //添加一笔新数据
double averageSoFar() const; //返回平均速度
protected:
private:
};
考虑averageSoFar。做法之一是在class内设计一个成员变量,记录至今以来所有速度的平均值。当averageSoFar被调用,只返回那个成员变量就好。
另一个做法是令averageSoFar每次被调用时,重新计算平均值,此函数有权力调取收集器内的每一笔速度值。
哪一种做法更好,在内存吃紧的机器上,第一种做法会增加SpeedDataCollection对象的占用空间,在频繁需要平均值的应用程序中,内存不是重点。
所以,成员变量隐藏在函数接口的背后,可以为“所有可能的实现”提供弹性。例如这可使得成员变量被读或被写时轻松通知其他对象、可以验证class的约束条件及函数的前提和事后状态、可以在多线程环境中执行同步控制。。。等等。
封装性非常重要。如果对客户隐藏成员变量(也就是)封装,保留了日后变更实现的权力。public意味着不封装,不封装意味着不可改变
特别是被广泛使用的classes而言,因为他们最能够从“改采用一个较佳实现版本”中获益。
protected成员变量就像public成员变量一样缺乏封装性:
成员变量的封装性与“成员变量的内容改变时所破坏的代码数量”成反比,假设一个public成员变量,我们取消了它。所有使用它的客户码都会被破坏,那是一个不可知的大量。所以public成员函数完全没有封装性。假设一个protected成员变量,我们取消了它,所有使用它的derived classes都会被破坏,往往也是一个不可知的大量。
最后总结一下:
-
切记将成员变量声明为private,这可赋予客户访问数据的一致性、可细微划分访问控制、允诺约束条件获得保证,并提供class作者以充分的实现弹性
-
protected并不比public更具封装性
设计模式(七)————工厂方法模式
使用一个例子是实现一个雷锋的例子,使用工厂方法模式:定义一个用于创建对象的接口,让子类决定实例化哪一个类。工厂方法使一个类的实例延迟到其子类!
注意:它和简单工厂的区别是没有违背开放–封闭原则,简单工厂在工厂类里面通过逻辑实例话对应的类,违背了开放–封闭原则
雷锋和雷锋工厂的头文件是这样实现的:
#ifndef LEIFENG
#define LEIFENG
#include <QtDebug>
class LeiFeng
{
public:
void Sweep()
{
qDebug() <<"扫地";
}
void Wash()
{
qDebug() <<"洗衣服";
}
void BuyRice()
{
qDebug() <<"买米";
}
virtual ~LeiFeng(){}
};
class Undergraduate final:public LeiFeng
{};
class Volunteer final:public LeiFeng
{};
#endif // LEIFENG
#ifndef LEIFENGFACTORY
#define LEIFENGFACTORY
#include "leifeng.h"
class LeiFenngInterface
{
public:
virtual LeiFeng *CreateLeiFeng() = 0;
virtual ~LeiFenngInterface(){}
};
class UndergraduateFactory final:public LeiFenngInterface
{
public:
LeiFeng *CreateLeiFeng() override
{
return new Undergraduate();
}
};
class VolunteerFactory final:public LeiFenngInterface
{
public:
LeiFeng *CreateLeiFeng() override
{
return new Volunteer();
}
};
#endif // LEIFENGFACTORY
然后就可以在主函数中这样测试工厂方法模式。
#include "leifengfactory.h"
#include "leifeng.h"
#include <QtDebug>
int main(int argc , char *argv[])
{
LeiFenngInterface *factory = new UndergraduateFactory();
LeiFeng *lf = factory->CreateLeiFeng();
lf->BuyRice();
lf->Sweep();
lf->Wash();
qDebug() <<"测试完毕!";
return 0;
}