295 lines
11 KiB
Python
295 lines
11 KiB
Python
'''
|
|
Created on Jan 18, 2016
|
|
|
|
@author: graffy
|
|
'''
|
|
|
|
# standard libraries
|
|
import os
|
|
import functools
|
|
# import math
|
|
# import sys
|
|
# from fileinput import close
|
|
|
|
import ase.io
|
|
# 3rd party libraries
|
|
import wx
|
|
# import cairo
|
|
# import wx.lib.wxcairo
|
|
|
|
# local libraries
|
|
import msspecgui.datafloweditor as datafloweditor
|
|
import msspecgui.dataflow as dataflow
|
|
from msspecgui.dataflow import IDataType
|
|
from msspecgui.msspec.gui.bulk_interface import BulkFrame
|
|
from msspecgui.msspec.gui.viewmanager import IViewCreator
|
|
from msspecgui.datafloweditor import OperatorGui
|
|
|
|
# based on example https://github.com/wxWidgets/wxPython/blob/master/demo/Cairo.py
|
|
|
|
|
|
class BackgroundContextMenu(wx.Menu):
|
|
'''
|
|
the context menu that the user sees when right-clicking on the background of the dataflow
|
|
|
|
'''
|
|
def __init__(self, panel):
|
|
"""
|
|
:type panel: msspecgui.msspec.gui.ClusterEditor
|
|
"""
|
|
wx.Menu.__init__(self)
|
|
self.m_panel = panel
|
|
|
|
# wx.ID_ABOUT and wx.ID_EXIT are standard IDs provided by wxWidgets.
|
|
# self.add_cluster_modifier_menu_item = self.Append(wx.ID_ANY,"Add Cluster Modifier"," adds a cluster modifier in the cluster editor")
|
|
|
|
self.add_cluster_modifier_menu = wx.Menu()
|
|
self.add_cluster_modifier_menu_item = self.AppendMenu(wx.ID_ANY, "Add Cluster Modifier...", self.add_cluster_modifier_menu)
|
|
|
|
# self.m_panel.Bind(wx.EVT_MENU, self.on_add_cluster_modifier, self.add_cluster_modifier_menu_item)
|
|
|
|
data_flow = self.m_panel.get_cluster_flow()
|
|
|
|
for creator in data_flow.get_operator_creators():
|
|
operator_type_id = creator.get_operator_type_id()
|
|
|
|
menu_item = self.add_cluster_modifier_menu.Append(wx.ID_ANY, operator_type_id, " adds a cluster modifier in the cluster editor")
|
|
# self.m_panel.Bind(wx.EVT_MENU, functools.partial( creator.create_operator, dflow = data_flow), menu_item)
|
|
self.m_panel.Bind(wx.EVT_MENU, functools.partial(self.on_add_cluster_modifier, operator_type_id=operator_type_id), menu_item)
|
|
|
|
def on_add_cluster_modifier(self, event, operator_type_id):
|
|
"""Callback that is invoked when the user chooses to add a specific cluster modifier using the context menu
|
|
|
|
:param operator_type_id: the type of cluster modifier to add (eg 'ipr.msspec.cutsphere')
|
|
"""
|
|
self.m_panel.create_operator(operator_type_id)
|
|
|
|
|
|
class OperatorContextMenu(wx.Menu):
|
|
'''
|
|
the context menu that the user sees when right-clicking on an operator of the dataflow
|
|
|
|
'''
|
|
def __init__(self, panel, operator):
|
|
"""
|
|
:type panel: msspecgui.msspec.gui.ClusterEditor
|
|
:type operator: dataflow.operator.Operator
|
|
"""
|
|
wx.Menu.__init__(self)
|
|
self.m_panel = panel
|
|
|
|
menu_item = self.Append(wx.ID_ANY, 'delete', "deletes this operator")
|
|
self.m_panel.Bind(wx.EVT_MENU, functools.partial(self.on_delete_operator, operator=operator), menu_item)
|
|
|
|
for action in operator.creator.get_actions():
|
|
|
|
menu_item = self.Append(wx.ID_ANY, action.get_name(), " performs this action on the operator")
|
|
# self.m_panel.Bind(wx.EVT_MENU, functools.partial( creator.create_operator, dflow = data_flow), menu_item)
|
|
self.m_panel.Bind(wx.EVT_MENU, functools.partial(self.on_perform_operator_action, action=action, operator=operator), menu_item)
|
|
|
|
def on_perform_operator_action(self, event, action, operator):
|
|
"""Callback that is invoked when the user chooses to add a specific cluster modifier using the context menu
|
|
|
|
:param operator_type_id: the type of cluster modifier to add (eg 'ipr.msspec.cutsphere')
|
|
:type action: dataflow.ioperatorcreator.IOperatorCreator.IAction
|
|
:type operator: dataflow.operator.Operator
|
|
"""
|
|
action.execute_on_operator(operator)
|
|
|
|
def on_delete_operator(self, event, operator):
|
|
"""Callback that is invoked when the user chooses to delete an operator
|
|
|
|
:param dataflow.Operator operator: the operator that needs to be deleted
|
|
"""
|
|
data_flow = operator.data_flow
|
|
data_flow.delete_operator(operator)
|
|
|
|
|
|
class WireContextMenu(wx.Menu):
|
|
'''
|
|
the context menu that the user sees when right-clicking on a wire of the dataflow
|
|
|
|
'''
|
|
def __init__(self, panel, wire):
|
|
"""
|
|
:param msspecgui.msspec.gui.ClusterEditor panel:
|
|
:param dataflow.Wire wire: the wire for which this context menu is created
|
|
"""
|
|
wx.Menu.__init__(self)
|
|
self.m_panel = panel
|
|
|
|
# # create a menu item for each wire action
|
|
# for wire_action in self.m_panel.get_cluster_flow().wire_actions:
|
|
# menu_item = self.Append(wx.ID_ANY, data_action.get_name(), " performs this action on the operator")
|
|
# self.m_panel.Bind(wx.EVT_MENU, functools.partial(self.on_perform_data_action, data_action=data_action, data=wire.input_plug.get_value()), menu_item)
|
|
menu_item = self.Append(wx.ID_ANY, 'delete', "deletes this wire")
|
|
self.m_panel.Bind(wx.EVT_MENU, functools.partial(self.on_delete_wire, wire=wire), menu_item)
|
|
|
|
for data_action in wire.data_type.get_actions():
|
|
menu_item = self.Append(wx.ID_ANY, data_action.get_name(), " performs this action on the operator")
|
|
self.m_panel.Bind(wx.EVT_MENU, functools.partial(self.on_perform_data_action, data_action=data_action, data=wire.input_plug.get_value()), menu_item)
|
|
|
|
# def on_perform_wire_action(self, event, wire_action, wire):
|
|
# """Callback that is invoked when the user chooses to add a specific cluster modifier using the context menu
|
|
#
|
|
# :param dataflow.Wire.IAction wire_action: the action to perform
|
|
# :param dataflow.Wire wire: the wire on which to perform the action
|
|
# """
|
|
# wire_action.execute_on_wire(wire)
|
|
|
|
def on_perform_data_action(self, event, data_action, data):
|
|
"""Callback that is invoked when the user chooses to add a specific data action using the context menu
|
|
|
|
:param dataflow.IDataType.IAction data_action: the action to perform on the given data
|
|
:param data: the data on which to perform the action
|
|
"""
|
|
assert isinstance(data, data_action.datatype.get_python_class())
|
|
data_action.execute_on_data(data)
|
|
|
|
def on_delete_wire(self, event, wire):
|
|
"""Callback that is invoked when the user chooses to delete a wire using the context menu
|
|
|
|
:param dataflow.Wire wire: the wire that needs to be deleted
|
|
"""
|
|
data_flow = wire.data_flow
|
|
data_flow.delete_wire(wire)
|
|
|
|
|
|
def opj(path):
|
|
"""Convert paths to the platform-specific separator"""
|
|
platform_path = apply(os.path.join, tuple(path.split('/')))
|
|
# HACK: on Linux, a leading / gets lost...
|
|
if path.startswith('/'):
|
|
platform_path = '/' + platform_path
|
|
return platform_path
|
|
|
|
|
|
class BulkGui(dataflow.IOperatorCreator.IAction):
|
|
|
|
def get_name(self):
|
|
return 'properties via gui'
|
|
|
|
def execute_on_operator(self, operator):
|
|
"""
|
|
:type operator: dataflow.operator.Operator
|
|
"""
|
|
dialog = BulkFrame(None, title="create a single cell", bulk_operator=operator)
|
|
print("execute_on_operator : before MainLoop")
|
|
result = dialog.ShowModal()
|
|
if result == wx.ID_OK:
|
|
print("execute_on_operator : signaling operator %d as modified" % operator.id)
|
|
operator.data_flow.on_modified_operator(operator)
|
|
print "OK"
|
|
else:
|
|
print "Cancel"
|
|
dialog.Destroy()
|
|
|
|
|
|
class ExportCluster(IDataType.IAction):
|
|
|
|
def __init__(self, cluster_flow):
|
|
"""
|
|
:param msspecgui.cluster.ClusterFlow cluster_flow:
|
|
"""
|
|
super(ExportCluster, self).__init__(cluster_flow.get_data_type('physics.atomscluster'))
|
|
|
|
def get_name(self):
|
|
return 'export cluster'
|
|
|
|
def execute_on_data(self, data):
|
|
"""
|
|
:param data: the data on which this action needs to be performed
|
|
"""
|
|
assert isinstance(data, self.datatype.get_python_class())
|
|
dlg = wx.FileDialog(None, message="Choose a file to store this cluster (the format of the file is defined by the file's extension)", defaultDir='', defaultFile='', style=wx.FD_SAVE)
|
|
if dlg.ShowModal() == wx.ID_OK:
|
|
cluster_file_path = dlg.GetPath()
|
|
ase.io.write(cluster_file_path, data)
|
|
dlg.Destroy()
|
|
|
|
|
|
class ClusterEditor(datafloweditor.DataflowView):
|
|
|
|
class Creator(IViewCreator):
|
|
VIEW_TYPE_NAME = 'cluster editor'
|
|
|
|
def __init__(self, workspace):
|
|
"""
|
|
:param msspecgui.msspec.Workspace workspace: the workspace that is associated with the cluster editors created by this creator
|
|
"""
|
|
self._workspace = workspace
|
|
|
|
@property
|
|
def view_type_name(self):
|
|
"""
|
|
:return str:
|
|
"""
|
|
return self.VIEW_TYPE_NAME
|
|
|
|
def create_view(self, parent):
|
|
"""
|
|
:param wx.Window parent: the wx.Window that owns the view
|
|
:return wx.Panel:
|
|
"""
|
|
return ClusterEditor(parent, self._workspace.get_cluster_flow())
|
|
|
|
def __init__(self, parent, cluster_flow):
|
|
"""
|
|
:param cluster_flow: the dataflow that this editor manipulates
|
|
:type cluster_flow: msspec.cluster.clusterflow.ClusterFlow
|
|
|
|
"""
|
|
super(ClusterEditor, self).__init__(parent, cluster_flow, -1)
|
|
|
|
for operator_creator in cluster_flow.get_operator_creators():
|
|
if operator_creator.get_operator_type_id() == 'ase.lattice.bulk':
|
|
operator_creator.register_operator_action(BulkGui())
|
|
else:
|
|
operator_creator.register_operator_action(OperatorGui())
|
|
|
|
datatype = cluster_flow.get_data_type('physics.atomscluster')
|
|
datatype.register_datatype_action(ExportCluster(cluster_flow))
|
|
|
|
def create_operator(self, operator_type_id):
|
|
operator = self.get_cluster_flow().create_operator(operator_type_id)
|
|
self.get_cluster_flow().add_operator(operator)
|
|
|
|
def get_cluster_flow(self):
|
|
"""
|
|
:rtype: msspec.cluster.clusterflow.ClusterFlow
|
|
"""
|
|
return self.dataflow
|
|
|
|
def on_background_context_menu(self, event):
|
|
'''
|
|
called whenever the user right-clicks in the background of the dataflow
|
|
'''
|
|
pos = event.GetPosition()
|
|
# print(pos)
|
|
pos = self.ScreenToClient(pos)
|
|
self.PopupMenu(BackgroundContextMenu(self), pos)
|
|
|
|
def on_operator_context_menu(self, event, operator):
|
|
'''
|
|
called whenever the user right-clicks in an operator of the dataflow
|
|
|
|
:type event: wx.Event
|
|
:type operator: dataflow.operator.Operator
|
|
'''
|
|
pos = event.GetPosition()
|
|
# print(pos)
|
|
pos = self.ScreenToClient(pos)
|
|
self.PopupMenu(OperatorContextMenu(self, operator), pos)
|
|
|
|
def on_wire_context_menu(self, event, wire):
|
|
'''
|
|
called whenever the user right-clicks in a wire of the dataflow
|
|
|
|
:type event: wx.Event
|
|
:param dataflow.Wire wire:
|
|
'''
|
|
pos = event.GetPosition()
|
|
# print(pos)
|
|
pos = self.ScreenToClient(pos)
|
|
self.PopupMenu(WireContextMenu(self, wire), pos)
|