aims_project 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/README +5 -0
- data/bin/AimsCalc +367 -0
- data/bin/AimsProject +88 -0
- data/bin/AimsProjectManager +39 -0
- data/lib/aims_project.rb +158 -0
- data/lib/aims_project/aims_project_exception.rb +42 -0
- data/lib/aims_project/aims_project_geometry.rb +52 -0
- data/lib/aims_project/app_controller.rb +298 -0
- data/lib/aims_project/atom.rb +95 -0
- data/lib/aims_project/calculation.rb +406 -0
- data/lib/aims_project/calculation_tree.rb +65 -0
- data/lib/aims_project/calculation_window.rb +141 -0
- data/lib/aims_project/crystal_viewer.rb +981 -0
- data/lib/aims_project/crystal_viewer_options.rb +103 -0
- data/lib/aims_project/geometry_editor.rb +111 -0
- data/lib/aims_project/geometry_file.rb +184 -0
- data/lib/aims_project/geometry_window.rb +172 -0
- data/lib/aims_project/green_arrow.jpg +0 -0
- data/lib/aims_project/inspector.rb +183 -0
- data/lib/aims_project/material.rb +30 -0
- data/lib/aims_project/octree.rb +5 -0
- data/lib/aims_project/pan.gif +0 -0
- data/lib/aims_project/project.rb +102 -0
- data/lib/aims_project/project_tree.rb +62 -0
- data/lib/aims_project/rotate.gif +0 -0
- data/lib/aims_project/thread_callback_event.rb +19 -0
- data/lib/aims_project/zoom.gif +0 -0
- data/skeleton/Capfile +37 -0
- data/skeleton/config/aims.sh +58 -0
- data/skeleton/config/tasks.rb +145 -0
- data/skeleton/config/user_variables.rb +37 -0
- data/skeleton/control/example.erb +41 -0
- data/skeleton/geometry/example +1 -0
- metadata +136 -0
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
|
|
3
|
+
require "rubygems"
|
|
4
|
+
|
|
5
|
+
require "wx"
|
|
6
|
+
require "erb"
|
|
7
|
+
require "gl"
|
|
8
|
+
require "glu"
|
|
9
|
+
|
|
10
|
+
require 'aims'
|
|
11
|
+
require 'aims_project'
|
|
12
|
+
require 'aims_project/material.rb'
|
|
13
|
+
require 'aims_project/atom.rb'
|
|
14
|
+
require 'aims_project/inspector.rb'
|
|
15
|
+
require 'aims_project/crystal_viewer.rb'
|
|
16
|
+
require 'aims_project/geometry_editor.rb'
|
|
17
|
+
require 'aims_project/project_tree.rb'
|
|
18
|
+
require 'aims_project/calculation_tree.rb'
|
|
19
|
+
require 'aims_project/app_controller.rb'
|
|
20
|
+
require 'aims_project/geometry_window.rb'
|
|
21
|
+
require 'aims_project/calculation_window.rb'
|
|
22
|
+
require 'aims_project/thread_callback_event.rb'
|
|
23
|
+
require 'aims_project/crystal_viewer_options.rb'
|
|
24
|
+
|
|
25
|
+
controller = AimsProject::AppController.new
|
|
26
|
+
|
|
27
|
+
unless __FILE__.nil?
|
|
28
|
+
cwd = File.dirname(File.expand_path("."))
|
|
29
|
+
controller.working_dir = cwd
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
controller.open_file(ARGV[0]) if ARGV[0]
|
|
33
|
+
|
|
34
|
+
project_obj_files = Dir["*.yaml"]
|
|
35
|
+
controller.project = AimsProject::Project.load(project_obj_files.first) unless project_obj_files.empty?
|
|
36
|
+
AimsProject::ThreadCallbackEvent.set_event_type(Wx::Event.new_event_type)
|
|
37
|
+
Wx::EvtHandler.register_class(AimsProject::ThreadCallbackEvent, AimsProject::ThreadCallbackEvent.event_type, "evt_thread_callback", 0)
|
|
38
|
+
|
|
39
|
+
controller.main_loop
|
data/lib/aims_project.rb
ADDED
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
|
|
2
|
+
# AimsProject is an application for visualizing and managing projects for
|
|
3
|
+
# the FHI-AIMS package
|
|
4
|
+
|
|
5
|
+
require "rubygems"
|
|
6
|
+
require 'aims'
|
|
7
|
+
require 'yaml'
|
|
8
|
+
|
|
9
|
+
require 'aims_project/project.rb'
|
|
10
|
+
require 'aims_project/calculation.rb'
|
|
11
|
+
require 'aims_project/geometry_file.rb'
|
|
12
|
+
require 'aims_project/aims_project_exception.rb'
|
|
13
|
+
|
|
14
|
+
# AimsProject is a set of tools for managing and organizing
|
|
15
|
+
# calculations for executaion on high-performance computing clusters.
|
|
16
|
+
# It is designed specifically for the FHI-AIMS package published
|
|
17
|
+
# by the Fritz-Haber Institute (https://aimsclub.fhi-berlin.mpg.de),
|
|
18
|
+
# but can probably be generatlized to other codes.
|
|
19
|
+
#
|
|
20
|
+
# Author: Joshua Shapiro (email:joshua.shapiro@gmail.com)
|
|
21
|
+
# Copyright: 2012 Joshua Shapiro
|
|
22
|
+
# License: TBD
|
|
23
|
+
#
|
|
24
|
+
# = Why should I use AimsProject?
|
|
25
|
+
# Good organization is crucial to obtaining meaningful results with
|
|
26
|
+
# the FHI-AIMS package. Careful testing of convergence
|
|
27
|
+
# across parameters in the control and geometry files easily requires
|
|
28
|
+
# dozens of calculations. The novice and expert user alike can quickly
|
|
29
|
+
# lose track of which calculations are complete, which are still pending,
|
|
30
|
+
# which calculations were errors, and which calculations failed or were aborted.
|
|
31
|
+
# In this framework, even the most experienced user can and will make mistakes.
|
|
32
|
+
#
|
|
33
|
+
# <em>The aim of this tool is to simplify and streamline the calculation
|
|
34
|
+
# pipeline, so the user can focus on the results.</em>
|
|
35
|
+
#
|
|
36
|
+
# = Features
|
|
37
|
+
# * Automated generation of calculations from control & geometry files.
|
|
38
|
+
# * Automated synchronization between a workstation and the compute cluster.
|
|
39
|
+
# * Automated job submission of pending calculations to the queue
|
|
40
|
+
# * Status tracking of calculations from creation to completion.
|
|
41
|
+
# * Simple organizational structure with human readable metadata.
|
|
42
|
+
#
|
|
43
|
+
# = Planned Features
|
|
44
|
+
# * Input file validation (To catch mistakes before submitting to a queue)
|
|
45
|
+
# * Geometry input and output visualization
|
|
46
|
+
#
|
|
47
|
+
# = Quick Start
|
|
48
|
+
# == Installation
|
|
49
|
+
#
|
|
50
|
+
# gem install aims_project
|
|
51
|
+
#
|
|
52
|
+
# == Creating a Project
|
|
53
|
+
#
|
|
54
|
+
# Type:
|
|
55
|
+
# AimsProject myProject
|
|
56
|
+
#
|
|
57
|
+
# This will create the directory structure
|
|
58
|
+
# myProject
|
|
59
|
+
# -> calculations/
|
|
60
|
+
# -> config/
|
|
61
|
+
# -> control/
|
|
62
|
+
# -> geometry/
|
|
63
|
+
# -> Capfile
|
|
64
|
+
# -> myProject.yaml
|
|
65
|
+
#
|
|
66
|
+
#
|
|
67
|
+
# +calculations+:: Contains one subdirectory for each calculation.
|
|
68
|
+
# +config+:: Contains special configuration files for automation.
|
|
69
|
+
# +geometry+:: This is where you will place all your geometry files.
|
|
70
|
+
# +control+:: This is where you place all your control files.
|
|
71
|
+
# +Capfile+:: Location where you customize the interaction with the compute cluster
|
|
72
|
+
# +myProject.yaml+:: Human readable metadata related to this project.
|
|
73
|
+
#
|
|
74
|
+
# == Creating a calculation
|
|
75
|
+
#
|
|
76
|
+
# Assume you are investigating two atomic configurations _alpha_ and _beta_, and
|
|
77
|
+
# you want to calculate them with the _light_ and _tight_ settings.
|
|
78
|
+
#
|
|
79
|
+
# Create the FHI-AIMS formatted input files and name them
|
|
80
|
+
# * +geometry/alpha+
|
|
81
|
+
# * +geometry/beta+
|
|
82
|
+
# * +control/light+
|
|
83
|
+
# * +control/tight+
|
|
84
|
+
#
|
|
85
|
+
# Now run
|
|
86
|
+
# > AimsCalc create alpha light
|
|
87
|
+
# > AimsCalc create beta light
|
|
88
|
+
# > AimsCalc create alpha tight
|
|
89
|
+
# > AimsCalc create beta tight
|
|
90
|
+
#
|
|
91
|
+
# This will create four subdirectories inside +calculations/+.
|
|
92
|
+
# > ls calculations/*
|
|
93
|
+
# calculations/alpha.light:
|
|
94
|
+
# calc_status.yaml control.in geometry.in
|
|
95
|
+
#
|
|
96
|
+
# calculations/alpha.tight:
|
|
97
|
+
# calc_status.yaml control.in geometry.in
|
|
98
|
+
#
|
|
99
|
+
# calculations/beta.light:
|
|
100
|
+
# calc_status.yaml control.in geometry.in
|
|
101
|
+
#
|
|
102
|
+
# calculations/beta.tight:
|
|
103
|
+
# calc_status.yaml control.in geometry.in
|
|
104
|
+
#
|
|
105
|
+
# Notice that each calculation directory has the required +control.in+ and
|
|
106
|
+
# +geometry.in+ file. These were directly copied from the geometry and control files
|
|
107
|
+
# passed to AimsCalc. *Note* It is possible to embed variables inside the control
|
|
108
|
+
# and geometry files, see AimsCalc for more details. Each directory also contains
|
|
109
|
+
# a file named +calc_status.yaml+. This is a metadata file used for tracking
|
|
110
|
+
# the history and status of the calculation. Currently this file looks something like
|
|
111
|
+
# --- !ruby/object:AimsProject::Calculation
|
|
112
|
+
# control: light
|
|
113
|
+
# geometry: alpha
|
|
114
|
+
# status: STAGED
|
|
115
|
+
#
|
|
116
|
+
#
|
|
117
|
+
# Feel free to get more creative with the geometry and control file names. You should encode
|
|
118
|
+
# as much information in the file name as possible, as this will make it easy
|
|
119
|
+
# for you to identify the calculation later. For example, a geometry file
|
|
120
|
+
# named +alpha2x2_5layers_relaxed+ and a control file named +light_tier1_norelax_6x6x1_lomem+
|
|
121
|
+
# will result in a calulation directory named +alpha2x2_5layers_relaxed.light_tier1_norelax_6x6x1_lomem+
|
|
122
|
+
#
|
|
123
|
+
# == Running a Calculation
|
|
124
|
+
# At this point the calculation status is +STAGED+. To run the calculation, first customize
|
|
125
|
+
# the +Capfile+ with details necessary for running FHI-AIMS on the computing cluster.
|
|
126
|
+
# Check this file carefully, this is where you define the name and location of the aims executable,
|
|
127
|
+
# the name of the server, and the method for submitting jobs to the queue. Once this file
|
|
128
|
+
# is properly configured, the calculations are submitted with one line:
|
|
129
|
+
#
|
|
130
|
+
# cap aims:enqueue
|
|
131
|
+
#
|
|
132
|
+
# This command invokes custom tasks in +Capistrano+, a 3rd party tool for automated deployment,
|
|
133
|
+
# that will upload the calculations to the server and submit them to the queue.
|
|
134
|
+
#
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
module AimsProject
|
|
139
|
+
# Constants
|
|
140
|
+
STAGED = "STAGED"
|
|
141
|
+
HOLD = "HOLD"
|
|
142
|
+
QUEUED = "QUEUED"
|
|
143
|
+
RUNNING = "RUNNING"
|
|
144
|
+
COMPLETE = "COMPLETE"
|
|
145
|
+
ABORTED = "ABORTED"
|
|
146
|
+
CANCELED = "CANCELED"
|
|
147
|
+
|
|
148
|
+
CONFIG_DIR = "config"
|
|
149
|
+
CALCULATION_DIR = "calculations"
|
|
150
|
+
GEOMETRY_DIR = "geometry"
|
|
151
|
+
CONTROL_DIR = "control"
|
|
152
|
+
CALC_STATUS_FILENAME = "calc_status.yaml"
|
|
153
|
+
PROJECT_VARIABLES_FILENAME = "project_vars.rb"
|
|
154
|
+
|
|
155
|
+
GLOBAL_CONFIG_DIR = File.join(ENV["HOME"], ".aims_project")
|
|
156
|
+
GLOBAL_VARIABLES_FILENAME = "aims_project_vars.rb"
|
|
157
|
+
|
|
158
|
+
end
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
|
|
2
|
+
module AimsProject
|
|
3
|
+
class AimsProjectException < StandardError
|
|
4
|
+
end
|
|
5
|
+
|
|
6
|
+
class InvalidFilenameException < AimsProjectException
|
|
7
|
+
def initialize(filename)
|
|
8
|
+
if filename.nil?
|
|
9
|
+
super "No filename specified"
|
|
10
|
+
else
|
|
11
|
+
super "The filename #{filename} is invalid."
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
class ObjectFileNotFoundException < AimsProjectException
|
|
17
|
+
def initialize(filename)
|
|
18
|
+
super "The serialized yaml file was not found: #{filename}"
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
class CorruptObjectFileException < AimsProjectException
|
|
23
|
+
def initialize(filename)
|
|
24
|
+
super "The serialized yaml file is corrupt: #{filename}"
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
class GeometryValidationException < AimsProjectException
|
|
29
|
+
attr_reader :violations
|
|
30
|
+
def initialize(violations)
|
|
31
|
+
@violations = violations
|
|
32
|
+
super "The Geometry failed validation."
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
class GeometryEvaluationException < AimsProjectException
|
|
37
|
+
def initialize
|
|
38
|
+
super "Error evaluating geometry"
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
end
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
module AimsProject
|
|
2
|
+
|
|
3
|
+
# A class that encapsulates the Aims::Geometry and other
|
|
4
|
+
# state information important for AimsProject
|
|
5
|
+
class AimsProjectGeometry
|
|
6
|
+
|
|
7
|
+
# Boolean flag indicating whether the atoms should be displaced to all lie
|
|
8
|
+
# within the unit cell
|
|
9
|
+
attr_accessor :correct_geometry
|
|
10
|
+
|
|
11
|
+
# The filename defining this geometry
|
|
12
|
+
attr_reader :filename
|
|
13
|
+
|
|
14
|
+
def initialize(file)
|
|
15
|
+
@filename = file
|
|
16
|
+
@geometry_original = Aims::GeometryParser.parse(@filename)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def geometry
|
|
20
|
+
if self.correct_geometry
|
|
21
|
+
geometry_corrected
|
|
22
|
+
else
|
|
23
|
+
geometry_original
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def geometry_corrected
|
|
28
|
+
if @geometry_corrected.nil?
|
|
29
|
+
@geometry_corrected = @geometry_original.correct
|
|
30
|
+
end
|
|
31
|
+
@geometry_corrected
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def geometry_original
|
|
35
|
+
@geometry_original
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def to_s
|
|
39
|
+
@filename
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def save
|
|
44
|
+
unless @filename
|
|
45
|
+
raise InvalidFilenameException
|
|
46
|
+
end
|
|
47
|
+
raise "AimsProjectGeometry.save is not yet implemented"
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
end
|
|
@@ -0,0 +1,298 @@
|
|
|
1
|
+
|
|
2
|
+
module AimsProject
|
|
3
|
+
class AppController < Wx::App
|
|
4
|
+
|
|
5
|
+
include Wx
|
|
6
|
+
|
|
7
|
+
ID_NEW = 102
|
|
8
|
+
ID_SAVE_IMAGE = 103
|
|
9
|
+
ID_MOVE_CLIP_PLANE = 104
|
|
10
|
+
ID_SAVE_AS = 105
|
|
11
|
+
|
|
12
|
+
ID_INSPECTOR = 201
|
|
13
|
+
|
|
14
|
+
ID_DELETE_ATOM = 301
|
|
15
|
+
|
|
16
|
+
@frame = nil
|
|
17
|
+
@menubar = nil
|
|
18
|
+
@inspector = nil
|
|
19
|
+
@statusbar = nil
|
|
20
|
+
|
|
21
|
+
# The project
|
|
22
|
+
attr_accessor :project
|
|
23
|
+
|
|
24
|
+
# Used to synchronize directory in open/save dialogs
|
|
25
|
+
attr_accessor :working_dir
|
|
26
|
+
|
|
27
|
+
# The root frame
|
|
28
|
+
attr_accessor :frame
|
|
29
|
+
|
|
30
|
+
# Build the application
|
|
31
|
+
def on_init
|
|
32
|
+
|
|
33
|
+
self.app_name = "AimsViewer"
|
|
34
|
+
# Create the frame, toolbar and menubar and define event handlers
|
|
35
|
+
size = [1000,700]
|
|
36
|
+
@frame = Frame.new(nil, -1, "AimsViewer", DEFAULT_POSITION, size)
|
|
37
|
+
@statusbar = @frame.create_status_bar
|
|
38
|
+
|
|
39
|
+
# This timer will cause the main thread to pass every 2 ms so that other threads
|
|
40
|
+
# can get work done.
|
|
41
|
+
timer = Wx::Timer.new(self, Wx::ID_ANY)
|
|
42
|
+
evt_timer(timer.id) {Thread.pass}
|
|
43
|
+
timer.start(2)
|
|
44
|
+
|
|
45
|
+
# Initialize the selection
|
|
46
|
+
@selection = {}
|
|
47
|
+
|
|
48
|
+
# Initialize the inspector
|
|
49
|
+
@inspector = Inspector.new(self, @frame)
|
|
50
|
+
|
|
51
|
+
# Create the notebook
|
|
52
|
+
@notebook = Notebook.new(@frame)
|
|
53
|
+
|
|
54
|
+
# Create the geometry notebook page
|
|
55
|
+
@geomWindow = GeometryWindow.new(self, @notebook)
|
|
56
|
+
|
|
57
|
+
# Create the control window
|
|
58
|
+
# The base window is a horizontal splitter
|
|
59
|
+
# Left side is a list of control files
|
|
60
|
+
# right side is a Rich Text Control
|
|
61
|
+
controlWindow = SplitterWindow.new(@notebook)
|
|
62
|
+
@controlList = ListCtrl.new(controlWindow);
|
|
63
|
+
@controlEditor = RichTextCtrl.new(controlWindow)
|
|
64
|
+
controlWindow.split_horizontally(@controlList, @controlEditor)
|
|
65
|
+
|
|
66
|
+
# Create the calculations window
|
|
67
|
+
# Similar to the geometryWindow
|
|
68
|
+
# Left side is a list control
|
|
69
|
+
# Right side is a crystal viewer
|
|
70
|
+
calcWindow = CalculationWindow.new(self, @notebook)
|
|
71
|
+
|
|
72
|
+
# Add windows to the notebook
|
|
73
|
+
@notebook.add_page(@geomWindow, 'Geometry')
|
|
74
|
+
@notebook.add_page(controlWindow, "Control")
|
|
75
|
+
@notebook.add_page(calcWindow, "Calculations")
|
|
76
|
+
|
|
77
|
+
evt_notebook_page_changed(@notebook) {|evt|
|
|
78
|
+
cp = @notebook.get_current_page
|
|
79
|
+
if cp.respond_to? :show_inspector
|
|
80
|
+
@notebook.get_current_page.show_inspector
|
|
81
|
+
end
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
# Set the selected notebook page
|
|
85
|
+
@notebook.set_selection(2)
|
|
86
|
+
|
|
87
|
+
# @tree = ProjectTree.new(self, hsplitter)
|
|
88
|
+
@frame.set_menu_bar(menubar)
|
|
89
|
+
# @toolbar = @frame.create_tool_bar
|
|
90
|
+
# populate_toolbar(@toolbar) if @toolbar
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
# Check off the current tool
|
|
94
|
+
# set_tool
|
|
95
|
+
|
|
96
|
+
# Display
|
|
97
|
+
@frame.show
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
# Process a menu event
|
|
101
|
+
def process_menu_event(event)
|
|
102
|
+
case event.id
|
|
103
|
+
when ID_INSPECTOR
|
|
104
|
+
show_inspector
|
|
105
|
+
when ID_DELETE_ATOM
|
|
106
|
+
delete_atom
|
|
107
|
+
when Wx::ID_OPEN
|
|
108
|
+
open_file
|
|
109
|
+
when Wx::ID_SAVE
|
|
110
|
+
save_geometry
|
|
111
|
+
when ID_NEW
|
|
112
|
+
new_geometry_file
|
|
113
|
+
when ID_SAVE_AS
|
|
114
|
+
save_geometry_as
|
|
115
|
+
when ID_SAVE_IMAGE
|
|
116
|
+
save_image
|
|
117
|
+
when Wx::ID_EXIT
|
|
118
|
+
exit(0)
|
|
119
|
+
else
|
|
120
|
+
event.skip
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
# Return the menubar. If it is undefined, then define it and attach the event handler
|
|
127
|
+
def menubar
|
|
128
|
+
unless @menubar
|
|
129
|
+
fileMenu = Menu.new
|
|
130
|
+
fileMenu.append(ID_NEW, "New Geometry ...")
|
|
131
|
+
fileMenu.append(Wx::ID_OPEN, "Open ...\tCTRL+o")
|
|
132
|
+
fileMenu.append(Wx::ID_SAVE, "Save Geometry\tCTRL+s")
|
|
133
|
+
fileMenu.append(ID_SAVE_AS, "Save Geometry As...")
|
|
134
|
+
fileMenu.append(ID_SAVE_IMAGE, "Export Image ...")
|
|
135
|
+
fileMenu.append(Wx::ID_EXIT, "Exit")
|
|
136
|
+
|
|
137
|
+
editMenu = Menu.new
|
|
138
|
+
editMenu.append(ID_DELETE_ATOM, "Delete Atom\tCTRL+d")
|
|
139
|
+
|
|
140
|
+
toolsMenu = Menu.new
|
|
141
|
+
# toolsMenu.append(ID_ROTATE, "rotate", "Rotate", Wx::ITEM_CHECK)
|
|
142
|
+
# toolsMenu.append(ID_ZOOM, "zoom", "Zoom", Wx::ITEM_CHECK)
|
|
143
|
+
# toolsMenu.append(ID_PAN, "pan", "Pan", Wx::ITEM_CHECK)
|
|
144
|
+
# toolsMenu.append(ID_MOVE_CLIP_PLANE, "move cilp plane", "Move", Wx::ITEM_CHECK)
|
|
145
|
+
|
|
146
|
+
viewMenu = Menu.new
|
|
147
|
+
viewMenu.append(ID_INSPECTOR, "inspector\tCTRL+i")
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
@menubar = MenuBar.new
|
|
151
|
+
@menubar.append(fileMenu, "File")
|
|
152
|
+
@menubar.append(editMenu, "Edit")
|
|
153
|
+
@menubar.append(viewMenu, "Views")
|
|
154
|
+
@menubar.append(toolsMenu, "Tools")
|
|
155
|
+
|
|
156
|
+
evt_menu @menubar, :process_menu_event
|
|
157
|
+
end
|
|
158
|
+
@menubar
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
def set_status(string)
|
|
162
|
+
@frame.set_status_text(string)
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
# Get the inspector
|
|
166
|
+
def inspector
|
|
167
|
+
if @inspector.nil?
|
|
168
|
+
@inspector = Inspector.new(self, @frame)
|
|
169
|
+
end
|
|
170
|
+
@inspector
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
# Show the inspector
|
|
174
|
+
def show_inspector
|
|
175
|
+
@inspector.show(true)
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
# Hide the inspector
|
|
179
|
+
def hide_inspector
|
|
180
|
+
@inspector.hide
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
# Create a new geometry file
|
|
184
|
+
def new_geometry_file(file = nil)
|
|
185
|
+
fd = TextEntryDialog.new(@frame, :message => "New Geometry", :caption => "Specify name of geometry:")
|
|
186
|
+
if Wx::ID_OK == fd.show_modal
|
|
187
|
+
begin
|
|
188
|
+
geom_name = fd.get_value
|
|
189
|
+
geometry = GeometryFile.new("")
|
|
190
|
+
geometry = geometry.save_as(File.new(File.join(GEOMETRY_DIR, geom_name), "w"))
|
|
191
|
+
@geomWindow.add_geometry(geometry)
|
|
192
|
+
rescue Exception => e
|
|
193
|
+
error_dialog(e)
|
|
194
|
+
end
|
|
195
|
+
end
|
|
196
|
+
end
|
|
197
|
+
|
|
198
|
+
# Display a file dialog and attempt to open and display the file
|
|
199
|
+
def open_geometry_file(file = nil)
|
|
200
|
+
begin
|
|
201
|
+
unless file
|
|
202
|
+
fd = FileDialog.new(@frame, :message => "Open", :style => FD_OPEN, :default_dir => @working_dir)
|
|
203
|
+
if ID_OK == fd.show_modal
|
|
204
|
+
file = fd.get_path
|
|
205
|
+
@working_dir = fd.get_directory
|
|
206
|
+
else
|
|
207
|
+
return
|
|
208
|
+
end
|
|
209
|
+
end
|
|
210
|
+
puts "Opening #{file}"
|
|
211
|
+
|
|
212
|
+
@frame.set_title(file)
|
|
213
|
+
@geomEditor.show
|
|
214
|
+
@calcTree.hide
|
|
215
|
+
|
|
216
|
+
if (project)
|
|
217
|
+
erb = ERB.new(File.read(file))
|
|
218
|
+
show_geometry Aims::GeometryParser.parse_string(erb.result(project.get_binding))
|
|
219
|
+
else
|
|
220
|
+
show_geometry Aims::GeometryParser.parse(file)
|
|
221
|
+
end
|
|
222
|
+
|
|
223
|
+
|
|
224
|
+
rescue Exception => dang
|
|
225
|
+
error_dialog(dang)
|
|
226
|
+
end
|
|
227
|
+
end
|
|
228
|
+
alias_method :open_file, :open_geometry_file
|
|
229
|
+
|
|
230
|
+
def save_geometry
|
|
231
|
+
page = @notebook.get_current_page
|
|
232
|
+
if (page.respond_to? :geometry) && not(page.geometry.nil?)
|
|
233
|
+
geometry = @notebook.get_current_page.geometry
|
|
234
|
+
begin
|
|
235
|
+
geometry.save
|
|
236
|
+
rescue Exception => e
|
|
237
|
+
error_dialog(e)
|
|
238
|
+
end
|
|
239
|
+
end
|
|
240
|
+
end
|
|
241
|
+
|
|
242
|
+
# Save the geometry
|
|
243
|
+
def save_geometry_as
|
|
244
|
+
|
|
245
|
+
page = @notebook.get_current_page
|
|
246
|
+
if (page.respond_to? :geometry) && not(page.geometry.nil?)
|
|
247
|
+
geometry = @notebook.get_current_page.geometry
|
|
248
|
+
|
|
249
|
+
fd = TextEntryDialog.new(@frame, :message => "Save Geometry", :caption => "Specify name of geometry:")
|
|
250
|
+
if Wx::ID_OK == fd.show_modal
|
|
251
|
+
begin
|
|
252
|
+
geom_name = fd.get_value
|
|
253
|
+
new_geom = geometry.save_as(File.new(File.join(GEOMETRY_DIR, geom_name), "w"))
|
|
254
|
+
@geomWindow.add_geometry(new_geom)
|
|
255
|
+
rescue Exception => e
|
|
256
|
+
error_dialog(e)
|
|
257
|
+
end
|
|
258
|
+
end
|
|
259
|
+
else
|
|
260
|
+
error_dialog("No geometry selected.")
|
|
261
|
+
end
|
|
262
|
+
end
|
|
263
|
+
|
|
264
|
+
# Display an error dialog for the exception
|
|
265
|
+
def error_dialog(exception)
|
|
266
|
+
message = if exception.is_a? String
|
|
267
|
+
exception
|
|
268
|
+
elsif exception.is_a? Exception
|
|
269
|
+
exception.message + "\n\n" + exception.backtrace[0..2].join("\n")
|
|
270
|
+
end
|
|
271
|
+
puts message
|
|
272
|
+
MessageDialog.new(@frame, message, "Error", Wx::ICON_ERROR).show_modal
|
|
273
|
+
end
|
|
274
|
+
|
|
275
|
+
def save_image
|
|
276
|
+
|
|
277
|
+
begin
|
|
278
|
+
page = @notebook.get_current_page
|
|
279
|
+
if (page.respond_to? :image)
|
|
280
|
+
image = page.image
|
|
281
|
+
fd = FileDialog.new(@frame, :message => "Save Image", :style => FD_SAVE, :default_dir => @working_dir)
|
|
282
|
+
if Wx::ID_OK == fd.show_modal
|
|
283
|
+
@working_dir = fd.get_directory
|
|
284
|
+
puts "Writing #{fd.get_path}"
|
|
285
|
+
image.mirror(false).save_file(fd.get_path)
|
|
286
|
+
end
|
|
287
|
+
else
|
|
288
|
+
error_dialog("Sorry, could not generate an image.")
|
|
289
|
+
end
|
|
290
|
+
|
|
291
|
+
rescue Exception => e
|
|
292
|
+
error_dialog(e)
|
|
293
|
+
end
|
|
294
|
+
end
|
|
295
|
+
|
|
296
|
+
end
|
|
297
|
+
end
|
|
298
|
+
|