first prototype
This commit is contained in:
129
.gitignore
vendored
Normal file
129
.gitignore
vendored
Normal 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
78
eval.py
Normal 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
41
fake.py
Normal 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
34
listentry.py
Normal 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
87
mathentry.py
Normal 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
150
scam.py
Executable 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
59
tools.py
Normal 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
|
||||||
|
|
||||||
|
|
||||||
|
|
Reference in New Issue
Block a user