Init scomm project
This commit is contained in:
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
*.pyc
|
||||
223
scomm.json
Executable file
223
scomm.json
Executable file
@@ -0,0 +1,223 @@
|
||||
{
|
||||
"Button":[
|
||||
{
|
||||
"name":"btn-scan",
|
||||
"text":"串口设置",
|
||||
"column": 1,
|
||||
"weight": 0,
|
||||
"row": 1
|
||||
},
|
||||
{
|
||||
"name":"btn-onoff",
|
||||
"text":"打开串口",
|
||||
"column": 5,
|
||||
"row": 1
|
||||
},
|
||||
{
|
||||
"name":"btn-clear",
|
||||
"text":"清除窗口",
|
||||
"column": 1,
|
||||
"row": 3
|
||||
},
|
||||
{
|
||||
"name":"btn-sendfile",
|
||||
"text":"发送文件",
|
||||
"column": 1,
|
||||
"row": 4
|
||||
},
|
||||
{
|
||||
"name":"btn-pack01",
|
||||
"text":"pack-01",
|
||||
"column": 2,
|
||||
"row": 5
|
||||
},
|
||||
{
|
||||
"name":"btn-pack02",
|
||||
"text":"pack-02",
|
||||
"column": 3,
|
||||
"row": 5
|
||||
},
|
||||
{
|
||||
"name":"btn-pack03",
|
||||
"text":"pack-03",
|
||||
"column": 4,
|
||||
"row": 5
|
||||
},
|
||||
{
|
||||
"name":"btn-pack04",
|
||||
"text":"pack-04",
|
||||
"column": 5,
|
||||
"row": 5
|
||||
},
|
||||
{
|
||||
"name":"btn-pack05",
|
||||
"text":"pack-05",
|
||||
"column": 6,
|
||||
"row": 5
|
||||
},
|
||||
{
|
||||
"name":"btn-send",
|
||||
"text":"发送",
|
||||
"column": 10,
|
||||
"row": 6
|
||||
}
|
||||
],
|
||||
"Label":[
|
||||
{
|
||||
"name":"empty-01",
|
||||
"text":" ",
|
||||
"column": 9,
|
||||
"colweight": 1,
|
||||
"row": 1
|
||||
},
|
||||
{
|
||||
"name":"label-sscript",
|
||||
"text":"组帧脚本:",
|
||||
"column": 1,
|
||||
"row": 5
|
||||
}
|
||||
],
|
||||
"Checkbutton":[
|
||||
{
|
||||
"name":"ckbtn-rhex",
|
||||
"text":"HEX显示",
|
||||
"sticky": "w",
|
||||
"column": 2,
|
||||
"row": 3
|
||||
},
|
||||
{
|
||||
"name":"ckbtn-shex",
|
||||
"text":"HEX发送",
|
||||
"sticky": "w",
|
||||
"column": 2,
|
||||
"row": 4
|
||||
},
|
||||
{
|
||||
"name":"ckbtn-0d",
|
||||
"text":"追加\\r",
|
||||
"sticky": "w",
|
||||
"column": 3,
|
||||
"row": 4
|
||||
},
|
||||
{
|
||||
"name":"ckbtn-0a",
|
||||
"text":"追加\\n",
|
||||
"sticky": "w",
|
||||
"column": 4,
|
||||
"row": 4
|
||||
}
|
||||
],
|
||||
"Entry": [
|
||||
{
|
||||
"name":"entry-com",
|
||||
"bg":"gold",
|
||||
"width":20,
|
||||
"column": 2,
|
||||
"columnspan": 2,
|
||||
"row": 1
|
||||
},
|
||||
{
|
||||
"name":"entry-baud",
|
||||
"bg":"gold",
|
||||
"width":10,
|
||||
"column": 4,
|
||||
"row": 1
|
||||
},
|
||||
{
|
||||
"name":"entry-sendText",
|
||||
"bg":"gold",
|
||||
"column": 1,
|
||||
"columnspan": 9,
|
||||
"row": 6
|
||||
}
|
||||
],
|
||||
"Text":{
|
||||
"name":"text-recv",
|
||||
"bg":"#cccccc",
|
||||
"column": 1,
|
||||
"columnspan": 9,
|
||||
"rowweight": 1,
|
||||
"row": 2
|
||||
},
|
||||
"Frame":{
|
||||
"name":"frame-opBtns",
|
||||
"row":2,
|
||||
"column": 10,
|
||||
"Label":[
|
||||
{
|
||||
"name":"label-rscript",
|
||||
"text":"解帧脚本:",
|
||||
"column": 2,
|
||||
"row": 1
|
||||
},
|
||||
{
|
||||
"name":"label-data",
|
||||
"text":"预置数据:",
|
||||
"column": 2,
|
||||
"row": 7
|
||||
}
|
||||
],
|
||||
"Button":[
|
||||
{
|
||||
"name":"btn-unpack01",
|
||||
"text":"unpack-01",
|
||||
"column": 2,
|
||||
"row": 2
|
||||
},
|
||||
{
|
||||
"name":"btn-unpack02",
|
||||
"text":"unpack-02",
|
||||
"column": 2,
|
||||
"row": 3
|
||||
},
|
||||
{
|
||||
"name":"btn-unpack03",
|
||||
"text":"unpack-03",
|
||||
"column": 2,
|
||||
"row": 4
|
||||
},
|
||||
{
|
||||
"name":"btn-unpack04",
|
||||
"text":"unpack-04",
|
||||
"column": 2,
|
||||
"row": 5
|
||||
},
|
||||
{
|
||||
"name":"btn-unpack05",
|
||||
"text":"unpack-05",
|
||||
"column": 2,
|
||||
"row": 6
|
||||
},
|
||||
{
|
||||
"name":"btn-data01",
|
||||
"text":"data-01",
|
||||
"column": 2,
|
||||
"row": 8
|
||||
},
|
||||
{
|
||||
"name":"btn-data02",
|
||||
"text":"data-02",
|
||||
"column": 2,
|
||||
"row": 9
|
||||
},
|
||||
{
|
||||
"name":"btn-data03",
|
||||
"text":"data-03",
|
||||
"column": 2,
|
||||
"row": 10
|
||||
},
|
||||
{
|
||||
"name":"btn-data04",
|
||||
"text":"data-04",
|
||||
"column": 2,
|
||||
"row": 11
|
||||
},
|
||||
{
|
||||
"name":"btn-data05",
|
||||
"text":"data-05",
|
||||
"column": 2,
|
||||
"row": 12
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
8
scomm.py
Executable file
8
scomm.py
Executable file
@@ -0,0 +1,8 @@
|
||||
#! /usr/bin/env python3
|
||||
|
||||
import tkgen.gengui
|
||||
|
||||
if __name__ == '__main__':
|
||||
root = tkgen.gengui.TkJson('scomm.json', title='scomm串口调试助手')
|
||||
#root = tkgen.gengui.TkJson('rsa_ui.json', title='scomm串口调试助手')
|
||||
root.mainloop()
|
||||
468
tkgen/gengui.py
Executable file
468
tkgen/gengui.py
Executable file
@@ -0,0 +1,468 @@
|
||||
#
|
||||
# Copyright (c) 2011. All rights reserved.
|
||||
#
|
||||
# This library is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU Lesser General Public
|
||||
# License as published by the Free Software Foundation; either
|
||||
# version 2.1 of the License, or (at your option) any later version.
|
||||
#
|
||||
# This library is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
# Lesser General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public
|
||||
# License along with this library; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||
# MA 02110-1301 USA
|
||||
#
|
||||
"""
|
||||
Module for easy creation of Tkinter GUIs.
|
||||
|
||||
Created on Apr 21, 2011
|
||||
|
||||
@author: tmetsch
|
||||
"""
|
||||
|
||||
try:
|
||||
# python 3
|
||||
import tkinter
|
||||
except ImportError:
|
||||
# python 2
|
||||
import Tkinter as tkinter
|
||||
|
||||
try:
|
||||
# python 3
|
||||
from tkinter import ttk
|
||||
except ImportError:
|
||||
# python 2
|
||||
import ttk
|
||||
|
||||
import json
|
||||
import os
|
||||
import traceback
|
||||
import yaml
|
||||
|
||||
|
||||
def _contains_dict(items):
|
||||
"""
|
||||
Checks if a set of items contains a dictionary.
|
||||
|
||||
items -- The set of items to test.
|
||||
"""
|
||||
result = False
|
||||
for item in items.keys():
|
||||
if isinstance(items[item], dict):
|
||||
result = True
|
||||
break
|
||||
return result
|
||||
|
||||
|
||||
def _contains_list(items):
|
||||
"""
|
||||
Checks if a set of items contains a list.
|
||||
|
||||
items -- The set of items to test.
|
||||
"""
|
||||
result = False
|
||||
for item in items.keys():
|
||||
if isinstance(items[item], list) and item is not item.lower():
|
||||
# the .lower check ensures that I can have attribute lists
|
||||
result = True
|
||||
break
|
||||
return result
|
||||
|
||||
|
||||
class TkJson(tkinter.Tk):
|
||||
"""
|
||||
Simple class which wraps Tk and uses some JSON to contruct a GUI.
|
||||
"""
|
||||
|
||||
menu = None
|
||||
widgets = {}
|
||||
|
||||
def __init__(self, filename, title='Tk', prefer_tk=True, file_type='json'):
|
||||
"""
|
||||
Initialize a Tk root and created the UI from a JSON file.
|
||||
|
||||
Returns the Tk root.
|
||||
"""
|
||||
# Needs to be done this way - because base class do not derive from
|
||||
# object :-(
|
||||
tkinter.Tk.__init__(self)
|
||||
self.prefer_tk = prefer_tk
|
||||
self.title(title)
|
||||
|
||||
if file_type == 'json':
|
||||
user_interface = json.load(open(filename)) if os.path.isfile(
|
||||
filename) else json.loads(filename)
|
||||
elif file_type == 'yaml':
|
||||
user_interface = yaml.load(open(filename)) if os.path.isfile(
|
||||
filename) else yaml.loads(filename)
|
||||
else:
|
||||
raise ValueError('Only json or yaml file definitions are'
|
||||
'supported.')
|
||||
|
||||
self.create_widgets(self, user_interface)
|
||||
|
||||
def create_widgets(self, parent, items):
|
||||
"""
|
||||
Creates a set of Tk widgets.
|
||||
"""
|
||||
for name in items.keys():
|
||||
current = items[name]
|
||||
if isinstance(current, dict) and not _contains_list(
|
||||
current) and not _contains_dict(current):
|
||||
self._create_widget(name, parent, current)
|
||||
|
||||
elif isinstance(current, dict) and _contains_list(current):
|
||||
widget = self._create_widget(name, parent, current)
|
||||
if not widget:
|
||||
break
|
||||
|
||||
self.create_widgets(widget, current)
|
||||
elif isinstance(current, dict) and _contains_dict(current):
|
||||
widget = self._create_widget(name, parent, current)
|
||||
if not widget:
|
||||
break
|
||||
|
||||
self.create_widgets(widget, current)
|
||||
elif isinstance(current, list):
|
||||
for item in current:
|
||||
self.create_widgets(parent, {name: item})
|
||||
|
||||
def _create_widget(self, name, parent, desc):
|
||||
"""
|
||||
Tries to resolve the widget from Tk and create it.
|
||||
|
||||
Returns the newly created widget.
|
||||
|
||||
name -- Name of the widget.
|
||||
parent -- The parent widget.
|
||||
desc -- Dictionary containing the description for this widget.
|
||||
"""
|
||||
position, weight, padding, opt = self._get_options(desc)
|
||||
|
||||
try:
|
||||
widget_factory = getattr(
|
||||
tkinter, name) if self.prefer_tk else getattr(ttk, name)
|
||||
except AttributeError:
|
||||
try:
|
||||
widget_factory = getattr(
|
||||
ttk, name) if self.prefer_tk else getattr(tkinter, name)
|
||||
except AttributeError:
|
||||
traceback.print_exc()
|
||||
raise AttributeError(
|
||||
'Neither Tkinter nor ttk have a widget named: ', name)
|
||||
|
||||
while True:
|
||||
try:
|
||||
widget = widget_factory(parent, **opt)
|
||||
break
|
||||
except Exception as exp:
|
||||
if len(opt) == 0:
|
||||
break
|
||||
del opt[str(exp).split()[2][2:-1]]
|
||||
# widget = widget_factory(parent,**opt)
|
||||
|
||||
# todo: tkinter Label for debug
|
||||
#tkinter.Label(text=position[0],relief=tkinter.SUNKEN).grid(row=position[0],column=0,sticky='news')
|
||||
#tkinter.Label(text=position[1],relief=tkinter.SUNKEN).grid(row=0,column=position[1],sticky='news')
|
||||
widget.grid(row=position[0],
|
||||
column=position[1],
|
||||
columnspan=weight[0],
|
||||
rowspan=weight[1],
|
||||
sticky=padding[2],
|
||||
padx=padding[0],
|
||||
pady=padding[1])
|
||||
|
||||
# propaget size settings when needed.
|
||||
if 'width' in opt or 'height' in opt:
|
||||
widget.grid_propagate(0)
|
||||
|
||||
# set parent weight of the cells
|
||||
if weight[2] > 0:
|
||||
parent.rowconfigure(position[0], weight=weight[2])
|
||||
if weight[3] > 0:
|
||||
parent.columnconfigure(position[1], weight=weight[3])
|
||||
|
||||
self.widgets[widget._name] = widget
|
||||
|
||||
return widget
|
||||
|
||||
def _get_options(self, dictionary):
|
||||
"""
|
||||
Extracts the needed options from a dictionary.
|
||||
|
||||
dictionary -- Dictionary with all the options in it.
|
||||
"""
|
||||
options = {}
|
||||
|
||||
row = 0
|
||||
column = 0
|
||||
|
||||
colspan = 1
|
||||
rowspan = 1
|
||||
rowweight = 0
|
||||
colweight = 0
|
||||
|
||||
padx = 2
|
||||
pady = 2
|
||||
|
||||
sticky = 'news'
|
||||
|
||||
if 'row' in dictionary:
|
||||
row = dictionary['row']
|
||||
dictionary.pop('row')
|
||||
if 'column' in dictionary:
|
||||
column = dictionary['column']
|
||||
dictionary.pop('column')
|
||||
|
||||
if 'columnspan' in dictionary:
|
||||
colspan = dictionary['columnspan']
|
||||
dictionary.pop('columnspan')
|
||||
if 'rowspan' in dictionary:
|
||||
rowspan = dictionary['rowspan']
|
||||
dictionary.pop('rowspan')
|
||||
if 'rowweight' in dictionary:
|
||||
rowweight = dictionary['rowweight']
|
||||
dictionary.pop('rowweight')
|
||||
if 'colweight' in dictionary:
|
||||
colweight = dictionary['colweight']
|
||||
dictionary.pop('colweight')
|
||||
if 'weight' in dictionary:
|
||||
colweight = dictionary['weight']
|
||||
rowweight = dictionary['weight']
|
||||
dictionary.pop('weight')
|
||||
|
||||
if 'padx' in dictionary:
|
||||
padx = dictionary['padx']
|
||||
dictionary.pop('padx')
|
||||
if 'pady' in dictionary:
|
||||
pady = dictionary['pady']
|
||||
dictionary.pop('pady')
|
||||
if 'sticky' in dictionary:
|
||||
sticky = dictionary['sticky']
|
||||
dictionary.pop('sticky')
|
||||
|
||||
for key in dictionary.keys():
|
||||
if not isinstance(dictionary[key],
|
||||
dict) and not isinstance(dictionary[key], list):
|
||||
options[str(key)] = str(dictionary[key])
|
||||
elif isinstance(dictionary[key], list) and key == key.lower():
|
||||
# so we have an attribute list...
|
||||
options[str(key)] = dictionary[key]
|
||||
|
||||
return [row, column], [colspan, rowspan, rowweight, colweight], \
|
||||
[padx, pady, sticky], options
|
||||
|
||||
##
|
||||
# Rest is public use :-)
|
||||
##
|
||||
|
||||
def button(self, name, cmd, focus=False):
|
||||
"""
|
||||
Associate a Tk widget with a function.
|
||||
|
||||
name -- Name of the widget.
|
||||
cmd -- The command to trigger.
|
||||
focus -- indicates wether this item has the focus.
|
||||
"""
|
||||
item = self.get(name)
|
||||
item.config(command=cmd)
|
||||
|
||||
if focus:
|
||||
item.focus_set()
|
||||
|
||||
def checkbox(self, name, focus=False):
|
||||
"""
|
||||
Associates a IntVar with a checkbox.
|
||||
|
||||
name -- Name of the Checkbox.
|
||||
focus -- indicates wether this item has the focus.
|
||||
"""
|
||||
var = tkinter.IntVar()
|
||||
item = self.get(name)
|
||||
item.config(variable=var)
|
||||
|
||||
if focus:
|
||||
item.focus_set()
|
||||
|
||||
return var
|
||||
|
||||
def entry(self, name, key=None, cmd=None, focus=False):
|
||||
"""
|
||||
Returns the text of a TK widget.
|
||||
|
||||
name -- Name of the Tk widget.
|
||||
key -- Needed if a key should be bound to this instance.
|
||||
cmd -- If key is defined cmd needs to be defined.
|
||||
focus -- Indicates wether this entry should take focus.
|
||||
"""
|
||||
var = tkinter.StringVar()
|
||||
|
||||
item = self.get(name)
|
||||
item.config(textvariable=var)
|
||||
|
||||
if focus:
|
||||
item.focus_set()
|
||||
|
||||
if key is not None and cmd is not None:
|
||||
item.bind(key, cmd)
|
||||
|
||||
return var
|
||||
|
||||
def label(self, name):
|
||||
"""
|
||||
Associate a StringVar with a label.
|
||||
|
||||
name -- Name of the Label.
|
||||
"""
|
||||
var = tkinter.StringVar()
|
||||
item = self.get(name)
|
||||
item.config(textvariable=var)
|
||||
return var
|
||||
|
||||
def get(self, name):
|
||||
"""
|
||||
Find a Tk widget by name and return it.
|
||||
"""
|
||||
if name in self.widgets.keys():
|
||||
return self.widgets[name]
|
||||
else:
|
||||
raise KeyError('Widget with the name ` ' + name + ' ` not found.')
|
||||
|
||||
def xscroll(self, widget_name, scrollbar_name):
|
||||
"""
|
||||
Add a horizontal scrollbar to a widget.
|
||||
|
||||
widget_name -- name of the widget.
|
||||
scollbar_name -- name of the scrollbar.
|
||||
"""
|
||||
widget = self.get(widget_name)
|
||||
scrollbar = self.get(scrollbar_name)
|
||||
|
||||
widget.config(xscrollcommand=scrollbar.set)
|
||||
scrollbar.config(command=widget.xview)
|
||||
|
||||
def yscroll(self, widget_name, scrollbar_name):
|
||||
"""
|
||||
Add a vertical scrollbar to a widget.
|
||||
|
||||
widget_name -- name of the widget.
|
||||
scollbar_name -- name of the scrollbar.
|
||||
"""
|
||||
widget = self.get(widget_name)
|
||||
scrollbar = self.get(scrollbar_name)
|
||||
|
||||
widget.config(yscrollcommand=scrollbar.set)
|
||||
scrollbar.config(command=widget.yview)
|
||||
|
||||
def create_menu(self, commands, name=None, parent=None, popup=False):
|
||||
"""
|
||||
Creates a menu(entry) if non is available. Returns the created menu so
|
||||
you can define submenus.
|
||||
|
||||
commands -- dict with 'label':'command' structure.
|
||||
name -- Needs to be provided if it is a dropdown or submenu.
|
||||
parent -- Needs to be provided if it is a submenu.
|
||||
popup -- indicates if it is an popup menu or not (Default: False)
|
||||
"""
|
||||
if self.menu is None and popup is False:
|
||||
# If no menu exists create one and add it to the Tk root.
|
||||
self.menu = tkinter.Menu(self, tearoff=0)
|
||||
self.config(menu=self.menu)
|
||||
|
||||
if name is None and parent is None and popup is False \
|
||||
and len(commands.keys()) > 0:
|
||||
# Just create a Menu entry.
|
||||
for key in commands:
|
||||
self.menu.add_command(label=key, command=commands[key])
|
||||
return self.menu
|
||||
elif name is not None and popup is False and len(commands.keys()) > 0:
|
||||
if parent is None:
|
||||
# Create a top-level drop down menu.
|
||||
tmp_menu = tkinter.Menu(self.menu, tearoff=0)
|
||||
self.menu.add_cascade(label=name, menu=tmp_menu)
|
||||
else:
|
||||
# Create a submenu.
|
||||
tmp_menu = tkinter.Menu(parent, tearoff=0)
|
||||
parent.add_cascade(label=name, menu=tmp_menu)
|
||||
|
||||
for key in commands:
|
||||
tmp_menu.add_command(label=key, command=commands[key])
|
||||
|
||||
return tmp_menu
|
||||
elif popup is True and len(commands.keys()) > 0:
|
||||
tmp_menu = tkinter.Menu(self, tearoff=0)
|
||||
for key in commands:
|
||||
tmp_menu.add_command(label=key, command=commands[key])
|
||||
|
||||
return tmp_menu
|
||||
else:
|
||||
raise AttributeError('Invalid parameters provided')
|
||||
|
||||
# Move?
|
||||
|
||||
def create_from_file(self, parent, filename):
|
||||
"""
|
||||
Create a set of widgets and add them to the given parent.
|
||||
|
||||
parent -- The parent of the to be created widgets.
|
||||
filename -- The JSON definition file.
|
||||
"""
|
||||
ui_file = open(filename)
|
||||
definition = json.load(ui_file)
|
||||
self.create_widgets(parent, definition)
|
||||
|
||||
def notebook(self, parent, filename, name='Tab'):
|
||||
"""
|
||||
Add a tab to a tkk notebook widget.
|
||||
|
||||
parent -- The parent notebook widget instance.
|
||||
filename -- The file which describes the content of the tab.
|
||||
name -- The name of the tab.
|
||||
"""
|
||||
frame = tkinter.Frame()
|
||||
self.create_from_file(frame, filename)
|
||||
parent.add(frame, text=name)
|
||||
|
||||
def toplevel(self, filename, title='Dialog'):
|
||||
"""
|
||||
Open a Toplevel widget.
|
||||
|
||||
parent -- The parent notebook widget instance.
|
||||
title -- The title for the dialog.
|
||||
"""
|
||||
dialog = tkinter.Toplevel()
|
||||
dialog.title(title)
|
||||
self.create_from_file(dialog, filename)
|
||||
dialog.grid()
|
||||
return dialog
|
||||
|
||||
def treeview(self, treeview, name, values, parent='', index=0):
|
||||
"""
|
||||
Adds an item to a treeview.
|
||||
|
||||
treeview -- The treeview to add the items to.
|
||||
name -- The name of the value.
|
||||
values -- The values itself.
|
||||
parent -- Default will create root items, if specified create a leaf.
|
||||
index -- If index < current # of items - insert at the top.
|
||||
"""
|
||||
return treeview.insert(parent, index, text=name, values=values)
|
||||
|
||||
|
||||
class TkYaml(TkJson):
|
||||
"""
|
||||
Wrapper class for parsing yaml files.
|
||||
"""
|
||||
|
||||
def __init__(self, filename, title='Tk', prefer_tk=True):
|
||||
"""
|
||||
Initialize a Tk root and created the UI from a YAML file.
|
||||
|
||||
Returns the Tk root.
|
||||
"""
|
||||
super(TkYaml, self).__init__(filename, title, prefer_tk,
|
||||
file_type='yaml')
|
||||
Reference in New Issue
Block a user