說明:PyQt API 包含 400 多個類。 QObject 類位於類層次結構的頂部。而常見的又實用的一些知識點需要優先掌握,本篇就將一些常用要點展現出來。
QObject 類位於類層次結構的頂部。它是所有 Qt 對象的基類。此外,QPaintDevice 類是所有可繪制對象的基類。因此:
QApplication 類管理 GUI 應用程序的主要設置和控制流程。它包含主事件循環,在其中處理和調度由窗口元素和其他源生成的事件。它還處理系統范圍和應用程序范圍的設置。
QWidget 類,派生自 QObject 和 QPaintDevice 類,是所有用戶界面對象的基類。 QDialog 和 QFrame 類也派生自 QWidget 類。他們有自己的子類系統。
下圖描述了其層次結構中的一些重要類。
QLabel
Used to display text or image
2QLineEdit
Allows the user to enter one line of text
3QTextEdit
Allows the user to enter multi-line text
4QPushButton
A command button to invoke action
5QRadioButton
Enables to choose one from multiple options
6QCheckBox
Enables choice of more than one options
7QSpinBox
Enables to increase/decrease an integer value
8QScrollBar
Enables to access contents of a widget beyond display aperture
9QSlider
Enables to change the bound value linearly.
10QComboBox
Provides a dropdown list of items to select from
11QMenuBar
Horizontal bar holding QMenu objects
12QStatusBar
Usually at bottom of QMainWindow, provides status information.
13QToolBar
Usually at top of QMainWindow or floating. Contains action buttons
14QListView
Provides a selectable list of items in ListMode or IconMode
15QPixmap
Off-screen image representation for display on QLabel or QPushButton object
16QDialog
Modal or modeless window which can return information to parent window
一個典型的基於 GUI 的應用程序的頂層窗口是由 QMainWindow 小部件對象創建的。上面列出的一些小部件在此主窗口中占據指定位置,而其他小部件則使用各種布局管理器放置在中央小部件區域。
下圖顯示了 QMainWindow 框架 -
Qt提供了下列主視窗管理和相關的用戶界面組件的類別:
Qt的圖形用戶界面的基礎是QWidget。Qt中所有類型的GUI組件如按鈕、標簽、工具欄等都派生自QWidget,而QWidget本身則為QObject的子類。Widget負責接收鼠標,鍵盤和來自窗口系統的其他事件,並描繪了自身顯示在屏幕上。每一個GUI組件都是一個widget,widget還可以作為容器,在其內包含其他Widget。
QWidget不是一個抽象類別。並且可以被放置在一個已存在的用戶界面中;若是Widget沒有指定父Widget,當它顯示時就是一個獨立的視窗、或是一個頂層widget。QWidget顯示能力包含了透明化及Double-Buffering。Qt提供一種托管機制,當Widget於創建時指定父對象,就可把自己的生命周期交給上層對象管理,當上層對象被釋放時,自己也被釋放。確保對象不再使用時都會被刪除。
以下是一個QGraphicsView的例子,實現滑動的窗體效果,工具欄和圖片均為場景中的Item。
QGraphicsView用來顯示一個滾動視圖區的QGraphicsScene內容。QGraphicsScene提供了QGraphicsItem的容器功能。通常與QGraphicsView一起使用來描述可視化圖形項目。
QGraphicsScene提供了一個視圖的場景,通過在這樣一個場景之上加入不同的QGraphicsItem來構建視圖。而QGraphicsView則提供了一個widget來顯示QGraphicsScene的內容。所以要想成功構建一個視圖,這三個元素共同搭配,合成整體。
Qt利用信號與槽(signals/slots)機制取代傳統的callback來進行對象之間的溝通。當操作事件發生的時候,對象會發提交一個信號(signal);而槽(slot)則是一個函數接受特定信號並且執行槽本身設置的動作。信號與槽之間,則透過QObject的靜態方法connect來鏈接。
信號在任何執行點上皆可發射,甚至可以在槽裡再發射另一個信號,信號與槽的鏈接不限定為一對一的鏈接,一個信號可以鏈接到多個槽或多個信號鏈接到同一個槽,甚至信號也可連接到信號。
以往的callback缺乏類型安全,在調用處理函數時,無法確定是傳遞正確類型的參數。但信號和其接受的槽之間傳遞的資料類型必須要相符合,否則編譯器會提出警告。信號和槽可接受任何數量、任何類型的參數,所以信號與槽機制是完全類型安全。
信號與槽機制也確保了低耦合性,發送信號的類別並不知道是哪個槽會接受,也就是說一個信號可以調用所有可用的槽。此機制會確保當在"連接"信號和槽時,槽會接受信號的參數並且正確執行。
布局管理類別用於描述一個應用程序的用戶界面中的Widget是如何放置。當視窗縮放時,布局管理器會自動調整widget的大小、位置或是字號,確保他們相對的排列和用戶界面整體仍然保有可用性。
Qt內置的布局管理類型有:QHBoxLayout、QVBoxLayout、QGridLayout和QFormLayout。這些類別繼承自QLayout,但QLayout非繼承自QWidget而是直接源於QObject。他們負責widget的幾何管理。想要創建更復雜的版面配置,可以繼承QLayout來自定義版面配置管理員。
[cpp] view plain copy
#include <QtCore>
#include <QtGui>
#include <QtSvg>
/*程序中用到了svg格式的圖片,所以需包含QtSvg*/
#define PAGE_COUNT 5
/*定義滑動窗體的窗體數*/
定義工具欄,NviBar繼承自QGraphicsRectItem,用法與QGraphicsItem類似。
[cpp] view plain copy
class NaviBar : public QObject, public QGraphicsRectItem
{
Q_OBJECT
public:
NaviBar();
void setPageOffset(qreal ofs);
signals:
void pageSelected(int page);
protected:
void mousePressEvent(QGraphicsSceneMouseEvent *event);
void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = 0);
private:
QList<QGraphicsSvgItem*> m_icons;
QGraphicsRectItem *m_cursor;
};
函數實現
[cpp] view plain copy
#define ICON_SIZE 50
#define ICON_PAD 4
NaviBar::NaviBar() : QGraphicsRectItem()
{
setRect(0, 0, 5 * ICON_SIZE, ICON_SIZE);
setPen(Qt::NoPen);
QStringList names;
names << "map" << "web" << "home" << "weather" << "contacts";
for (int i = 0; i < names.count(); ++i) {
QString fname = names[i];
fname.prepend(":/icons/");
fname.append("-page.svg");
QGraphicsSvgItem *icon = new QGraphicsSvgItem(fname);
icon->setParentItem(this);
const int dim = ICON_SIZE - ICON_PAD * 2;
qreal sw = dim / icon->boundingRect().width();
qreal sh = dim / icon->boundingRect().height();
icon->setTransform(QTransform().scale(sw, sh));
icon->setZValue(2);
m_icons << icon;
}
m_cursor = new QGraphicsRectItem;
m_cursor->setParentItem(this);
m_cursor->setRect(0, 0, ICON_SIZE, ICON_SIZE);
m_cursor->setZValue(1);
m_cursor->setPen(Qt::NoPen);
m_cursor->setBrush(QColor(Qt::white));
m_cursor->setOpacity(0.6);
}
void NaviBar::setPageOffset(qreal ofs)
{
m_cursor->setPos(ofs * ICON_SIZE, 0);
for (int i = 0; i < m_icons.count(); ++i) {
int y = (i == static_cast<int>(ofs + 0.5)) ? ICON_PAD : ICON_PAD * 2;
m_icons[i]->setPos(i * ICON_SIZE + ICON_PAD, y);
m_icons[i]->setOpacity(1);
}
}
void NaviBar::mousePressEvent(QGraphicsSceneMouseEvent *event)
{
emit pageSelected(static_cast<int>(event->pos().x() / ICON_SIZE));
}
void NaviBar::paint(QPainter * painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{
painter->setBrush(Qt::white);
painter->setOpacity(0.2);
painter->drawRect(option->rect.adjusted(-20, ICON_PAD, 20, 0));
}
定義視圖
[cpp] view plain copy
class ParallaxHome: public QGraphicsView
{
Q_OBJECT
public:
QGraphicsScene m_scene;
NaviBar *m_naviBar;
QGraphicsPixmapItem *m_wallpaper;
QTimeLine m_pageAnimator;
qreal m_pageOffset;
QList<QGraphicsPixmapItem*> m_items;
QList<QPointF> m_positions;
public:
ParallaxHome(QWidget *parent = 0)
: QGraphicsView(parent)
, m_pageOffset(-2) {
setupScene();
setScene(&m_scene);
setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
setFrameShape(QFrame::NoFrame);
setWindowTitle("Parallax Home");
connect(&m_pageAnimator, SIGNAL(frameChanged(int)), SLOT(shiftPage(int)));
m_pageAnimator.setDuration(500);
m_pageAnimator.setFrameRange(0, 100);
m_pageAnimator.setCurveShape(QTimeLine::EaseInCurve);
pageChanged(static_cast<int>(m_pageOffset));
}
signals:
void pageChanged(int page);
public slots:
void slideRight() {
if (m_pageAnimator.state() != QTimeLine::NotRunning)
return;
int edge = -(m_pageOffset - 1);
if (edge < PAGE_COUNT)
slideBy(-1);
}
void slideLeft() {
if (m_pageAnimator.state() != QTimeLine::NotRunning)
return;
if (m_pageOffset < 0)
slideBy(1);
}
void slideBy(int dx) {
int start = m_pageOffset * 1000;
int end = (m_pageOffset + dx) * 1000;
m_pageAnimator.setFrameRange(start, end);
m_pageAnimator.start();
}
void choosePage(int page) {
if (m_pageAnimator.state() != QTimeLine::NotRunning)
return;
if (static_cast<int>(-m_pageOffset) == page)
return;
slideBy(-page - m_pageOffset);
}
private slots:
void shiftPage(int frame) {
int ww = width();
int hh = height() - m_naviBar->rect().height();
int oldPage = static_cast<int>(-m_pageOffset);
m_pageOffset = static_cast<qreal>(frame) / qreal(1000);
int newPage = static_cast<int>(-m_pageOffset);
m_naviBar->setPageOffset(-m_pageOffset);
if (oldPage != newPage)
emit pageChanged(newPage);
int ofs = m_pageOffset * ww;
for (int i = 0; i < m_items.count(); ++i) {
QPointF pos = m_positions[i];
QPointF xy(pos.x() * ww, pos.y() * hh);
m_items[i]->setPos(xy + QPointF(ofs, 0));
}
int center = m_wallpaper->pixmap().width() / 2;
const int parallax = 3;
int base = center - (ww / 2) - (PAGE_COUNT >> 1) * (ww / parallax);
int wofs = base - m_pageOffset * ww / parallax;
m_wallpaper->setPos(-wofs, 0);
}
protected:
void resizeEvent(QResizeEvent *event) {
Q_UNUSED(event);
layoutScene();
}
void keyPressEvent(QKeyEvent *event) {
if (event->key() == Qt::Key_Right)
slideRight();
if (event->key() == Qt::Key_Left)
slideLeft();
event->accept();
}
private:
void layoutScene() {
int ww = width();
int hh = height();
m_scene.setSceneRect(0, 0, PAGE_COUNT * ww - 1, hh - 1);
centerOn(ww / 2, hh / 2);
int nw = m_naviBar->rect().width();
int nh = m_naviBar->rect().height();
m_naviBar->setPos((ww - nw) / 2, hh - nh);
shiftPage(m_pageOffset * 1000);
}
void setupScene() {
qsrand(QTime::currentTime().second());
QStringList names;
names << "brownies" << "cookies" << "mussels" << "pizza" << "sushi";
names << "chocolate" << "fish" << "pasta" << "puding" << "trouts";
for (int i = 0; i < PAGE_COUNT * 2; ++i) {
QString fname = names[i];
fname.prepend(":/images/");
fname.append(".jpg");
QPixmap pixmap(fname);
pixmap = pixmap.scaledToWidth(200);
QGraphicsPixmapItem *item = m_scene.addPixmap(pixmap);
m_items << item;
qreal x = (i >> 1) + (qrand() % 30) / 100.0;
qreal y = (i & 1) / 2.0 + (qrand() % 20) / 100.0;
m_positions << QPointF(x, y);
item->setZValue(1);
}
m_naviBar = new NaviBar;
m_scene.addItem(m_naviBar);
m_naviBar->setZValue(2);
connect(m_naviBar, SIGNAL(pageSelected(int)), SLOT(choosePage(int)));
m_wallpaper = m_scene.addPixmap(QPixmap(":/icons/surfacing.png"));
m_wallpaper->setZValue(0);
m_scene.setItemIndexMethod(QGraphicsScene::NoIndex);
}
};
main函數
[cpp] view plain copy
#include "parallaxhome.moc"
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
ParallaxHome w;
w.resize(360, 640);
w.show();
return app.exec();
}