python窗体——pyqt初体验
连续两周留作业要写ftp的作业,从第一周就想实现一个窗体版本的,但是时间实在太短,qt零基础选手表示压力很大,幸好又延长了一周时间,所以也就有了今天这篇文章。。。只是为了介绍一些速成的方法,还有初学者会遇到的问题。。。
这里先介绍一个安装连接,一条龙服务,各种安装配置在这里都找得到:http://blog.sina.com.cn/s/blog_4c18e3160101a12g.html
什么是pyqt?
简而言之,qt是一个开发窗体程序的模块,原本是是C++的库,PyQt是Python的移植版本,所以我们安装了pyqt之后,就可以在python上面进行窗体的开发了。python有自带的窗体模块Tkinter,但是不够好用,为什么不够好用呢?因为画窗体什么的都要自己写代码...用了qt以后,我们就可以通过qt designer进行开发了,什么是qt disigner呢?
就是个啦,你可以在这里设计你的窗口,画编辑框,画按钮,想画什么画什么,总之,不用写代码看天书的东西都是好东西!
什么是eric?
我们有了qt,可以直接画窗体了,但是悲剧的是我们画好了窗体,生成的是一个.ui文件,python不认识啊,这个时候eric的作用就体现出来了,它可以把ui文件转换成py文件,简直强大~~~这里就不多介绍,直接来说说窗体开发,我是怎么入手的。
拥有一个属于自己的窗体程序
跳过那些坑,我们直接来说说如何快速拥有一个窗体程序。按照我上面提供的连接安装好开发需要的工具,我们可以在eric的安装目录找到一个叫做eric.pyw的东西,启动它,就打开了eric。可以看到如下左图的界面,点击project可以新建一个工程,这里不详述,还是推荐一个链接:使用eric创建一个PyQt项目http://www.pythoner.com/89.html
按照刚刚提供的链接,我们创建了一个新工程,甚至在新的工程中添加了窗口,还给窗口画了很多组件,这里来介绍几个我用到的,下图中展示了我画好的两个界面:登录和主窗口:
我这个简易版的ftp只用到了这5个组件。。。天,这样说会不会得不了A。。。嘻嘻,不管怎么样,反正就用到了这几个:lable是标签,lineEdit是编辑框,pushButton是按钮,listWidget是列表框,TextBrowser是文本框。画好了这些东西之后,我们关闭并保存qt文件,就会在eric中看到两个ui文件,右键选择生成文件,就生成了.py文件了,这个网上有大量资料,随便一搜就搜到了哈~
正文
好啦,看到这里才算进入正题,现在才是我要介绍的主要内容~~~我们现在已经有了一个ui界面和python文件,但是这个时候我们运行.py文件什么也不会发生,它会报错。。。错误的具体内容不太记得了,只记得是说没有QApplication,那么QApplication是从哪里来的呢?看下面的图:
详细的解释看右边→_→:
1 # -*- coding: utf-8 -*- 2 __author__ = ‘Eva_J‘ 3 4 # Form implementation generated from reading ui file ‘E:\ericWorkSpace\12-21\FTPhomework\views\login.ui‘ 5 # 6 # Created by: PyQt4 UI code generator 4.11.4 7 # 8 # WARNING! All changes made in this file will be lost! 9 10 from PyQt4 import QtCore, QtGui 11 12 try: 13 _fromUtf8 = QtCore.QString.fromUtf8 14 except AttributeError: 15 def _fromUtf8(s): 16 return s 17 18 try: 19 _encoding = QtGui.QApplication.UnicodeUTF8 20 def _translate(context, text, disambig): 21 return QtGui.QApplication.translate(context, text, disambig, _encoding) 22 except AttributeError: 23 def _translate(context, text, disambig): 24 return QtGui.QApplication.translate(context, text, disambig) 25 26 class Ui_login(object): 27 def setupUi(self, login): 28 login.setObjectName(_fromUtf8("login")) 29 login.resize(400, 300) 30 login.setSizeGripEnabled(True) 31 self.login_pushButton = QtGui.QPushButton(login) 32 self.login_pushButton.setGeometry(QtCore.QRect(160, 210, 75, 23)) 33 self.login_pushButton.setObjectName(_fromUtf8("login_pushButton")) 34 self.register_pushButton = QtGui.QPushButton(login) 35 self.register_pushButton.setGeometry(QtCore.QRect(250, 210, 75, 23)) 36 self.register_pushButton.setObjectName(_fromUtf8("register_pushButton")) 37 self.usr_lineEdit = QtGui.QLineEdit(login) 38 self.usr_lineEdit.setGeometry(QtCore.QRect(160, 126, 161, 21)) 39 self.usr_lineEdit.setObjectName(_fromUtf8("usr_lineEdit")) 40 self.pwd_lineEdit = QtGui.QLineEdit(login) 41 self.pwd_lineEdit.setGeometry(QtCore.QRect(160, 166, 161, 21)) 42 self.pwd_lineEdit.setObjectName(_fromUtf8("pwd_lineEdit")) 43 self.usr_label = QtGui.QLabel(login) 44 self.usr_label.setGeometry(QtCore.QRect(91, 130, 54, 12)) 45 self.usr_label.setObjectName(_fromUtf8("usr_label")) 46 self.pwd_label = QtGui.QLabel(login) 47 self.pwd_label.setGeometry(QtCore.QRect(91, 172, 54, 12)) 48 self.pwd_label.setObjectName(_fromUtf8("pwd_label")) 49 self.ip_label = QtGui.QLabel(login) 50 self.ip_label.setGeometry(QtCore.QRect(80, 44, 91, 20)) 51 self.ip_label.setObjectName(_fromUtf8("ip_label")) 52 self.ip_lineEdit = QtGui.QLineEdit(login) 53 self.ip_lineEdit.setGeometry(QtCore.QRect(160, 44, 161, 21)) 54 self.ip_lineEdit.setObjectName(_fromUtf8("ip_lineEdit")) 55 self.port_lineEdit = QtGui.QLineEdit(login) 56 self.port_lineEdit.setGeometry(QtCore.QRect(160, 86, 161, 21)) 57 self.port_lineEdit.setObjectName(_fromUtf8("port_lineEdit")) 58 self.port_label = QtGui.QLabel(login) 59 self.port_label.setGeometry(QtCore.QRect(80, 86, 91, 20)) 60 self.port_label.setObjectName(_fromUtf8("port_label")) 61 62 self.retranslateUi(login) 63 QtCore.QMetaObject.connectSlotsByName(login) 64 65 def retranslateUi(self, login): 66 login.setWindowTitle(_translate("login", "Dialog", None)) 67 self.login_pushButton.setText(_translate("login", "登录", None)) 68 self.register_pushButton.setText(_translate("login", "注册", None)) 69 self.usr_label.setText(_translate("login", "用户名", None)) 70 self.pwd_label.setText(_translate("login", "密 码", None)) 71 self.ip_label.setText(_translate("login", "服务器地址", None)) 72 self.port_label.setText(_translate("login", "服务器端口", None)) 73 74 #这里开始是我们自己写的 75 import sys 76 from PyQt4 import QtGui 77 def LoginController(argv): 78 app = QtGui.QApplication(argv) 79 dlg = Ui_login() 80 dlg.show() 81 sys.exit(app.exec_()) 82 83 if __name__ == ‘main‘: 84 LoginController(sys.argv)
为控件绑定事件
执行上面的代码我们就可以有一个自己的登录窗体了,这只是一个花瓶摆件,中看不中用,我们点一点那些按钮和编辑框,没有一个搭理我们。。。肿么办肿么办,这里我也纠结了好久,最后发现qt和eric的强大不仅仅是给我们提供了画窗体的功能,还提供了信号和槽的功能(不理解的就背下来,这里其实我也不太懂。。。),先来看怎么做。。。网上说了一大堆什么在qt里添加信号和槽,亲测无效。。。所以这里还是按照我的方法来:
首先右键UI文件(当然,你刚刚已经使用Compile form生成过一个文件了),这里再使用Generate...这个生成一个对话文件,我们姑且这么翻译,这个时候会弹出右边的对话框,我们填好上面的那些信息,勾选你要给哪些组件添加事件,点击ok,就又生成了一个新文件。
我们可以看到现在目录变成了上面的模样,不要问我他们为什么不在一个文件夹下,因为你一开始选择的路径就是不同的。现在我来说说他们的关系,ui文件和py文件没什么关系。。。但是我们第一次生成的窗口的描述文件和第二次生成的对话文件之间是有联系的!有联系的!联系的!重要的事情说三遍。。。实际操作中我们可以看到,第二次生成的文件中事件方法所在的类是继承了窗口描述文件中的类的。。。所以我们在这一次的实例化中,只需要初始化派生类的对象就可以了。。。这里不理解的去看类继承的知识。。。
所以这个时候我们想要让这个login窗口显示只需要在派生类下执行那些代码就好了,很容易理解吧?代码也贴上来
1 # -*- coding: utf-8 -*- 2 3 """ 4 Module implementing Login. 5 """ 6 import sys 7 from PyQt4.QtCore import pyqtSignature,QString 8 from PyQt4.QtGui import QDialog 9 10 from views.Ui_login import Ui_login 11 from PyQt4 import QtGui 12 import ClientClass 13 import mainWindowEvent 14 15 class Login(QDialog, Ui_login): 16 global TEST 17 """ 18 Class documentation goes here. 19 """ 20 21 def __init__(self,parent=None): 22 QDialog.__init__(self, parent) 23 self.setupUi(self) 24 25 @pyqtSignature("") 26 def on_login_pushButton_clicked(self): 27 pass 28 29 @pyqtSignature("") 30 def on_register_pushButton_clicked(self): 31 """ 32 Slot documentation goes here. 33 """ 34 # TODO: not implemented yet 35 raise NotImplementedError 36 37 import sys 38 from PyQt4 import QtGui 39 def LoginController2(argv): 40 app = QtGui.QApplication(argv) 41 dlg = Login() 42 dlg.show() 43 sys.exit(app.exec_()) 44 45 if __name__ == ‘main‘: 46 LoginController(sys.argv)
现在这两个事件就和我们窗口中的按钮们绑定在一起了,随便你在里面写什么,只要一点按钮,就会触发这个事件,执行这个方法里面的内容。。。
窗口的切换
好了,假装我们已经实现了登录的功能,在登录方法中进行了一系列操作,那么问题来了, 当我们登录成功之后,如何切换窗口呢?加入在本窗口中实例化ftp窗口的话,那么登录窗口关闭之后,ftp的窗口也会跟着关闭了,是不是很闹心,在这里纠结一天。。。不卖关子了,直接上代码:
就是这样,accept是关键。。。到现在为止,我们已经实现了窗口的切换。我的心在滴血,为什么没有人写教程。。。
窗口切换中的数据交换问题
刚刚我们已经实现了数据的交换,开篇我就说过,我要实现的是一个ftp程序,那么在输入了各种ip、端口、用户名和密码登录之后,这个连接已经建立起来了,在这个窗体中建立起来的对象要怎么传递给ftp这个新窗体呢?
未完。。。