编辑推荐: |
文章主要首先对tkinter进行描述,然后介绍了tkinter中常用的控件,以及最后举例说明如何编写按钮事件,希望对大家有帮助。
本文来自于微信公众号-小陈学Python ,由火龙果软件Alice编辑,推荐。 |
|
最近我同学在做一个聊天程序,服务器已经全部完成了,客户端也已经使用CMD版本的进行过测试了,为了做的好一点,需要弄一个图形用户界面的客户端,Python编写图形界面的模块有好多,这里使用的是Python内置的模块——tkinter。
首先来大概看一下tkinter的描述,依旧是先导入然后使用使用help查看,如图所示。
我稍微翻译一下DESCRIPTION下面那几段。
Tkinter模块提供允许显示,位置安排和控制控件的类。顶层控件是Tk和Toplevel两个类,其他控件是框架、标签、条目、文本、画布、按钮、单选按钮、检查按钮、缩放、列表框、滚动条、选项菜单、旋转框标签框架和窗格窗口。
控件的属性用关键字参数来指定。关键字参数与Tk下的相应资源同名。
通过Place,Pack,Grid三个几何管理器的某一个类,控件被放置。这些管理器可以通过在每一个控件里都有的place,pack,grid三个方法被调用。
行为通过资源(关键字参数)或者方法被绑定到事件中。
下面是一个例子,这个例子太简单了,我就不做解释了,我们跳到下面PACKAGE CONTENTS,如图所示。
通过这个大家应该可以发现,更准确的说,tkinter不是一个模块,而是一个包,这个包下面还有好多个模块和一个包。有些人就觉得见鬼了,既然tkinter是包,包就是文件夹,在其中必须有一个__init__.py,为什么我还可以from
tkinter import Tk?这个Tk类到地方在哪里?绝对不可能直接放在tkinter下的文件夹!实际上,这个类直接放在了__init__.py,导入时也就只要从tkinter中导入,而不必写成from
tkinter.__init__ import Tk。大家应该明白了包当中的__init__.py并不仅仅是用来去分包和文件夹的,还可以在其中定义一些直接通过包导入的类,而不需要通过包下的模块导入。
下面我直接通过编写GUI客户端来向大家介绍一些tkinter中常用的控件,在编写之前,我们首先想一下连接服务器并登录聊天室的过程。连接服务器,我们需要提供主机名和端口号,登录时我们需要提供用户名。既然如此,我们可以定义三个单行文本框(Entry类的实例),这样还不够,仅仅三个单行文本框,一点提示都没有,鬼知道哪个是输入主机名,哪个是输入端口号,哪个是输入用户名,所以我们应该在单行文本框的左边放上一些提示信息(Label类的实例)。这样还不够,因为登陆进去还是需要触发一个事件,这个事件我们可以绑定在一个按钮上,当点击按钮时就触发该事件。下面我给出如何编写该事件的具体过程。
在编写之前,我们应该看一下服务器上的某一段代码,如图所示。
当客户端一旦连接上服务器(还没登录),就在LoginRoom类的实例里面,同时,服务器还会发送一条欢迎信息到客户端,这条欢迎信息对于判断是否登录成功一点作用都没有,但是它又必须要被接收,因为这个项目中,客户端和服务器之间的通信是基于TCP协议的,TCP协议规定发送的数据一定要被对方接收,接收后返回一个确认给发送方,让发送方继续发送数据,如果这个确认没有收到,发送方就认为连接超时,需要重传。
然后是unknown方法,这个可以不用管了,因为图形用户界面命令和按钮事件绑定在一起了,已经不需要用户手动输入命令了,也就是已经不存在未知命令了。
下面这个方法最重要,客户端按钮的登录事件可能就需要参考这个方法。这个方法一点也不难,如果用户名为空,登录失败;又如果用户名被占用,登录失败;否则,登陆成功。当登录失败时,会将失败的理由发送给客户端,那么客户端就可以通过接收到的消息来判断是否登录成功。在这里大家应该会有一个问题,既然登陆成功什么数据都不发送,客户端到底该怎么做接收?如果一直让客户端等待数据到来这也不现实,因为根本就没有数据需要接收,这样这个程序在运行不久就会一直陷入阻塞,导致无响应。在这里,我们给接收设一个超时,超过一定时间还没收到数据就认为登陆成功。
通过我上面介绍的方法,大家编写这个按钮事件的方法应该已经不难了吧,如果还是觉得很难,可以参考一下我的,如图所示。
如果上面的代码有不懂的可以加群(群号见文末)咨询群主,也就是本人。我这里登陆成功后直接跳转到聊天客户端界面,下面我们就来编写聊天客户端界面。
这个也不难,我们需要一个控件用来设置发送的数据,这个控件就是一个单行文本框。接下来就是用来处理接受的数据的一个控件,这个控件是一个滚动文本框,把接收到的数据就显示在其中,然后就是分别用来处理发送数据、查看聊天室都有谁、查看谁已登录、退出的按钮。按钮中同样要绑定相应的事件,这四个事件的编写一点也不难,每当点击一个按钮(退出按钮除外),我们就发送相应的命令到服务器执行,然后清空用来发送数据的单行文本框的内容,如图所示。
退出按钮的时候,我们发送完退出命令之后,直接摧毁整个窗口,关闭连接。
既然发送数据相关的一系列事件已经处理好了,接收数据又该通过何种方式来实现呢?因为发送数据是用户自己点击按钮来实现的,接收数据是一有数据就要立刻接收,因此我们把接收数据丢给一个单独的线程来处理就可以了,具体怎么接收数据,我们只需要一个无限死循环即可,只要不是关闭连接,它就会一直等数据。代码实现起来也是很简单,如图所示。
还有就是在这里,我通过动态地修改那个滚动文本框的一个属性来确保用户无法修改,但是我的程序可以修改它。
在给出完整代码之前,我首先来说两个比较重要的细节,1.这个用来接收数据的线程必须是守护线程,因为主线程结束,它也不能存在,如果不是守护线程,这样的话连接资源可能就一直被该线程占用;2.点击退出按钮的事件到最后会把连接资源关闭,可是有些用户并不是通过这种方式退出,然后点击右上角的×来关闭,因为在点击×之后,没有发送logout命令到服务器,服务器并不会删除这个连接,为了确保在这种情况下,连接资源也被释放,我们可以做一个回调,在点击右上角的×时也会调用退出按钮的事件即可。
下面我直接给出完整的代码。
运行该程序之前一定要先运行服务器程序!最后我来展示一下运行效果,如图所示。
2018年只剩下最后一个月了,大家今年还有什么未完成的目标吗?如果有,那就去尽自己最大努力实现它;如果已经完成了,那么你就可以提前规划一下2019年的目标。如果到现在都还在怨天尤人发牢骚,一直在抱怨自己不能实现今年的目标,就会留下不值一提,不堪回首的遗憾!虽然未实现今年的目标确实会留下遗憾,但是只要尽力去实现就可以,因为结果不重要,重要的是过程!毕竟,青春有遗憾,这并不可怕,怕的是这遗憾没有价值,不值一提,不堪回首。
|