1. 程式人生 > >基於視訊的電熔鎂爐工況識別系統→6.電熔鎂爐伺服器設計

基於視訊的電熔鎂爐工況識別系統→6.電熔鎂爐伺服器設計

《基於視訊的電熔鎂爐工況識別系統→6.電熔鎂爐伺服器設計》

程式目的 總結
  1. 主執行緒
    1. 當有新的客戶端連線時
      1. 顯示工況資料表
      2. 讀取資料庫工況資料
        1. 啟動子執行緒1thread使能子執行緒1處理函式isStop= false
        2. 主執行緒發出訊號startThread,用於啟動子執行緒1中處理函式VideoThread::myVideoPlay讀取資料
      3. 繪製需量趨勢圖
        1. 啟動繪圖子執行緒2,此時並沒有啟動子執行緒2中處理函式,啟動子執行緒2的處理函式的觸發訊號在子執行緒1
    2. 子執行緒1的處理:
      1. 當接收到工況資料訊號mySignal(data)
        時,使用dealSignal槽函式處理一條組包為data的工況資料
      2. 在dealSignal槽函式中:
        1. 顯示奇數幀視訊圖片
        2. 傳輸資料到客戶端
        3. 在訊息滾動中播放工況狀態
        4. 通過playStopFlag來控制全部資料處理完後的操作
    3. 子執行緒2的處理:
      1. 當接收到影象資料訊號updateImage(image)時,使用getImage槽函式接收一張座標x軸為11個時間單點陣圖片
      2. 在getImage槽函式中,將圖片顯示到主介面中
    4. 斷開連線按鈕:
      1. 關閉子執行緒
      2. 主動和客戶端斷開連線
    5. 其他按鈕:暫停、繼續;加速、減速;
  2. 在子執行緒1處理函式VideoThread::myVideoPlay
    1. 讀取一條工況資料,組包為data,發出mySignal(data)訊號
    2. 每25幀獲取需量資料到demmandTre陣列中一次,每隔11s即11個25幀傳送一次繪圖訊號paintSignal(i),用於啟動執行緒2中處理函式VoltTreThread::myVoltPaint來繪製圖像
    3. 其他:
      1. 使用sleepSpeed控制視訊播放速度,預設為80,即80ms傳輸一次資料訊號mySignal(data)
        1. 由於顯示奇數幀視訊圖片,因此相當於40ms顯示1幀,1s顯示25幀原視訊幀率
  3. 在子執行緒2處理函式VoltTreThread::myVoltPaint(int sec)
    1. 每隔11s繪製一張曲線圖,併發出updateImage(image)訊號
錯誤趙
  1. 如果void Widget::dealSignal(QString data)中不新增
1
2
3
4
if(tcpSocket==NULL)
    {
        return;
    }
那麼在點選斷開連線時程式會出錯,因為 斷開連線後tcpSocket==NULL,但是子執行緒1還沒完全關閉,還會發送工況資料訊號mySignal(data)觸發dealSignal槽函式,此時針對tcpSocket的write操作會使程式崩潰(2.173)
  1. release下繼續播放按鈕失效,但debug狀態下有反應
    1. 因此在debug模式釋出程式
    2. 在debug模式釋出程式也是可以的,按照需要手動新增模組就行 參考見Qt補充→新增資原始檔以及釋出程式

FMP Server.pro
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
QT       += core gui sql network

greaterThan(QT_MAJOR_VERSION, 4): QT += widgets

TARGET = FMP Server
TEMPLATE = app


SOURCES += main.cpp\
        widget.cpp \
    videothread.cpp \
    volttrethread.cpp

HEADERS  += widget.h \
    videothread.h \
    volttrethread.h

FORMS    += widget.ui

CONFIG += C++11

RESOURCES += \
    image.qrc
main.cpp
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#include "widget.h"
#include <QApplication>
#include <QSqlDatabase>
#include <QSqlQuery>
#include <QSqlTableModel>

QSqlDatabase db;
QSqlQuery query;
QSqlTableModel *model;

