jerryxjr1220 发表于 2017-3-11 16:40:33

[可视化GUI编程 Visual TKinter] “不想学PYQT又想做好看的界面”之懒人的福音

本帖最后由 jerryxjr1220 于 2017-3-11 16:56 编辑

PYQT有图形化编辑界面,可以很方便的制作漂亮的界面,但是缺点是代码比较复杂(相对Tkinter),而且需要依赖第三方库PYQT。

TKinter的优势在于它本身就是python的标准库,只要安装了python就可以直接使用,但是缺点是没有现成的图形化编辑界面。

那么有没有既可以有图形化编辑界面,又可以编辑TKinter呢?

今天就来分享这个强大而神奇的软件,它可以直接调用VB6的编辑器进行图形化编辑GUI界面,然后直接生成python代码,一举两得!!很好很强大!!


在VB中编辑(我相信VB的傻瓜式编程应该都会的吧,只要把想用的控件拖上去即可)

https://raw.githubusercontent.com/cdhigh/tkinter-designer/master/Setup/Screenshots/TkinterDesigner_ScrPrnt.JPG
然后就可以直接生成python代码

2楼放出我做的一个具体实例Demo

VB6迷你破解版和Visual TKinter打包下载
**** Hidden Message *****

简介

这是一个VB6的ADDIN(外接程序),用于使用VB6开发工具直接拖放控件,直接可视化完成Python的TKinter的GUI布局和设计,可以在VB界面上设置控件的一些属性,最终自动生成必要的代码(包括回调函数框架),代码生成后仅需要在对应的回调函数中增加相应的逻辑功能代码即可。

这个工具支持绝大部分TKiner控件,可应付一般GUI的需求。 (列表参见下面的控件说明)。

适用对象

适用于学习了TKinter并不想太麻烦写GUI代码,也不想用其他工具和框架比如wxPython,PyQt4的同学。
适用于界面不太复杂的小程序开发,界面复杂的还是适用wxPython等框架吧。
因为TKinter为Python标准库,使用TKinter完成的Python程序可以称为“绿色软件”,不需要目标机器上安装wxPython,PyQt4等框架,只要有Python的机器就能运行。
如果软件逻辑不是很复杂,通常一个*.py搞定,不像其他框架,需要几个文件。 > (如果不希望py运行时弹出黑漆漆的命令行窗口,后缀名请改为pyw)
使用方法简介

首先注册此插件,可以使用自带的安装程序,或自己手动完成。

打开VB6,新建一个标准EXE工程,在窗体上设计自己的GUI布局,这个工作估计没有VB基础的同学都可以完成,同时可以设置相应的控件属性。

如果使用自带安装程序安装了插件,现在VB的工具条上应该有一个新图标(一片橙红色羽毛),如果没有,到菜单"外接程序"|"外接程序管理器"里面启动Tkinter Designer,Tkinter Designer图标和菜单应该会出现。

启动Tkinter Designer后,先按“刷新窗体列表”按钮,列出当前工程的所有窗体和控件列表。

逐个确认各控件的输出属性,在要输出的选项前打钩,如果必要,可以在属性列表中双击修改属性的值。(一般情况不需要再修改控件属性)。 Tkinter Designer尽量的将VB控件属性翻译成Tkinter控件属性,比如字体、颜色、初始值、外观、状态等,甚至包括按钮类和菜单的快捷键设置等待。当然了,如果部分属性没有对应关系的,需要在TkinterDesigner界面上设置。

按“生成代码”按钮则在代码预览窗口生成代码,可以双击代码预览窗口放大阅读,也可以直接修改代码。

确认完成后可以将代码拷贝到剪贴板或保存到文件。 布局可以使用百分比定位(相对定位)或绝对坐标定位(按像素定位),百分比定位为有一个好处,主界面大小变化后,控件也可以相对变化大小。如果不希望主界面大小变化后控件跟随变化,可以选择绝对坐标定位。 注:如果修改了以前设计的界面,可以选择仅输出main函数或界面生成类。不影响外部已经实现的逻辑代码。

