first prototype

This commit is contained in:
2021-09-29 16:10:29 +02:00
parent 653be355d7
commit 400cbdbd92
8 changed files with 583 additions and 0 deletions

129
.gitignore vendored Normal file
View File

@ -0,0 +1,129 @@
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class
# C extensions
*.so
# Distribution / packaging
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
pip-wheel-metadata/
share/python-wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST
# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec
# Installer logs
pip-log.txt
pip-delete-this-directory.txt
# Unit test / coverage reports
htmlcov/
.tox/
.nox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
*.py,cover
.hypothesis/
.pytest_cache/
# Translations
*.mo
*.pot
# Django stuff:
*.log
local_settings.py
db.sqlite3
db.sqlite3-journal
# Flask stuff:
instance/
.webassets-cache
# Scrapy stuff:
.scrapy
# Sphinx documentation
docs/_build/
# PyBuilder
target/
# Jupyter Notebook
.ipynb_checkpoints
# IPython
profile_default/
ipython_config.py
# pyenv
.python-version
# pipenv
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
# However, in case of collaboration, if having platform-specific dependencies or dependencies
# having no cross-platform support, pipenv may install dependencies that don't work, or not
# install all needed dependencies.
#Pipfile.lock
# PEP 582; used by e.g. github.com/David-OConnor/pyflow
__pypackages__/
# Celery stuff
celerybeat-schedule
celerybeat.pid
# SageMath parsed files
*.sage.py
# Environments
.env
.venv
env/
venv/
ENV/
env.bak/
venv.bak/
# Spyder project settings
.spyderproject
.spyproject
# Rope project settings
.ropeproject
# mkdocs documentation
/site
# mypy
.mypy_cache/
.dmypy.json
dmypy.json
# Pyre type checker
.pyre/

78
eval.py Normal file
View File

@ -0,0 +1,78 @@
import ast
import operator
from utils import typename
BIN_OPS = {
ast.Add: operator.add,
ast.Sub: operator.sub,
ast.Mult: operator.mul,
ast.Div: operator.truediv,
ast.Mod: operator.mod
}
UNARY_OPS = {
ast.UAdd: operator.pos,
ast.USub: operator.neg
}
def forgiving_eval(value):
try:
return arithmetic_eval(value)
except:
return value
def arithmetic_eval(s):
node = ast.parse(s, mode="eval")
return ast_node_eval(node.body)
def ast_node_eval(node):
if isinstance(node, ast.Expression):
return ast_node_eval(node.body)
elif isinstance(node, ast.Str):
return node.s
elif isinstance(node, ast.Num):
return node.n
elif isinstance(node, ast.BinOp):
op = get_operator(node, BIN_OPS)
left = ast_node_eval(node.left)
right = ast_node_eval(node.right)
return op(left, right)
elif isinstance(node, ast.UnaryOp):
op = get_operator(node, UNARY_OPS)
operand = ast_node_eval(node.operand)
return op(operand)
else:
tn = typename(node)
raise ArithmeticEvalError(f"Unsupported node type {tn}")
def get_operator(node, ops):
op_type = type(node.op)
try:
op = ops[op_type]
except KeyError as e:
nn = typename(node)
on = typename(node.op)
raise ArithmeticEvalError(f"Unsupported {nn} {on}") from e
else:
return op
class ArithmeticEvalError(Exception):
pass
#TODO:
#print like SyntaxError:
# "something with an error here"
# ^
#this needs full string and offset of current node within full string stored

41
fake.py Normal file
View File

@ -0,0 +1,41 @@
data = {
"TEST0_psen_db": {
"x": None,
"aaa": "aaa",
"bbbbbb": 2,
"c": "cccccc"
},
"TEST1_psen_db1": {
"x": None,
"bbbbbb": 2,
"c": "cccccc"
},
"TEST2TEST2_psen_db": {
"x": None,
"y": [1,2,3],
"aaa": "aaa",
"c": "cccccc"
},
"ignored": {}
}
server_info = {
"active_instances": list(data.keys())
}
class PipelineClient:
def __init__(*args, **kwargs):
pass
def get_server_info(self):
return server_info
def get_instance_config(self, name):
return data[name]