bool isStop = false;//結束和開啟子執行緒1的處理函式
int sendSuccess;//每條工況資料傳送成功標誌
bool playStopFlag = true;//暫停、繼續子執行緒1處理函式
int demmandTre[1550];//繪圖用到的需量值,使用下標1-1449儲存資料
int sleepSpeed = 80;//視訊播放執行緒速度控制,預設25幀/s
int i = 1;//用於繪圖的時間累加器,單位s,每11s傳遞一次繪圖訊號

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    Widget w;
    w.show();

    return a.exec();
}


widget.h
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
#ifndef WIDGET_H
#define WIDGET_H

#include <QMainWindow>
#include <QSqlDatabase>
#include <QSqlQuery>
#include <QSqlTableModel>
#include <QTcpServer>
#include <QTcpSocket>

#include "videothread.h"
#include "volttrethread.h"
#include <QThread>


namespace Ui {
class Widget;
}

extern QSqlDatabase db;
extern QSqlQuery query;
extern QSqlTableModel *model;

extern bool isStop;//結束和開啟子執行緒1的處理函式
extern int sendSuccess;//每條工況資料傳輸成功標誌
extern bool playStopFlag;//暫停、繼續子執行緒1處理函式
extern int demmandTre[1550];//繪圖用到的需量值,使用下標1-1449儲存資料
extern int sleepSpeed;//視訊播放執行緒速度控制,預設25幀/s
extern int i;//用於繪圖的時間累加器,單位s,每11s傳遞一次繪圖訊號

class Widget : public QMainWindow
{
    Q_OBJECT

public:
    explicit Widget(QWidget *parent = 0);
    ~Widget();

    void dealSignal(QString data);//自定義播放視訊執行緒訊號處理槽函式
    void getImage(QImage); //自定義繪圖執行緒訊號處理槽函式
    void dataFinished();//資料傳送完成 or 斷開連線

signals:
    void startThread(); //觸發視訊播放執行緒處理函式的訊號

private slots:
    void on_stopSelcet_clicked();//暫停
    void on_playSelcet_clicked();//繼續播放
    void on_accelerateSelcet_clicked();//加速資料傳輸
    void on_moderateSelcet_clicked();//減速資料傳輸
    void on_help_clicked();//幫助按鈕
    void on_stopConnect_clicked();//停止連線按鈕

private:
    Ui::Widget *ui;
    VideoThread *myT;
    QThread *thread;//視訊播放子執行緒

    VoltTreThread *myT2;
    QThread *thread2;//繪圖子執行緒
    QImage image;//儲存子執行緒繪製的圖

    QTcpServer *tcpServer; //監聽套接字
    QTcpSocket *tcpSocket; //通訊套接字

};

#endif // WIDGET_H

widget.cpp
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
#include "widget.h"
#include "ui_widget.h"

#include <QPixmap>
#include <QMenuBar>
#include <QMenu>
#include <QMessageBox>
#include <QDebug>