如果程序有多个GUI界面,可以在VB工程中添加窗体,就可以选择产生哪个窗体的对应代码。

针对结构化代码,如果要在Python代码中引用和修改其他控件的值,可以使用全局字典gComps,这个字典保存了所有的GUI元素和一些对应的控件变量,可以直接使用形如gComps["Text1Var"].set("new Text")的代码来访问对应控件。 如果输出的是面向对象代码,则可以在界面派生类Application中直接访问 对应的控件。

一般的GUI框架都会将UI部分和逻辑代码部分分别放在不同的文件中,在逻辑代码文件中导入UI文件,实现修改UI不影响逻辑代码。因为对于实现简单的程序来说,我偏爱单文件,所以我将UI类和逻辑代码类都放在同一个文件中,在修改界面后,你可以直接覆盖对应的Application_ui类即可实现界面的变更,不过如果增加了新的事件回调函数,需要在子类Application中增加才行。

目前支持的控件列表

Label 标签条在VB和Python中基本一样。如果不启用ttk,则在文本中插入\n来换行, 如果启用了ttk,则只支持单行文本(多行可以使用Message控件实现)。
TextBox Python文本框有两种:Entry和Text,如果VB的TextBox的MultiLine=False,则 生成Entry,否则生成Text。
Frame 对应Python的LabelFrame控件,做为其他控件的容器,或做为界面元素视觉分类。
CommandButton 对应Python的Button,没有太多区别。 为了代码简洁,窗体的退出按钮可以设置Cancel属性为True,然后程序自动生成 对应Tkinter的destroy回调,这样就不需要再实现一个回调函数。 在VB里面字母前增加一个"&"符号可以直接绑定一个快捷键Alt+对应字母, TkinterDesigner也支持此设置,自动生成对应的事件绑定代码。 其他控件比如CheckBox等有"标题"属性的控件一样如此处理。
CheckBox 多选按钮对应Python的Checkbutton。
OptionButton 单选按钮对应Python的Radiobutton。 tkinter中Radiobutton的分组方法和VB有些不一样(分组意味着组内的单选按钮自动 互斥,用户选择一个则其他的自动取消)。在VB中,如果你使用Frame将几个 OptionButton圈起来,则这几个OptionButton自动成为一组。 但是在tkinter中,你要将需要分成一组的Radiobutton的variable属性设置为同一个 变量,然后各个Radiobutton的value值要不一样,具体是什么值你可以随便设置, 反正不一样就行,最简单的就是1/2/3等,或者一个设置为man,另一个设置为woman, 在对应的Radiobutton被选择后,variable变量自动设置为对应的value值,读取即可 知道哪个Radiobutton被选中了,反之设置variable变量会导致对应的Radiobutton 被选中。
ComboBox 组合框在Tkinter中没有对应的控件,比较类似的只有OptionMenu,类似ComboBox 的Style=2 (Dropdown List)时的表现,一个下拉列表,只能在列表中选择一个值, 不能直接输入。所以建议在VB的ComboBox中写下所有的下拉列表值。 如果启用了TTK主题扩展库支持,则直接对应到TTK的Combobox,外形和行为基本 一致。
ListBox 列表框对应Python的Listbox,行为也类似,可以在设计阶段设置初始列表。 如果需要滚动,则在适当位置创建滚动条,如果滚动条紧靠着列表框的右边或下边, 并且长度(水平滚动条)或高度(垂直滚动条)差不多,则滚动条和列表框自动绑定, 如果没有自动绑定,则可以在Addin界面选择其xscrollcommand或yscrollcommand 属性为对应滚动条的.set方法。
HScrollBar, VScrollBar 滚动条在Python中为Scrollbar,通过设置orient来控制水平还是垂直。
Slider 类似对应Python中的Scale。
PictureBox 简单对应到Python中的Canvas,用做其他控件的容器或画图容器使用。 如果需要滚动,则在适当位置创建滚动条,如果滚动条紧靠着图像框的右边或下边, 并且长度(水平滚动条)或高度(垂直滚动条)差不多,则滚动条和图像框自动绑定, 如果没有自动绑定,可以在Addin界面选择其xscrollcommand和yscrollcommand 属性为对应滚动条的.set方法。
Menu 可以使用VB的菜单编辑器来设计Python的菜单。 在VB中的菜单标题为"-"是分隔条。 也可以在正常的菜单标题中增加(&+字母)的方式添加Alt快捷键。 除Alt快捷快捷键外,在VB菜单编辑器中选择菜单对应的快捷键则会直接显示快捷键 信息在菜单标题后面,并自动注册对应的bind命令。
Line 可以用于组织复杂界面,仅支持水平或垂直线。

