存档在 2014年4月

远程手机与本地PC通信

2014年4月30日

今晚和工作室的童鞋谈到手机在远程拍照,然后将图片传回到本地的控制台。就我理解,必须得需要第三方的公网服务器(当然,局域网测试则不需要公网)参与,大概流程如下:

手机拍照—->上传到服务器——>
本地控制台定时向服务器查询—->(如果有新图片)——–>连接到服务器并下载图片。

当然会谈到P2P,在电脑上,由第三方的公网服务器参与协调,可以实现P2P通信,也少不了这个关键的第三方公网服务器(局域网测试则不需要公网)。正如QQ的通信模式,互传文件的双方通过服务器协调后,建立直接连接实现P2P通信。而手机则不能,因为就从我使用手机的经历来讲,手机联网接入到internet与电脑接入到intenet很不相同,毕竟手机的网络掌控在运营商那里,并且手机好像也未分配IP地址,而是由基站来进行下发。

综上,我个人觉得要远程手机与本地PC传图片,必须得用到第三方公网服务器,而且必须是手机–>服务器–>本地PC这个过程,而不能实现手机—>本地PC的P2P通信。

五一快乐 & 做自己的事

2014年4月29日

晃眼,五一到了,日子过得真快。这一个月,我终于找回到以前的那种努力状态,这么做不是为了追求什么,仅仅满足我心里一种学习的强烈欲望,人生很长,而我的人生才开始,但愿到时候回头想想,年轻时我也算一个风云人物,能从自己身上发现积淀的更多知识。

有时候觉得人很渺小,很多事情不能如自己所想就有所发生。人需要低调,需要能够静下心来做事,做好每一件在别人眼里觉得可笑,甚至自己都觉得没意义的事情,我愿意做自己以前不愿意做的事情,我不是宠儿,我是最最普通的一个人,我是新手,我是从零开始,这能磨砺我的意志。

希望随时都有好消息降临。

Creater/文子 于2014_04_29_21_20_389

Qt_Mysql异步单线程多任务类

2014年4月28日

在QT使用mysql时,函数都为同步,往往使得界面卡死或者延误其他事情,经过思考,花了一晚上时间实现了使用一个单独线程来执行sql任务的类,在执行完成后异步回调。使用两个链表来维护所有mysql操作,一个经常使用指令队列,一个很少使用指令队列。针对每个指令操作时间较短,设计了一个线程实现类似线程池效果

由于这些操作是在两个线程中完成的,而且信号槽队列中的数据类型必须是系统能识别的元类型,而我的代码中使用了QVector作为信号和槽函数的参数。在线程之间传递signal与在一个线程内传递signal是不一样的。在一个线程内传递signal时,emit语句会直接调用所有连接的slot并等待到所有slot被处理完;在线程之间传递signal时,slot会被放到队列中(queue),而emit这个signal后会马上返回。

默认情况,线程之间使用queue机制,而线程内使用direct机制,为了能在线程之间使用自定义的类型作为信号参数,有以下两种解决办法:

1.在connect的第五个参数设置为Qt::DirectConnection直接发送(有弊端),违背了异步的主旨,修改connect如下:

    
 typedef QVector<QPointF> MyVector;
/*   */
connect(&sqlthread,SIGNAL(getResult(MyVector&, int)),
              this,SLOT(mydata(MyVector&, int)), Qt::DirectConnection);

2.使用qRegisterMetaType()进行注册
在定义QVector这个模板类型的地方,进行以下声明。

typedef QVector<QPointF> MyVector;
Q_DECLARE_METATYPE(MyVector)

在主函数main中,进行注册

    qRegisterMetaType<MyVector>("MyVector");
    qRegisterMetaType<MyVector>("MyVector&");

以下为类SQLThread的部分源代码

下面给出SQLThread 类的部分源代码,欢迎提出意见与修改建议。
sqlthread.h

#ifndef SQL_THREAD_H
#define SQL_THREAD_H

#include "sql.h"
#include <QThread>
#include <QSemaphore>
#include <QVector>
#include <QMetaType>