Widget::Widget(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::Widget)
{
    ui->setupUi(this);

    //===設定標題及頁面===
    setWindowTitle("電熔鎂爐資料中繼伺服器");
    setWindowIcon(QIcon(QPixmap(":/image/伺服器.bmp")));

    //===設定介面按鈕外觀===
    ui->playSelcet->setStyleSheet("QPushButton{border-image: url(:/image/播放.bmp);}\
                                  QPushButton:hover{border-image: url(:/image/播放滑鼠懸浮.bmp);}");
    ui->stopSelcet->setStyleSheet("QPushButton{border-image: url(:/image/暫停.bmp);}\
                                  QPushButton:hover{border-image: url(:/image/暫停滑鼠懸浮.bmp);}");
    ui->accelerateSelcet->setStyleSheet("QPushButton{border-image: url(:/image/加速.bmp);}\
                                        QPushButton:hover{border-image: url(:/image/加速滑鼠懸浮.bmp);}");
    ui->moderateSelcet->setStyleSheet("QPushButton{border-image: url(:/image/減速.bmp);}\
                                      QPushButton:hover{border-image: url(:/image/減速滑鼠懸浮.bmp);}");
    ui->stopConnect->setStyleSheet("QPushButton{border-image: url(:/image/斷開連線.bmp);}\
                                   QPushButton:hover{border-image: url(:/image/斷開連線滑鼠懸浮.bmp);}");
    ui->help->setStyleSheet("QPushButton{border-image: url(:/image/幫助.bmp);}\
                            QPushButton:hover{border-image: url(:/image/幫助滑鼠懸浮.bmp);}");

    //===MenuBar===
    QMenuBar *mBar = menuBar();
    QMenu *pFile = mBar->addMenu("編輯");
    QAction *pNew = pFile->addAction("斷開資料傳輸");
    connect(pNew, &QAction::triggered, this, &Widget::dataFinished);
    QMenu *pFile2 = mBar->addMenu("幫助");
    QAction *pNew2 = pFile2->addAction("關於伺服器");
    connect(pNew2, &QAction::triggered,
           [=]()
           {
               QMessageBox::about(this,"關於伺服器",\
               QString("<center><h4>伺服器端功能</h4></center><BODY>\
                        ---線上工況視訊播放<BR>---工況資料表顯示<BR>\
                        ---需量趨勢圖繪製<BR>---資料傳輸控制(調速、啟停)<BR>\
                        ---客戶端連線資訊顯示<BR>---當前工況播報</BODY>"));
           }
           );

    //===資料庫===
    //新增資料庫連線
    db = QSqlDatabase::addDatabase("QSQLITE","a");
    //設定資料庫資訊
    db.setDatabaseName("../Resource/database.db");

    //開啟資料庫
    if(!db.open())
    {
        qDebug() << "Failed to open database1";
    }
    else
    {
        qDebug() << "succeed in opening database1";
    }
    //以下執行相關sql語句
    query = QSqlQuery(db);
    //設定模型,替代QSqlQuery
    model = new QSqlTableModel(this,db);
    //指定資料庫表
    model->setTable("sheet1");
    //顯示model裡的資料
    model->select();

    //===SOCKET===
    tcpSocket = NULL;
    //監聽套接字
    tcpServer = new QTcpServer(this);
    //QHostAddress::Any繫結我當前網絡卡所有的iP 正式監聽
    tcpServer->listen(QHostAddress::Any, 8888);
    //如果連線成功,伺服器會觸發newConnection()
    connect(tcpServer, &QTcpServer::newConnection,
            [=]()
            {
                qDebug() << "伺服器被客戶端連線!";

                //資料初始化
                sleepSpeed = 80;//視訊播放執行緒速度控制,預設25幀/s
                playStopFlag = true;//暫停、繼續子執行緒1處理函式
                for(int m=0;m<1550;m++)
                {
                    demmandTre[m] = 0;//繪圖用到的需量值,使用下標1-1449儲存資料
                }
                i = 1;//用於繪圖的時間累加器,單位s,每11s傳遞一次繪圖訊號

                //==顯示工況資料表==
                //把model放在QTabelView元件中
                ui->tableView->setModel(model);
                //設定model的編輯策略:不能編輯
                ui->tableView->setEditTriggers(QAbstractItemView::NoEditTriggers);
                ui->tableView->setAlternatingRowColors(true);//奇偶行區分顏色樣式

                //==讀取資料庫工況資料==
                //啟動子執行緒1
                thread->start();
                //使能子執行緒1處理函式
                isStop = false;
                //主執行緒發出訊號,用於啟動子執行緒1中處理函式來讀取資料
                emit startThread();

                //==繪製需量趨勢圖==
                //啟動繪圖子執行緒2
                thread2->start();

                //==顯示客戶端連線資訊==
                //取出佇列中建立好連線的通訊套接字
                tcpSocket = tcpServer->nextPendingConnection();
                //獲取對方(客戶端)的IP和埠
                              QString ip = tcpSocket->peerAddress().toString();
                              qint16 port = tcpSocket->peerPort();
                              QString temp = QString("<BR><BR><BR><center><h3>客戶端[%1:%2]\
                                                成功連線</h3></center>").arg(ip).arg(port);
                              ui->textEditRead->setText(temp);
                connect(tcpSocket, &QTcpSocket::readyRead,
                        [=]()
                        {
                            QByteArray array = tcpSocket->readAll();
                            ui->textEditRead->append(array);
                        }
                        );
            }
            );

    //===子執行緒1處理===
    myT = new VideoThread;
    thread = new QThread(this);
    myT->moveToThread(thread);
    connect(this, &Widget::startThread, myT, &VideoThread::myVideoPlay);
    connect(myT, &VideoThread::mySignal, this, &Widget::dealSignal);

    //===子執行緒2處理===
    myT2 = new VoltTreThread;
    thread2 =new QThread(this);
    myT2->moveToThread(thread2);
    connect(myT,&VideoThread::paintSignal,myT2,&VoltTreThread::myVoltPaint);
    connect(myT2,&VoltTreThread::updateImage,this,&Widget::getImage);
}


