声明下:这只是我自己在学习Qt的时候,想到的,也不知道这方法合不合适,反正是能够实现换肤功能。
一、思路及准备
双击主程序界面,弹出换肤界面,点击换肤界面上的图片,主程序改变相应界面背景图片。
我准备了四张图片,big_spring.png,big_summer.png,small_spring.png,small_summer.png.看名字就知道换肤界面列出来small_spring.png和small_summer.png,点击图片后,主程序界面显示相应大图。
二、皮肤窗口
新建Qt4空项目,新建C++类,继承自QWidget,取名SkinWidget。
头文件暂时不修改,看看SkinWidget.cpp
#include "skinwidget.h" #include <QPushButton> #include <QHBoxLayout> SkinWidget::SkinWidget(QWidget *parent) : QWidget(parent) { QStringList btnText; btnText<<"spring"<<"summer"; QHBoxLayout *mainLayout=new QHBoxLayout; for(int i=0;i<btnText.length();i++) { QPushButton *btn=new QPushButton; QString qss="background-image:url(:/img/small_"+btnText[i]+".png)"; btn->setToolTip(btnText[i]);//鼠标停留在按钮上的提示 btn->setStyleSheet(qss);//设置样式表,按钮的背景图片 btn->setFixedSize(150,120);//设置按钮固定大小为背景图片的大小 btn->setFlat(true);//设置按钮的平坦属性,也就是按钮无边框 mainLayout->addWidget(btn); } setLayout(mainLayout); setFixedSize(sizeHint().width(),sizeHint().height());//设置皮肤窗体为合适的大小 }
新建个main.cpp测试下,
#include <QApplication> #include "skinwidget.h" int main(int argc,char *argv[]) { QApplication app(argc,argv); SkinWidget skin; skin.show(); return app.exec(); }
看样子还马马虎虎,可以继续进行。
三、主程序窗口
新建C++类MainWidget,继承自QWidget。我们想实现双击主程序,弹出换肤窗体,就必须捕捉到双击时间,类似于MFC的OnDbClick。
先看看基类QWidget提供的虚函数
虚函数就是基类提供的,在我们需要的时候去重载的接口,所以在MainWidget头文件中,重载鼠标双击事件函数。
MainWidget.h
#ifndef MAINWIDGET_H #define MAINWIDGET_H #include <QWidget> class MainWidget : public QWidget { Q_OBJECT public: explicit MainWidget(QWidget *parent = 0); signals: public slots: protected: virtual void mouseDoubleClickEvent(QMouseEvent *);//重载鼠标双击事件函数 从QWidget继承 }; #endif // MAINWIDGET_H
MainWidget.cpp
#include "mainwidget.h" #include <QMouseEvent>//鼠标事件 #include <QPalette>//调色板 设置背景颜色 #include <QMessageBox> MainWidget::MainWidget(QWidget *parent) : QWidget(parent) { setFixedSize(500,400); QPalette palette; palette.setBrush(QPalette::Background,QBrush(QPixmap(":/img/big_spring.png"))); setPalette(palette); } void MainWidget::mouseDoubleClickEvent(QMouseEvent *event) { //如果双击鼠标左键,弹出测试对话框 if(event->button()&Qt::LeftButton) QMessageBox::information(this,"skin","leftButton double click test"); }
测试一下,结果还行。
四、皮肤窗口与主程序窗口的交互 和QSignalMapper类
在主窗口双击事件中,弹出皮肤窗体,然后把皮肤窗体各个按钮的clicked信号同主程序不同背景的槽连接起来
SkinWidget.h
#ifndef SKINWIDGET_H #define SKINWIDGET_H #include <QWidget> class SkinWidget : public QWidget { Q_OBJECT public: explicit SkinWidget(QWidget *parent = 0); signals: void changeSkinSpring();//与春天按钮的clicked信号绑定 void changeSkinSummer();//与夏天按钮的clicked信号绑定 private slots: }; #endif // SKINWIDGET_H
SkinWidget.cpp
#include "skinwidget.h" #include <QPushButton> #include <QHBoxLayout> SkinWidget::SkinWidget(QWidget *parent) : QWidget(parent) { QStringList btnText; btnText<<"spring"<<"summer"; QHBoxLayout *mainLayout=new QHBoxLayout; for(int i=0;i<btnText.length();i++) { QPushButton *btn=new QPushButton; QString qss="background-image:url(:/img/small_"+btnText[i]+".png)"; btn->setToolTip(btnText[i]);//鼠标停留在按钮上的提示 btn->setStyleSheet(qss);//设置样式表,按钮的背景图片 btn->setFixedSize(150,120);//设置按钮固定大小为背景图片的大小 btn->setFlat(true);//设置按钮的平坦属性,也就是按钮无边框 mainLayout->addWidget(btn); switch(i) { case 0: QObject::connect(btn,SIGNAL(clicked()),this,SIGNAL(changeSkinSpring())); break; case 1: QObject::connect(btn,SIGNAL(clicked()),this,SIGNAL(changeSkinSummer())); break; } } setLayout(mainLayout); setFixedSize(sizeHint().width(),sizeHint().height());//设置皮肤窗体为合适的大小 }
MainWidget.h
#ifndef MAINWIDGET_H #define MAINWIDGET_H #include <QWidget> class MainWidget : public QWidget { Q_OBJECT public: explicit MainWidget(QWidget *parent = 0); signals: private slots: void setBkSpring();//与SkinWidget的changeSkinSpring信号连接 void setBkSummer();//与SkinWidget的changeSkinSummer信号连接 protected: virtual void mouseDoubleClickEvent(QMouseEvent *);//重载鼠标双击事件函数 从QWidget继承 }; #endif // MAINWIDGET_H
MainWidget.cpp
#include "mainwidget.h" #include "skinwidget.h" #include <QMouseEvent>//鼠标事件 #include <QPalette>//调色板 设置背景颜色 MainWidget::MainWidget(QWidget *parent) : QWidget(parent) { setFixedSize(500,400); QPalette palette; palette.setBrush(QPalette::Background,QBrush(QPixmap(":/img/big_spring.png"))); setPalette(palette); } void MainWidget::mouseDoubleClickEvent(QMouseEvent *event) { SkinWidget *skin=new SkinWidget; skin->setAttribute(Qt::WA_DeleteOnClose); QObject::connect(skin,SIGNAL(changeSkinSpring()),this,SLOT(setBkSpring())); QObject::connect(skin,SIGNAL(changeSkinSummer()),this,SLOT(setBkSummer())); skin->show(); } //private slot 换肤 春天 void MainWidget::setBkSpring() { QPalette palette; palette.setBrush(QPalette::Background,QBrush(QPixmap(":/img/big_spring.png"))); setPalette(palette); } //private slot 换肤 夏天 void MainWidget::setBkSummer() { QPalette palette; palette.setBrush(QPalette::Background,QBrush(QPixmap(":/img/big_summer.png"))); setPalette(palette); }
运行下,确实可以。
但是,这是个好办法么,如果有1000张候选图片,我们就要建立1000个槽函数????
如果可以把图片的名字当作信号的参数多好啊,就是点击春天按钮的时候,把“spring”这个字符串当作信号的参数。点击夏天按钮的时候,把“summer”这个字符串当作信号的参数。问题转化为:如何把一个无参信号同一个有参信号联系起来。
终于,在网上搜到了这个类QSignalMapper。这个类可以看成信号的翻译和转发器,可以把无参信号翻译成带int参数、QString参数、QObject*参数或者QWidget*参数的信号, 并将之转发。
源码如下:
SkinWidget.h
#ifndef SKINWIDGET_H #define SKINWIDGET_H #include <QWidget> class QSignalMapper; class SkinWidget : public QWidget { Q_OBJECT public: explicit SkinWidget(QWidget *parent = 0); signals: void skinChange(QString); private slots: private: QSignalMapper *signalMapper; }; #endif // SKINWIDGET_H
SkinWidget.cpp
#include "skinwidget.h" #include <QPushButton> #include <QHBoxLayout> #include <QSignalMapper> SkinWidget::SkinWidget(QWidget *parent) : QWidget(parent) { signalMapper=new QSignalMapper(this); QStringList btnText; btnText<<"spring"<<"summer"; QHBoxLayout *mainLayout=new QHBoxLayout; for(int i=0;i<btnText.length();i++) { QPushButton *btn=new QPushButton; QString qss="background-image:url(:/img/small_"+btnText[i]+".png)"; btn->setToolTip(btnText[i]);//鼠标停留在按钮上的提示 btn->setStyleSheet(qss);//设置样式表,按钮的背景图片 btn->setFixedSize(150,120);//设置按钮固定大小为背景图片的大小 btn->setFlat(true);//设置按钮的平坦属性,也就是按钮无边框 mainLayout->addWidget(btn); QObject::connect(btn,SIGNAL(clicked()),signalMapper,SLOT(map())); signalMapper->setMapping(btn,btnText[i]); } connect(signalMapper,SIGNAL(mapped(QString)),this,SIGNAL(skinChange(QString))); setLayout(mainLayout); setFixedSize(sizeHint().width(),sizeHint().height());//设置皮肤窗体为合适的大小 }
MainWidget.h
#ifndef MAINWIDGET_H #define MAINWIDGET_H #include <QWidget> class MainWidget : public QWidget { Q_OBJECT public: explicit MainWidget(QWidget *parent = 0); signals: private slots: void setBk(QString); protected: virtual void mouseDoubleClickEvent(QMouseEvent *);//重载鼠标双击事件函数 从QWidget继承 }; #endif // MAINWIDGET_H
MainWidget.cpp
#include "mainwidget.h" #include "skinwidget.h" #include <QMouseEvent>//鼠标事件 #include <QPalette>//调色板 设置背景颜色 MainWidget::MainWidget(QWidget *parent) : QWidget(parent) { setFixedSize(500,400); setBk("spring"); } void MainWidget::mouseDoubleClickEvent(QMouseEvent *event) { if(event->button()&Qt::LeftButton) { SkinWidget *skin=new SkinWidget; skin->setAttribute(Qt::WA_DeleteOnClose); QObject::connect(skin,SIGNAL(skinChange(QString)),this,SLOT(setBk(QString))); skin->show(); } } //private slot void MainWidget::setBk(QString picName) { QPalette palette; palette.setBrush(QPalette::Background,QBrush(QPixmap(":/img/big_"+picName+".png"))); setPalette(palette); }
main.cpp
#include <QApplication> #include "skinwidget.h" #include "mainwidget.h" int main(int argc,char *argv[]) { QApplication app(argc,argv); // SkinWidget skin; // skin.show(); MainWidget mainWindow; mainWindow.show(); return app.exec(); }
运行结果: