最近编程遇到很多问题。本质上是做得多学得少导致的。所以现在打算多学习一点教程,静下心来想一想,再往下做。
第一篇教程(主要探讨关于主程序入口)
参考教程:PyQt5(designer)入门教程
编辑菜单
不能直接使用转存来的py代码的原因
此时尝试运行刚刚生成的“HelloWorld.py”是没用的,因为生成的文件并没有程序入口。因此我们在同一个目录下另外创建一个程序叫做“main.py”
为什么所有的教程都告诉我们要两个文件?不能放在同一个文件里吗?
ctrl+R预览
ui和逻辑(main.py)分离 因为我们已经将UI(HelloWorld.py/HelloWorld.ui)跟逻辑(main.py)分离,因此直接重复步骤7-8即可完成UI的更新,无需改动逻辑(main.py)部分。
看起来真的很不错!我应该是没有分离的。因为我如果重新画图,并不能做到直接导出就能用。那么它在main.py里实现了程序入口,也要在main.py里实现信号和槽吗?
实战 汇率转换器
为了传参,在main.py头部from functools import partial
partial的用法:partial(function,arg1,arg2,……)
它就可以给function传递arg1,agr2等参数并且运行这个函数。
这是很有用的,在之前我写的时候,虽然也可以用self.pushButton.clicked.connect(function)
但因为function里没有(),自然也就不能再function里家参数。现在用partial,就可以把参数设置成ui。可是ui是什么呢?
关于这个教程中程序入口和我常用的程序入口的区别
# 创建mainWin类并传入Ui_MainWindow
class mainWin(QMainWindow, Ui_MainWindow):
def __init__(self, parent=None):
super(mainWin, self).__init__(parent)
self.setupUi(self)
if __name__ == '__main__':
# 下面是使用PyQt5的固定用法
app = QApplication(sys.argv)
main_win = mainWin()
main_win.show()
sys.exit(app.exec_())
先创建一个可用类mainwin类。这个类的两个父类一个是Qmainwindow,一个是自然类。
这个可用类构造方法,一方面继承了(不知道什么父类或者是两个父类的)构造方法。我觉得应该是调用了QMainWindow的构造方法,因为Ui_MainWindow没有构造方法(…)另一方面还会完成的一件事是调用了自己的setupUi方法。这是来自Ui_MainWindow的吗?那为什么不需要super().setupUi呢?
下面这一段是主程序。参考教程:PyQt中主函数app=QApplication(sys.argv) sys.exit(app.exec_())的作用
对于任何一个使用Qt的图形用户界面应用程序,都正好存在一个QApplication对象,而不论这个应用程序在同一时间内是不是有0、1、2或更多个窗口。
在具体代码里就是app吧。
参考教程:python PyQt5.QtWidgets.QApplication类(sys.argv)(app应用对象类)
import sys
from PyQt5.QtWidgets import QApplication, QWidget
app = QApplication(sys.argv) # 实例化一个应用对象
w = QWidget() # 窗口界面的基本控件,它提供了基本的应用构造器。默认情况下,构造器是没有父级的,没有父级的构造器被称为窗口(window)。Dontla 20200402 啥意思,没看懂??
w.show() # 让控件在桌面上显示出来。控件在内存里创建,之后才能在显示器上显示出来。
sys.exit(app.exec_()) # 确保主循环安全退出
和我们的基本一样,但是它没有用qtdesigner。PyQt5是一个大的模块,QtWidgets是其中的一个模块,QApplication, QWidget都是下面的类。QWidget对应在qtdesigner做法中应该是qmainwindow,都是窗口界面,功能差不多。
现在再来看一下这个教程里的程序入口长啥样。
if __name__ == '__main__':
app = QApplication(sys.argv)
MainWindow = QMainWindow()
ui = gui_file_name.Ui_MainWindow()
ui.setupUi(MainWindow)
MainWindow.show()
sys.exit(app.exec_())
我突然觉得挺像的。都是先用QMainWindow的构造方法构造一个标准化窗口,然后用ui_mainwindow的setup方法来setup这个标准化窗口使之具有相应的布局等。
补充1:python子类的构造函数
单继承
若子类不重写__init__,则会自动调用父类的__init__
init(self,arg1,arg2),那么在类的实例化过程中,我们就会这样写:father=Father(arg1,arg2)
class Father(object):
def __init__(self, name):
self.name=name
print ( "name: %s" %( self.name) )
def getName(self):
return 'Father ' + self.name
class Son(Father):
def getName(self):
return 'Son '+self.name
if __name__=='__main__':
son=Son('runoob')
print ( son.getName() )
子类也可以完全重写__init__方法
class Father(object):
def __init__(self, name):
self.name=name
print ( "name: %s" %( self.name) )
def getName(self):
return 'Father ' + self.name
class Son(Father):
def __init__(self, name):
print ( "hi" )
self.name = name
def getName(self):
return 'Son '+self.name
if __name__=='__main__':
son=Son('runoob')
print ( son.getName() )
如果重写时要继承父类的__init__方法:
super(子类,self).__init__(参数1,参数2,....)
父类名称.__init__(self,参数1,参数2,...)
这里的参数是不是至少应该包括父类的构造方法中的全部参数,可以增加,不能减少?
例如:
class Father(object):
def __init__(self, name):
self.name=name
print ( "name: %s" %( self.name))
def getName(self):
return 'Father ' + self.name
class Son(Father):
def __init__(self, name):
super(Son, self).__init__(name)
print ("hi")
self.name = name
def getName(self):
return 'Son '+self.name
if __name__=='__main__':
son=Son('runoob')
print ( son.getName() )
son的构造函数里可不可以把self.name = name
去掉?因为父类的构造函数里已经有这句话了。
多继承
参考教程:python中单继承和多继承中子类默认继承父类的哪个构造函数__init__
(1)子类继承于多个父类,并且子类无__init__时,则按继承顺序,哪个父类在最前面且有自己的__init__,则继承它;若最前面的父类无__init__,则继承第二个父类的__init__,若还是无__init__,则依次往后寻找,直到继承的某个父类含__init__
子类继承于多个父类,并且子类含__init__时,和单继承的(2)类似,不会自动调用所有父类__init__,如需使用某个父类__init__中的变量,则需要在子类__init__中显式调用,此处不再赘述
可是这两种情况不是子类直接继承就是子类直接重写,有没有继承并且改写?是不是默认改写第一个?
参考教程:python 多重类继承__init__
super.父类函数(arg……)就可以在子类中调用父类的函数
class A:
def __init__(self):
self.x = 0
class B(A):
def __init__(self):
super().__init__()
self.y = 1
上面这段代码就是用super().调用了父类的构造函数。
第二篇教程(designer的各种功能)
用qtdesigner的时候最好在制作完界面后对界面进行布局。在布局中你可以多运用Horizontal Spacer 和vertical spacer。一般我会一行一行进行水平布局之后再整体进行垂直布局。
#首先你需要在引用的python文件内导入该对象所在的文件,也就是main_menu.py
import main_menu #导入该对象所在文件
Ui_MainWindow = main_menu.Ui_MainWindow#指定Ui_MainWindow 为main_menu文件下的Ui_MainWindow对象。
class CoperQt(QtWidgets.QMainWindow,Ui_MainWindow):#创建一个Qt对象
#这里的第一个变量是你该窗口的类型,第二个是该窗口对象。
#这里是主窗口类型。所以设置成当QtWidgets.QMainWindow。
#你的窗口是一个会话框时你需要设置成:QtWidgets.QDialog
def __init__(self):
QtWidgets.QMainWindow.__init__(self) # 创建主界面对象
Ui_MainWindow.__init__(self)#主界面对象初始化
self.setupUi(self) #配置主界面对象
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
window = CoperQt()#创建QT对象
window.show()#QT对象显示
sys.exit(app.exec_())
这段代码告诉我们:
QtWidgets.QMainWindow是主窗口类型,所以如果主窗口类型是对话框,还要改成QtWidgets.QDialog。
QtWidgets.QMainWindow.__init__(self)
Ui_MainWindow.__init__(self)
这两句话居然放在了创建类里,所以这两句话和super(mainWin, self).__init__(parent)
这一句话是一个功能。
这一整个类是一个Qt对象。
按钮
self.Button0.clicked.connect(self.start_find) # button0的点击事件绑定start_find函数
关于绑定函数的传参除了可以使用全局变量还可以使用python的lambda,如我要传入x:
self.Button0.clicked.connect(command=lambda:button_process(x))
虽然我也不知道参数是怎么来的,但是也可以用
partial。
这篇文章里还讲了定时器。
ctrl+R可以实现窗口的预览。
下拉选项栏comboBox
选项栏有两个标识,一个是每一栏的编号index(从0开始),一个是每一栏的文本内容text:
self.comboBox.insertItem(0, self.tr("None")) # 第一个选项插入空
self.comboBox.insertItem(0, "x") 第一个选项插入x字符
self.comboBox.currentText() 读取选项栏文本:
# self.comboBox.currentIndex() 读取选项栏编号:
# self.comboBox.setCurrentIndex() 设置当前选项栏显示位置:(通过编号)
#self.comboBox.setCurrentText() 设置当前选项栏显示位置:(通过文本)
#self.comboBox.clear() 清空当前选项框内元素
如何让选项栏选中后不用按按钮就触发事件。实际上就是运用到上面的定时器,你可以将定时器绑定上选项栏读取函数,这个函数每隔200MS或者1s读取选项栏的数值,这样,用户的只要更换选项立马能够做出反映。
单选框radioButton
self.radioButton.clicked.connect(self.change_radio) # 单选按钮选中绑定函数
self.radioButton.hide() # 单选框2隐藏
self.radioButton.setChecked(True)#单选框选中,反之False
self.radioButton.isChecked() #单选框是否选中,选中返回True反之False
复选框checkBox
可以右键单选框得到。区别是可以都选可以都不选。
文本框lineEdit
输入框.png
self.lineEdit.returnPressed.connect(self.check_info) # 文本栏回车绑定函数
self.lineEdit.setEchoMode(QtWidgets.QLineEdit.Password)#设置成密码模式,也就是输入内容显示为实心的圆
self.lineEdit.setEchoMode(QtWidgets.QLineEdit.Normal)#设置成普通模式(默认)
self.lineEdit.setText(“hello world”) # 设置输入文本
self.lineEdit.text()#返回输入框文本内容
self.lineEdit.show()#显示文本框
self.lineEdit.hide()#隐藏文本框
self.lineEdit.clear()#清空文本框
第三篇教程 关于页面设计和布局
参考教程:
PyQT5 之 Qt Designer 介绍与入门
第四篇教程 一个分离了逻辑和页面设计的例子
参考教程:PyQt5+Qt designer实战
clat.py原封不动地保存,另建run.py
class mwindow(QWidget, Ui_Form):
def __init__(self):
super(mwindow, self).__init__()
self.setupUi(self)
def ps_bt(self):
self.textBrowser.clear()
原本的话应该只有上面这一段构造函数,但是现在,除了构造函数之外,还有很多新的函数。
class mwindow(QWidget, Ui_Form):
def __init__(self):
super(mwindow, self).__init__()
self.setupUi(self)
def ps_bt(self):
self.textBrowser.clear()
def ps_bt1(self):
self.lineEdit.insert('/')
def ps_bt2(self):
self.lineEdit.insert('*')
def ps_bt3(self):
self.lineEdit.insert('+')
def ps_bt4(self):
self.lineEdit.insert('-')
def ps_bt5(self):
self.lineEdit.insert('=')
self.calculate()
def ps_bt6(self):
self.lineEdit.insert('.')
def ps_bt7(self):
self.lineEdit.insert('0')
def ps_bt8(self):
self.lineEdit.insert('1')
def ps_bt9(self):
self.lineEdit.insert('2')
def ps_bt10(self):
self.lineEdit.insert('3')
def ps_bt11(self):
self.lineEdit.insert('4')
def ps_bt12(self):
self.lineEdit.insert('5')
def ps_bt13(self):
self.lineEdit.insert('6')
def ps_bt14(self):
self.lineEdit.insert('7')
def ps_bt15(self):
self.lineEdit.insert('8')
def ps_bt16(self):
self.lineEdit.insert('9')
def ps_bt17(self):
self.lineEdit.backspace()
def ps_bt18(self):
self.lineEdit.clear()
def ps_bt19(self):
self.close()
def lineEdit_clear(self):
self.lineEdit.clear()
def calculate(self):
# text = self.lineEdit.text()
# self.lineEdit.setText('%s= %.2f' % (text, eval(text)))
text = self.lineEdit.text()
self.textBrowser.append('%s= %.2f' % (text, eval(text)))
总的来说,在这个可用类的定义中,有构造函数和各种槽函数,但是没有写触发条件。
if __name__ == '__main__':
app = QApplication(sys.argv)
w = mwindow()
w.pushButton.clicked.connect(w.ps_bt)
w.pushButton_1.clicked.connect(w.ps_bt1)
w.pushButton_2.clicked.connect(w.ps_bt2)
w.pushButton_3.clicked.connect(w.ps_bt3)
w.pushButton_4.clicked.connect(w.ps_bt4)
w.pushButton_5.clicked.connect(w.calculate)
w.pushButton_6.clicked.connect(w.ps_bt6)
w.pushButton_7.clicked.connect(w.ps_bt7)
w.pushButton_8.clicked.connect(w.ps_bt8)
w.pushButton_9.clicked.connect(w.ps_bt9)
w.pushButton_10.clicked.connect(w.ps_bt10)
w.pushButton_11.clicked.connect(w.ps_bt11)
w.pushButton_12.clicked.connect(w.ps_bt12)
w.pushButton_13.clicked.connect(w.ps_bt13)
w.pushButton_14.clicked.connect(w.ps_bt14)
w.pushButton_15.clicked.connect(w.ps_bt15)
w.pushButton_16.clicked.connect(w.ps_bt16)
w.pushButton_17.clicked.connect(w.ps_bt17)
w.pushButton_18.clicked.connect(w.ps_bt18)
w.pushButton_19.clicked.connect(w.ps_bt19)
w.show()
sys.exit(app.exec_())
主函数。这个例子告诉我们,即使一个页面已经实例化了,它也可以再将元件的信号和槽连接在一起。就以普通的代码呈现,而不一定要在自然类的setup方法里。
另外如果想加入界面背景:
def resizeEvent(self, event):
palette = QPalette()
pix = QPixmap('resources/background.jpg')
pix = pix.scaled(self.width(), self.height())
palette.setBrush(QPalette.Background, QBrush(pix))
self.setPalette(palette)