PyQt4表达式求值程序
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了PyQt4表达式求值程序相关的知识,希望对你有一定的参考价值。
30行的表达式求值程序
应用特点
这个应用程序用到了两个窗口部件:一个是QTextBrowser
,这是一个只读的多行文本框,既可以显示普通文本又可以显示html
;还有一个是QLineEdit
,这是一个单行文本框, 可用来显示普通文本。在PyQt
的窗口部件中,所有文本都会采用统一字符编码标准(Unicode
), 尽管在必要时也可以将其转码成其他编码形式。
运行效果
源代码
# encoding:utf-8
from __future__ import division
from __future__ import print_function
from __future__ import unicode_literals
from future_builtins import *
import sys
from math import *
from PyQt4.QtCore import (SIGNAL)
from PyQt4.QtGui import (QApplication,
QDialog,
QLineEdit,
QTextBrowser,
QVBoxLayout)
class Form(QDialog):
def __init__(self, parent=None):
super(Form, self).__init__(parent)
self.browser = QTextBrowser()
self.lineedit = QLineEdit("Type an expression and press Enter")
self.lineedit.selectAll()
layout = QVBoxLayout()
layout.addWidget(self.browser)
layout.addWidget(self.lineedit)
self.setLayout(layout)
self.lineedit.setFocus()
self.connect(self.lineedit, SIGNAL("returnPressed()"),
self.updateUi)
self.setWindowTitle("Calculate")
def updateUi(self):
try:
text = unicode(self.lineedit.text())
self.browser.append("{0} = <b>{1}</b>".format(text,
eval(text)))
except:
self.browser.append("<font color=red>{0} is invalid!</font>"
.format(text))
if __name__ == '__main__':
app = QApplication(sys.argv)
form = Form()
form.show()
app.exec_()
解析
from __future__ import division
from __future__ import print_function
from __future__ import unicode_literals
from future_builtins import *
import sys
from math import *
from PyQt4.QtCore import (SIGNAL)
from PyQt4.QtGui import (QApplication,
QDialog,
QLineEdit,
QTextBrowser,
QVBoxLayout)
由于我们要做的只是数学计算也不想要其他诸如截断除法这类的算法,只需确保能够实现浮点数除法即可。一般可以通过使用import modules
这样的语法来导入非PyQt
的模块,但由于想让该程序的用户能够用到math
模块里面的全部函数和常量,所以只需把math
模块里面的所有方法导入到当前的命名空间即可。
class Form(QDialog):
def __init__(self, parent=None):
super(Form, self).__init__(parent)
self.browser = QTextBrowser()
self.lineedit = QLineEdit("Type an expression and press Enter")
self.lineedit.selectAll()
layout = QVBoxLayout()
layout.addWidget(self.browser)
layout.addWidget(self.lineedit)
self.setLayout(layout)
self.lineedit.setFocus()
self.connect(self.lineedit, SIGNAL("returnPressed()"),
self.updateUi)
self.setWindowTitle("Calculate")
def updateUi(self):
try:
text = unicode(self.lineedit.text())
self.browser.append("{0} = <b>{1}</b>".format(text,
eval(text)))
except:
self.browser.append("<font color=red>{0} is invalid!</font>"
.format(text))
正如之前看到的,任何窗口部件都可以用做顶级窗口。但在大多数情况下,我们会通过对 QDialog
或者QMainWindow
,偶尔也会用到QWidget
,子类化的方法来创建自己的顶级窗口。但无论是QDialog
还是QMainWindow
,实际上,PyQt
里面的所有窗口部件都是继承自 QWidget
的,也都是新类型(new-style
)的类。通过继承QDialog
可以得到空白的表单(form
),这是一个灰色的矩形,还可以得到一些方便的行为和方法。例如,如果用户单击关闭按钮X
,那么这个对话框就会关闭掉。默认情况下,关闭一个窗口部件,事实上仅仅是将其隐藏起来了;当然,就像下一张将会看到的那样,我们当然也可以改变这一行为。
令Form
类_init_()
方法默认的父类parent
为None
,用super
方法对其进行初始化。没有父类的窗口部件就会变成顶级窗口,这正是该Form
类所需要的。然后,创建两个窗口部件,为了后续在_init_
之外仍旧能够访问它们,还需要对它们保持引用。由于这些窗口部件没有给定父类,看上去它们也会变成顶级窗口的,但其实这并不是我们所期待的。 在初始化函数中,将会看到它们是如何获得父类的。对于QLineEdit
可以给定一些作为初始文本,并将这些文本全部选中。这样就可以确保当用户开始输入信息时,我们这里所给定的这些文本会被直接覆盖。
我们想让这些窗口部件能够一个接一个地在窗口中竖直显示出来。要达到这一效果, 可以通过先创建一个QVBoxLayot
并向其添加我们所创建的那两个窗口部件,然后再对Form
的布局进行设置。如果运行该应用程序并改变该程序的大小,可以发现,那些竖向多余的空间都会分配给QTextBrowser
,而这两个窗口部件则都会在水平方向上拉长。这些都会由布局管理器(layout manager
)自动处理的,而通过设置布局策略还可以获得非常精准的调整效果。
使用布局会出现一个重要的副作用:PyQt
会自动将布局中的各个窗口部件重定义其父类。 所以,尽管对于我们的那两个窗口部件并没有给定各自的父类(在该Form
实例中),但是当调用setLayout
的时候,布局管理器还是会获得这两个窗口部件和该Form
自身的所有权, 也会获得任何嵌套布局自身的所有权。这样一来,布局后的窗口部件就没有哪一个再是顶级窗口了,因为它们都有父类了,而这也正是我们想要的。因此,当删除Form
实例的时候,它 所有的子窗口部件和布局都会随它一起按照正确的顺序删除掉。
对于表单Form
里面的每个窗口部件都可以使用多种技术进行布局。可以使用resize ()
和move ()
方法为它们给定绝对大小和位置;可以重写resizeEvent ()
方法来动态地计算它们 的大小和位置坐标,或者也可以直接使用PyQt
的布局管理器。使用绝对大小和位置会非常不 方便。一方面,需要进行大量的人工计算,而另一方面,如果布局改变了,就要重新计算。动态计算大小和位置是一种更好些的方案,不过这依然需要我们编写许多冗长枯燥的计算代码。
使用布局管理器会让这一切变得容易很多。同时,布局管理器也要灵活得多:会自动适应改变大小的事件并去满足这些改变。任何习惯了不同版本Windows
下的对话框的人,相信都会更喜欢大小可以改变(也是很符合实际的),而不是强制使用一个小小的、不能改变大小的对话框,因为这样的话会非常不方便,尤其是当其内容很多而无法满足的时候。布局管理器也会让程序在国际化时变得更为简单,因为它们可以自动适应内容,所以翻译后的标签就不会出现因目标语言比源语言冗长而被截断的现象。
PyQt
提供了三种布局管理器:一是垂直布局
,二是水平布局
,三是网格布局
。这些布局可以相互嵌套,所以即使是非常复杂的布局也有可能做出来。当然,也有其他的布局方式,比如 还可以使用分隔符(splitter
)或者tab
窗口部件。
让光标从QLineEdit
开始,为此需要调用setFocus ()
。 在设置完布局后,就必须要做这一步。
有关connect
的调用简单地说,任何一个窗口部件(以及其他某些QObject
对象)都可以通过发射(emit
) “信号”的方式来声明状态和槽发生了改变。这些信号(与UNIX的信号没有任何关系)通常会被忽略掉。然而,我们也可以选择任何感兴趣的信号,通过识别想知道的QObject
就可以做到这一点,因为该QObject
所发射的信号就恰是我们想知道的,而在这个信号发射的时候就会调用我们想要调用的函数或者方法。
所以,在这种情况下,当用户在QLineEdit
上按下回车键Enter
(或者换行键Return
) 的时候,returnPressed()
信号将会像往常一样发射出来,但因为有connect()
调用,当这个信号一发射,就会调用updateUi()
方法。于是,瞬间我们就会看到发生了什么。
正如很快就会看到的一样,创建了表单Form
,对其调用了show ()
方法。一旦事件循环开始,表单就会显示出来,好像再没有其他什么会发生了。应用程序会一直运行事件循环,等待 用户去按下鼠标或者键盘。而一旦用户开始交互,所有的交互动作就会得以处理。因此,如果用户输入了一个表达式,QLineEdit
就会将用户输人的表达式显示出来,而只要用户按下的是回车键,就会调用updateUi()
方法。
当调用updateUi()
时,它会获得QLineEdit
的文本,即刻将它转换为Unicode
对象。然 后,使用Python
的eval
函数计算这个作为表达式的字符串的值。如果成功,将计算的结果添加到带有表达式文字、一个等号=
的QTextBrowser
的后面,再把结果加粗显示。尽管通常会尽可能地把QString
转换为Unicode
字符,也可以向那些能够接受QString
的各个PyQt
方法传人QString
、Unicode
、strs
字符串,而PyQt
则会自动进行所需的转换工作。 如果发生了异常,就把错误信息添加到QTextBrowser
里面。像这样使用一个捕获所有异常的except
代码块在实际编程时并不是好方法,不过在这个只有30行的程序里,还算情有可原的。 .
使用eval
可以避免由语法和解析所带来的全部工作,而如果使用的是编译型语言的话,这些工作都应自己完成。
if __name__ == '__main__':
app = QApplication(sys.argv)
form = Form()
form.show()
app.exec_()
实例化创建好的类。这样就完成了全部程序。然而,这并不是故事的结局。目前还没有涉及用户该如何来结束这个程序。由于我们的Form
类是从QDialog
派生的,因而就会继承一些有用的行为。例如,如果用户点击了X
关闭按钮,或者是按下了Esc
键,那么表单(form
)就会关闭。当一个表单(form
)关闭的时候,它只不过是隐藏起来了。当表单(form
)隐藏起来时,PyQt
将会检测该程序不要再有可见的窗口,这样也就不可能再有更多的交互了。PyQt
就会删除该表单(form
) 并对该应用程序执行清空操作。
有些情况下,即使应用程序不可见,但仍旧希望该程序依然能够运行,例如,一个服务器应用程序。对于这些情况,可以调用QApplication.setQuitOnLastWindowClosed (False)
来实现前述目的。也有可能,尽管这种情况非常少见,在关闭应用程序的最后一个窗口时,要能通知用户。
以上是关于PyQt4表达式求值程序的主要内容,如果未能解决你的问题,请参考以下文章