34
listentry.py Normal file
View File

@ -0,0 +1,34 @@
import wx
from mathentry import MathEntry
class ListEntry(wx.BoxSizer):
def __init__(self, parent, id=wx.ID_ANY, value=(), style=wx.TE_RIGHT):
super().__init__(wx.HORIZONTAL)
self.parent = parent
self.style = style
self.entries = []
self.SetValue(value)
def SetValue(self, value):
self.entries.clear()
self.Clear(True)
for v in value:
new = MathEntry(self.parent, value=v, style=self.style)
self.entries.append(new)
self.Add(new, flag=wx.EXPAND)
def GetValue(self):
return [e.GetValue() for e in self.entries]
def Disable(self):
return [e.Disable() for e in self.entries]
def Enable(self):
return [e.Enable() for e in self.entries]

87
mathentry.py Normal file
View File

@ -0,0 +1,87 @@
import wx
from eval import arithmetic_eval
from utils import typename
class MathEntry(wx.TextCtrl):
def __init__(self, *args, value="", **kwargs):
if "style" in kwargs:
kwargs["style"] |= wx.TE_PROCESS_ENTER
else:
kwargs["style"] = wx.TE_PROCESS_ENTER
self.value_type = type(value)
value = str(value)
wx.TextCtrl.__init__(self, *args, value=value, **kwargs)
self._alarm = False
self._last_good_value = self.GetValue()
self.Bind(wx.EVT_TEXT_ENTER, self.on_enter)
self.Bind(wx.EVT_KEY_UP, self.on_escape)
def GetValue(self):
val = super().GetValue()
try:
val = self.value_type(val)
except ValueError:
pass
return val
def SetValue(self, val):
val = str(val)
super().SetValue(val)
def on_enter(self, event):
val = super().GetValue()
self._unset_alarm()
try:
val = arithmetic_eval(val)
self.value_type = type(val)
except SyntaxError as e:
en = typename(e)
msg = e.args[0]
msg = f"{en}: {msg}"
self._set_alarm(msg)
self.SetInsertionPoint(e.offset)
except Exception as e:
en = typename(e)
msg = f"{en}: {e}"
self._set_alarm(msg)
self.SetInsertionPointEnd()
else:
self.SetValue(val)
self._last_good_value = val
event.Skip()
def on_escape(self, event):
code = event.GetKeyCode()
if code != wx.WXK_ESCAPE:
event.Skip()
return
if self._alarm:
self.SetValue(self._last_good_value)
self._unset_alarm()
def _set_alarm(self, msg):
self._alarm = True
self.SetToolTip(msg)
self.SetForegroundColour(wx.RED)
def _unset_alarm(self):
self._alarm = False
self.SetToolTip(None)
self.SetForegroundColour(wx.NullColour)

150
scam.py Executable file
View File