class SQLThread : public QThread
{
    Q_OBJECT
public:
    enum SQLTasks
    {
        //连接数据库
        ConnectDb,
        //创建表
        CreatTable,
        //插入数据
        InsertData,
        //查询数据
        SelectData,
        NTasks //3
    };
    //很少使用数据队列
    struct SQLDataRarelyUsed
    {
        /*!
         *该构造函数默认为建表指令或者查询指令,由t区分
         */
        SQLDataRarelyUsed(QString table_name,
                          SQLTasks t): name(table_name), task(t)
        {
        }

        /*!
         *该构造函数默认为连接数据库指令
         */
        SQLDataRarelyUsed(QString db_name,
                          QString user_, QString pass_, SQLTasks t):
            name(db_name), user(user_), passwd(pass_), task(t)
        {
        }

        /*!
         * 用于连接数据库用,用于建表用,用于查询表用
         */
        QString name; //db_name,table_name
        /*!
         * 用于连接数据库用
         */
        QString user;
        QString passwd;
        SQLThread::SQLTasks task;
    };
    //常使用的插入指令队列
    struct SQLDataFrequentUsed
    {
        /*!
         * 构造插入数据指令
         */
        SQLDataFrequentUsed(QString table_name, long id_, float data_):
            table(table_name), id(id_), data(data_){}
        QString table;
        long id;
        float data;
    };

    SQLThread();
    virtual ~SQLThread();
    void run();

    //外部调用,连接到数据库
    void connectDb(QString, QString, QString );
    //外部调用,创建数据表
    void creatTable(QString);
    //外部调用,向表中插入数据
    void insertData(QString, long, float);
    //外部调用,查询表
    void selectTable(QString);



private:
    SQL sql;

    QSemaphore freeSemaphore;
    QSemaphore usedSemaphore;
    QList<SQLDataRarelyUsed> rarelyUsed;
    QList<SQLDataFrequentUsed> frequentUsed;

    //用于查询数据时返回数据条数
    int cnt;
    bool exitFlag;

Q_SIGNALS:
    //异步消息分发
     void openError(QString);
     //异步获取查询表的结果
     void getResult(MyVector&, int);
};

#endif

sqlthread.cpp

#include "sqlthread.h"
#include "sql.h"
SQLThread::~SQLThread()
{
    exitFlag = true;
    usedSemaphore.release();
    freeSemaphore.release();
    wait();
}
SQLThread::SQLThread() :
    freeSemaphore(10), usedSemaphore(0)
{
    //10 > 10 + 3
    exitFlag = false;
    rarelyUsed.reserve(3);
    frequentUsed.reserve(10);
}

void SQLThread::run()
{
    bool haveError = false;
    int tryCnt = 3;
    while(true)
    {
        while(rarelyUsed.empty() && frequentUsed.empty())
        {
            usedSemaphore.acquire();
            if(exitFlag)
            {
                rarelyUsed.clear();
                frequentUsed.clear();
                return;
            }
        }

        if(!rarelyUsed.empty())
        {
           // usedSemaphore.acquire();
            //连接数据库
            if( rarelyUsed.front().task == ConnectDb)
            {
                 sql.setDb("test", rarelyUsed.front().user,
                           rarelyUsed.front().passwd);
                 if(!sql.openDb())
                 {
                    emit openError("Can not open the MYSQL database, please cheack...");
                    if(++tryCnt <= 3)
                    {
                        connectDb("test", rarelyUsed.front().user,
                                  rarelyUsed.front().passwd);
                    }
                    else
                        haveError = true;
                    break;
                 }

                 if(!sql.createDb(rarelyUsed.front().name))
                 {
                    emit openError("Create database failure....");
                    break;
                 }

                 if(!sql.changeDb(rarelyUsed.front().name))
                 {
                     emit openError("Change database failure....");
                     break;
                 }

            }
            else if( rarelyUsed.front().task == CreatTable)
            {
                if(! sql.createTable(rarelyUsed.front().name))
                {
                    emit openError("Create table failure....");
                    break;
                }
            }
             else if( rarelyUsed.front().task == SelectData)
            {

                emit getResult(sql.selectTable(rarelyUsed.front().name, cnt), cnt);
            }
            rarelyUsed.pop_front();
            freeSemaphore.release();
            continue;
        }
        //处理第二队列
        if(!frequentUsed.empty())
        {
          //  usedSemaphore.acquire();
            sql.insertData(frequentUsed.front().table,
                           frequentUsed.front().id, frequentUsed.front().data);
            frequentUsed.pop_front();
            freeSemaphore.release();
        }
    }
}

