本数据处理程序为用Python编写的通用性框架,可以根据实际项目需求动态配置。可配置的内容在界面上展示为三部分:左侧项目树,中间数据表或图表,右侧交互按钮和输入框。项目树中的每个项目可以包含多个数据表或图表,每个数据表或图表可以对应多个按钮和输入框。配置数据存储于程序所在目录下的./config和./script和./data三个文件夹中。./config文件夹中存放YAML格式配置文件,./script文件夹中存放Python脚本,./data文件夹中存放CSV格式表格数据。所有配置文件均可直接用记事本等文本编辑器直接打开查看。
项目树由./config/default.yml文件中tree键值配置。tree键值下可以有多个节点,每个节点有如下属性:
数据表和图表由./config/default.yml文件中display键值配置。display键值下面第一级为项目id,必须和上面项目树里的id相同。项目id下可有多个数据表或图表,每个数据表或图表有如下属性:
- - a
- b
- - d
- e
会展示为
| a | b |
| c | d |
交互按钮和输入框由./config/default.yml文件中interact键值配置。interact键值下面第一级为数据表或图表id,必须和上面display中的id相同。数据表或图表id下可有多个交互按钮和输入框,每个交互按钮和输入框有如下属性:
目前支持对表格的读写和绘图,并不支持动态地创建或删除表格或图表。样例参见./script文件夹里已有脚本。
class project:
list_id = []
def __init__(self, id):
self.id = id
def __enter__(self):
project.list_id.append(self.id)
return self
def __exit__(self, *args):
project.list_id.pop()
class sheet:
list_widget = []
def __init__(self, sheet_id):
widget = window.widget_display.map_table[sheet_id]
self.id = sheet_id
self.widget = widget
def __enter__(self):
sheet.list_widget.append(self.widget)
return self
def __exit__(self, *args):
sheet.list_widget.pop()
def at(self, *args):
c = cell(*args, widget = self.widget)
return c
def __getitem__(self, index):
if type(index) is tuple:
return self.at(*index)
else:
return self.at(index)
@property
def row(self):
return self.widget.row
@row.setter
def row(self, row):
self.widget.row = row
@property
def col(self):
return self.widget.col
@col.setter
def col(self, col):
self.widget.col = col
def clear(self):
self.widget.clear()
def clear_content(self):
self.widget.clearContents()
def initial(self):
self.widget.initial()
def reset_content(self):
self.widget.reset_content()
def fit_content(self):
self.widget.fit_content()
def load_data(self, *arg, **kwarg):
self.widget.load_data(*arg, **kwarg)
def dump_data(self, *arg, **kwarg):
return self.widget.dump_data(*arg, **kwarg)
def load_str(self, *arg, **kwarg):
self.widget.load_str(*arg, **kwarg)
def dump_str(self, *arg, **kwarg):
return self.widget.dump_str(*arg, **kwarg)
def load_file(self, *arg, **kwarg):
self.widget.load_file(*arg, **kwarg)
def dump_file(self, *arg, **kwarg):
self.widget.dump_file(*arg, **kwarg)
class cell:
def __init__(self, *arg, widget = None):
if len(arg) == 1:
pos = arg[0]
pos = pos.lower()
row = None
col = None
match = re.match(r"([a-z]+)(\d+)", pos)
if not match:
raise RuntimeError(f"unrecognized pos: \"{pos}\"")
row = match.group(2)
cl = match.group(1)
row = int(row) - 1
col = 0
for chr in cl:
col *= 26
col += ord(chr) - ord("a")
elif len(arg) == 2:
row = arg[0]
col = arg[1]
else:
raise RuntimeError("The number of params should not be more than 2")
self.item = None
if widget is None and sheet.list_widget:
widget = sheet.list_widget[-1]
if widget:
self.item = widget.at(row, col)
@property
def text(self):
return self.item.text
@text.setter
def text(self, s):
self.item.text = s
@property
def value(self):
return self.item.value
@value.setter
def value(self, v):
self.item.value = v
def empty(self):
return self.item.empty()
def clear(self):
self.item.value = None
class graph:
list_widget = []
def __init__(self, graph_id):
widget = window.widget_display.map_graph[graph_id]
self.id = graph_id
self.widget = widget
def __enter__(self):
graph.list_widget.append(self.widget)
return self
def __exit__(self, *args):
graph.list_widget.pop()
def plot(self, *arg, **kwarg):
self.widget.plot(*arg, **kwarg)
def plot(*arg, **kwarg):
widget = graph.list_widget[-1]
widget.plot(*arg, **kwarg)
def param(id):
map_input = window.widget_interact.widget_grid.map_input
result = map_input.get(id)
return result
class string:
dtype = str
def __init__(self):
pass
def str2val(self, s):
return s
def val2str(self, v):
return v
class integer:
dtype = int
def __init__(self, fmt = "%d"):
self.fmt = fmt
def str2val(self, s):
s = s.strip()
if not s:
return None
try:
v = int(s)
except:
v = int(float(s))
return v
def val2str(self, v):
return self.fmt % v
class decimal:
dtype = float
def __init__(self, fmt = "%.3f", epsilon = 1e-4):
self.fmt = fmt
self.epsilon = epsilon
def str2val(self, s):
s = s.strip()
if not s:
return None
return float(s)
def val2str(self, v):
if abs(v) < self.epsilon:
v = 0.0
return self.fmt % v
class kdecimal:
dtype = float
def __init__(self, fmt = "%07.3f", epsilon = 1e-4):
self.fmt = fmt
self.epsilon = epsilon
def str2val(self, s):
s = s.strip()
if not s:
return None
match = re.match(r"[kK](\d+)\+(\d+(?:\.\d+)?)", s)
if match is None:
raise RuntimeError(f"pattern not matched: {s}")
a = match.group(1)
b = match.group(2)
return float(a + b)
def val2str(self, v):
a = v // 1000
b = v % 1000
if abs(b - 1000) < self.epsilon:
a += 1
b = 0.0
elif abs(b) < self.epsilon:
b = 0.0
return f"K%d+{self.fmt}" % (a, b)
class angle:
dtype = float
def __init__(self):
pass
def str2val(self, s):
s = s.strip()
if not s:
return None
match = re.match(r"(\d+)°(\d+)′(\d+(?:\.\d+)?)″", s)
if match is None:
raise RuntimeError(f"pattern not matched: {s}")
d = int(match.group(1))
m = int(match.group(2))
s = float(match.group(3))
return dms2rad(d, m, s)
def val2str(self, v):
return "%2d°%02d′%05.2f″" % rad2dms(v)