Widget::~Widget()
{
    delete ui;
}

//接收執行緒1中的一條組包為data的工況資料並處理
void Widget::dealSignal(QString data)
{
    //在新的連線被建立時,才可以取出佇列中建立好連線的通訊套接字
    //在此之前,tcpSocket僅是個沒有指向的指標,因此執行tcpSocket操作會出錯
    if(tcpSocket==NULL)
    {
        return;
    }
    int frame = data.section("b",0,0).toInt();
    //顯示奇數幀視訊圖片
    if(frame%2 != 0)
    {
       ui->videoPlay->setPixmap(QPixmap(QString("../Resource/videoPictureWhole/%1.jpg").arg(frame)));
       ui->videoPlay->setScaledContents(true);
    }
    //傳輸資料到客戶端
    sendSuccess = tcpSocket->write( data.toUtf8() );
    while(sendSuccess==-1)
    {
       QMessageBox::warning(this,"警告","傳送失敗,請關閉此次連線建立新連線");
    }
    //顯示工況狀態
    switch (data.section("b",5,5).toInt()) {
    case 1:
       ui->messageScroll->setText(QString("<BR><BR><BR><center><h3>當前資料\
        傳送到第 %1 幀<br>狀態:火焰形態穩定、顏色明亮</h3></center>").arg(frame));
       break;
    case 2:
       ui->messageScroll->setText(QString("<BR><BR><BR><center><h3>當前資料\
        傳送到第 %1 幀<br>狀態:火焰面積明顯小於正常熔鍊及欠燒的火焰面積,\
        火焰顏色發暗</h3></center>").arg(frame));
       break;
    case 3:
       ui->messageScroll->setText(QString("<BR><BR><BR><center><h3>當前資料\
        傳送到第 %1 幀<br>狀態:亮度持續增加,火焰可視區域覆蓋整個爐口</h3></center>").arg(frame));
       break;
    case 4:
       ui->messageScroll->setText(QString("<BR><BR><BR><center><h3>當前資料\
        傳送到第 %1 幀<br>狀態:爐口有火星濺出</h3></center>").arg(frame));
       break;
    case 5:
       ui->messageScroll->setText(QString("<BR><BR><BR><center><h3>當前資料\
       傳送到第 %1 幀<br>狀態:亮度相對正常工況偏低</h3></center>").arg(frame));
       break;
    default:
       break;
    }
    //全部資料處理完後的操作
    if (frame == 36233)
    {
       //暫停子執行緒1處理函式
       playStopFlag = false;

    //       int ret = QMessageBox::question(this,"提示","全部資料已經傳輸完成,\
       是否要繼續重新傳送資料?",
    //                             QMessageBox::Ok|QMessageBox::Cancel);
       int ret = QMessageBox::Ok;
       switch (ret) {
       case QMessageBox::Ok:
           //繼續子執行緒1處理函式,再讀一遍工況資料
           playStopFlag = true;
           break;
       case QMessageBox::Cancel:
           Widget::dataFinished();
           break;
       default:
           break;
       }
    }
}
//將線上程2中繪製的圖片顯示到主介面中
void Widget::getImage(QImage temp)
{
    ui->voltageTrend->setPixmap(QPixmap::fromImage(temp));
    ui->voltageTrend->setScaledContents(true);
}