以下的控件需要在VB的'控件工具箱'中按右键添加'部件',选择“Microsoft Windows Common Controls 6.0”
ProgressBar 对应到Python的Progressbar,需要启用TTK主题扩展(默认)

TreeView 对应到Python的Treeview,树形显示控件,可以选择是否显示标题行, 需要启用TTK主题扩展(默认) 如果需要滚动,则在适当位置创建滚动条,如果滚动条紧靠着TreeView的右边或下边, 并且长度(水平滚动条)或高度(垂直滚动条)差不多,则滚动条和TreeView自动绑定, 如果没有自动绑定,可以在Addin界面选择其xscrollcommand和yscrollcommand 属性为对应滚动条的.set方法。
TabStrip 选项卡控件,对应到Python的Notebook,需要启用TTK主题扩展(默认)。 如果要布局各个页面内的控件,按以下步骤: 1.每个选项页对应一个Frame或PictureBox,命名为:TabStrip的名字 加'__Tab'(双下划线),再加一个序号,从1开始,比如TabStrip的名字为TabStrip1, 则你可以创建一个PictureBox,命名为'TabStrip1__Tab1'(注意大小写)。 2.然后在PictureBox/Frame内摆放你需要的其他控件,生成代码后此容器内自动添加 到对应的选项页,TkinterDesigner会在后台为您做这一切。 标签页对应的PictureBox/Frame可以放置在窗体的可视范围外,也就是说设计好 对应的选项页后,缩小IDE中的窗体为你需要的大小。 注意:

你需要使用相对坐标,PictureBox或Frame容器的大小请和TabStrip内部大小一致或 接近,否则选项页内的控件将会通过拉伸或收缩来适配可伸缩来适配可用空间,这样有些 控件看起来会比较怪。如果使用绝对坐标,则PictureBox/Frame可以不用和TabStrip 一样大,PictureBox/Frame内的控件将以TabStrip的左上角为原点放置,大小和长宽比例 会和设计时一致。 所以还是建议如果有TabStrip控件的话,使用绝对坐标。
Frame和PictureBox均可作为容器,如果使用Frame作为容器,则其标题可以作为选项页 标题,如果你没有设置选项页标题的话。(选项卡控件的标题设置优先)
CommonDialog 这个控件也算支持,如果VB窗体中有这个控件,则在Python代码中导入 filedialog、simpledialog、colorchooser这三个模块,这三个模块提供简单的 文件选择、输入框、颜色选择对话框功能。 需要在控件工具箱增加"Microsoft Common Dialog Control 6.0"

其他建议

不支持使用控件数组,界面可以显示,但是后面的同名控件名会覆盖前面定义的。
窗体的ScaleMode建议保持默认值(vbTwips),如果要设置为其他值,则Frame控件内就不要再放Frame控件了,否则其内部的控件布局错误。
如果仅需要简体汉字界面,则可以删掉Language.lng文件,仅需TkinterDesigner.dll一个文件。
ttk库额外说明

ttk主题扩展看起来很漂亮,在不同操作系统下界面呈现为本地化风格,建议使用, 只是要注意以下几个ttk的BUG:

TTK的Entry和Combobox控件背景色设置无效(可以设置,不报错,但是界面不变)。
tkinter的Label控件可以通过插入'\n'来换行,但是ttk的Label只能通过wraplength属性来换行。
LabelFrame和Notebook控件的字体单独设置无效,但是可以设置ttk的全局字体属性来改变,比如:self.style.configure('.', font=('宋体',12))。
Python 2.7.3附带的ttk中的Treeview字体设置无效,但3.2.3的Treeview的字体设置有效。
@~风介~@冬雪雪冬 @小甲鱼