//连接数据库
void SQLThread::connectDb(QString db_name, QString user, QString passwd)
{
    freeSemaphore.acquire();
    rarelyUsed.push_back(SQLDataRarelyUsed(db_name,
                                           user, passwd, SQLThread::ConnectDb));
    usedSemaphore.release();

}
//创建表
void SQLThread::creatTable(QString table_)
{
    freeSemaphore.acquire();
    rarelyUsed.push_back(SQLDataRarelyUsed(table_,
                                           SQLThread::CreatTable));
    usedSemaphore.release();

}
void SQLThread::insertData(QString table_, long time_, float data_)
{
    freeSemaphore.acquire();
    frequentUsed.push_back(SQLDataFrequentUsed(
                               table_, time_, data_));
    usedSemaphore.release();

}
void SQLThread::selectTable(QString table_)
{
    freeSemaphore.acquire();
    rarelyUsed.push_back(SQLDataRarelyUsed(table_,
                                           SQLThread::SelectData));
    usedSemaphore.release();
}

一个循环队列帧完整性匹配算法

2014年4月27日

今天早上思考了下关于循环队列中帧匹配的问题,以前在其他项目中,都是用了两个线程的单生产者-单消费者模式,但是有一定的弊端,那就是效率相对来说不是太高,而且线程同步也相对复杂。以下是今天早上思考的结论,算法时间复杂度O(n),而且上锁,解锁的次数大幅度降低。

看似一段简单的代码,个人觉得还是比较有技巧的,重点在于每次读取两个字节,而只处理一个字节。以帧头FB,FD,帧尾为FC,FE(都为16进制)为例,通过调整参数door,越大则及时性不足,越小则消耗更多时间。




const int FRAME_LENGTH = 11;
const char HEAD_FIRST = 0xfb;
const char HEAD_SECOND = 0xfd;
const char TAIL_FIRST = 0xfc;
const char TAIL_SECOND = 0xfe;
const int DOOR = FRAME_LENGTH ;
void run()
{
	bool bstart;
	int cnt;
	char tmp[9];
	char buff[2];
	while(true)
	{
		mutex.lock();
		while(cir.size() <= DOOR )
			con.wait(&mutex);

		if(!bstart)
		{
			while(true)
			{
				if(cir.size() < 2) continue;
				cir.get(buff, 2);
				if(!(buff[0] ^ HEAD_FIRST) && !(buff[1] ^ HEAD_SECOND))
				{
					bstart = true;
					cnt = 0;
					cir.remove(2);
					break;
				}
				else					
					cir.remove(1);
			}
		}
		else
		{
			while(true)
			{
				if(cir.size() < 2) continue;
				cir.get(buff,2);
				if(!(buff[0] ^ TAIL_FIRST) && !(buff[1] ^ TAIL_SECOND))
				{
					/*至此,检索到一个完整的有效数据序列,并保存在tmp中*/
					bstart = false;
					cnt = 0;
					cir.remove(2);
					break;
				}
				else
				{
					cir.remove(1);
					tmp[cnt++] = buff[0];
				}

			}
		}
		mutex.unlock();
				
	}


}

QString< >QDateTime

2014年4月25日

主要使用下面4个API即可。

QString toString ( const QString & format ) const
QString toString ( Qt::DateFormat format = Qt::TextDate ) const
QDate fromString ( const QString & string, Qt::DateFormat format = Qt::TextDate )
QDate fromString ( const QString & string, const QString & format )

