連續兩周留作業要寫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這個新窗體呢?我嘗試了很多種方法,比如將這個外部類實例化的對象變成這個類的一個字段,等等,均未果,但是在這里不得不說,全局變量很好用,很好用,很好用,但是直接定義一個變量是不好用的,因為直接定義一個變量,那么在它再次賦值的時候。
直接上代碼。

就是這樣!定義一個全局變量,分分鍾解決問題。
這就是我的窗體程序開發初體驗。。。當然還有一些控件的使用方法和事件的綁定。其實窗體開發還有很多可研究的地方,但是由於事件關系,我只研究了這么多。如果以后還有機會做相關的工作,我會繼續更新噠~~~