jerryxjr1220 发表于 2017-3-11 16:52:02

本帖最后由 jerryxjr1220 于 2017-3-11 16:53 编辑

Visual TKinter - Demo

自制的简单计算器


如果这个界面直接在python的节面编辑的话,总感觉不太方便,但是在VB中就非常方便了,控件可以随意拖放,按钮大小、背景颜色,只要VB中可调的,都能设置。

界面设置完以后直接点“生成代码”:
#!/usr/bin/env python
#-*- coding:utf-8 -*-

import os, sys
try:
    from tkinter import *
except ImportError:#Python 2.x
    PythonVersion = 2
    from Tkinter import *
    from tkFont import Font
    from ttk import *
    #Usage:showinfo/warning/error,askquestion/okcancel/yesno/retrycancel
    from tkMessageBox import *
    #Usage:f=tkFileDialog.askopenfilename(initialdir='E:/Python')
    #import tkFileDialog
    #import tkSimpleDialog
else:#Python 3.x
    PythonVersion = 3
    from tkinter.font import Font
    from tkinter.ttk import *
    from tkinter.messagebox import *
    #import tkinter.filedialog as tkFileDialog
    #import tkinter.simpledialog as tkSimpleDialog    #askstring()

class Application_ui(Frame):
    #这个类仅实现界面生成功能,具体事件处理代码在子类Application中。
    def __init__(self, master=None):
      Frame.__init__(self, master)
      self.master.title('Visual Tkinter - Demo')
      self.master.geometry('287x193')
      self.master.resizable(0,0)
      self.createWidgets()

    def createWidgets(self):
      self.top = self.winfo_toplevel()

      self.style = Style()

      self.Text1Var = StringVar(value='')
      self.Text1 = Entry(self.top, textvariable=self.Text1Var)
      self.Text1.place(relx=0.474, rely=0.124, relwidth=0.394, relheight=0.42)

      self.Command2 = Button(self.top, text=u'退出', command=self.Command2_Cmd)
      self.Command2.place(relx=0.557, rely=0.622, relwidth=0.31, relheight=0.212)

      self.Command1 = Button(self.top, text=u'计算', command=self.Command1_Cmd)
      self.Command1.place(relx=0.111, rely=0.622, relwidth=0.338, relheight=0.212)

      self.style.configure('Label1.TLabel',anchor='center')
      self.Label1 = Label(self.top, text=u'计算结果', style='Label1.TLabel')
      self.Label1.place(relx=0.111, rely=0.124, relwidth=0.254, relheight=0.13)

      self.Text2Var = StringVar(value='')
      self.Text2 = Entry(self.top, textvariable=self.Text2Var)
      self.Text2.place(relx=0.084, rely=0.415, relwidth=0.31, relheight=0.093)


class Application(Application_ui):
    #这个类实现具体的事件处理回调函数。界面生成代码在Application_ui中。
    def __init__(self, master=None):
      Application_ui.__init__(self, master)

    def Command2_Cmd(self, event=None):
      #TODO, Please finish the function here!
      pass

    def Command1_Cmd(self, event=None):
      #TODO, Please finish the function here!
      pass

if __name__ == "__main__":
    top = Tk()
    Application(top).mainloop()
    try: top.destroy()
    except: pass

这段代码就已经自动生成了。

剩下我们要做的就是完善2个Command的命令(生成的代码中已经帮你标示出来了,非常人性化!)

我们修改代码后,直接保存即可。
#!/usr/bin/env python
#-*- coding:utf-8 -*-

import os, sys
try:
    from tkinter import *
except ImportError:#Python 2.x
    PythonVersion = 2
    from Tkinter import *
    from tkFont import Font
    from ttk import *
    #Usage:showinfo/warning/error,askquestion/okcancel/yesno/retrycancel
    from tkMessageBox import *
    #Usage:f=tkFileDialog.askopenfilename(initialdir='E:/Python')
    #import tkFileDialog
    #import tkSimpleDialog