//斷開連線
void Widget::dataFinished()
{
    //在新的連線被建立時,才可以取出佇列中建立好連線的通訊套接字
    //在此之前,tcpSocket僅是個沒有指向的指標,因此執行tcpSocket操作會出錯
    if(tcpSocket==NULL)
    {
        return;
    }
    //關閉子執行緒
    isStop = true;//結束子執行緒1的處理函式
    thread->terminate();//結束子執行緒1
//    thread->wait();
    thread2->terminate();//結束子執行緒2
//    thread2->wait();

    //主動和客戶端斷開連線
    tcpSocket->disconnectFromHost();
    tcpSocket->close();
    tcpSocket = NULL;

    //關閉提示
    ui->videoPlay->setPixmap(QPixmap(":/image/出錯了.bmp"));
    ui->voltageTrend->clear();
    ui->textEditRead->setText("<center><h3>連線已經斷開</h3></center>");
    ui->messageScroll->setText("<center><h3>連線已經斷開</h3></center>");
    ui->tableView->setModel(new QSqlTableModel(this));
    model->select();//不顯示資料
}

//暫停
void Widget::on_stopSelcet_clicked()
{
    playStopFlag = false;
}
//繼續播放
void Widget::on_playSelcet_clicked()
{
    playStopFlag = true;
}

//資料加速傳輸
void Widget::on_accelerateSelcet_clicked()
{
    if(sleepSpeed != 80)
    {
        sleepSpeed -= 80;
    }
    else
    {
        QMessageBox::about(this,"警告","速度已經到達最大!!!");

    }
}
//資料減速傳輸
void Widget::on_moderateSelcet_clicked()
{
    if(sleepSpeed != 480)
    {
        sleepSpeed += 80;
    }
    else
    {
        QMessageBox::about(this,"警告","速度已經是最小!!!");
    }
}
//幫助按鈕
void Widget::on_help_clicked()
{
    QMessageBox::about(this,"關於伺服器",\
                       QString("<center><h4>伺服器端功能</h4></center><BODY>\
                                ---線上工況視訊播放<BR>---工況資料表顯示<BR>\
                                ---需量趨勢圖繪製<BR>---資料傳輸控制(調速、啟停)<BR>\
                                ---客戶端連線資訊顯示<BR>---當前工況播報</BODY>"));
}
//停止按鈕
void Widget::on_stopConnect_clicked()
{
    dataFinished();
}

videothread.h
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
#ifndef VideoThread_H
#define VideoThread_H
#include <QObject>
#include <QSqlQuery>

extern QSqlQuery query;

extern bool isStop;//結束和開啟子執行緒1的處理函式
extern int sendSuccess;//每條工況資料傳輸成功標誌
extern bool playStopFlag;//暫停、繼續子執行緒1處理函式標誌
extern int demmandTre[1550];//繪圖的時候按秒計時
extern int sleepSpeed;//視訊播放執行緒速度控制,預設25幀/s
extern int i;//用於繪圖的時間累加器,單位s,每11s傳遞一次繪圖訊號

class VideoThread : public QObject
{
    Q_OBJECT
public:
    explicit VideoThread(QObject *parent = 0);

    //執行緒處理函式
    void myVideoPlay();
    //工況資料
    int frame;//36233幀
    double current1;
    double current2;
    double current3;
    int voltage;
    int label;
    int demand;

signals:
    void mySignal(QString data);//播放視訊訊號
    void paintSignal(int sec);//畫圖訊號 每秒畫一次圖

private:

public slots:
};

#endif // VideoThread_H

videothread.cpp
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
#include "videothread.h"
#include <QVariant>
#include <QThread>
#include <QDebug>

VideoThread::VideoThread(QObject *parent) : QObject(parent)
{

}

