# Copyright (c) 2014, Varian Medical Systems, Inc. (VMS)
# All rights reserved.
#
# Veritas is an open source tool for TrueBeam Developer Mode provided by Varian Medical Systems, Palo Alto.
# It lets users generate XML beams without assuming any prior knowledge of the underlying XML-schema rules.
# This version is based on the schema for TrueBeam 1.5 and 1.6.
#
# Veritas is licensed under the Varian Open Source License.
# You may obtain a copy of the License at:
#
# http://radiotherapyresearchtools.com/license/
#
# For questions, please send us an email at: TrueBeamDeveloper@varian.com
#
# Developer Mode is intended for non-clinical use only and is NOT cleared for use on humans.
# Created on: 11:11:34 AM, Jul 7, 2014
# Author: Pankaj Mishra
# *************************************
# This file represents the dialog after pressing the imaging button from the main window
# As of 05/12/2015, imaging.py does not obey the data flow structure that
# is used in beamon.py and other locations. Please read the below
# carefully.
# In general, any given class has the following structure:
# 1) Each class, in the root folder corresponds logically to
# a single window in the main application
# 2) The visual layout and UI elements are specified by an import
# of form ui_xxxxx where xxxx is the name of the class
# 3) UNLIKE other files, imaging does all of its own data processing
# and xml conversion. This might change at a future date.
# 4) The class itself handles the semantics of what UI elements
# map to what actions and what data passes through to cp_data
# 5) Any instance attributes inside a class is used as bookkeeping
# to keep track of data before it gets sent to cp_data
# The above will be reproduced in the preface document, and is included
# to be an inline reference to the structure.
# Up to date as of May, 12 2015.
# %%%
# The main data flow for this file is as follows:
# Data from the main window is recieved to configure the
# initial state of the image window.
# The image window slowly gets filled up with various
# imaging points. Upon pressing done, this class
# uses its implicit state to generate an xml tree, that
# then immediately gets saved to disk, if possible.
# CPData is used for nothing other than a factory to generate a fake header
# Any non-responsive or buggy UI elements will be here
# Any xml import or export errors will be in the methods involved
# with exporting xml
# Any other problems will be in other files
# *************************************
try:
from PySide.QtGui import QDialog, QFileDialog, QRadioButton, QLineEdit, QComboBox, QSpinBox, QLabel
from PySide.QtGui import QWidget, QMessageBox
import numpy as np
except ImportError:
#print('Pyside not getting imported')
pass
from xml.etree import ElementTree as ET
from UI import ui_Imaging, ui_kvImageMode, ui_mvImageMode
from utils import SetBeam20
import os
from models.CPData import CPData
[docs]class cpImage(object):#QDialog, ui_Imaging.Ui_Imaging):
'''
Main imaging control point window. This window contains
kV, MV imaging points. The window also contains navigation option to
go backward and forward and determine how to
'''
def __init__(self, parent=None, infile='', image_only=None):
super(cpImage, self).__init__(parent)
self.setupUi(self)
# Open the Imaging mode option
[r.clicked.connect(self.selectMode) for r in self.findChildren(QRadioButton)]
# Imaging Mode Options
self.kvButtonImageMode.clicked.connect(self.openImageMode) # Select single vs. continuous
self.mvButtonImageMode.clicked.connect(self.openImageMode) # Select single vs. continuous
self.previousButton.clicked.connect(self.prevCP) # Save the current cp and get previous cp
self.nextButton.clicked.connect(self.nextCP) # Save the current cp and get next cp
self.doneButton.clicked.connect(self.doneCP) # Done button for XMLbeam output
self.gotoIndex.editingFinished.connect(self.randomCP) # Jump to random point
self.setModal(True)
self.selectKV.click()
# Change behavior depending on dropdown on main window
if image_only == 'Outside Treatment':
self.image_only = True
self.image_CP = True
self.dispCount.setText('CP')
self.gotoIndex.setText("CP")
self.selectKV.hide()
self.selectMV.hide()
self.mvFrame.setEnabled(False)
self.kvFrame.setEnabled(True)
# Selectively disable useless fields
kv_disable = self.kvFrame.findChildren(QWidget)
kv_enable = [self.kvAxisValueStop, self.kvAxisValue, self.kvAxis] + self.kvAxis.findChildren(QWidget)
kv_items = set(kv_disable) - set(kv_enable)
map(lambda item: item.setEnabled(False), kv_items)
else:
self.image_only = False
self.image_CP = False
self.fakeCPTitle.hide()
if infile:
self.infile = os.path.abspath(infile)
else:
fileObj = QFileDialog.getOpenFileName(self, "Choose an .xml file", dir="output",
filter="Text files (*.xml)")
self.infile = fileObj[0]
self.kvAxisValueStop.hide()
self.kvStopLabel.hide()
self.mvAxisValueStop.hide()
self.mvStopLabel.hide()
# Each entry in the image point list is a tuple of (Option kv_dict, Option mv_dict)
# Option A is of type A or None
self.image_points = [None]
self.continuous_balanced = True
self.image_index = 0
self.tree = None
self.xml_file = None
self.fake_cp = None
# Navigation options
[docs] def prevCP(self):
'''
Go to previous imaging control point after saving
data
'''
self._jump_cp(self.image_index, self.image_index-1)
[docs] def nextCP(self):
'''
Save all the values for current control point and
jump to the next imaging control point.
:param fieldVars:
'''
self._jump_cp(self.image_index, self.image_index+1)
# Jump is performed after entering number into textbox and
# pressint <Enter>
[docs] def randomCP(self):
'''
Jump to random control point specified by gotoIndex
'''
index = int(self.gotoIndex.text())
self._jump_cp(self.image_index, index)
# Activate after pressing Done button
[docs] def doneCP(self, file_name=''):
'''
Copy the values for fractional imaging control points
from imaging sub-windows
'''
if self.image_points[-1] is None:
self.image_points.pop()
kvf = self.kvFrame
mvf = self.mvFrame
# Add header
self._init_sbeam_tag()
# currently, this is broken, since previously it assumed
# the tree was updated live, whereas now it's updated
# after the cps have been filled out
self._set_image_params()
for index, value in enumerate(self.image_points):
kv_cp, mv_cp = value
if kv_cp:
cur_frac_cp = self._generate_cp(kv_cp, "kvAxis", "kvAxisValue")
self.kvImagingNode(self.root, kv_cp, cur_frac_cp, index)
elif mv_cp:
cur_frac_cp = self._generate_cp(mv_cp, "mvAxis", "mvAxisValue")
self.mvImagingNode(self.root, mv_cp, cur_frac_cp, index)
self.imgOutput(file_name)
# Open kv or mv imaging mode window after pressing Options
[docs] def openImageMode(self):
'''
Driver function for calling window corresponding to the
selected image mode. KV image mode has 7 different options while
MV image mode has 4 different option.
'''
if self.kvFrame.isEnabled():
mode = kvImageMode()
label = self.kvImageModeLabel
elif self.mvFrame.isEnabled():
mode = mvImageMode()
label = self.mvImageModeLabel
# Display the image mode dialog box
mode.show()
# Now update the label to reflect chosen value
if(mode.exec_()):
[label.setText(r.objectName()) for r in mode.findChildren(QRadioButton) if r.isChecked()]
kvcontinuousImageModes = ['DynamicGainFluoro', 'DynamicGainLowFramerateFluoro', 'Fluoro']
kvsingleImageModes = ['DynamicGain', 'DynamicGainLowFramerate', 'LowDose', 'HighQuality']
if label.text() in kvcontinuousImageModes:
self.kvAxisValueStop.show()
self.kvStopLabel.show()
elif label.text() in kvsingleImageModes:
self.kvAxisValueStop.hide()
self.kvStopLabel.hide()
mvcontinuousImageModes = ['Continuous', 'Dosimetry']
mvsingleImageModes = ['Highres', 'Lowres']
if label.text() in mvcontinuousImageModes:
self.mvAxisValueStop.show()
self.mvStopLabel.show()
elif label.text() in mvsingleImageModes:
self.mvAxisValueStop.hide()
self.mvStopLabel.hide()
# Enable either kv frame or mv frame
[docs] def selectMode(self):
'''
Choose kV, MV or both sub-windows
'''
if(self.selectKV.isChecked()):
self.kvFrame.setEnabled(True)
self.mvFrame.setEnabled(False)
elif(self.selectMV.isChecked()):
self.mvFrame.setEnabled(True)
self.kvFrame.setEnabled(False)
def _jump_cp(self, start, end):
self._add_edit_image(start)
self._go_to_image(end)
def _add_edit_image(self, index):
is_kv = self.kvFrame.isEnabled()
is_mv = self.mvFrame.isEnabled()
if is_kv:
field_vars = self.getVariables(self.kvFrame)
val_string = 'kvAxisValue'
elif is_mv:
field_vars = self.getVariables(self.mvFrame)
val_string = 'mvAxisValue'
else:
raise TypeError
cur_cp = self._grab_values(field_vars)
if self.image_CP:
self.image_CP = False
self.fake_cp = cur_cp
self.fakeCPTitle.hide()
self.kvFrame.setEnabled(True)
kv_disable = self.kvFrame.findChildren(QWidget)
kv_enable = [self.kvAxisValueStop, self.kvAxisValue, self.kvAxis] + self.kvAxis.findChildren(QWidget)
kv_items = set(kv_disable) - set(kv_enable)
map(lambda item: item.setEnabled(True), kv_items)
self.selectKV.show()
self.selectMV.show()
self._go_to_image(0)
return
if is_kv:
image_pair = (cur_cp, None)
elif is_mv:
image_pair = (None, cur_cp)
# Enforce mandatory values
if not cur_cp.get(val_string):
return
if self.image_points[index] is None:
if self._is_balanced(cur_cp, index-1):
self.image_points[index] = image_pair
self.image_points.append(None)
else:
QMessageBox.warning(self,
u"Errror!",
u"Continuous tags unbalanced!\n" \
u"Add a single continuous tag of the previous type to proceed",
QMessageBox.Ok,
QMessageBox.NoButton)
elif cur_cp[val_string]:
self.image_points[index] = image_pair
# --- navigation UI controller helpers
def _grab_values(self, field_vars):
result_cp = {}
# Save all the field values
if field_vars is None:
return None
for (field, line_edit) in field_vars:
if isinstance(line_edit, QLineEdit):
result_cp[field] = line_edit.text()
elif isinstance(line_edit, QComboBox):
result_cp[field] = line_edit.currentText()
elif isinstance(line_edit, QSpinBox):
result_cp[field] = str(line_edit.value())
elif isinstance(line_edit, QLabel):
result_cp[field] = str(line_edit.text())
return result_cp
def displayCP(self, image_pair):
'''Display entries for currentCP'''
kv_cp, mv_cp = image_pair
if kv_cp:
frame = self.kvFrame
cur_cp = kv_cp
elif mv_cp:
frame = self.mvFrame
cur_cp = mv_cp
field_vars = self.getVariables(frame)
for (field, line_edit) in field_vars:
if isinstance(line_edit, QLineEdit):
line_edit.setText(cur_cp[field])
elif isinstance(line_edit, QComboBox):
qbox_index = line_edit.findText(cur_cp[field])
line_edit.setCurrentIndex(qbox_index)
elif isinstance(line_edit, QSpinBox):
line_edit.setValue(int(cur_cp[field]))
elif isinstance(line_edit, QLabel):
line_edit.setText(cur_cp[field])
def _go_to_image(self, index):
# guard against invalid indices
if index < 0 or index > len(self.image_points)-1:
return
if self.image_points[index] is None:
kv_field_vars = self.getVariables(self.kvFrame) # Find out what field_vars should be
mv_field_vars = self.getVariables(self.mvFrame)
self.clearCP(kv_field_vars)
self.clearCP(mv_field_vars)
else:
self.displayCP(self.image_points[index])
self.image_index = index
self.dispCount.setText(str(index))
self.gotoIndex.setText(str(index))
def getVariables(self, frame):
'''
Get variables entered in sub-windows
:param frame: choose kV or MV sub-window
'''
widgetLst = [QLineEdit, QComboBox, QSpinBox] # List of object to be extracted
self.lineedits = frame.findChildren(QLabel, "kvImageModeLabel") # For either kV or MV
self.lineedits.extend(frame.findChildren(QLabel, "mvImageModeLabel")) # For either kV or MV
[self.lineedits.extend(frame.findChildren(lst)) for lst in widgetLst]
# Remove lineedit from spinbox
self.lineedits = [lst for lst in self.lineedits if lst.objectName() != 'qt_spinbox_lineedit']
self.fields = [lst.objectName() for lst in self.lineedits]
return zip(self.fields, self.lineedits)
def clearCP(self, fieldVars):
'''
Clear all the field variables in the current
sub-window
:param fieldVars:
'''
# Default kv imaging parameters value
sbValue = {"KiloVolts": 40, "MilliAmperes": 20, "MilliSeconds": 10}
# Clear all the field values
for (_, lineEdit) in fieldVars:
if isinstance(lineEdit, QLineEdit):
lineEdit.clear()
elif isinstance(lineEdit, QSpinBox):
lineEdit.setProperty("value", sbValue[lineEdit.objectName()])
# Switch to the default option
if self.kvFrame.isEnabled():
self.kvAxis.setEnabled(False) # Now the axis is locked
elif self.mvFrame.isEnabled():
self.mvAxis.setEnabled(False) # Now the axis is locked
# --- end navigation UI controller helpers
# --- _edit_add helpers. State machine for balancing continuous + single
# image points
def _is_balanced(self, cur_cp, prev_index):
'''
This function checks to see if the previous control point
results in a pair of balanced tags via a simple state machine.
'''
# Error checking
if self.continuous_balanced:
self.continuous_balanced = not self._is_single_continuous(cur_cp)
return True
prev_cp = self.image_points[prev_index][0] if self.image_points[prev_index][0] else self.image_points[prev_index][1]
if self._is_same_continuous(cur_cp, prev_cp) and self._is_single_continuous(cur_cp):
self.continuous_balanced = True
return True
return False
def _is_single_continuous(self, cur_point):
image_modes = {'kvcontinuous' : ['DynamicGainFluoro', 'DynamicGainLowFramerateFluoro', 'Fluoro'],
'kvsingle' : ['DynamicGain', 'DynamicGainLowFramerate', 'LowDose', 'HighQuality'],
'mvcontinuous' : ['Continuous', 'Dosimetry'],
'mvsingle' : ['Highres', 'Lowres']}
if cur_point.has_key("kvAxisValueStop") or cur_point.has_key("mvAxisValueStop"):
return False
if (cur_point.get("kvImageModeLabel") in image_modes["kvcontinuous"] or
cur_point.get("mvImageModeLabel") in image_modes["mvcontinuous"]):
return True
return False
def _is_same_continuous(self, cur_cp, prev_cp):
cur_cp_kv = cur_cp.get('kvAxisValue')
prev_cp_kv = prev_cp.get('kvAxisValue')
if cur_cp_kv and prev_cp_kv:
return prev_cp_kv == prev_cp_kv
cur_cp_mv = cur_cp.get('mvAxisValue')
prev_cp_mv = prev_cp.get('mvAxisValue')
if cur_cp_mv and prev_cp_mv:
return prev_cp_mv == prev_cp_mv
# --- end _edit_add state machine methods
def openImaging(self):
'''
Driver function for opening the imaging window
'''
self.show() # Show the header window
# --- done button private helpers (before xml generation)
def _init_sbeam_tag(self):
if not self.image_only and self.tree is None:
with open(self.infile, 'r') as f:
self.tree = ET.parse(f)
self.root = self.tree.getroot()
self.sbeam = self.root.getchildren()[0]
elif self.image_only:
self.root = self._generate_fake_cp_root()
self.sbeam = self.root.getchildren()[0]
def _generate_fake_cp_root(self):
cp_data = CPData()
cp_data.set_energy("0", "k")
cp_data.set_MLCmodel("NDS120HD")
cp_data.set_drate("0")
dict1 = {"Mu": "0"}
dict1[self.fake_cp["kvAxis"]] = self.fake_cp["kvAxisValue"]
cp_data.add_cp(**dict1)
dict2 = {self.fake_cp["kvAxis"] : self.fake_cp["kvAxisValueStop"]}
cp_data.add_cp(**dict2)
return cp_data.create_xml()
# --- end done helpers
# --- xml_generation helper methods
def _set_image_params(self):
iparams = ET.SubElement(self.sbeam, 'ImagingParameters')
if self.image_only:
outside = ET.SubElement(iparams, 'OutsideTreatment')
ET.SubElement(outside, 'MaxMu').text = '0'
else:
ET.SubElement(iparams, 'DuringTreatment')
ET.SubElement(iparams, 'ImagingPoints')
ET.SubElement(iparams, 'ImagingTolerances')
def _generate_cp(self, cur_cp, axis, axis_val):
'''
Generate the fractional cp number given
the main xml tree
'''
values = list()
#===================================================================
# Get all the axis values. First read the selected axis name from
# self.currCP["kvAxis"]) and then tree.iter extracts all the
# values in "values"
#===================================================================;
for lst in self.root.findall(".//ControlPoints/Cp/" + cur_cp[axis]):
values.append(float(lst.text))
# Take care of counter clockwise Gantry Rotation
values_before = values
if cur_cp[axis] == "GantryRtn":
values = np.array((np.sort(np.asarray(values)))).tolist()
pt = float(cur_cp[axis_val])
frac_cp = self.calculateFraction(values, pt)
result = self._reversed_cp(frac_cp, values_before, values)
return result
def kvImagingNode(self, root, cur_cp, fractional_cp, cur_index):
'''
Create an XML object corresponding to the kV imaging points.
These XMl tags are based on XML schema 1.5
:param root: The root element of the existing XML beam.
:param fractional_cp: MV imaging control point to be added to the 'root'
'''
node = root.findall(".//ImagingPoints")
elem = ET.SubElement(node[0], "ImagingPoint") # Add Imaging point tag
ET.SubElement(elem,"Cp").text = str(fractional_cp) # Add fractional control point (cp)
# Single vs. Continuous imaging mode
continuousImageModes = ['DynamicGainFluoro', 'DynamicGainLowFramerateFluoro', 'Fluoro']
singleImageModes = ['DynamicGain', 'DynamicGainLowFramerate', 'LowDose', 'HighQuality']
imageModeStart = root.findall(".//AcquisitionStart")
imageModeStop = root.findall(".//AcquisitionStop")
if not imageModeStart or (len(imageModeStart) == len(imageModeStop)):
if cur_cp["kvImageModeLabel"] in singleImageModes:
ac = ET.SubElement(elem,"Acquisition")
elif cur_cp["kvImageModeLabel"] in continuousImageModes:
ac = ET.SubElement(elem,"AcquisitionStart")
if cur_cp["kvAxisValueStop"]:
stop_frac_cp = self._generate_cp(cur_cp, "kvAxis", "kvAxisValueStop")
del cur_cp["kvAxisValueStop"]
self.kvImagingNode(root, cur_cp, stop_frac_cp, cur_index+1)
ET.SubElement(ac,"AcquisitionId").text = str(cur_index+1)
acSpecs = ET.SubElement(ac,"AcquisitionSpecs")
ET.SubElement(acSpecs,"Handshake").text = "true"
ET.SubElement(acSpecs,"KV").text = "true"
params = ET.SubElement(ac,"AcquisitionParameters")
ET.SubElement(params,"ImageMode", id = cur_cp["kvImageModeLabel"])
ET.SubElement(params,"CalibrationSet").text = "DefaultCalibrationSetId"
kvWattage = ET.SubElement(params,"KV")
ET.SubElement(kvWattage,"KiloVolts").text = str(cur_cp["KiloVolts"])
ET.SubElement(kvWattage,"MilliAmperes").text = str(cur_cp["MilliAmperes"])
ET.SubElement(kvWattage,"MilliSeconds").text = str(cur_cp["MilliSeconds"])
ET.SubElement(kvWattage,"eFluoroLevelControl").text = "None"
# Optional kV detector and source tags
# kV detector Kvd tag (Optional)
kvd = ET.SubElement(elem, 'Kvd')
pos = ET.SubElement(kvd, 'Positions')
kvd_pos_mappings = [('Lat', 'kvdLat'),('Lng','kvdLng'), ('Vrt', 'kvdVrt'), ('Pitch', 'kvdPitch')]
self._populate_subelements(cur_cp, pos, kvd_pos_mappings)
# kV source Kvs tag (Optional)
kvs = ET.SubElement(elem, 'Kvs')
kvs_pos = ET.SubElement(kvs, 'Positions')
kvs_pos_mappings = [('Lat', "kvsLat"), ('Lng', "kvsLng"), ('Vrt', "kvsVrt"), ('Pitch', "kvsPitch")]
self._populate_subelements(cur_cp, kvs_pos, kvs_pos_mappings)
# kV Filters (Optional)
kvFilters = ET.SubElement(elem,'KvFilters')
kvfilt_mappings = [('Foil', "kvFoil"), ('Shape', "kvShape")]
self._populate_subelements(cur_cp, kvFilters, kvfilt_mappings)
elif(len(imageModeStop) < len(imageModeStart)):
ac = ET.SubElement(elem,"AcquisitionStop")
ET.SubElement(ac,"AcquisitionId").text = str(cur_index)
ET.SubElement(ac,"AcquisitionSpecs")
def mvImagingNode(self, root, cur_cp, fractional_cp, cur_index):
'''
Create XML object corresponding to the MV imaging points.
This XMl tags are based on XML schema 1.5
:param root: The root element of the existing XML beam.
:param cp: MV imaging control point to be added to the 'root'
'''
node = root.findall(".//ImagingPoints")
elem = ET.SubElement(node[0],"ImagingPoint") # Add Imaging point tag
ET.SubElement(elem,"Cp").text = str(fractional_cp) # Add fractional control point (cp)
# Define single and continuous imaging mode types
continuousImageModes = ['Continuous', 'Dosimetry']
singleImageModes = ['Highres', 'Lowres']
# Look for pre-existing continuous mode XML tags
imageModeStart = root.findall(".//AcquisitionStart")
imageModeStop = root.findall(".//AcquisitionStop")
# Add the appropriate i.e., single or continuous XML tag
# Check if there are any continuous tags
if not imageModeStart or (len(imageModeStart) == len(imageModeStop)):
if cur_cp["mvImageModeLabel"] in singleImageModes:
ac = ET.SubElement(elem,"Acquisition")
elif cur_cp["mvImageModeLabel"] in continuousImageModes:
ac = ET.SubElement(elem,"AcquisitionStart")
# If user specified end, generate the fractional cp
# and automatically close the acquisition start by generating
# a phantom point guaranteed to trip the stop flag
if cur_cp.get("mvAxisValueStop"):
stop_frac_cp = self._generate_cp(cur_cp, "mvAxis", "mvAxisValueStop")
del cur_cp["mvAxisValueStop"]
self.kvImagingNode(self, root, cur_cp, stop_frac_cp, cur_index+1)
ET.SubElement(ac,"AcquisitionId").text = str(cur_index+1)
ET.SubElement(ac,"AcquisitionSpecs")
params = ET.SubElement(ac,"AcquisitionParameters")
ET.SubElement(params,"ImageMode", id = cur_cp["mvImageModeLabel"])
ET.SubElement(params,"CalibrationSet").text = "DefaultCalibrationSetId"
ET.SubElement(params,"MV")
# Optional MV detector
# kV detector Kvd tag (Optional)
mvd = ET.SubElement(elem, 'Mvd')
pos = ET.SubElement(mvd, 'Positions')
if cur_cp["mvdLat"]: ET.SubElement(pos, 'Lat').text = str(cur_cp["mvdLat"])
if cur_cp["mvdLng"]: ET.SubElement(pos, 'Lng').text = str(cur_cp["mvdLng"])
if cur_cp["mvdVrt"]: ET.SubElement(pos, 'Vrt').text = str(cur_cp["mvdVrt"])
if cur_cp["mvdPitch"]: ET.SubElement(pos, 'Pitch').text = str(cur_cp["mvdPitch"])
elif(len(imageModeStop) < len(imageModeStart)):
ac = ET.SubElement(elem,"AcquisitionStop")
ET.SubElement(ac,"AcquisitionId").text = str(cur_index)
ET.SubElement(ac,"AcquisitionSpecs")
def _reversed_cp(self, cp, before, after):
if not np.array_equal(np.asarray(before), np.asarray(after)):
result = 1.0 - cp
else:
result = cp
return result
def calculateFraction(self, values, pt):
'''
Calculate fractional control point based on
an array of control point 'values' and the imaging point 'pt'
:param values: An array containing al the control points
:param pt: Value of fractional control point
'''
# Get fractional control point
if pt == values[0]:
cp = 0
return np.around(cp, decimals=2)
elif pt == values[-1]:
cp = len(values) -1
return np.around(cp, decimals=2)
else:
a = next(index for index, value in enumerate(values) if value > pt)
cp = ((a-1)+(values[a-1]-pt)/(values[a-1]-values[a]))
# Round the control point to 2 decimal places
cp = np.around(cp, decimals=2)
return np.around(cp, decimals=2)
def _populate_subelements(self, cur_cp, tag, mappings):
for target, name in mappings:
if cur_cp[name]:
ET.SubElement(tag, target).text = str(cur_cp[name])
else:
pass
# --- end xml_generation helpers
def imgOutput(self, file_name):
'''
Display the output xml image
'''
self.close()
if file_name:
self.xml_file = file_name
else:
qout = QFileDialog.getSaveFileName(self, "Save output XML file",
dir = 'imgOutput', filter = "XML Files (*.xml);;All Files (*.*)")
self.xml_file = qout[0]
SetBeam20.parse(self.root, self.xml_file, True)
class kvImageMode(object):#QDialog, ui_kvImageMode.Ui_imageMode):
'''
Create an kV image mode class with a set of imaging options
kV image mode has 7 different image modes.
'''
def __init__(self, parent=None):
super(kvImageMode, self).__init__(parent)
self.setupUi(self)
self.setModal(True)
class mvImageMode(object):#QDialog, ui_mvImageMode.Ui_imageMode):
'''
Create an MV image mode class with a set of imaging options.
MV image mode has 4 different image modes.
'''
def __init__(self, parent=None):
super(mvImageMode, self).__init__(parent)
self.setupUi(self)
self.setModal(True)