else:#Python 3.x
    PythonVersion = 3
    from tkinter.font import Font
    from tkinter.ttk import *
    from tkinter.messagebox import *
    #import tkinter.filedialog as tkFileDialog
    #import tkinter.simpledialog as tkSimpleDialog    #askstring()

class Application_ui(Frame):
    #这个类仅实现界面生成功能,具体事件处理代码在子类Application中。
    def __init__(self, master=None):
      Frame.__init__(self, master)
      self.master.title('Visual Tkinter - Demo')
      self.master.geometry('287x193')
      self.master.resizable(0,0)
      self.createWidgets()

    def createWidgets(self):
      self.top = self.winfo_toplevel()

      self.style = Style()

      self.Text1Var = StringVar(value='')
      self.Text1 = Entry(self.top, textvariable=self.Text1Var)
      self.Text1.place(relx=0.474, rely=0.124, relwidth=0.394, relheight=0.42)
      gComps['Text1Var'] = self.Text1Var
      
      self.Command2 = Button(self.top, text=u'退出', command=self.Command2_Cmd)
      self.Command2.place(relx=0.557, rely=0.622, relwidth=0.31, relheight=0.212)

      self.Command1 = Button(self.top, text=u'计算', command=self.Command1_Cmd)
      self.Command1.place(relx=0.111, rely=0.622, relwidth=0.338, relheight=0.212)

      self.style.configure('Label1.TLabel',anchor='center')
      self.Label1 = Label(self.top, text=u'计算结果', style='Label1.TLabel')
      self.Label1.place(relx=0.111, rely=0.124, relwidth=0.254, relheight=0.13)

      self.Text2Var = StringVar(value='')
      self.Text2 = Entry(self.top, textvariable=self.Text2Var)
      self.Text2.place(relx=0.084, rely=0.415, relwidth=0.31, relheight=0.093)
      gComps['Text2Var'] = self.Text2Var

class Application(Application_ui):
    #这个类实现具体的事件处理回调函数。界面生成代码在Application_ui中。
    def __init__(self, master=None):
      Application_ui.__init__(self, master)

    def Command2_Cmd(self, event=None):
      try: top.destroy()
      except: pass
      sys.exit()

    def Command1_Cmd(self, event=None):
      txt = gComps['Text1Var'].get()
      result = eval(txt)
      gComps['Text2Var'].set(str(result).decode('utf-8'))
      

gComps = {}

if __name__ == "__main__":
    top = Tk()
    Application(top).mainloop()
    try: top.destroy()
    except: pass


轻松加愉快,整个过程5分钟搞定!{:5_95:}

jerryxjr1220 发表于 2017-3-11 17:08:51

可以用pyinstaller编译成exe可执行文件,直接windows中运行,无需安装任何第三方库(无需安装VB库),非常方便!

冬雪雪冬 发表于 2017-3-11 18:15:25

很方便的可视化工具。一直希望python能像VB,delphi等那样用鼠标就可很方便的画出GUI。
版本更新见 https://github.com/cdhigh/tkinter-designer

chenggeer 发表于 2017-3-11 20:58:50

谢谢班主

小甲鱼 发表于 2017-3-11 21:20:02

{:10_256:}很是可以!

燕雁 发表于 2017-3-11 21:56:16

太有帮助了。。。。。。。。。。。。。。。。。。。。。。

jerryxjr1220 发表于 2017-3-12 00:23:54

用visual Tkinter做的翻译器,调用金山词霸的api



10分钟就能搞定源代码,以往弄这么个界面怎么也得个把小时了:
#!/usr/bin/env python
#-*- coding:utf-8 -*-

import os, sys
import requests, json
try:
    from tkinter import *
except ImportError:#Python 2.x
    PythonVersion = 2
    from Tkinter import *
    from tkFont import Font
    from ttk import *
    #Usage:showinfo/warning/error,askquestion/okcancel/yesno/retrycancel
    from tkMessageBox import *
    #Usage:f=tkFileDialog.askopenfilename(initialdir='E:/Python')
    #import tkFileDialog
    #import tkSimpleDialog
