主窗體
部件構成
菜單欄、工具欄、主窗體、狀態欄。
UI 編輯器設計主窗體
💡 簡易記事本的實現(part 1)
菜單欄
工具欄(圖標)
主窗體
完善菜單欄:
mainwindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"MainWindow::MainWindow(QWidget *parent) :QMainWindow(parent),ui(new Ui::MainWindow)
{ui->setupUi(this);this->setWindowTitle("新建文本文檔 - txt");this->setWindowIcon(QIcon("://notepad.png"));
}MainWindow::~MainWindow()
{delete ui;
}
Functions
狀態欄顯示提示信息
設置顯示的時長,時間單位:ms。
void showMessage(const QString & message, int timeout = 0);
設置標題欄的標題、圖標
void setWindowTitle(const QString &); // 設置標題欄的標題
void setWindowIcon(const QIcon & icon); // 設置標題欄的圖標
設置圖標然后運行,如果不能正常顯示,不用理會,是系統的問題。
QFile
繼承關系
QObject – QIODevice – QFileDevice – QFile
QIODevice:QT 對輸入輸出設備的抽象,提供了操作設備的一系列接口。
接口
QFile(const QString &name); // 使用文件名構造一個 QFile 對象,文件名可以包含路徑和文件名void setFileName(const QString & name); // 如果構造沒指定,也可以通過這個接口指定一下
bool exists(); // 判斷文件是否存在
bool open(OpenMode mode); // 繼承自 QIODevice,OpenMode 取值:QIODevice::ReadOnlyQIODevice::WriteOnlyQIODevice::ReadWrite...bool atEnd(); // 到達文件尾部
bool remove(); // 刪除文件
bool rename(const QString & newName); // 重命名文件
qint64 read(char * data, qint64 maxSize);
QByteArray read(qint64 maxSize); // 讀取最大maxSize字節,并返回一個QByteArray
QByteArray readAll(); // 讀取文件中所有數據,并返回一個QByteArrayqint64 write(const char * data, qint64 maxSize);
qint64 write(const QByteArray & byteArray);qint64 pos() const; // 得到當前讀寫指針的位置
bool seek(qint64 pos); // 重新設置讀寫指針位置bool QFileDevice::resize(qint64 sz); // 重設文件大小,可用于清空文件,比如 resize(0) qint64 size() const; // 獲取文件大小
💡 簡易記事本的實現(part 2)
mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H#include <QMainWindow>
#include <QtWidgets>namespace Ui {
class MainWindow;
}class MainWindow : public QMainWindow
{Q_OBJECTpublic:explicit MainWindow(QWidget *parent = 0);~MainWindow();private slots:void on_action_O_triggered();void on_action_S_triggered();private:Ui::MainWindow *ui;
};#endif // MAINWINDOW_H
mainwindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"MainWindow::MainWindow(QWidget *parent) :QMainWindow(parent),ui(new Ui::MainWindow)
{ui->setupUi(this);this->setWindowTitle("新建文本文檔 - txt");this->setWindowIcon(QIcon("://notepad.png"));
}MainWindow::~MainWindow()
{delete ui;
}void MainWindow::on_action_O_triggered()
{ui->statusBar->showMessage("Hello~", 0);QString filename = QFileDialog::getOpenFileName(this, tr("打開文件"), \".", tr("text (*.c *.cpp *.txt)"));if (filename.isEmpty()){ui->statusBar->showMessage("打開失敗", 1500);return ;}QFile file(filename);if (!file.open(QIODevice::ReadOnly)){ui->statusBar->showMessage("打開失敗", 1500);return ;}ui->statusBar->showMessage("打開成功", 1500);QByteArray buffer = file.readAll();ui->textEdit->setText(QString(buffer));
// ui->textEdit->setText(QString::fromUtf8(buffer)); // 可以// QString str = QString::fromLocal8Bit(buffer); // 不行,因為下面是 toUtf8
// ui->textEdit->setText(QString(str)); }void MainWindow::on_action_S_triggered()
{QString filename = QFileDialog::getSaveFileName(this, tr("保存文件"), \QDir::homePath(), tr("text (*.txt)"));if (filename.isEmpty()){ui->statusBar->showMessage("保存失敗", 1500);return ;}QFile file(filename);if (!file.open(QIODevice::WriteOnly | QIODevice::Text)){ui->statusBar->showMessage("保存失敗", 1500);return ;}QTextStream out(&file);QString str = ui->textEdit->toPlainText();QByteArray buffer;buffer.clear();buffer = str.toUtf8();out << buffer; // file.write(buffer);ui->statusBar->showMessage("保存成功", 1500);file.close();
}
QIODevice::Text 是一種打開方式,用于文件的讀寫操作。當以 QIODevice::Text 方式打開文件時,在讀取文件內容時,回車換行符(\r\n)會被自動轉換為換行符(\n);在寫入文件內容時,換行符(\n)會被自動轉換為系統的換行符。
編碼轉換
QT 界面只支持 utf-8 編碼。
QString fromLocal8Bit(const QByteArray &str); // 將QByteArray按照本地編碼轉換為QString
QString fromUtf8(const QByteArray &str); // 將QByteArray按照utf-8編碼轉換為QStringQByteArray toLocal8Bit(); // 把QString按照本地編碼轉換成QByteArray
QByteArray toUtf8(); // 把QString按照utf-8編碼轉換成QByteArray
事件
概念
事件由 窗口系統 或 QT 自身 以及 外部外設 產生,用以響應各種行為或情況。如:當按下鼠標或釋放鼠標時,會產生鼠標事件;按下鍵盤時出現按鍵事件。窗體有時需要捕捉這些事件并做出業務邏輯處理。
步驟
文字描述版:
所有的事件處理都是通過重寫某個事件方法來實現的:
圖片展示版:(以 QEvent::KeyPress 為例)
💡 完善登錄模塊
要求:按回車鍵觸發登錄事件,按ESC 鍵退出登錄界面。
widget.h
#ifndef WIDGET_H
#define WIDGET_H#include <QtWidgets>
#include <QPushButton>
#include <QLabel>
#include <QLineEdit>class Widget : public QWidget
{Q_OBJECTQPushButton *btn;QLineEdit *username;QLineEdit *password;QString usr;QString pwd;QLabel *label;QLabel *ulabel;QLabel *plabel;public:Widget(QWidget *parent = 0);~Widget();public slots:void verifySlot();protected:void keyPressEvent(QKeyEvent * event);};
widget.cpp
#include "widget.h"
#include <QDebug>Widget::Widget(QWidget *parent): QWidget(parent)
{resize(480, 300);btn = new QPushButton("Login", this); // 需要在堆區創建,不能在棧區創建btn->move(180, 220);ulabel = new QLabel("Username: ", this);ulabel->move(60, 80);username = new QLineEdit(this);username->move(180, 80);username->setPlaceholderText("Please input...");plabel = new QLabel("Password: ", this);plabel->move(60, 160);password = new QLineEdit(this);password->move(180, 160);password->setEchoMode(QLineEdit::Password);password->setPlaceholderText("Please input...");QObject::connect(btn, SIGNAL(clicked()), this, SLOT(verifySlot()));label = new QLabel(this);
}void Widget::verifySlot()
{usr = username->text();pwd = password->text();label->resize(480, 300);if (usr == "0828" && pwd == "0828"){
// label->setText("Logined successfully!"); // 成功顯示,但不美觀qDebug("Logined successfully!");this->close();QWidget *homePage = new QWidget;homePage->resize(309, 500);homePage->show();}else{// QObject::connect(btn, SIGNAL(clicked()), this, SLOT(close())); // 不能這樣寫,需要點擊兩次按鈕才會關閉小窗口this->close();}
}void Widget::keyPressEvent(QKeyEvent * event)
{
// qDebug("%x", event->key());if(event->key() == Qt::Key_Return || event->key() == Qt::Key_Enter)this->verifySlot();else if(event->key() == Qt::Key_Escape)this->close();elseQWidget::keyPressEvent(event);
}Widget::~Widget()
{}
💡 禁止輸入特殊字符
彈框
QToolTip::showText(this->mapToGlobal(this->pos()), "不能使用特殊字符", this);
widget.cpp
#include "widget.h"
#include "ui_widget.h"
#include "mytextedit.h"Widget::Widget(QWidget *parent) :QWidget(parent),ui(new Ui::Widget)
{ui->setupUi(this);MyTextEdit *mine = new MyTextEdit(this);
}Widget::~Widget()
{delete ui;
}
mytextedit.h
#ifndef MYTEXTEDIT_H
#define MYTEXTEDIT_H#include <QTextEdit>
#include <QtWidgets>class MyTextEdit : public QTextEdit // .cpp, Line 25
{Q_OBJECT
public:explicit MyTextEdit(QWidget *parent = 0);signals:public slots:protected:void keyPressEvent(QKeyEvent * event);};#endif // MYTEXTEDIT_H
mytextedit.cpp
#include "mytextedit.h"MyTextEdit::MyTextEdit(QWidget *parent) :QTextEdit(parent)
{
}void MyTextEdit::keyPressEvent(QKeyEvent * event)
{qDebug("%x", event->key());switch(event->key()){case Qt::Key_Backslash: // 5c、case Qt::Key_Slash: // 2f、case Qt::Key_Colon: // 3a、case Qt::Key_Asterisk: // 2a、case Qt::Key_Question: // 3f、case Qt::Key_QuoteDbl: // 22、case Qt::Key_Less: // 3c、case Qt::Key_Greater: // 3e、case Qt::Key_Bar: // 7cQToolTip::showText(this->mapToGlobal(this->pos()), "不能使用特殊字符", this);break;default:QTextEdit::keyPressEvent(event); // .h, Line 7break;}
// if (event->key() == Qt::Key_Backslash)
// QToolTip::showText(this->mapToGlobal(this->pos()), "不能使用特殊字符", this);
// else
// QTextEdit::keyPressEvent(event);
}
運行結果如下:
💡 使用鼠標滾輪 操作雙向進度條
int numDegrees = event->delta() / 8; // 滾動的角度,*8就是鼠標滾動的距離
int numSteps = numDegrees / 15; // 滾動的步數,*15就是鼠標滾動的角度// numSteps即上下滾動的步數,體現到數值上是 1/-1。相關事件類:QWheelEvent
widget.h
#ifndef WIDGET_H
#define WIDGET_H#include <QtWidgets>namespace Ui {
class Widget;
}class Widget : public QWidget
{Q_OBJECTpublic:explicit Widget(QWidget *parent = 0);~Widget();private:Ui::Widget *ui;QProgressBar *lbar;QProgressBar *rbar;int lval;int rval;protected:void wheelEvent(QWheelEvent * event);
};#endif // WIDGET_H
widget.cpp
#include "widget.h"
#include "ui_widget.h"Widget::Widget(QWidget *parent) :QWidget(parent),ui(new Ui::Widget)
{ui->setupUi(this);this->resize(600, 50);// 初始化兩個進度條lbar = new QProgressBar;rbar = new QProgressBar;// 為進度條設置默認值lbar->setValue(0);rbar->setValue(0);this->lval= 0;this->rval= 0;// 取消顯示進度條的百分比lbar->setTextVisible(false);rbar->setTextVisible(false);// 使進度條可以縱向拉伸lbar->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);rbar->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);// 翻轉左進度條lbar->setInvertedAppearance(true);// 添加進度條樣式,每個進度條中包含有 1px 的 border邊框// lbar->setStyleSheet(PROGRESS_LEFT_STYLE);// rbar->setStyleSheet(PROGRESS_RIGHT_STYLE);// 左右兩進度條橫向布局QHBoxLayout *box = new QHBoxLayout;box->addWidget(lbar);box->addWidget(rbar);box->setMargin(0);box->setSpacing(0);// 添加進度條兩側的空白,再次橫向布局QHBoxLayout *hbox = new QHBoxLayout;hbox->addStretch(1);hbox->addLayout(box, 28);hbox->addStretch(1);hbox->setMargin(0);hbox->setSpacing(0);// 添加進度條上下的空白,然后縱向布局QVBoxLayout *vbox = new QVBoxLayout(this);vbox->addStretch(1);vbox->addLayout(hbox, 5);vbox->addStretch(1);vbox->setMargin(0);vbox->setSpacing(0);
}void Widget::wheelEvent(QWheelEvent * event)
{int numDegrees = event->delta() / 8;int numSteps = numDegrees / 15;// qDebug() << numDegrees << numSteps; // ±15, ±1if (numDegrees < 0){if (this->rval < 0){this->rval = 0;this->lval += 10;if (this->lval >= 100)this->lval = 100;lbar->setValue(this->lval);qDebug() << "left: " << this->lval;}this->rval -= 10;rbar->setValue(this->rval);qDebug() << "right: " << this->rval;}else if (numDegrees > 0){if (this->lval < 0){this->lval = 0;this->rval += 10;if (this->rval >= 100)this->rval = 100;rbar->setValue(this->rval);qDebug() << "right: " << this->rval;}this->lval -= 10;lbar->setValue(this->lval);qDebug() << "left: " << this->lval;}else { }
}Widget::~Widget()
{delete ui;
}