/*******************************************************************************************/
一、QT文件操作
文件操作是應用程序必不可少的 部分。Qt 作為一個通用開發庫,提供了跨平台的文件操作能力。
Qt 通過QIODevice提供了對 I/O 設備的抽象,這些設備具有讀寫字節塊的能力。
qt中的文件(IO)層次:
QIODevice是基類,下面派生出了各個子類,其中:
QBuffer 類似與數組,操作內存緩沖區
QProcess 類似system函數,開啟一個其他進程
QFileDevice 普通文件,類似文件描述符
QAbsrtacSocket 網絡套接字
具體見圖1:
具體說明:
QIODevice:所有 I/O 設備類的父類,提供了字節塊讀寫的通用操作以及基本接口;
QFileDevice:Qt5新增加的類,提供了有關文件操作的通用實現。
QFlie:訪問本地文件或者嵌入資源;
QTemporaryFile:創建和訪問本地文件系統的臨時文件;
QBuffer:讀寫QbyteArray, 內存文件;
QProcess:運行外部程序,處理進程間通訊;
QAbstractSocket:所有套接字類的父類;
QTcpSocket:TCP協議網絡數據傳輸;
QUdpSocket:傳輸 UDP 報文;
QSslSocket:使用 SSL/TLS 傳輸數據;
文件操作分類:
順序訪問設備:
是指它們的數據只能訪問一遍:從頭走到尾,從第一個字節開始訪問,直到最后一個字節,中途不能返回去讀取上一個字節,這其中,QProcess、QTcpSocket、QUdpSoctet和QSslSocket是順序訪問設備。
隨機訪問設備:
可以訪問任意位置任意次數,還可以使用QIODevice::seek()函數來重新定位文件訪問位置指針,QFile、QTemporaryFile和QBuffer是隨機訪問設備,
/*******************************************************************************************/
二、QFile文件操作(基本文件操作)
文件操作是應用程序必不可少的部分。Qt 作為一個通用開發庫,提供了跨平台的文件操作能力。在所有的 I/O 設備中,
文件 I/O 是最重要的部分之一。因為我們大多數的程序依舊需要首先訪問本地文件(當然,在雲計算大行其道的將來,這一觀點可能改變)。
QFile提供了從文件中讀取和寫入數據的能力。
我們通常會將文件路徑作為參數傳給QFile的構造函數。不過也可以在創建好對象最后,使用setFileName()來修改。QFile需要使用 / 作為文件分隔符,
不過,它會自動將其轉換成操作系統所需要的形式。例如 C:/windows 這樣的路徑在 Windows 平台下同樣是可以的。
QFile主要提供了有關文件的各種操作,比如打開文件、關閉文件、刷新文件等。我們可以使用QDataStream或QTextStream類來讀寫文件,
也可以使用QIODevice類提供的read()、readLine()、readAll()以及write()這樣的函數。
值得注意的是,有關文件本身的信息,比如文件名、文件所在目錄的名字等,則是通過QFileInfo獲取,而不是自己分析文件路徑字符串。
1.QFile讀文件:
void Widget::on_buttonRead_clicked()
{
QString path = QFileDialog::getOpenFileName(this,
"open", "../", "TXT(*.txt)");//獲取文件路徑
if(path.isEmpty() == false)
{
//創建文件對象
QFile file(path);
//打開文件,只讀方式
bool isOk = file.open(QIODevice::ReadOnly);
if(isOk == true)
{
#if 0
//讀文件
QByteArray array = file.readAll();//一次性讀完,這個讀的接口默認都的文本是只識別utf8編碼
//,要讀其他編碼的要借助於后面講的文本流
//顯示到編輯區
//ui->textEdit->setText(QString(array));
ui->textEdit->setText(array);//內部自動轉換了類型
#endif
QByteArray array;
while( file.atEnd() == false)
{
//讀一行
array += file.readLine();//默認讀完一行,換行符也讀進去了
}
ui->textEdit->setText(array);
}
//關閉文件
file.close();
}
}
2.QFile寫文件:
void Widget::on_buttonWrite_clicked()
{
//獲取保存文件的路徑
QString path = QFileDialog::getSaveFileName(this, "save", "../", "TXT(*.txt)");
if(path.isEmpty() == false)
{
QFile file; //創建文件對象
//關聯文件名字
file.setFileName(path);
//打開文件(沒有則創建),只寫方式
bool isOk = file.open(QIODevice::WriteOnly);
if(isOk == true)
{
//獲取編輯區內容
QString str = ui->textEdit->toPlainText();//qt里文本用的是utf8
//寫文件
// QString -> QByteArray
//file.write(str.toUtf8());//保存的結果是utf8的文本,可以正常打開並顯示,無亂碼
//QString -> c++ string -> char *
//file.write(str.toStdString().data());//保存的結果是utf8的文本,可以正常打開並顯示,無亂碼
//轉換為本地平台編碼(win默認編碼,一般是ansi),可以正常打開並顯示,無亂碼
file.write(str.toLocal8Bit());
//QString -> QByteArray 轉換方法:
QString buf = "123";
QByteArray a = buf.toUtf8(); //中文//QByteArray是utf8格式的文本
a = buf.toLocal8Bit(); //本地編碼(win默認編碼)//QByteArray是本地編碼的文本
//QByteArray -> char *
char *b = a.data();
//char * -> QString
char *p = "abc";
QString c = QString(p);
}
file.close();
}
}
3.QfileInfo獲取文件信息
//獲取文件信息
QFileInfo info(path);
qDebug() << "文件名字:" << info.fileName().toUtf8().data();//轉換為utf8才能打印出來
qDebug() << "文件后綴:" << info.suffix();
qDebug() << "文件大小:" << info.size();
qDebug() << "文件創建時間:" <<
info.created().toString("yyyy-MM-dd hh:mm:ss"); //2016-01-04 15:13:00
具體見《QFile》