else:#Python 3.x
    PythonVersion = 3
    from tkinter.font import Font
    from tkinter.ttk import *
    from tkinter.messagebox import *
    #import tkinter.filedialog as tkFileDialog
    #import tkinter.simpledialog as tkSimpleDialog    #askstring()

class Application_ui(Frame):
    #这个类仅实现界面生成功能,具体事件处理代码在子类Application中。
    def __init__(self, master=None):
      Frame.__init__(self, master)
      self.master.title(u'翻译器')
      self.master.geometry('324x146')
      self.master.resizable(0,0)
      self.createWidgets()

    def createWidgets(self):
      self.top = self.winfo_toplevel()

      self.style = Style()

      self.Text2Var = StringVar(value='')
      self.Text2 = Text(self.top)
      self.Text2.place(relx=0.519, rely=0.329, relwidth=0.423, relheight=0.5)
      gComps['t2'] = self.Text2
      
      self.Text1Var = StringVar(value='')
      self.Text1 = Text(self.top)
      self.Text1.place(relx=0.049, rely=0.329, relwidth=0.423, relheight=0.5)
      gComps['t1'] = self.Text1
      
      self.style.configure('Label1.TLabel',anchor='w')
      self.Label1 = Label(self.top, text=u'请输入要翻译的内容:', style='Label1.TLabel')
      self.Label1.place(relx=0.074, rely=0.164, relwidth=0.398, relheight=0.116)

      self.Command1 = Button(self.top, text=u'翻译', command=self.Command1_Cmd)
      self.Command1.place(relx=0.593, rely=0.164, relwidth=0.299, relheight=0.116)


class Application(Application_ui):
    #这个类实现具体的事件处理回调函数。界面生成代码在Application_ui中。
    def __init__(self, master=None):
      Application_ui.__init__(self, master)

    def Command1_Cmd(self, event=None):
      txt = gComps['t1'].get(1.0,END)
      translation = self.translate(txt)
      gComps['t2'].delete(1.0,END)
      gComps['t2'].insert(1.0,translation)

    def translate(self,txt=''):
      url = 'http://fy.iciba.com/ajax.php?a=fy'
      req = requests.Session()
      head = {'User-Agent':"Mozilla/5.0 (Windows NT 5.1; rv:52.0) Gecko/20100101 Firefox/52.0"}
      data = {'f':'auto', 't':'auto', 'w':txt}
      res = req.post(url, data=data, headers=head)
      result = json.loads(res.text)
      try:
            out = result['content']['out']
      except:
            out = '\n'.join(result['content']['word_mean'])
      try:
            out = out.replace('<br/>','\n')
      except:
            pass
      return out

gComps = {}

if __name__ == "__main__":
    top = Tk()
    Application(top).mainloop()
    try: top.destroy()
    except: pass

谦虚求学 发表于 2017-3-12 11:33:12

jerryxjr1220 发表于 2017-3-11 16:52
Visual TKinter - Demo

自制的简单计算器


MFC可以不{:5_94:}

mjsz 发表于 2017-3-12 13:58:26

这真的可以用不

QR520 发表于 2017-3-13 16:10:12

谢谢版主

WylLy 发表于 2017-3-13 17:13:26

强烈支持一下,tkinter 写界面太坑了

nccy 发表于 2017-3-13 19:36:11

不错

Deast 发表于 2017-3-15 14:53:15

看看

deadofpeople 发表于 2017-3-15 16:37:10

可以试试wxpython

角手 发表于 2017-3-18 17:42:46

哇 很强

最好是明天 发表于 2017-3-27 20:30:52

好东西,可以玩玩看{:5_109:}

msi 发表于 2017-4-1 15:59:17

正好需要

lenovolty 发表于 2017-4-18 18:12:00

很好很强大

花田半亩 发表于 2017-4-26 23:37:09

谢谢版主
页: [1] 2 3 4 5 6 7 8 9 10
查看完整版本: [可视化GUI编程 Visual TKinter] “不想学PYQT又想做好看的界面”之懒人的福音