@ -0,0 +1,150 @@
#!/usr/bin/env python3
import wx
#from cam_server_client import PipelineClient
from fake import PipelineClient
from tools import EXPANDING, STRETCH, make_filled_vbox, make_filled_hbox
from mathentry import MathEntry
from listentry import ListEntry
pc = PipelineClient("http://sf-daqsync-01:8889")
si = pc.get_server_info()
ai = si["active_instances"]
pls = (i for i in ai if "psen_db" in i)
pls = sorted(pls)
class MainFrame(wx.Frame):
def __init__(self, parent=None, title="test"):
super().__init__(parent, title=title)
panel_main = MainPanel(self)
self.sizer = sizer = make_filled_vbox([panel_main])
self.SetSizerAndFit(sizer)
class MainPanel(wx.Panel):
def __init__(self, parent):
super().__init__(parent)
self.cb_pls = cb_pls = wx.ComboBox(self, choices=pls)
self.entries = entries = SettingsList(self)
self.btn_print = btn_print = wx.Button(self, label="Print")
cb_pls.Bind(wx.EVT_COMBOBOX, self.on_select)
btn_print.Bind(wx.EVT_BUTTON, self.on_print)
widgets = [cb_pls, entries, btn_print]
sizer = make_filled_vbox(widgets)
self.SetSizer(sizer)
def on_select(self, event):
instance = self.cb_pls.GetValue()
cfg = pc.get_instance_config(instance)
self.entries.update(cfg)
self._adjust_size()
def _adjust_size(self):
parent = self.GetParent()
parent.sizer.Layout()
parent.Fit()
def on_print(self, event):
data = self.entries.get()
print(data)
class SettingsList(wx.GridSizer):
def __init__(self, parent, hgap=5, vgap=5):
super().__init__(cols=2, hgap=hgap, vgap=vgap)
self.parent = parent
self.children = []
def update(self, cfg):
self.clear()
for k, v in sorted(cfg.items()):
self.add(k, v)
def clear(self):
self.Clear(True)
def add(self, *args):
new = Setting(self.parent, *args)
self.children.append(new)
self.Add(new.state)
self.Add(new.text, 0, wx.EXPAND|wx.ALL)
def get(self):
res = {}
for i in self.children:
name = i.get_name()
print(name)
if i.get_state():
value = i.get_value()
else:
value = None
res[name] = value
return res
class Setting:
def __init__(self, parent, label, value):
self.state = state = wx.CheckBox(parent, label=label)
if isinstance(value, list):
self.text = text = ListEntry(parent, style=wx.TE_RIGHT)
else:
self.text = text = MathEntry(parent, style=wx.TE_RIGHT)
state.Bind(wx.EVT_CHECKBOX, self.on_state_change)
if value is None:
state.SetValue(False)
text.Disable()
else:
state.SetValue(True)
text.SetValue(value)
def get_name(self):
return self.state.GetLabel()
def get_state(self):
return self.state.GetValue()
def get_value(self):
return self.text.GetValue()
def on_state_change(self, event):
if self.get_state():
self.text.Enable()
else:
self.text.Disable()
app = wx.App()
frame = MainFrame()
frame.Show()
app.MainLoop()

59
tools.py Normal file
View File

@ -0,0 +1,59 @@
import wx
WX_DEFAULT_RESIZABLE_DIALOG_STYLE = wx.DEFAULT_DIALOG_STYLE|wx.RESIZE_BORDER|wx.MINIMIZE_BOX|wx.MAXIMIZE_BOX
class EXPANDING: pass
class STRETCH: pass
def post_event(event, source):
evt = wx.PyCommandEvent(event.typeId, source.GetId())
wx.PostEvent(source, evt)
def copy_to_clipboard(val):
clipdata = wx.TextDataObject()
clipdata.SetText(val)
wx.TheClipboard.Open()
wx.TheClipboard.SetData(clipdata)
wx.TheClipboard.Close()
def make_filled_vbox(widgets, proportion=0, flag=wx.ALL|wx.EXPAND, border=0, box=None):
return make_filled_box(wx.VERTICAL, widgets, proportion, flag, border, box)
def make_filled_hbox(widgets, proportion=1, flag=wx.ALL|wx.EXPAND, border=0, box=None):
return make_filled_box(wx.HORIZONTAL, widgets, proportion, flag, border, box)
def make_filled_box(orient, widgets, proportion, flag, border, box):
if box is None:
box = wx.BoxSizer(orient)
OTHER_PROP = {
0: 1,
1: 0
}
expand = False
for i in widgets:
if i is STRETCH:
box.AddStretchSpacer()
elif i is EXPANDING:
expand = True # store for (and then apply to) next widget
else:
prop = proportion
if expand:
expand = False # apply only once
prop = OTHER_PROP[prop] # other proportion makes widget expanding
box.Add(i, proportion=prop, flag=flag, border=border)
return box

5
utils.py Normal file
View File

@ -0,0 +1,5 @@
def typename(obj):
return type(obj).__name__