| 
                             
                              | 
                                   
                                    | 编辑推荐: |   
                                    | 本文主要介绍python界面开发中一些最常用的各个控件布局、属性的具体实现过程,希望对大家有帮助。 本文来自于简书 ,由火龙果软件Alice编辑,推荐。
 |  |   下面这张就是实现的整体界面的示意图 
 
                            基本功能介绍: 
                            点击按键next video会在下面的界面播放视频 
                            视频播放完成后在预测语句后的文本框中出现对该视频的语义描述 
                            点击按键标注语句可以在其后的文本框中展示对该视频的标注语句 
                            这篇文章涉及的知识点还是挺多的哦: 
                            Tkinter基本控件实现 
                            TKinter控件属性优化 
                            Tkinter实现视频播放 
                            Threading模块实现多线程编程 
                            1 具体实现 
                            1.0 导入包、创建界面类 
                           
                              | from Tkinter 
                                import * class Application(Frame):
 def __init__(self, master=None):
 Frame.__init__(self, master)
 self.pack()
 if __name__=='__main__':
 app = Application()
 # to do
 app.mainloop()
 |  
                            1.1 设置窗口标题、界面最大化 
                            具体的函数如下所示,先获得当前屏幕的大小,然后设置窗口大小。 
                           
                              | def window_init(self): self.master.title('welcome to video-captioning 
                                system')
 width,height=self.master.maxsize()
 self.master.geometry("{}x{}".format(width, 
                                height))
 |  
                            在__init__初始化中调用即可。 
                            1.2 实现各个控件布局 
                            1) 布局方式介绍 
                            布局就是控制各个控件在整个界面中的位置,Tkinter总共有3中布局管理器,分别是pack grid和place 
                            这篇博客里给了很详细的说明:Python GUI之tkinter布局管理 
                            总结一些最重要的东西吧: 
                            pack可以算是最常用的一种布局方式。就是像容器中从上到下(或者从左到右...)依次添加组件。 
                            grid很形象地又称为网格布局。把矩形界面分成一个几行几列的网格块,这样就可以把组件放置于网格中固定的位置. 
                            place这是三中布局中最灵活的一种方式。根据组件在界面中的坐标来制定位置,所以会比较灵活,但也意味着不那么稳定。 
                            所有的三种布局都可以使用容器中放容器的方法,这是非常使用的一个技巧。 
                            2) 布局设计图 
                            我选择全部用pack的方式来完成页面的布局工作。  具体的设计图如下所示: 
 
                            我已经尽力去画了,不知道这样是不是清楚~ 
                            总共从上到下分成3个Frame: fm1   fm2   fm3 
                            第一个Frame里面包含一个用来显示titile的Label 
                            第三个Frame里面包含一个用来显示视频帧图像的Label 
                            第二个Frame里面又分成左右两个小的容器:fm2_left   fm2_right 
                            右边的fm2_right就包含一个用来播放视频的Buttton  左边的fm2_left又分为上下两个更小的容器:fm2 _ left_ 
                            top   fm2_lef_bottom 
                            fm2_left_top fm2_lef_bottom里面都各自包含一个按钮Button和一个用来显示文本的Entry 
                            3) 布局实现代码 
                            我们先不管具体的细节,直接把各个控件的布局位置给定下来,界面优化的部分放到后面去再添加。  按照上面的布局设计图,视频区域暂且用一张图片替代,用pack()实现基本布局如下: 
                             
                              | def createWidgets(self): # fm1
 self.fm1=Frame(self)
 self.titleLabel=Label(self.fm1,text='video
 -captioning 
                                system')
 self.titleLabel.pack()
 self.fm1.pack(side= TOP)
 # fm2
 self.fm2=Frame(self)
 self.fm2_left=Frame(self.fm2)
 self.fm2_right=Frame(self.fm2)
 self.fm2_left_top=Frame(self.fm2_left)
 self.fm2_left_bottom=Frame(self.fm2_left)
 self.predictButton=Button(self.fm2_left_top,
 text='predict 
                                  sentence')
 self.predictButton.pack(side=LEFT)
 self.predictEntry=Entry(self.fm2_left_top)
 self.predictEntry.pack(side=LEFT)
 self.fm2_left_top.pack(side=TOP)
 self.truthButton=Button(self.fm2_left_bottom,
 text='ground 
                                  truth')
 self.truthButton.pack(side=LEFT)
 self.truthEntry=Entry(self.fm2_left_bottom)
 self.truthEntry.pack(side=LEFT)
 self.fm2_left_bottom.pack(side=TOP)
 self.fm2_left.pack(side=LEFT)
 self.nextVideoButton=Button(self.fm2_right,text
 ='next 
                                  video')
 self.nextVideoButton.pack()
 self.fm2_right.pack(side=LEFT)
 self.fm2.pack(side=TOP)
 
 # fm3
 self.fm3=Frame(self)
 load = Image.open('/home/hl/Desktop/lovelyqian/me.jpg')
 print (load)
 render= ImageTk.PhotoImage(load)
 
 self.img = Label(self.fm3,image=render)
 self.img.image = render
 self.img.pack()
 self.fm3.pack(side=TOP)
 |  
                            注: 图片的处理要加入头文件 
                            from PIL import Image, ImageTk 
                            最终效果图: 
 
                            对比上下两张图,可能这就是理想与现实的差距吧,哈哈哈哈 
                            这里还只是实现了布局的定义和基本位置的放置,虽然真的很丑,但是不要着急,接下来我们就可以在这个基础上做一些控件大小的修改以及其他的界面美化工作。 
                            4) 控件属性设置及界面优化 
                            设置主窗口背景为黑色 
                            利用 Frame的bg属性 
                            要记得pack()的时候设置为全扩充 
                            以最大的底窗口为例: 
                           
                              | class Application(Frame): def __init__(self, master=None):
 Frame.__init__(self, master,bg='black')
 self.pack(expand=YES,fill=BOTH)
 |  
                            各个控件属性设置 
                            本来打算一点点记录具体实现的过程的,但发现实在是太麻烦了。 
                            主要就是边学变用,参考资料都在下面列出来了。 
                            改动了常用的一些属性,比如字体大小、字体前景色、后景色、按钮大小等等。  具体的实现代码先用图片的方式放在这里,在最后会给出完整的代码。 
 
                            感兴趣的话跟上面那段代码对比一下就可以很方便的知道各个属性的控制方法,这里就不再罗列了。  最后的实现效果: 
 
                            差不多先这样吧,去吃个午饭,下午回来继续写功能函数~~~ 
                            1.3 各个函数功能 
                            1) 显示文本 
                            这里只需要学习一下Entry这个控件。 
                            Entry是用来接收和显示字符串的控件。 
                            主要了解接受文字、发送文字、清除文字这3个功能就可以了。 
                            myEntry.get()获取内容 
                            myEntry.insert(10,"hello")在位置10处,添加内容 
                            myEntry.delete(first,last=None)删除从 “first” 到 “last” 
                            指定范围内的字符. 使用 delete(0, END)可以删除 Entry 控件已输入的全部字符. 
                            2) 按钮触发 
                            只需要在按钮中添加属性command即可。 
                            以点击predictButton按钮实现将文本显示到后面的文本框中为例: 
                           
                              | #... self.predictButton=Button(self.fm2_left_top,text='predict 
                                sentence',bg='#FF4081',fg='white',font=('微软雅黑',36),width='16',command=self.output_predict_sentence)
 def output_predict_sentence(self):
 predicted_sentence_str='hello world.'
 self.predictEntry.delete(0,END)
 self.predictEntry.insert(0,predicted_sentence_str)
 |   这样每次点击按键就会先把框框内的文字清空,然后输出你指定的文本。 
 3) 视频播放 
                            这个可能是最难的一个部分了呢~ 
                            Tkinter是没有支持视频播放的原生控件的,百度找了好久都没有找到解决方案,感谢Google大法,在这里找到了一个非常好的例子,还好木有放弃~~ 
                            这个例子实现的是从摄像头获取视频流,然后放在Tkinter实现的GUI窗口中,按钮可以实现视频截屏并保存的效果。 
                            对代码做了简化之后实现将视频播放在TKinter窗口中代码如下所示: 
                           
                              | from __future__ 
                                import print_function from PIL import Image,ImageTk
 import Tkinter as tki
 import threading
 import imutils
 import cv2
 import time
 import imageio
 class videoPlayer():
 def __init__(self):
 self.frame = None
 self.thread = None
 self.stopEvent = None
 # initialize the root window and image panel
 self.root = tki.Tk()
 self.panel = None
 # start a thread that constantly pools the 
                                  video sensor for
 # the most recently read frame
 self.stopEvent = threading.Event()
 self.thread = threading.Thread(target=self.videoLoop,
 args=())
 self.thread.start()
 
 def videoLoop(self):
 # keep looping over frames until we are instructed 
                                  to stop
 video_path='/home/hl/Desktop/lovelyqian/CV_Learning/
 video_to_sequence/video_to_sequence-master/data/
 youtube_videos/_0nX-El-ySo_83_93.avi'
 video=imageio.get_reader(video_path,'ffmpeg')
 for frame in video:
 self.frame = imutils.resize(frame, width=300)
 image = cv2.cvtColor(self.frame, cv2.COLOR_BGR2RGB)
 image = Image.fromarray(image)
 image = ImageTk.PhotoImage(image)
 
 # if the panel is not None, we need to initialize 
                                  it
 if self.panel is None:
 self.panel = tki.Label(image=image)
 self.panel.image = image
 self.panel.pack(side="left", padx=10, 
                                  pady=10)
 # otherwise, simply update the panel
 else:
 self.panel.configure(image=image)
 self.panel.image = image
 time.sleep(0.02)
 myVideoPLayer = videoPlayer()
 myVideoPLayer.root.mainloop()
 |  
                            对上面的代码再做一个修改整合就可以了。 
                            4) 多线程问题 
                            因为要视频视频播放的功能,所以我们还要学习一下python的多线程。 
                            也不是很难,想具体了解的可以点击文末的参考资料。 
                            import threading导入线程模块  self.thread=threading.Thread ( target=self.play 
                            _ next _ video, args = ())就定义好了先的线程。 
                            target指明该线程启动后会调用哪个函数 
                            self.thread.start()启动该线程 
                            当函数执行结束时,该线程就自动结束  所以每次想要用多线程执行视频播放的时候,只要生成一个 target 
                            = self . paly _ next_video的线程即可。 
                            那么能不能只定义一次线程,然后每次start它呢,答案是不能的哈哈哈哈。 
                            2 完整代码 
                            最后要放大招啦,准备好了吗? 
                            完整的代码都在下面了,有需要自取哦。 
                           
                              | #-*- coding:utf-8 
                                -*- from Tkinter import *
 from PIL import Image, ImageTk
 import threading
 import imageio
 import imutils
 import time
 import cv2
 class Application(Frame):
 def __init__(self, master=None):
 Frame.__init__(self, master,bg='black')
 self.pack(expand=YES,fill=BOTH)
 self.window_init()
 self.createWidgets()
 def window_init(self):
 self.master.title('welcome to video-captioning 
                                  system')
 self.master.bg='black'
 width,height=self.master.maxsize()
 self.master.geometry("{}x{}".format(width, 
                                  height))
 
 def createWidgets(self):
 # fm1
 self.fm1=Frame(self,bg='black')
 self.titleLabel=Label(self.fm1,text="video-captioning
 system",font=('微软雅黑',64),fg = "white",bg='black')
 self.titleLabel.pack()
 self.fm1.pack(side= TOP,expand=YES,fill='x',pady=20)
 # fm2
 self.fm2=Frame(self,bg='black')
 self.fm2_left=Frame(self.fm2,bg='black')
 self.fm2_right=Frame(self.fm2,bg='black')
 self.fm2_left_top=Frame(self.fm2_left,bg='black')
 self.fm2_left_bottom=Frame(self.fm2_left,bg='black')
 self.predictEntry=Entry(self.fm2_left_top,font=
 ('微软雅黑',24),width='72',fg='#FF4081')
 self.predictButton=Button(self.fm2_left_top,text='
 predict 
                                  sentence',bg='#FF4081',fg='white',font=
 ('微软雅黑',36),width='16',command=self.output_predict_sentence)
 self.predictButton.pack(side=LEFT)
 self.predictEntry.pack(side=LEFT,fill='y',padx=20)
 self.fm2_left_top.pack(side=TOP,fill='x')
 self.truthEntry=Entry(self.fm2_left_bottom,font=
 ('微软雅黑',24),width='72',fg='#22C9C9')
 self.truthButton=Button(self.fm2_left_bottom,text='
 ground 
                                  truth',bg='#22C9C9',fg='white',font=
 ('微软雅黑',36),
 width='16',command=self.output_ground_truth)
 self.truthButton.pack(side=LEFT)
 self.truthEntry.pack(side=LEFT,fill='y',padx=20)
 self.fm2_left_bottom.pack(side=TOP,pady=10,fill='x')
 self.fm2_left.pack(side=LEFT,padx=60,pady=20,
 expand=YES,fill='x')
 self.nextVideoImg= ImageTk.PhotoImage(file = 
                                  '/home/hl/
 Desktop/lovelyqian/nextVideo.png')
 self.nextVideoButton=Button(self.fm2_right,image=self.
 nextVideoImg,text='next 
                                  video',bg='black',command=self.
 start_play_video_thread)
 self.nextVideoButton.pack(expand=YES,fill=BOTH)
 self.fm2_right.pack(side=RIGHT,padx=60)
 self.fm2.pack(side=TOP,expand=YES,fill="x")
 
 # fm3
 self.fm3=Frame(self,bg='black')
 load = Image.open('/home/hl/Desktop/lovelyqian/me.jpg')
 initIamge= ImageTk.PhotoImage(load)
 self.panel = Label(self.fm3,image=initIamge)
 self.panel.image = initIamge
 self.panel.pack()
 self.fm3.pack(side=TOP,expand=YES,fill=BOTH,pady=10)
 def output_predict_sentence(self):
 predicted_sentence_str='hello world.'
 self.predictEntry.delete(0,END)
 self.predictEntry.insert(0,predicted_sentence_str)
 def output_ground_truth(self):
 ground_truth='this is ground truth.'
 self.truthEntry.delete(0,END)
 self.truthEntry.insert(0,ground_truth)
 def start_play_video_thread(self):
 self.thread=threading.Thread(target=self
 .play_next_video,args=())
 self.thread.start()
 def play_next_video(self):
 self.predictEntry.delete(0,END)
 self.truthEntry.delete(0,END)
 
 # to play video
 self.video_path='/home/hl/Desktop/lovelyqian
 /CV_Learning/video_to_sequence/video_to_sequence-master/data/youtube
 _videos/_0nX-El-ySo_83_93.avi'
 self.video=imageio.get_reader(self.video_path,
 'ffmpeg')
 for self.videoFrame in self.video:
 self.videoFrame=imutils.resize(self.videoFrame,
 width=1760,
 height=1080) 
                                  # to do
 self.image=cv2.cvtColor(self.videoFrame,cv2.
 COLOR_BGR2RGB)
 self.image=Image.fromarray(self.image)
 self.image=ImageTk.PhotoImage(self.image)
 self.panel.configure(image=self.image)
 self.panel.image=self.image
 time.sleep(0.02)
 if __name__=='__main__':
 app = Application()
 # to do
 app.mainloop()
 
 |    |