matplotlib

Source code for imaging

# 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)