从 QT3 到 QT4, designer 到 creator,QT UI文件的变化
转帖!
一些关于qt3和qt4的理解
如果你用过了qt3,也用过qt4的话,肯定觉得变化很大,不是吗?
qt4没有了pro文件的管理
Removed project manager. Qt Designer now only reads and edits .ui files. It has no notion of a project (.pro file)
Qt4改变了.ui的文件格式,虽然提供了转化工具,大家还是觉得不好玩。
Changed format of .ui files. Qt Designer 4 cannot read files created by Qt Designer 3 and vice versa. However, we provide the tool uic3 to generate Qt 4 code out of Qt 3 .ui files, and to convert old .ui files into a format readable by Qt Designer 4.
更抠门的是,居然连c++的编辑器也不给我们用。
Removed code editor. Qt Designer can no longer be used to edit source files.
更深入一层来说,看一些qt4提供的例子,怎么觉得有点不大像qt3 c++的习惯,c++是可以这样的吗?qt4中自己的槽在哪里添加啊?
同样我们也会对qt3以前的.ui.h这个文件感到有点纳闷,看上去是头文件,但是进去看怎么看都觉得像是观念中的源文件,因为毕竟里面是实现特殊函数――槽(SLOT)功能的地方。
更烦的是,Qt3中用designer设计好了的.pro文件,然后用vc工具打开,添加一些自己改动,等到下次由于编译需要clean一下,再次用工具打开pro文件,天哪,我辛辛苦苦添加的东西到哪里去了啊?!!
本文试着根据自身的一些实践,结合论坛中的一些网友遇到的问题,说说自己的看法,希望能抛砖引玉,对大家学习qt有所帮助。
按照历史顺序,首先说qt3。
在qt3或者以前的版本中,可能大家的习惯做法是,打开designer,添加pro文件,设置头文件路径,设置是app还是lib,设置要连接lib的路径和对应的lib的名称;接着开始新建一个dialog,在上面添加按钮,然后保存成.ui文件;开始写main函数,像我这么懒的人,main函数都是根据designer生成的。一切都顺利的话,你可以选择开始qmake了,接着make(或者nmake等等);如果在WINDOWS下,也可以选择用vc中的qt工具栏打开pro文件,编译链接,一般情况下,你都会很高兴的看到自己第一个qt的界面。
对了,要添加自己的槽呢?
你可以在(Object)下面的Member选项卡里面找到Slot一项,添加一个槽,如果你在某个slot点击Edit的话,qt就会弹出一个窗口,这个窗口上面提到的.ui.h的文件所在。如果想初始化自己定义的一些变量,可以在function里面添加init()函数,当添加完以后,你会发现后面会多了(Constructor)这个东西,其实在.ui.h前面那段注释的代码里面就说得很清楚了,
/****************************************************************************
** ui.h extension file, included from the uic-generated form implementation.
**
** If you want to add, delete, or rename functions or slots, use
** Qt Designer to update this file, preserving your code.
**
** You should not define a constructor or destructor in this file.
** Instead, write your code in functions called init() and destroy().
** These will automatically be called by the form's constructor and
** destructor.
*****************************************************************************/
也可以通过打开根据ui生成的Form1.cpp看到,类的构造函数的最后面调用了init()这个函数,这个函数在没有添加的时候是没有出现在ui生成的Form1.cpp文件里面的。init()对应的就是destory()了,这个就不多说了。
不喜欢Form1这个类的名字,要改变类的名字怎么办?可以在ui上面点击右键选择Form setting ,在弹出的对话框里面会看到class name的一项,在那里写上自己喜欢的类名吧。这个类名就是相当于Form1,Form1怎么用,这个类就怎么用。
大家是不是觉得这些步骤很像vc的做法呢?具体点就是MFC的做法呢?
也有一些网友会根据qt文档里面的说法去做:写好了.h文件和.cpp文件,接着用
qmake –project
这样的方法来生成pro文件,个人觉得这样的做法不提倡,为什么呢?因为一般来说一个工程都要包含其他目录下的头文件,需要链接别的不是qt的库,这样的话就要对每次生成的pro文件进行添加修改,不是很方便。其次,有人喜欢把头文件和源文件分别放在不同的目录下,这样的话管理很方便,但是
qmake –project
只对当前目录下的头文件和源文件起作用。
至少在windows下,用
qmake –project
生成pro文件的话,后面的生成的.obj文件就很乱的
最好还是在designer里面的pro设置里面做这些吧。
论坛里面的有些牛人自己写pro文件的,这个就不是本人能够到达的境界了,没有能力在这里讨论了。
不管怎样,不就是为了得到Makefile吗?下面就专门看它怎么发飙了?
插播一下,真的要谢谢qmake,它那么容易就让我们得到了Makefile,Makefile是什么?!后面的一切都是听他指挥的,要是我们自己写的话,像我怎么懒的人才不写呢!!况且我也不大会写呢~_~
随便用一个文本编辑器打开Makefile,大概会看到下面的一些东西吧qt332版本的
#############################################################################
# Makefile for building: myqt332test
# Generated by qmake (1.07a) (Qt 3.3.2) on: Thu Jun 08 22:41:07 2006
# Project: myqt332test.pro
# Template: app
# Command: $(QMAKE) -o Makefile myqt332test.pro
#############################################################################
####### Compiler, tools and options
CC = cl
CXX = cl
LEX = flex
YACC = byacc
CFLAGS = -nologo -Zm200 -W3 -MDd -Zi -Gm -DUNICODE -DWIN32 -DQT_DLL -DQT_THREAD_SUPPORT
CXXFLAGS = -nologo -Zm200 -W3 -w34100 -w34189 -MDd -Zi -Gm -DUNICODE -DWIN32 -DQT_DLL -DQT_THREAD_SUPPORT
LEXFLAGS =
YACCFLAGS =-d
INCPATH = -I"$(QTDIR)\include" -I"D:\qttest\myqt332test" -I"D:\QT332\mkspecs\win32-msvc.net"
LINK = link
LFLAGS = /NOLOGO /DEBUG /SUBSYSTEM:WINDOWS /LIBPATH:"$(QTDIR)\lib"
LIBS = "qt-mt332.lib" "qtmain.lib" "kernel32.lib" "user32.lib" "gdi32.lib" "comdlg32.lib" "advapi32.lib" "shell32.lib" "ole32.lib" "oleaut32.lib" "uuid.lib" "imm32.lib" "winmm.lib" "wsock32.lib" "winspool.lib" "delayimp.lib" "opengl32.lib" "glu32.lib"
MOC = $(QTDIR)\bin\moc.exe
UIC = $(QTDIR)\bin\uic.exe
QMAKE = qmake
IDC = $(QTDIR)\bin\idc.exe
IDL = midl
ZIP = zip -r -9
COPY_FILE = copy
COPY_DIR = copy
DEL_FILE = del
DEL_DIR = rmdir
MOVE = move
CHK_DIR_EXISTS = if not exist
MKDIR = mkdir
INSTALL_FILE= $(COPY_FILE)
INSTALL_DIR = $(COPY_DIR)
####### Files
HEADERS =
SOURCES = main.cpp
OBJECTS = main.obj \
myqt332test.obj
FORMS = myqt332test.ui
UICDECLS = myqt332test.h
UICIMPLS = myqt332test.cpp
SRCMOC = moc_myqt332test.cpp
OBJMOC = moc_myqt332test.obj
DIST =
TARGET = myqt332test.exe
####### Implicit rules
.SUFFIXES: .c .cpp .cc .cxx .C
{.}.cpp{}.obj::
$(CXX) -c $(CXXFLAGS) $(INCPATH) -Fo @<<
$<
<<
{.}.cc{}.obj::
$(CXX) -c $(CXXFLAGS) $(INCPATH) -Fo @<<
$<
<<
{.}.cxx{}.obj::
$(CXX) -c $(CXXFLAGS) $(INCPATH) -Fo @<<
$<
<<
{.}.C{}.obj::
$(CXX) -c $(CXXFLAGS) $(INCPATH) -Fo @<<
$<
<<
{.}.c{}.obj::
$(CC) -c $(CFLAGS) $(INCPATH) -Fo @<<
$<
<<
####### Build rules
all: Makefile $(TARGET)
$(TARGET): $(UICDECLS) $(OBJECTS) $(OBJMOC)
$(LINK) $(LFLAGS) /OUT:$(TARGET) @<<
$(OBJECTS) $(OBJMOC) $(LIBS)
<<
mocables: $(SRCMOC)
uicables: $(UICIMPLS) $(UICDECLS)
Makefile: myqt332test.pro D:\QT332\mkspecs\win32-msvc.net\qmake.conf ..\..\QT332\lib\qtmain.prl
$(QMAKE) -o Makefile myqt332test.pro
qmake:
@$(QMAKE) -o Makefile myqt332test.pro
dist:
$(ZIP) myqt332test.zip $(SOURCES) $(HEADERS) $(DIST) $(FORMS) D:/qttest/myqt332test/myqt332test.pro ..\..\QT332\lib\qtmain.prl myqt332test.ui.h
uiclean:
-$(DEL_FILE) myqt332test.h
-$(DEL_FILE) myqt332test.cpp
mocclean:
-$(DEL_FILE) moc_myqt332test.cpp
-$(DEL_FILE) moc_myqt332test.obj
clean: uiclean mocclean
-$(DEL_FILE) main.obj
-$(DEL_FILE) myqt332test.obj
-$(DEL_FILE) myqt332test.pdb
-$(DEL_FILE) myqt332test.ilk
-$(DEL_FILE) vc*.pdb
-$(DEL_FILE) vc*.idb
FORCE:
distclean: clean
-$(DEL_FILE) $(TARGET)
####### Compile
main.obj: main.cpp \
myqt332test.h \
myqt332test.h: myqt332test.ui
$(UIC) myqt332test.ui -o myqt332test.h
myqt332test.cpp: myqt332test.h myqt332test.ui
$(UIC) myqt332test.ui -i myqt332test.h -o myqt332test.cpp
myqt332test.obj: myqt332test.cpp \
myqt332test.ui.h \
myqt332test.h \
moc_myqt332test.obj: moc_myqt332test.cpp myqt332test.h
moc_myqt332test.cpp: $(MOC) myqt332test.h
$(MOC) myqt332test.h -o moc_myqt332test.cpp
####### Install
install:
uninstall:
在里面,我们可以看到,myqt332test.h和myqt332test.cpp p是怎么生成,moc_*.cpp是怎么生成的,使用的是那个c++的编译器,clean的话会清除什么东西
有点晕了吧,坚持一下,你会有收获的,关键来看看这里
myqt332test.h: myqt332test.ui
$(UIC) myqt332test.ui -o myqt332test.h
myqt332test.cpp: myqt332test.h myqt332test.ui
$(UIC) myqt332test.ui -i myqt332test.h -o myqt332test.cpp
myqt332test.obj: myqt332test.cpp \
myqt332test.ui.h \
myqt332test.h \
moc_myqt332test.obj: moc_myqt332test.cpp myqt332test.h
moc_myqt332test.cpp: $(MOC) myqt332test.h
$(MOC) myqt332test.h -o moc_myqt332test.cpp
myqt332test.h 是靠这句话生成的 $(UIC) myqt332test.ui -o myqt332test.h
myqt332test.cpp 是靠这句话生成的 $(UIC) myqt332test.ui -i myqt332test.h -o myqt332test.cpp
moc_myqt332test.cpp 是靠这句话生成的 $(MOC) myqt332test.h -o moc_myqt332test.cpp
至于$(UIC) 和 $(MOC)是什么意思,?
MOC = $(QTDIR)\bin\moc.exe
UIC = $(QTDIR)\bin\uic.exe
QMAKE = qmake
一切的一切,都在Makefile里面说得清清楚楚。
这也是经常提到的为什么我的一些修改会丢了呢?因为你的修改是在myqt332test.h和myqt332test.cpp里面的,clean以后 再次qmake会重新生成的。所以你要改什么控件的话,最好还是在designer里面修改,但是myqt332test.ui.h不一样,它不是生成的,不会被覆盖。其实myqt332test.ui.h里面是一些自己定义的东西,比如槽,比如变量的初始化,所以qt3的设计者就是要我们在myqt332test.ui.h里面写东西,在init()里面初始化,而不是像平时的c++一样,在myqt332test.h里面定义变量,跑到myqt332test.cpp里面写东西,这个在qt4里面改变了方式了,呵呵,不觉得myqt332test.ui.h有点怪怪的吗?
在生成的myqt332test.cpp 里面可以看到,里面的控件是怎么创建的。
myqt332test::myqt332test( QWidget* parent, const char* name, bool modal, WFlags fl )
: QDialog( parent, name, modal, fl )
{
if ( !name )
setName( "myqt332test" );
pushButton1 = new QPushButton( this, "pushButton1" );
pushButton1->setGeometry( QRect( 110, 115, 126, 26 ) );
pushButton2 = new QPushButton( this, "pushButton2" );
pushButton2->setGeometry( QRect( 110, 210, 131, 31 ) );
textLabel1 = new QLabel( this, "textLabel1" );
textLabel1->setGeometry( QRect( 370, 180, 81, 46 ) );
languageChange();
resize( QSize(600, 480).expandedTo(minimumSizeHint()) );
clearWState( WState_Polished );
init();
}
看到了吧,里面控件的父类都是this,在这里可能你觉得的很自然,到了qt4就不一样了,到qt4的时候再详细讨论。
看完了makefile ,再来看看qt3的designer的一些特色。
其实个人觉得qt4以前的designer很像vc,比如lib和include 的路径的设置,生成dll还是exe,新建一个槽或者函数,变量的添加,关键字的着色,都可以在designer里面做到,就差不能编译和调试,这也是很多人感到不爽的地方,qt4干脆就把这些砍掉,狠^_^
不过layout这个东西确实让人耳恳恍拢 褂芯褪悄歉龅 ?,
qt3大概就先说这些吧,以后想到什么再补吧,
下面把目光放在qt4身上。对于designer来说,她确实苗条一些了。
首先是生成的头文件的默认的名字由qt3的classname.h变成了ui_*.h,其次就是创建控件的方式有了很大不同,这或许是很多网友感到郁闷的地方,还有就是pro文件不再跟designer在一起了。更要命的是,很多控件类的名字都改变了。但是有一点值得肯定的是:事物总是朝着好的方向发展的。
随便在designer里面画几个控件。保存.ui文件。然后写一个main函数
大概是这个样子吧
#include <qapplication.h>
#include "ui_mytest.h"
int main(int argc, char *argv[])
{
QApplication app(argc,argv);
QWidget *dlg = new QWidget();
Ui::mytest ui;
ui.setupUi(dlg);
dlg->show();
return app.exec();
}
大家可能对这段身材比较感兴趣
QWidget *dlg = new QWidget();
Ui::mytest ui;
ui.setupUi(dlg);
dlg->show();
为什么show的是dlg呢?setupUi到底做了什么?为什么要Ui::mytest ui这样定义呢?
看头看尾,看来只好在ui_mytest.h里面找答案了;
#ifndef UI_MYTEST_H
#define UI_MYTEST_H
#include <QtCore/QVariant>
#include <QtGui/QAction>
#include <QtGui/QApplication>
#include <QtGui/QButtonGroup>
#include <QtGui/QLabel>
#include <QtGui/QPushButton>
#include <QtGui/QWidget>
class Ui_mytest
{
public:
QPushButton *pushButton;
QLabel *label;
QPushButton *pushButton_2;
void setupUi(QWidget *mytest)
{
mytest->setObjectName(QString::fromUtf8("mytest"));
mytest->resize(QSize(400, 300).expandedTo(mytest->minimumSizeHint()));
pushButton = new QPushButton(mytest);
pushButton->setObjectName(QString::fromUtf8("pushButton"));
pushButton->setGeometry(QRect(70, 100, 75, 23));
label = new QLabel(mytest);
label->setObjectName(QString::fromUtf8("label"));
label->setGeometry(QRect(183, 110, 111, 31));
pushButton_2 = new QPushButton(mytest);
pushButton_2->setObjectName(QString::fromUtf8("pushButton_2"));
pushButton_2->setGeometry(QRect(80, 200, 75, 23));
retranslateUi(mytest);
QMetaObject::connectSlotsByName(mytest);
} // setupUi
void retranslateUi(QWidget *mytest)
{
mytest->setWindowTitle(QApplication::translate("mytest", "Form", 0, QApplication::UnicodeUTF8));
pushButton->setText(QApplication::translate("mytest", "PushButton", 0, QApplication::UnicodeUTF8));
label->setText(QApplication::translate("mytest", "TextLabel", 0, QApplication::UnicodeUTF8));
pushButton_2->setText(QApplication::translate("mytest", "PushButton", 0, QApplication::UnicodeUTF8));
Q_UNUSED(mytest);
} // retranslateUi
};
namespace Ui {
class mytest: public Ui_mytest {};
} // namespace Ui
#endif // UI_MYTEST_H
看来答案已经差不多了。
main.cpp 里面的这句话其实创建一个“画板”,好让你在上面画东西;
QWidget *dlg = new QWidget();
Ui::mytest ui;
这句话的是定义名字空间namespace Ui 里面的一个类成员class mytest的一个对象 ui;
要看到这个class mytest 是共有继承这个类的public Ui_mytest,而这个类class Ui_mytest却不是继承任何qt的类。所以setupUi()要用把画板的指针传进来,
ui.setupUi(dlg);
然后所有的控件的创建都是在这个画板上面进行的。
最后我们show的时候就把dlg show出去。其实这个时候上面已经有我们画的东西(控件)了。
给你一张白纸(dlg),画好你的东西,然后帖到画板上(setupUi),show的时候当然就是你画的东西啦!
注意一个小细节,名字空间里面的类的名字怎么设置的?是在designer画ui的时候,右键菜单里面有个change objName,这里就是你改变
namespace Ui {
class mytest: public Ui_mytest {};
}
中的 class mytest的时候。
休息一下!!
对了,那些qt关键的槽在那里设置啊?这可是qt的卖点啊?
那是因为我们给的画板太白了!它只是简单的一个widget,没有我们自己的槽,还是自己动手吧!
因为我们自己的槽人家qt怎么知道是什么啊?
mytest.h
#include "ui_mytest.h"
#include <qwidget.h>
class mytest : public QWidget
{
Q_OBJECT//这个对于slots来说很重要,不要忘记了
public:
mytest();
virtual ~mytest();
public slots:
void myslots();
private :
Ui::mytest ui;
};
各位看官注意了!这个class mytest 和 Ui::mytest 是不一样的,这里就体现了qt4 中designer的特点,她只是画Ui而已,其他的回归c++
mytest.cpp
#include "mytest.h"
#include <qmessagebox.h>
mytest::mytest()
{
ui.setupUi(this);//以后写构造函数第一件事就是写这个函数
connect(ui.pushButton,SIGNAL(clicked()),this,SLOT(myslots()));
}
mytest::~mytest()
{
}
void mytest::myslots()
{
QMessageBox::information(this,"debug","myslots");
}
这里要注意的是connect(ui.pushButton,SIGNAL(clicked()),this,SLOT(myslots()));
中的ui.pushButton,而不是经常写的pushButton,这也是由于是在pushButton Ui里面的
,而不是在class mytest里面的。
对应的main.cpp就变成了这个样子了。
#include <qapplication.h>
//#include "ui_mytest.h"
#include "mytest.h"
int main(int argc, char *argv[])
{
QApplication app(argc,argv);
// QWidget *dlg = new QWidget();
// Ui::mytest ui;
// ui.setupUi(dlg);
// dlg->show();
mytest *dlg = new mytest();
dlg->show();
return app.exec();
}
一句话,到处都体现了qt4的设计思想,designer 只是画控件的工具。
很久没有写东西了,语句不是很通顺,也是第一次写这样的东西,难免有错漏,请各位网友指正,共同进步。