1 #ifndef WIDGET_H 2 #define WIDGET_H 3 4 #include <QWidget> 5 6 namespace Ui { 7 class Widget; 8 } 9 10 class Widget : public QWidget 11 { 12 Q_OBJECT 13 14 public: 15 explicit Widget(QWidget *parent = 0); 16 ~Widget(); 17 18 private slots: 19 void on_buttonRead_clicked(); 20 21 void on_buttonWrite_clicked(); 22 23 private: 24 Ui::Widget *ui; 25 }; 26 27 #endif // WIDGET_H

1 #include "widget.h" 2 #include "ui_widget.h" 3 #include <QFile> 4 #include <QFileDialog> 5 #include <QFileInfo> 6 #include <QDebug> 7 #include <QDateTime> 8 #include <QDataStream> 9 10 Widget::Widget(QWidget *parent) : 11 QWidget(parent), 12 ui(new Ui::Widget) 13 { 14 ui->setupUi(this); 15 } 16 17 Widget::~Widget() 18 { 19 delete ui; 20 } 21 22 void Widget::on_buttonRead_clicked() 23 { 24 QString path = QFileDialog::getOpenFileName(this, 25 "open", "../", "TXT(*.txt)"); 26 if(path.isEmpty() == false) 27 { 28 //文件對象 29 QFile file(path); 30 31 //打開文件,只讀方式 32 bool isOk = file.open(QIODevice::ReadOnly); 33 if(isOk == true) 34 { 35 #if 0 36 //讀文件,默認只識別utf8編碼 37 QByteArray array = file.readAll(); 38 //顯示到編輯區 39 //ui->textEdit->setText(QString(array)); 40 ui->textEdit->setText(array); 41 #endif 42 43 QByteArray array; 44 while( file.atEnd() == false) 45 { 46 //讀一行 47 array += file.readLine(); 48 } 49 ui->textEdit->setText(array); 50 51 } 52 53 //關閉文件 54 file.close(); 55 56 //獲取文件信息 57 QFileInfo info(path); 58 qDebug() << "文件名字:" << info.fileName().toUtf8().data(); 59 qDebug() << "文件后綴:" << info.suffix(); 60 qDebug() << "文件大小:" << info.size(); 61 qDebug() << "文件創建時間:" << 62 info.created().toString("yyyy-MM-dd hh:mm:ss"); //2016-01-04 15:13:00 63 64 } 65 66 67 } 68 69 void Widget::on_buttonWrite_clicked() 70 { 71 QString path = QFileDialog::getSaveFileName(this, "save", "../", "TXT(*.txt)"); 72 if(path.isEmpty() == false) 73 { 74 QFile file; //創建文件對象 75 //關聯文件名字 76 file.setFileName(path); 77 78 //打開文件,只寫方式 79 bool isOk = file.open(QIODevice::WriteOnly); 80 if(isOk == true) 81 { 82 //獲取編輯區內容 83 QString str = ui->textEdit->toPlainText(); 84 //寫文件 85 // QString -> QByteArray 86 //file.write(str.toUtf8()); 87 88 //QString -> c++ string -> char * 89 //file.write(str.toStdString().data()); 90 91 //轉換為本地平台編碼 92 file.write(str.toLocal8Bit()); 93 94 95 //QString -> QByteArray 96 QString buf = "123"; 97 QByteArray a = buf.toUtf8(); //中文 98 a = buf.toLocal8Bit(); //本地編碼 99 100 //QByteArray -> char * 101 char *b = a.data(); 102 103 //char * -> QString 104 char *p = "abc"; 105 QString c = QString(p); 106 107 108 } 109 110 file.close(); 111 112 } 113 114 115 116 117 }
/*******************************************************************************************/
三、QDataStream讀寫文件(二進制讀寫文件)
以二進制的方法操作文件,就要借助QDataStream
由於是操作文件,所以還得用到QFile
void Widget::writeData()
{
//創建文件對象
QFile file("../test.txt");
//打開文件, 只寫方式打開
bool isOk = file.open(QIODevice::WriteOnly);
if(true == isOk)
{
//創建數據流,和file文件關聯
//往數據流中寫數據,相當於往文件里寫數據
QDataStream stream(&file);
stream << QString("主要看氣質") << 250;//結果文件里都是不可見的二進制數據
file.close();
}
}
#define cout qDebug() << "[" << __FILE__ <<":" << __LINE__ << "]"
void Widget::readData()
{
//創建文件對象
QFile file("../test.txt");
//打開文件, 只讀方式打開
bool isOk = file.open(QIODevice::ReadOnly);
if(true == isOk)
{
//創建數據流,和file文件關聯
//往數據流中讀數據,相當於往文件里讀數據
QDataStream stream(&file);
//讀的時候,按寫的順序取數據
QString str;//先取出str,因為這個是之前先輸入的
int a;
stream >> str >> a;
//qDebug() << str.toUtf8().data() << a;
cout << str.toUtf8().data() << a;
file.close();
}
}
具體見《QDataStream》

1 #ifndef WIDGET_H 2 #define WIDGET_H 3 4 #include <QWidget> 5 6 namespace Ui { 7 class Widget; 8 } 9 10 class Widget : public QWidget 11 { 12 Q_OBJECT 13 14 public: 15 explicit Widget(QWidget *parent = 0); 16 ~Widget(); 17 18 void writeData(); 19 void readData(); 20 21 private: 22 Ui::Widget *ui; 23 }; 24 25 #endif // WIDGET_H

1 #include "widget.h" 2 #include "ui_widget.h" 3 #include <QDataStream> 4 #include <QTextStream> 5 #include <QFile> 6 #include <QDebug> 7 #define cout qDebug() << "[" << __FILE__ <<":" << __LINE__ << "]" 8 9 Widget::Widget(QWidget *parent) : 10 QWidget(parent), 11 ui(new Ui::Widget) 12 { 13 ui->setupUi(this); 14 15 writeData(); 16 readData(); 17 } 18 19 Widget::~Widget() 20 { 21 delete ui; 22 } 23 24 void Widget::writeData() 25 { 26 //創建文件對象 27 QFile file("../test.txt"); 28 29 //打開文件, 只寫方式打開 30 bool isOk = file.open(QIODevice::WriteOnly); 31 if(true == isOk) 32 { 33 //創建數據流,和file文件關聯 34 //往數據流中寫數據,相當於往文件里寫數據 35 QDataStream stream(&file); 36 37 stream << QString("主要看氣質") << 250; 38 39 file.close(); 40 41 } 42 43 } 44 45 void Widget::readData() 46 { 47 //創建文件對象 48 QFile file("../test.txt"); 49 50 //打開文件, 只讀方式打開 51 bool isOk = file.open(QIODevice::ReadOnly); 52 if(true == isOk) 53 { 54 //創建數據流,和file文件關聯 55 //往數據流中讀數據,相當於往文件里讀數據 56 QDataStream stream(&file); 57 //讀的時候,按寫的順序取數據 58 QString str; 59 int a; 60 stream >> str >> a; 61 //qDebug() << str.toUtf8().data() << a; 62 cout << str.toUtf8().data() << a; 63 64 file.close(); 65 66 } 67 }
/*******************************************************************************************/
四、QTextStream操作文件(文本的方式操作文件)
上一節我們介紹了有關二進制文件的讀寫。二進制文件比較小巧,卻不是人可讀的格式。而文本文件是一種人可讀的文件。
為了操作這種文件,我們需要使用QTextStream類。QTextStream和QDataStream的使用類似,只不過它是操作純文本文件的。
QTextStream會自動將 Unicode 編碼同操作系統的編碼進行轉換,這一操作對開發人員是透明的。它也會將換行符進行轉換,
同樣不需要自己處理。QTextStream使用 16 位的QChar作為基礎的數據存儲單位,同樣,它也支持 C++ 標准類型,如 int 等。
實際上,這是將這種標准類型與字符串進行了相互轉換。
通過文本的方式操作文件,這樣的好處是讀寫的時候可以指定編碼
QTextStream只能通過文本的方式操作文件
void Widget::writeData()
{
QFile file;
file.setFileName("../demo.txt");
bool isOk = file.open(QIODevice::WriteOnly);
if(true == isOk)
{
QTextStream stream(&file);
//指定編碼
stream.setCodec("UTF-8");
//默認以系統平台的編碼打印
stream << QString("主要看氣質") << 250;//格式化輸出為可見字符,可以顯示,類似格式化打印
file.close();
}
}
void Widget::readData()
{
QFile file;
file.setFileName("../demo.txt");
bool isOk = file.open(QIODevice::ReadOnly);
if(true == isOk)
{
QTextStream stream(&file);
//指定編碼,前面指定以什么編碼讀,最好后面也指定以什么編碼讀
stream.setCodec("UTF-8");
QString str;//
int a;
stream >> str >> a;//這種方式讀取,由於文件中寫入的都是文本字符串,所以第一次就
//把內容全部讀取給str了,a根本就獲取不到內容。所以不能使用這種方式,一般使用函數
//的方式,見下面的函數
cout << str.toUtf8().data() << a;//顯示的時候還是需要轉換為utf8
file.close();
}
}
void Widget::on_pushButton_clicked()
{
QString path = QFileDialog::getOpenFileName(this,
"open", "../" );
if(false == path.isEmpty())
{
QFile file;
file.setFileName(path);
bool isOk = file.open(QIODevice::ReadOnly);
if(true == isOk)
{
QTextStream stream(&file);
//指定編碼
stream.setCodec("UTF-8");
QString str = stream.readAll();//使用函數讀取自己想要的內容
ui->textEdit->setText(str);
file.close();
}
}
}
具體見《QTextStream》