特别是在使用数据库时,Qt的时间需要与数据库定义的时间格式相同才能互用。
1.QDateTime——>QString

QString strBuffer;
QDateTime time;
time = QDateTime::currentDateTime();
strBuffer = time.toString("yyyy-MM-dd hh:mm:ss");
// strBuffer = 2010-07-02 17:35:00

2.QString——>QDateTime
使用静态函数

QString strBuffer;
QDateTime time;
strBuffer = "2010-07-02 17:35:00";
time = QDateTime::fromString(strBuffer, "yyyy-MM-dd hh:mm:ss");

设置圆角窗口

2014年4月23日
QPainterPath path;
QRectF rect = QRectF(this->rect());
path.addRoundRect(rect,15,15);
QPolygon polygon= path.toFillPolygon().toPolygon();//获得这个路径上的所有的点
QRegion region(polygon);//根据这些点构造这个区域
setMask(region);

QwtPlotCurve::PaintAttribute解析

2014年4月21日

这个属性是为了修正画图算法用,默认的是使用了ClipPolygons和FilterPoints

ClipPolygons
Clip polygons before painting them. In situations, where points are far outside the visible area (f.e when zooming deep) this might be a substantial improvement for the painting performance
//在画图之前对不在可视区的点进行裁剪

FilterPoints
Tries to reduce the data that has to be painted, by sorting out duplicates, or paintings outside the visible area. Might have a notable impact on curves with many close points. Only a couple of very basic filtering algorithms are implemented.
//进行点滤波,减少点的数目

MinimizeMemory
Minimize memory usage that is temporarily needed for the translated points, before they get painted. This might slow down the performance of painting
//最小化内存消耗,意味着一些临时在内存中的数据将被清除,将会影响性能

ImageBuffer
Render the points to a temporary image and paint the image. This is a very special optimization for Dots style, when having a huge amount of points. With a reasonable number of points QPainter::drawPoints() will be faster.
//渲染一些点到临时图片中,提高性能

Qwt定制离散线与连续线

2014年4月21日

因为QwtPlotCurve里有如下定义

  enum CurveStyle
    {
        /*!Don't draw a curve. Note: This doesn't affect the symbols.*/
        NoCurve = -1,
        Lines,
        Sticks,
        Steps,
        Dots,
        UserCurve = 100
    };

为了能够定制曲线,需要重载以下函数,在QwtPlotCurve里的实现为

void QwtPlotCurve::drawCurve( QPainter *painter, int style,
    const QwtScaleMap &xMap, const QwtScaleMap &yMap,
    const QRectF &canvasRect, int from, int to ) const
{
    switch ( style )
    {
        case Lines:
            if ( testCurveAttribute( Fitted ) )
            {
                // we always need the complete
                // curve for fitting
                from = 0;
                to = dataSize() - 1;
            }
            drawLines( painter, xMap, yMap, canvasRect, from, to );
            break;
        case Sticks:
            drawSticks( painter, xMap, yMap, canvasRect, from, to );
            break;
        case Steps:
            drawSteps( painter, xMap, yMap, canvasRect, from, to );
            break;
        case Dots:
            drawDots( painter, xMap, yMap, canvasRect, from, to );
            break;
        case NoCurve:
        default:
            break;
    }
}

通过CurveStyle来分发不同的画线方式,我们可以使用UserCurve来区分
1.首先定义一个派生于QwtPlotCurve的类

class MyCurve: public QwtPlotCurve

2.重新定义

virtual void drawCurve( QPainter *p, int style, const QwtScaleMap &xMap,
                            const QwtScaleMap &yMap, const QRectF &canvasRect, int from, int to ) const
    {

        p->setPen(pen);
        switch ( style )
        {
            case QwtPlotCurve::UserCurve:
                drawMyCurve(p,  xMap, yMap, canvasRect, from, to);
                break;
            default:
                 QwtPlotCurve::drawCurve(p, style, xMap, yMap,canvasRect, from, to);
                break;
       }
    }

3.最后重新实现drawMyCurve即可。

至此,完美解决。