void VideoThread::myVideoPlay()
{
    while(!isStop)
    {
        query.exec("select * from sheet1");
        //query.next()指向查詢到的第一條記錄,然後每次後移一條記錄
        while (query.next())
        {
            //讀取資料庫每一行的工況資料
            frame = query.value(0).toInt();
            current1 = query.value(1).toDouble();
            current2 = query.value(2).toDouble();
            current3 = query.value(3).toDouble();
            voltage = query.value(4).toInt();
            label = query.value(5).toInt();
            demand = query.value(6).toDouble();
            //b:每個特徵的間隔 break;bw:每幀的間隔 break widely
            QString data = QString("%1b%2b%3b%4b%5b%6b%7bw")\
                    .arg(frame).arg(current1).arg(current2)\
                    .arg(current3).arg(voltage).arg(label).arg(demand);

            emit mySignal(data);
            //如果工況資料傳送失敗,停止子執行緒1處理函式來停止讀取資料
            while (sendSuccess == -1)
            {
                isStop = true;
            }

            //獲取需量資料到demmandTre陣列中,每隔11s傳送一次繪圖訊號
            if(frame>24)
            {
                //總共36233幀,當讀取到1449×25=36225時
                //demmandTre的[1~1449]被賦值
                if(frame%25==0 && frame<=36225)
                {
                    demmandTre[i] = demand;
                    if (i==1449)
                    {
                        i = 1;
                    }
                    //每隔11s傳送一次繪圖訊號,最後一次是i=1441
                    if (i%11 == 0)
                    {
                        emit paintSignal(i);
                    }
                    i++;
                }
            }
            //由於幀數太多,80ms顯示奇數幀
            QThread::msleep(sleepSpeed);
            //暫停、繼續子執行緒1處理函式
            while(!playStopFlag)
            {
                ;
            }
        }
    }
}

volttrethread.h
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#ifndef VoltTreThread_H
#define VoltTreThread_H

#include <QObject>
#include <QImage>

extern int demmandTre[1550];//繪圖用到的需量值,使用下標1-1449儲存資料

class VoltTreThread : public QObject
{
    Q_OBJECT
public:
    explicit VoltTreThread(QObject *parent = 0);
    //子執行緒2處理函式
    void myVoltPaint(int sec);

signals:
    void updateImage(QImage temp);

public slots:
};

#endif // VoltTreThread_H

volttrethread.cpp
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
#include "VoltTreThread.h"
#include <QPainter>
#include <QPen>
#include <QBrush>
#include <QDebug>
#include <QThread>