1 #ifndef WIDGET_H 2 #define WIDGET_H 3 4 #include <QWidget> 5 6 namespace Ui { 7 class Widget; 8 } 9 10 class Widget : public QWidget 11 { 12 Q_OBJECT 13 14 public: 15 explicit Widget(QWidget *parent = 0); 16 ~Widget(); 17 18 void writeData(); 19 void readData(); 20 21 private slots: 22 void on_pushButton_clicked(); 23 24 private: 25 Ui::Widget *ui; 26 }; 27 28 #endif // WIDGET_H

1 #include "widget.h" 2 #include "ui_widget.h" 3 #include <QTextStream> 4 #include <QFile> 5 #include <QDebug> 6 #include <QFileDialog> 7 #include <QBuffer> 8 #define cout qDebug() << "[" << __FILE__ <<":" << __LINE__ << "]" 9 10 Widget::Widget(QWidget *parent) : 11 QWidget(parent), 12 ui(new Ui::Widget) 13 { 14 ui->setupUi(this); 15 writeData(); 16 17 readData(); 18 } 19 20 Widget::~Widget() 21 { 22 delete ui; 23 } 24 25 void Widget::writeData() 26 { 27 QFile file; 28 file.setFileName("../demo.txt"); 29 30 bool isOk = file.open(QIODevice::WriteOnly); 31 if(true == isOk) 32 { 33 QTextStream stream(&file); 34 //指定編碼 35 stream.setCodec("UTF-8"); 36 37 stream << QString("主要看氣質") << 250; 38 39 file.close(); 40 } 41 } 42 43 void Widget::readData() 44 { 45 QFile file; 46 file.setFileName("../demo.txt"); 47 48 bool isOk = file.open(QIODevice::ReadOnly); 49 if(true == isOk) 50 { 51 QTextStream stream(&file); 52 //指定編碼 53 stream.setCodec("UTF-8"); 54 QString str; 55 int a; 56 stream >> str >> a; 57 58 cout << str.toUtf8().data() << a; 59 60 file.close(); 61 62 } 63 } 64 65 void Widget::on_pushButton_clicked() 66 { 67 QString path = QFileDialog::getOpenFileName(this, 68 "open", "../" ); 69 if(false == path.isEmpty()) 70 { 71 QFile file; 72 file.setFileName(path); 73 74 bool isOk = file.open(QIODevice::ReadOnly); 75 if(true == isOk) 76 { 77 QTextStream stream(&file); 78 //指定編碼 79 stream.setCodec("UTF-8"); 80 81 QString str = stream.readAll(); 82 ui->textEdit->setText(str); 83 84 file.close(); 85 } 86 87 } 88 89 90 }
/*******************************************************************************************/
四、QBuffer
QBuffer也叫內存文件,放在內存里面的
Widget::Widget(QWidget *parent) :
QWidget(parent),
ui(new Ui::Widget)
{
ui->setupUi(this);
QByteArray array;
QBuffer memFile(&array); //創建內存文件,指定了緩沖區&array,后續讀寫操作的都是這個&array
memFile.open(QIODevice::WriteOnly);
memFile.write("11111111111111111");//實際上是寫到緩沖區里面
memFile.write("22222222222222222222");//注意兩個之間沒有換行符
memFile.close();
qDebug() << memFile.buffer();//取出數據
qDebug() << "array" << array;//前后打印結果是一樣的
//內存文件配合數據流的使用
QBuffer memFile1;
memFile1.open(QIODevice::WriteOnly);
QDataStream stream(&memFile1);//內存文件在數據流中的使用
stream << QString("測試") << 250;
memFile1.close();
qDebug() << memFile1.buffer();//輸出顯示的是一堆二進制數據
memFile1.open(QIODevice::ReadOnly);
QDataStream in;
in.setDevice(&memFile1);
QString str;
int a;
in >> str >> a;
memFile1.close();
qDebug() << str.toUtf8().data() << a;
}
具體見《QBuffer》

