336 lines
11 KiB
Python
336 lines
11 KiB
Python
#-------------------------------------------------------------------------------
|
||
# Name: opal.py
|
||
# Purpose:
|
||
#
|
||
# Author: Anton Vakhrushev
|
||
#
|
||
# Created: 14.03.2012
|
||
# Copyright: (c) Anton Vakhrushev 2012
|
||
# Licence: LGPL
|
||
#-------------------------------------------------------------------------------
|
||
#!/usr/bin/env python#!/usr/bin/env python
|
||
#! coding: utf-8
|
||
|
||
import server
|
||
import task
|
||
import wx
|
||
import wx.propgrid as wxpg
|
||
import forms
|
||
import time
|
||
import datetime
|
||
import os
|
||
import threading
|
||
import re
|
||
|
||
#-----------------------------------------------------------------------------
|
||
# Главная форма
|
||
#-----------------------------------------------------------------------------
|
||
|
||
class MainFrame(forms.MainFrame):
|
||
def __init__(self):
|
||
forms.MainFrame.__init__(self, None)
|
||
|
||
self.name_id = 1
|
||
|
||
s = server.LocalServer()
|
||
s.LoadModels()
|
||
models = s.GetModels()
|
||
s.Start()
|
||
self.server = s
|
||
|
||
model = models[0]
|
||
|
||
self.m_user_models.Bind(wx.EVT_TREE_SEL_CHANGED,
|
||
self.OnModelActivated)
|
||
self.m_params.Bind(wxpg.EVT_PG_CHANGING,
|
||
self.OnParamChanging)
|
||
self.m_params.Bind(wxpg.EVT_PG_CHANGED,
|
||
self.OnParamChanged)
|
||
|
||
self.Bind(wx.EVT_MENU, self.OnTest, id = forms.ID_TEST)
|
||
self.Bind(wx.EVT_MENU, self.OnAddModelToRoot,
|
||
id = forms.ID_ADD_MODEL_ROOT)
|
||
self.Bind(wx.EVT_MENU, self.OnAddModelToSelected,
|
||
id = forms.ID_ADD_MODEL_SELECTED)
|
||
self.Bind(wx.EVT_MENU, self.OnDuplicate,
|
||
id = forms.ID_DUPLICATE_MODEL)
|
||
self.Bind(wx.EVT_MENU, self.OnModelProcess,
|
||
id = forms.ID_PROCESS_MODEL)
|
||
|
||
self.Bind(wx.EVT_CLOSE, self.OnClose)
|
||
self.Bind(wx.EVT_IDLE, self.OnIdle)
|
||
|
||
ov = threading.Thread(target = self.Overseer)
|
||
ov.daemon = 1
|
||
ov.start()
|
||
|
||
self.NewProject(model)
|
||
|
||
|
||
def OnClose(self, event):
|
||
self.server.Stop()
|
||
self.Destroy()
|
||
|
||
def Overseer(self):
|
||
|
||
def StateToStr(state):
|
||
if state == server.JOB_READY:
|
||
return 'Ready'
|
||
elif state == server.JOB_RUNNING:
|
||
return 'Running'
|
||
elif state == server.JOB_STOPPED:
|
||
return 'Stopped'
|
||
elif state == server.JOB_COMPLETED:
|
||
return 'Completed'
|
||
else:
|
||
return 'Unknown'
|
||
|
||
try:
|
||
um = self.m_user_models
|
||
cycle_count = 0
|
||
while True:
|
||
wx.MutexGuiEnter()
|
||
print 'cycle{:-8}'.format(cycle_count)
|
||
cycle_count += 1
|
||
item = um.GetRootItem()
|
||
while item.IsOk():
|
||
data = um.GetPyData(item)
|
||
if data:
|
||
jid = data[1]
|
||
if jid != None and self.server.IsJobChanged(jid):
|
||
state = self.server.GetJobState(jid)
|
||
um.SetItemText(item, StateToStr(state[0]), 1)
|
||
p = state[1]
|
||
p = 'Unknown' if p < 0 else '{:%}'.format(p)
|
||
um.SetItemText(item, p, 2)
|
||
um.SetItemText(item, state[2], 3)
|
||
print jid, state
|
||
|
||
item = um.GetNext(item)
|
||
wx.MutexGuiLeave()
|
||
time.sleep(0.2)
|
||
except Exception, e:
|
||
print 'Error in overseer: ', e
|
||
|
||
def CheckName(self, name):
|
||
"""
|
||
Проверяет имя на уникальность в иерархии пользовательских моделей.
|
||
Возвращает True, если имя уникально, иначе False.
|
||
"""
|
||
um = self.m_user_models
|
||
item = um.GetRootItem()
|
||
while item.IsOk():
|
||
item_name = um.GetItemText(item)
|
||
if item_name == name:
|
||
return False
|
||
item = um.GetNext(item)
|
||
return True
|
||
|
||
def GenerateName(self, name):
|
||
"""
|
||
На основе переданного имени генерирует новое имя модели таким образом,
|
||
чтобы оно осталось уникальным в рамках существующей иерархии моделей.
|
||
"""
|
||
m = re.match(r'(.+)\s+\d*', name)
|
||
basename = m.group(1) if m else name
|
||
while True:
|
||
name = basename + ' ' + str(self.name_id)
|
||
if self.CheckName(name):
|
||
return name
|
||
self.name_id += 1
|
||
|
||
def BuildSpecs(self, model):
|
||
"""
|
||
Выстраивает иерархию спецификаций для выбранной модели
|
||
"""
|
||
def DoItem(item, model):
|
||
sp.SetPyData(item, model)
|
||
for spec in model.GetSpecs():
|
||
child = sp.AppendItem(item, spec.GetTitle())
|
||
DoItem(child, spec)
|
||
|
||
sp = self.m_specs
|
||
sp.DeleteAllItems()
|
||
root = sp.AddRoot(model.GetTitle())
|
||
DoItem(root, model)
|
||
sp.ExpandAll()
|
||
sp.SortChildren(root)
|
||
|
||
def AddModelToRoot(self, model):
|
||
"""
|
||
Добавляет пользовательскую модель или спецификацию
|
||
в корень дерева моделей.
|
||
"""
|
||
|
||
ms = []
|
||
while model:
|
||
ms.append(model)
|
||
model = model.GetParent()
|
||
ms.reverse()
|
||
# ms: [root-model, child, child-of-child1, ..., model]
|
||
|
||
um = self.m_user_models
|
||
item = um.GetRootItem()
|
||
defparent = None
|
||
for m in ms:
|
||
name = self.GenerateName(m.GetTitle())
|
||
item = um.AppendItem(item, name)
|
||
data = task.DataDefinition(m, defparent)
|
||
defparent = data
|
||
jid = self.server.CreateJob() if m.IsExecutable() else None
|
||
um.SetPyData(item, [data, jid])
|
||
|
||
def NewProject(self, model):
|
||
# 1. загрузить спецификации модели
|
||
# 2. создать одну модель по умолчанию
|
||
|
||
self.BuildSpecs(model)
|
||
|
||
um = self.m_user_models
|
||
um.DeleteAllItems()
|
||
um.AddRoot('Root')
|
||
|
||
self.AddModelToRoot(model)
|
||
|
||
return True # Project(model)
|
||
|
||
def SelectUserModel(self, model_def, jid):
|
||
|
||
def SelectProperty(param_type):
|
||
"""
|
||
По указанному имени типа возвращает "свойство" для списка "свойств"
|
||
|
||
Смотри руководство пользователя для того, чтобы получить полную
|
||
информацию о всех типах данных, используемых в Opal.
|
||
"""
|
||
if param_type == 'bool':
|
||
return wxpg.BoolProperty
|
||
elif param_type == 'int':
|
||
return wxpg.IntProperty
|
||
elif param_type == 'float' or param_type == 'double':
|
||
return wxpg.FloatProperty
|
||
elif param_type == 'string':
|
||
return wxpg.StringProperty
|
||
elif param_type == 'list':
|
||
return wxpg.ArrayStringProperty
|
||
else:
|
||
# очень плохо, если это произошло
|
||
raise KeyError()
|
||
|
||
msg = model_def.PackParams()
|
||
pg = self.m_params
|
||
pg.ClearPage(0)
|
||
#pg.Append(wxpg.PropertyCategory('Model properties'))
|
||
for k, v in model_def.params.iteritems():
|
||
p = model_def.DD[k]
|
||
title = p.GetTitle() or k
|
||
prop = SelectProperty(p.GetType())
|
||
pid = pg.Append(prop(title, value = v))
|
||
pg.SetPropertyClientData(pid, k)
|
||
pg.SetPropertyHelpString(pid, p.GetComment())
|
||
|
||
pd = model_def.PackParams()
|
||
self.SetStatusText(pd, 0)
|
||
|
||
def OnModelActivated(self, event):
|
||
item = event.GetItem()
|
||
data = self.m_user_models.GetPyData(item)
|
||
if data:
|
||
self.SelectUserModel(data[0], data[1])
|
||
|
||
def OnParamChanging(self, event):
|
||
#value = event.GetValue()
|
||
#print repr(value)
|
||
#wx.MessageBox(value, 'changing')
|
||
#event.Veto()
|
||
pass
|
||
|
||
def OnParamChanged(self, event):
|
||
prop = event.GetProperty()
|
||
if not prop:
|
||
return
|
||
value = prop.GetValue()
|
||
param = prop.GetClientData()
|
||
um = self.m_user_models
|
||
id = um.GetSelection()
|
||
data, jid = um.GetItemPyData(id)
|
||
data[param] = value
|
||
|
||
def OnTest(self, event):
|
||
um = self.m_user_models
|
||
|
||
def OnAddModelToRoot(self, event):
|
||
item = self.m_specs.GetSelection()
|
||
if not item.IsOk():
|
||
return
|
||
print self.m_specs.GetItemText(item)
|
||
model = self.m_specs.GetPyData(item)
|
||
self.AddModelToRoot(model)
|
||
|
||
def OnAddModelToSelected(self, event):
|
||
"""
|
||
Добавляет пользовательскую спецификацию к указанной модели или в уже
|
||
существующую иерархию спецификаций.
|
||
"""
|
||
item = self.m_specs.GetSelection()
|
||
if not item.IsOk():
|
||
return
|
||
model = self.m_specs.GetPyData(item)
|
||
|
||
um = self.m_user_models
|
||
item = um.GetSelection()
|
||
if not item.IsOk():
|
||
return
|
||
|
||
pmdef, _ = um.GetPyData(item)
|
||
|
||
if pmdef.DD == model.parent:
|
||
modeldef = task.DataDefinition(model, pmdef)
|
||
name = self.GenerateName(model.GetTitle())
|
||
item = um.AppendItem(item, name)
|
||
jid = self.server.CreateJob() if model.IsExecutable() else None
|
||
um.SetPyData(item, [modeldef, jid])
|
||
else:
|
||
wx.MessageBox('It\'s impossible to append model', 'Error')
|
||
|
||
def OnDuplicate(self, event):
|
||
um = self.m_user_models
|
||
id = um.GetSelection()
|
||
title = um.GetItemText(id)
|
||
parent = um.GetItemParent(id)
|
||
md, jid = um.GetItemPyData(id)
|
||
|
||
child = um.AppendItem(parent, self.GenerateName(title))
|
||
jid = self.server.CreateJob()
|
||
um.SetPyData(child, [md.Copy(), jid])
|
||
self.SetStatusText('Copy for "{}" created'.format(title), 0)
|
||
|
||
def OnModelProcess(self, event):
|
||
um = self.m_user_models
|
||
for i in um.GetSelections():
|
||
data, jid = um.GetItemPyData(i)
|
||
self.server.LaunchJob(jid, data)
|
||
|
||
def OnIdle(self, event):
|
||
pass
|
||
|
||
#-----------------------------------------------------------------------------
|
||
# Приложение
|
||
#-----------------------------------------------------------------------------
|
||
|
||
class ThisApp(wx.App):
|
||
|
||
def OnInit(self):
|
||
# Создание главного окна
|
||
frame = MainFrame()
|
||
self.SetTopWindow(frame)
|
||
frame.Show(True)
|
||
return True
|
||
|
||
#-----------------------------------------------------------------------------
|
||
# Запуск приложения
|
||
#-----------------------------------------------------------------------------
|
||
|
||
if __name__ == "__main__":
|
||
app = ThisApp(redirect = False)
|
||
app.MainLoop() |