VoltTreThread::VoltTreThread(QObject *parent) : QObject(parent)
{

}
//每隔11s才被觸發一次,因此座標x軸為11個時間單位(s)
void VoltTreThread::myVoltPaint(int sec)
{
    //定義QImage繪圖裝置
    QImage image(1100, 300, QImage::Format_ARGB32);//QImage::Format_ARGB32背景是透明
    //填充背景為黑色
    image.fill(Qt::black);
    //定義畫家,指定繪圖裝置
    QPainter p(&image);
    //設定字型
    p.setFont(QFont("Times New Roman",14,50));//字型型別、大小、粗細
    //相對畫布為1200×300的畫布確定影象位置
    int pointx=100,pointy=250;//確定座標軸起點座標
    int width=960,height=230;//確定座標軸寬度跟高度

    //繪製座標框架
    QPen pen;
    pen.setColor(Qt::white);
    p.setPen(pen);
    p.drawRect(10,5,1080,290);//外圍的矩形
    p.drawLine(pointx,pointy,width+pointx,pointy);
    p.drawLine(pointx,pointy-height,pointx,pointy);

    //繪製需量曲線 ,需量最大值為3.7722e+03 最小值為2.7234e+03
    double kx=(double)width/(10); //x軸的比例係數 9.6
    double ky=(double)height/(10);//y方向的比例係數 2.3
    pen.setColor(Qt::green);//畫曲線的筆
    pen.setWidth(2);
    QPen penPoint;//描點的筆
    penPoint.setColor(Qt::white);
    penPoint.setWidth(5);
    p.setRenderHint(QPainter::Antialiasing);
    for(int k=sec-10;k<sec;k++)//1→10 12→21 23→32
    {
        //由於y軸是倒著的,所以y軸座標要pointy-demmandTre[i]*ky 其中ky為比例係數
        //曲線繪製
        p.setPen(pen);
        p.drawLine(pointx+kx*(k-(sec-10)),pointy-(demmandTre[k]-2500)/150*ky,pointx+kx*(1+k-(sec-10)),pointy-(demmandTre[k+1]-2500)/150*ky);
        //描點顯示
        p.setPen(penPoint);
        p.drawPoint(pointx+kx*(k-(sec-10)),pointy-(demmandTre[k]-2500)/150*ky);
    }
    p.drawPoint(pointx+kx*(sec-(sec-10)),pointy-(demmandTre[sec]-2500)/150*ky);//繪製最後一個點

    //繪製刻度線
    QPen penDegree,penDegree2;
    penDegree.setWidth(1);
    penDegree.setColor(Qt::green);
    penDegree.setStyle(Qt::DotLine);//繪製內部的綠點網格
    penDegree2.setWidth(2);
    penDegree2.setColor(Qt::white);
    penDegree2.setStyle(Qt::SolidLine);//最上、右邊的線是白實線
    p.setPen(penDegree);
    //x軸刻度線
    double xInterval = (double)10/10;//x軸刻度間隔為1
    for(int i=-1;i<9;i++)//畫10條(不算原點),最後一條在for外邊
    {
        //選取合適的座標,繪製一段長度為width的直線,用於表示刻度
        p.drawLine(pointx+(i+1)*width/10,pointy,pointx+(i+1)*width/10,pointy-height);
        p.setPen(penDegree2);
        p.drawText(pointx+(i+1)*width/10,
                         pointy+15,QString::number((int)(xInterval*(sec-9+i))));
        p.setPen(penDegree);
    }
    p.setPen(penDegree2);
    p.drawLine(pointx+(9+1)*width/10,pointy,pointx+(9+1)*width/10,pointy-height);
    p.drawText(pointx+(9+1)*width/10,
                     pointy+15,QString::number((int)(xInterval*(sec))));
    //y軸刻度線
    p.setPen(penDegree);
    double yInterval = (double)1500/10;//y軸刻度間隔150
    for(int j=0;j<9;j++)
    {
        //程式碼較長,但是掌握基本原理即可。
        //主要就是確定一個位置,然後畫一條短短的直線表示刻度。
        p.drawLine(pointx,pointy-(j+1)*height/10,
                         pointx+width,pointy-(j+1)*height/10);
        p.setPen(penDegree2);
        p.drawText(pointx-40,pointy-(j+1)*height/10,
                         QString::number((int)(yInterval*(j+1)+2500)));
        p.setPen(penDegree);
    }
    p.setPen(penDegree2);
    p.drawLine(pointx,pointy-(9+1)*height/10,
                     pointx+width,pointy-(9+1)*height/10);
    p.drawText(pointx-40,pointy-(9+1)*height/10,
                     QString::number((int)(yInterval*(9+1)+2500)));
    //座標名稱:(橫軸:Times;縱軸:Demand(KW))
    p.setFont(QFont("Times New Roman",14,75));
    p.drawText(pointx+(4+1)*width/10-15,
                     pointy+30,QString("Time(s)"));
    p.drawText(pointx-70,pointy-(7+1)*height/10,
                     QString("D"));
    p.drawText(pointx-70,pointy-(6+1)*height/10,
                     QString("e"));
    p.drawText(pointx-70,pointy-(5+1)*height/10,
                     QString("m"));
    p.drawText(pointx-70,pointy-(4+1)*height/10,
                     QString("a"));
    p.drawText(pointx-70,pointy-(3+1)*height/10,
                     QString("n"));
    p.drawText(pointx-70,pointy-(2+1)*height/10,
                     QString("d"));
    p.drawText(pointx-86,pointy-(1+1)*height/10,
                     QString("(KW)"));

    emit updateImage(image);

}


Show 執行結果
  1. 下圖是執行後的介面

  1. 當有客戶端連線時,會播放視訊,顯示工況資料表、需量趨勢圖