1 #ifndef WIDGET_H 2 #define WIDGET_H 3 4 #include <QWidget> 5 6 namespace Ui { 7 class Widget; 8 } 9 10 class Widget : public QWidget 11 { 12 Q_OBJECT 13 14 public: 15 explicit Widget(QWidget *parent = 0); 16 ~Widget(); 17 18 19 private: 20 Ui::Widget *ui; 21 }; 22 23 #endif // WIDGET_H

1 #include "widget.h" 2 #include "ui_widget.h" 3 #include <QBuffer>//內存文件 4 #include <QDebug> 5 #include <QDataStream> 6 7 Widget::Widget(QWidget *parent) : 8 QWidget(parent), 9 ui(new Ui::Widget) 10 { 11 ui->setupUi(this); 12 13 QByteArray array; 14 QBuffer memFile(&array); //創建內存文件 15 memFile.open(QIODevice::WriteOnly); 16 17 memFile.write("11111111111111111"); 18 memFile.write("22222222222222222222"); 19 20 memFile.close(); 21 22 qDebug() << memFile.buffer(); 23 qDebug() << "array" << array; 24 25 26 QBuffer memFile1; 27 memFile1.open(QIODevice::WriteOnly); 28 QDataStream stream(&memFile1); 29 stream << QString("測試") << 250; 30 memFile1.close(); 31 32 qDebug() << memFile1.buffer(); 33 34 memFile1.open(QIODevice::ReadOnly); 35 QDataStream in; 36 in.setDevice(&memFile1); 37 QString str; 38 int a; 39 in >> str >> a; 40 memFile1.close(); 41 42 qDebug() << str.toUtf8().data() << a; 43 44 } 45 46 Widget::~Widget() 47 { 48 delete ui; 49 }
/*******************************************************************************************/
五、實例___棋盤的制作
使用繪圖實現
核心算法:
1.等分窗口的寬和高,形成一個個相同的格子,同時記錄格子的寬和高方便繪圖
gridW = width()/10; //窗口寬度分10份
gridH = height()/10; //窗口高度分10份
//棋盤起點坐標
startX = gridW;
startY = gridH;
2.計算鼠標落在哪個格子上,使用除法,鼠標的坐標除以格子的寬和高取整就知道落在哪個格子上
知道落在哪個格子上,再乘以對應的寬和高就知道繪圖的坐標
{
// 棋盤的位置轉換轉換為坐標下標值
// 類似於a[i][j]的i和j
chessX = (x - startX)/gridW;
chessY = (y - startY)/gridH;
qDebug() << chessX << chessY;
//更新窗口,間接調用paintEvent()
update();
}
//畫棋子
if(chessX != -1 && chessY != -1)
{
p.drawPixmap(startX+chessX*gridW, startY+chessY*gridH,
gridW, gridH,
QPixmap(":/new/prefix1/Image/face.png")
);
}
具體見《ChessDemo》

1 #ifndef CHESSWIDGET_H 2 #define CHESSWIDGET_H 3 4 #include <QWidget> 5 6 class ChessWidget : public QWidget 7 { 8 Q_OBJECT 9 10 public: 11 ChessWidget(QWidget *parent = 0); 12 ~ChessWidget(); 13 14 protected: 15 //重寫繪圖事件 16 void paintEvent(QPaintEvent *); 17 //重寫鼠標按下事件 18 void mousePressEvent(QMouseEvent *e); 19 20 private: 21 int gridW; //棋盤水平方向一個格子的寬度 22 int gridH; //棋盤水平方向一個格子的高度 23 int startX; //棋盤起點x坐標 24 int startY; //棋盤起點y坐標 25 26 int chessX, chessY; //棋盤下標 27 28 }; 29 30 #endif // CHESSWIDGET_H

1 #include "chesswidget.h" 2 #include <QPainter> 3 #include <QPen> 4 #include <QMouseEvent> 5 #include <QDebug> 6 7 ChessWidget::ChessWidget(QWidget *parent) 8 : QWidget(parent) 9 { 10 chessX = -1; 11 chessY = -1; 12 } 13 14 ChessWidget::~ChessWidget() 15 { 16 17 } 18 19 void ChessWidget::paintEvent(QPaintEvent *) 20 { 21 gridW = width()/10; //窗口寬度分10份 22 gridH = height()/10; //窗口高度分10份 23 24 //棋盤起點坐標 25 startX = gridW; 26 startY = gridH; 27 28 QPainter p(this); //創建畫家,指定窗口為繪圖設備 29 30 //背景圖 31 p.drawPixmap(rect(), QPixmap(":/new/prefix1/Image/bk.jpg")); 32 33 //設置畫筆 34 QPen pen; 35 pen.setWidth(4); //線寬 36 p.setPen(pen); //將畫筆交給畫家 37 38 //取中間8份畫棋盤 39 for(int i = 0; i <= 8; i++) 40 { 41 //橫線 42 p.drawLine(startX, startY+i*gridH, startX+8*gridW, startY+i*gridH); 43 44 //豎線 45 p.drawLine(startX+i*gridW, startY, startX+i*gridW, startY+8*gridH); 46 } 47 48 //畫棋子 49 if(chessX != -1 && chessY != -1) 50 { 51 p.drawPixmap(startX+chessX*gridW, startY+chessY*gridH, 52 gridW, gridH, 53 QPixmap(":/new/prefix1/Image/face.png") 54 ); 55 } 56 } 57 58 void ChessWidget::mousePressEvent(QMouseEvent *e) 59 { 60 //獲取點擊的坐標 61 int x = e->x(); 62 int y = e->y(); 63 64 // 要保證點擊點在棋盤范圍里面 65 if(x >= startX && x <= startX+8*gridW 66 && y >= startY && y <= startX+8*gridH) 67 { 68 // 棋盤的位置轉換轉換為坐標下標值 69 // 類似於a[i][j]的i和j 70 chessX = (x - startX)/gridW; 71 chessY = (y - startY)/gridH; 72 qDebug() << chessX << chessY; 73 74 //更新窗口,間接調用paintEvent() 75 update(); 76 } 77 }