class orderListBase(wx.Panel): xrc = os.path.join(location(), "windows", "xrc", 'orderList.xrc') def PreCreate(self, pre): """ This function is called during the class's initialization. Override it for custom setup before the window is created usually to set additional window styles using SetWindowStyle() and SetExtraStyle().""" pass def __init__(self, parent, *args, **kw): """ Pass an initialized wx.xrc.XmlResource into res """ f = os.path.join(os.path.dirname(__file__), self.xrc) res = XmlResourceWithHandlers(f) # Two stage creation (see http://wiki.wxpython.org/index.cgi/TwoStageCreation) pre = wx.PrePanel() res.LoadOnPanel(pre, parent, "orderList") self.PreCreate(pre) self.PostCreate(pre) # Define variables for the controls self.Choices = XRCCTRL(self, "Choices") self.Type = XRCCTRL(self, "Type") if hasattr(self, "OnType"): self.Type.Bind(wx.EVT_CHOICE, self.OnType) self.Number = XRCCTRL(self, "Number") self.Add = XRCCTRL(self, "Add") if hasattr(self, "OnAdd"): self.Bind(wx.EVT_BUTTON, self.OnAdd, self.Add) self.Delete = XRCCTRL(self, "Delete") if hasattr(self, "OnDelete"): self.Bind(wx.EVT_BUTTON, self.OnDelete, self.Delete)
class panelOrderBase(wx.Panel): xrc = os.path.join(location(), "windows", "xrc", 'panelOrder.xrc') def PreCreate(self, pre): """ This function is called during the class's initialization. Override it for custom setup before the window is created usually to set additional window styles using SetWindowStyle() and SetExtraStyle().""" pass def __init__(self, parent, *args, **kw): """ Pass an initialized wx.xrc.XmlResource into res """ f = os.path.join(os.path.dirname(__file__), self.xrc) res = XmlResourceWithHandlers(f) # Two stage creation (see http://wiki.wxpython.org/index.cgi/TwoStageCreation) pre = wx.PrePanel() res.LoadOnPanel(pre, parent, "panelOrder") self.PreCreate(pre) self.PostCreate(pre) # Define variables for the controls self.Master = XRCCTRL(self, "Master") self.Orders = XRCCTRL(self, "Orders") self.Possible = XRCCTRL(self, "Possible") if hasattr(self, "OnPossible"): self.Possible.Bind(wx.EVT_CHOICE, self.OnPossible) self.New = XRCCTRL(self, "New") if hasattr(self, "OnNew"): self.Bind(wx.EVT_BUTTON, self.OnNew, self.New) self.DetailsParentPanel = XRCCTRL(self, "DetailsParentPanel") self.ArgumentLine = XRCCTRL(self, "ArgumentLine") self.DetailsBorderPanel = XRCCTRL(self, "DetailsBorderPanel") self.DetailsPanel = XRCCTRL(self, "DetailsPanel") self.ButtonsPanel = XRCCTRL(self, "ButtonsPanel") self.Message = XRCCTRL(self, "Message") self.Save = XRCCTRL(self, "Save") if hasattr(self, "OnSave"): self.Bind(wx.EVT_BUTTON, self.OnSave, self.Save) self.Revert = XRCCTRL(self, "Revert") if hasattr(self, "OnRevert"): self.Bind(wx.EVT_BUTTON, self.OnRevert, self.Revert) self.Delete = XRCCTRL(self, "Delete") if hasattr(self, "OnDelete"): self.Bind(wx.EVT_BUTTON, self.OnDelete, self.Delete)
class Panel(ObjectPanel.Panel): """ A wx.Panel for displaying and editing Categories """ def __init__(self, parent, id=wx.ID_ANY, style=wx.EXPAND): #load from XRC, need to use two-stage create pre = wx.PrePanel() res = gui.XrcUtilities.XmlResource('./gui/xrc/CategoryPanel.xrc') res.LoadOnPanel(pre, parent, "CategoryPanel") self.PostCreate(pre) ObjectPanel.Panel.Setup(self) self.OnCreate() def OnCreate(self): self.name_field = XRCCTRL(self, "name_field") self.desc_field = XRCCTRL(self, "desc_field") self.desc_field.Bind(wx.EVT_TEXT, self.CreateAttributeMonitor('description')) self.loaded = False def LoadObject(self, node): self.loading = True self.node = node self.object = node.GetObject() self.node.visible = True self.name_field.SetLabel(str(self.object.name)) self.desc_field.SetValue(str(self.object.description)) self.desc_field.attr_name = 'description' if self.object.errors.has_key('description'): print "Error in description!" self.SetErrorLabel('description', self.object.errors['description']) self.loaded = True self.loading = False self.Show() return self def cleanup(self): self.node.visible = False self.Hide() self.node.ClearObject() self.object = None self.loaded = False
class panelStarMapBase(wx.Panel): xrc = os.path.join(location(), "windows", "xrc", 'panelStarMap.xrc') def PreCreate(self, pre): """ This function is called during the class's initialization. Override it for custom setup before the window is created usually to set additional window styles using SetWindowStyle() and SetExtraStyle().""" pass def __init__(self, parent, *args, **kw): """ Pass an initialized wx.xrc.XmlResource into res """ f = os.path.join(os.path.dirname(__file__), self.xrc) res = XmlResourceWithHandlers(f) # Two stage creation (see http://wiki.wxpython.org/index.cgi/TwoStageCreation) pre = wx.PrePanel() res.LoadOnPanel(pre, parent, "panelStarMap") self.PreCreate(pre) self.PostCreate(pre) # Define variables for the controls self.MouseMode = XRCCTRL(self, "MouseMode") if hasattr(self, "OnMouseMode"): self.Bind(wx.EVT_BUTTON, self.OnMouseMode, self.MouseMode) self.ZoomLevel = XRCCTRL(self, "ZoomLevel") if hasattr(self, "OnZoomLevel"): self.Bind(wx.EVT_COMBOBOX, self.OnZoomLevel, self.ZoomLevel) self.Bind(wx.EVT_TEXT_ENTER, self.OnZoomLevel, self.ZoomLevel) if hasattr(self, "OnDirtyZoomLevel"): self.Bind(wx.EVT_TEXT, self.OnZoomLevel, self.ZoomLevel) self.Home = XRCCTRL(self, "Home") if hasattr(self, "OnHome"): self.Bind(wx.EVT_BUTTON, self.OnHome, self.Home) self.Find = XRCCTRL(self, "Find") if hasattr(self, "OnFind"): self.Bind(wx.EVT_BUTTON, self.OnFind, self.Find) self.DisplayMode = XRCCTRL(self, "DisplayMode") if hasattr(self, "OnDisplayMode"): self.DisplayMode.Bind(wx.EVT_CHOICE, self.OnDisplayMode) self.DisplayModeExtra = XRCCTRL(self, "DisplayModeExtra") self.FloatCanvas = XRCCTRL(self, "FloatCanvas")
def bindall(self, obj): for func_name, args, keys in self.binds[hash(self)]: keys = dict(keys) if keys.has_key('event'): event = keys['event'] del keys['event'] else: event = args[0] args = list(args[1:]) control = obj if keys.has_key('id'): if isinstance(keys['id'], str): keys['id'] = XRCID(keys['id']) if keys.has_key('control'): control = keys['control'] if isinstance(control, str): control = XRCCTRL(obj, control) del keys['control'] control.Bind(event, getattr(obj, func_name), *args, **keys)
class Panel(ObjectPanel.Panel): """ A wx.Panel for displaying and editing Properties """ def __init__(self, parent, id=wx.ID_ANY, style=wx.EXPAND): #load from XRC, need to use two-stage create pre = wx.PrePanel() res = gui.XrcUtilities.XmlResource('./gui/xrc/PropertyPanel.xrc') res.LoadOnPanel(pre, parent, "PropertyPanel") self.PostCreate(pre) ObjectPanel.Panel.Setup(self) self.OnCreate() def OnCreate(self): self.name_field = XRCCTRL(self, "name_field") self.rank_field = XRCCTRL(self, "rank_field") self.desc_field = XRCCTRL(self, "desc_field") self.disp_field = XRCCTRL(self, "disp_field") self.tpcl_disp_stc = XRCCTRL(self, "tpcl_disp_stc") self.tpcl_req_stc = XRCCTRL(self, "tpcl_req_stc") self.cat_choice = XRCCTRL(self, "cat_choice") self.desc_field.Bind(wx.EVT_TEXT, self.CreateAttributeMonitor('description')) self.rank_field.Bind(wx.EVT_TEXT, self.CreateAttributeMonitor('rank')) self.disp_field.Bind(wx.EVT_TEXT, self.CreateAttributeMonitor('display_text')) self.tpcl_disp_stc.Bind(wx.stc.EVT_STC_CHANGE, self.CreateAttributeMonitor('tpcl_display')) self.tpcl_req_stc.Bind(wx.stc.EVT_STC_CHANGE, self.CreateAttributeMonitor('tpcl_requires')) self.cat_choice.Bind(wx.EVT_CHECKLISTBOX, self.CreateAttributeMonitor('categories')) self.loaded = False def LoadObject(self, node): self.loading = True self.node = node self.object = node.GetObject() self.node.visible = True self.name_field.SetLabel(str(self.object.name)) self.rank_field.SetValue(str(self.object.rank)) if self.object.errors.has_key('rank'): print "Error in rank!" self.SetErrorLabel('rank', self.object.errors['rank']) self.desc_field.SetValue(str(self.object.description)) if self.object.errors.has_key('description'): print "Error in description!" self.SetErrorLabel('description', self.object.errors['description']) self.disp_field.SetValue(str(self.object.display_text)) if self.object.errors.has_key('display_text'): print "Error in display_text!" self.SetErrorLabel('display_text', self.object.errors['display_text']) self.tpcl_disp_stc.SetText(str(self.object.tpcl_display)) if self.object.errors.has_key('tpcl_display'): print "Error in tpcl_display!" self.SetErrorLabel('tpcl_display', self.object.errors['tpcl_display']) self.tpcl_req_stc.SetText(str(self.object.tpcl_requires)) if self.object.errors.has_key('tpcl_requires'): print "Error in tpcl_requires!" self.SetErrorLabel('tpcl_requires', self.object.errors['tpcl_requires']) #fill the category choice box self.cat_choice.Clear() for catnode in self.node.object_database.getObjectsOfType('Category'): idx = self.cat_choice.Append(catnode.name) if catnode.name in self.object.categories: self.cat_choice.Check(idx) if self.object.errors.has_key('categories'): print "Error in categories!" self.SetErrorLabel('categories', self.object.errors['categories']) self.loaded = True self.loading = False self.Show() return self def Destroy(self): self.Hide() def ReallyDestroy(self): wx.Panel.Destroy(self) def cleanup(self): self.CleanupErrorLabels() self.node.visible = False self.Hide() self.node.ClearObject() self.object = None self.loaded = False
class MainFrame(wx.Frame): def __init__(self, parent, id, title, res): # Initialize logging logger = logging.getLogger('dicompyler') # Configure the exception hook to process threads as well self.InstallThreadExcepthook() # Remap the exception hook so that we can log and display exceptions def LogExcepthook(*exc_info): # Log the exception text = "".join(traceback.format_exception(*exc_info)) logger.error("Unhandled exception: %s", text) pub.sendMessage('logging.exception', text) sys.excepthook = LogExcepthook # Modify the logging system from pydicom to capture important messages pydicom_logger = logging.getLogger('pydicom') for l in pydicom_logger.handlers: pydicom_logger.removeHandler(l) # Add file logger logpath = os.path.join(guiutil.get_data_dir(), 'logs') if not os.path.exists(logpath): os.makedirs(logpath) self.fh = logging.handlers.RotatingFileHandler(os.path.join( logpath, 'dicompyler.log'), maxBytes=524288, backupCount=7) self.fh.setFormatter( logging.Formatter( '%(asctime)s - %(name)s - %(levelname)s - %(message)s')) self.fh.setLevel(logging.WARNING) logger.addHandler(self.fh) pydicom_logger.addHandler(self.fh) # Add console logger if not frozen if not util.main_is_frozen(): self.ch = logging.StreamHandler() self.ch.setFormatter( logging.Formatter('%(levelname)s: %(message)s')) self.ch.setLevel(logging.WARNING) logger.addHandler(self.ch) pydicom_logger.addHandler(self.ch) # Otherwise if frozen, send stdout/stderror to /dev/null since # logging the messages seems to cause instability due to recursion else: devnull = open(os.devnull, 'w') sys.stdout = devnull sys.stderr = devnull # Set the window size if guiutil.IsMac(): size = (900, 700) else: size = (850, 625) wx.Frame.__init__(self, parent, id, title, pos=wx.DefaultPosition, size=size, style=wx.DEFAULT_FRAME_STYLE) # Set up the status bar self.sb = self.CreateStatusBar(3) # set up resource file and config file self.res = res # Set window icon if not guiutil.IsMac(): self.SetIcon(guiutil.get_icon()) # Load the main panel for the program self.panelGeneral = self.res.LoadPanel(self, 'panelGeneral') # Initialize the General panel controls self.notebook = XRCCTRL(self, 'notebook') self.notebookTools = XRCCTRL(self, 'notebookTools') self.lblPlanName = XRCCTRL(self, 'lblPlanName') self.lblRxDose = XRCCTRL(self, 'lblRxDose') self.lblPatientName = XRCCTRL(self, 'lblPatientName') self.lblPatientID = XRCCTRL(self, 'lblPatientID') self.lblPatientGender = XRCCTRL(self, 'lblPatientGender') self.lblPatientDOB = XRCCTRL(self, 'lblPatientDOB') self.choiceStructure = XRCCTRL(self, 'choiceStructure') self.lblStructureVolume = XRCCTRL(self, 'lblStructureVolume') self.lblStructureMinDose = XRCCTRL(self, 'lblStructureMinDose') self.lblStructureMaxDose = XRCCTRL(self, 'lblStructureMaxDose') self.lblStructureMeanDose = XRCCTRL(self, 'lblStructureMeanDose') self.cclbStructures = guiutil.ColorCheckListBox( self.notebookTools, 'structure') self.cclbIsodoses = guiutil.ColorCheckListBox(self.notebookTools, 'isodose') # Modify the control and font size on Mac controls = [self.notebookTools, self.choiceStructure] if guiutil.IsMac(): font = wx.SystemSettings.GetFont(wx.SYS_DEFAULT_GUI_FONT) font.SetPointSize(10) for control in controls: control.SetWindowVariant(wx.WINDOW_VARIANT_SMALL) control.SetFont(font) # Setup the layout for the frame mainGrid = wx.BoxSizer(wx.VERTICAL) hGrid = wx.BoxSizer(wx.HORIZONTAL) if guiutil.IsMac(): hGrid.Add(self.panelGeneral, 1, flag=wx.EXPAND | wx.ALL | wx.ALIGN_CENTRE, border=4) else: hGrid.Add(self.panelGeneral, 1, flag=wx.EXPAND | wx.ALL | wx.ALIGN_CENTRE) mainGrid.Add(hGrid, 1, flag=wx.EXPAND | wx.ALL | wx.ALIGN_CENTRE) # Load the menu for the frame menuMain = self.res.LoadMenuBar('menuMain') # If we are running on Mac OS X, alter the menu location if guiutil.IsMac(): wx.App_SetMacAboutMenuItemId(XRCID('menuAbout')) wx.App_SetMacPreferencesMenuItemId(XRCID('menuPreferences')) wx.App_SetMacExitMenuItemId(XRCID('menuExit')) # Set the menu as the default menu for this frame self.SetMenuBar(menuMain) # Setup Tools menu self.menuShowLogs = menuMain.FindItemById( XRCID('menuShowLogs')).GetMenu() self.menuPlugins = menuMain.FindItemById( XRCID('menuPluginManager')).GetMenu() # Setup Import menu self.menuImport = menuMain.FindItemById( XRCID('menuImportPlaceholder')).GetMenu() self.menuImport.Delete( menuMain.FindItemById(XRCID('menuImportPlaceholder')).GetId()) self.menuImportItem = menuMain.FindItemById(XRCID('menuImport')) self.menuImportItem.Enable(False) # Setup Export menu self.menuExport = menuMain.FindItemById( XRCID('menuExportPlaceholder')).GetMenu() self.menuExport.Delete( menuMain.FindItemById(XRCID('menuExportPlaceholder')).GetId()) self.menuExportItem = menuMain.FindItemById(XRCID('menuExport')) self.menuExportItem.Enable(False) # Bind menu events to the proper methods wx.EVT_MENU(self, XRCID('menuOpen'), self.OnOpenPatient) wx.EVT_MENU(self, XRCID('menuExit'), self.OnClose) wx.EVT_MENU(self, XRCID('menuPreferences'), self.OnPreferences) wx.EVT_MENU(self, XRCID('menuShowLogs'), self.OnShowLogs) wx.EVT_MENU(self, XRCID('menuPluginManager'), self.OnPluginManager) wx.EVT_MENU(self, XRCID('menuAbout'), self.OnAbout) wx.EVT_MENU(self, XRCID('menuHomepage'), self.OnHomepage) wx.EVT_MENU(self, XRCID('menuLicense'), self.OnLicense) # Load the toolbar for the frame toolbarMain = self.res.LoadToolBar(self, 'toolbarMain') self.SetToolBar(toolbarMain) self.toolbar = self.GetToolBar() # Setup main toolbar controls folderbmp = wx.Bitmap(util.GetResourcePath('folder_user.png')) self.maintools = [{ 'label': "Open Patient", 'bmp': folderbmp, 'shortHelp': "Open Patient...", 'eventhandler': self.OnOpenPatient }] for m, tool in enumerate(self.maintools): self.toolbar.AddLabelTool(m + 1, tool['label'], tool['bmp'], shortHelp=tool['shortHelp']) self.Bind(wx.EVT_TOOL, tool['eventhandler'], id=m + 1) self.toolbar.Realize() # Bind interface events to the proper methods wx.EVT_CHOICE(self, XRCID('choiceStructure'), self.OnStructureSelect) self.Bind(wx.EVT_ACTIVATE, self.OnActivate) self.Bind(wx.EVT_CLOSE, self.OnClose) self.notebook.Bind(wx.EVT_NOTEBOOK_PAGE_CHANGED, self.OnPageChanged) # Events to work around a focus bug in Windows self.notebook.Bind(wx.EVT_KEY_DOWN, self.OnKeyDown) self.notebook.Bind(wx.EVT_MOUSEWHEEL, self.OnMouseWheel) self.notebookTools.Bind(wx.EVT_KEY_DOWN, self.OnKeyDown) self.notebookTools.Bind(wx.EVT_MOUSEWHEEL, self.OnMouseWheel) self.SetSizer(mainGrid) self.Layout() #Set the Minumum size self.SetMinSize(size) self.Centre(wx.BOTH) # Initialize the welcome notebook tab panelWelcome = self.res.LoadPanel(self.notebook, 'panelWelcome') self.notebook.AddPage(panelWelcome, 'Welcome') # Set the version on the welcome notebook tab XRCCTRL(self, 'lblVersion').SetLabel('Version ' + __version__) # Initialize the tools notebook self.notebookTools.AddPage(self.cclbStructures, 'Structures') self.notebookTools.AddPage(self.cclbIsodoses, 'Isodoses') # Create the data folder datapath = guiutil.get_data_dir() if not os.path.exists(datapath): os.mkdir(datapath) # Initialize the preferences if guiutil.IsMac(): self.prefmgr = preferences.PreferencesManager(parent=None, appname='dicompyler') else: self.prefmgr = preferences.PreferencesManager(parent=None, appname='dicompyler', name='Options') sp = wx.StandardPaths.Get() self.generalpreftemplate = [{ 'DICOM Import Settings': [{ 'name': 'Import Location', 'type': 'choice', 'values': ['Remember Last Used', 'Always Use Default'], 'default': 'Remember Last Used', 'callback': 'general.dicom.import_location_setting' }, { 'name': 'Default Location', 'type': 'directory', 'default': unicode(sp.GetDocumentsDir()), 'callback': 'general.dicom.import_location' }] }, { 'Plugin Settings': [{ 'name': 'User Plugins Location', 'type': 'directory', 'default': unicode(os.path.join(datapath, 'plugins')), 'callback': 'general.plugins.user_plugins_location', 'restart': True }] }, { 'Calculation Settings': [{ 'name': 'DVH Calculation', 'type': 'choice', 'values': ['Use RT Dose DVH if Present', 'Always Recalculate DVH'], 'default': 'Use RT Dose DVH if Present', 'callback': 'general.calculation.dvh_recalc' }] }, { 'Advanced Settings': [{ 'name': 'Enable Detailed Logging', 'type': 'checkbox', 'default': False, 'callback': 'general.advanced.detailed_logging' }] }] self.preftemplate = [{'General': self.generalpreftemplate}] pub.sendMessage('preferences.updated.template', self.preftemplate) # Initialize variables self.ptdata = {} # Set up pubsub pub.subscribe(self.OnLoadPatientData, 'patient.updated.raw_data') pub.subscribe(self.OnStructureCheck, 'colorcheckbox.checked.structure') pub.subscribe(self.OnStructureUncheck, 'colorcheckbox.unchecked.structure') pub.subscribe(self.OnIsodoseCheck, 'colorcheckbox.checked.isodose') pub.subscribe(self.OnIsodoseUncheck, 'colorcheckbox.unchecked.isodose') pub.subscribe(self.OnUpdatePlugins, 'general.plugins.user_plugins_location') pub.subscribe(self.OnUpdatePreferences, 'general') pub.subscribe(self.OnUpdateStatusBar, 'main.update_statusbar') pub.subscribe(self.OnOpenPatient, 'dicomgui.show') # Send a message to the logging system to turn on/off detailed logging pub.sendMessage('preferences.requested.value', 'general.advanced.detailed_logging') # Create the default user plugin path self.userpluginpath = os.path.join(datapath, 'plugins') if not os.path.exists(self.userpluginpath): os.mkdir(self.userpluginpath) # Load and initialize plugins self.plugins = [] self.pluginsDisabled = [] pub.sendMessage('preferences.requested.value', 'general.plugins.user_plugins_location') pub.sendMessage('preferences.requested.value', 'general.calculation.dvh_recalc') pub.sendMessage('preferences.requested.value', 'general.plugins.disabled_list') pub.sendMessage('preferences.requested.values', 'general.window') ########################### Patient Loading Functions ########################## def OnOpenPatient(self, evt): """Load and show the Dicom RT Importer dialog box.""" dicomgui.ImportDicom(self) def OnLoadPatientData(self, msg): """Update and load the patient data.""" # Skip loading if the dataset is empty or if the dataset is the same if (not len(msg.data)) or (self.ptdata == msg.data): return else: self.ptdata = msg.data # Delete the previous notebook pages self.notebook.DeleteAllPages() # Delete the previous toolbar items for t in range(0, self.toolbar.GetToolsCount()): # Only delete the plugin toolbar items if (t >= len(self.maintools)): self.toolbar.DeleteToolByPos(len(self.maintools)) # Delete the previous plugin menus if len(self.menuDict): self.menuPlugins.Delete(wx.ID_SEPARATOR) for menuid, menu in self.menuDict.iteritems(): self.menuPlugins.Delete(menuid) self.menuDict = {} # Delete the previous export menus if len(self.menuExportDict): self.menuExportItem.Enable(False) for menuid, menu in self.menuExportDict.iteritems(): self.menuExport.Delete(menuid) self.menuExportDict = {} # Reset the preferences template self.preftemplate = [{'General': self.generalpreftemplate}] # Initialize the list of subplugins subplugins = [] # Set up the plugins for each plugin entry point of dicompyler for i, plugin in enumerate(self.plugins): # Skip plugin if it doesn't contain the required dictionary # or actually is a proper Python module p = plugin['plugin'] if not hasattr(p, 'pluginProperties') or \ (p.__name__ in self.pluginsDisabled): continue props = p.pluginProperties() # Only load plugin versions that are qualified if (props['plugin_version'] == 1): # Check whether the plugin can view the loaded DICOM data add = False if len(props['min_dicom']): for key in props['min_dicom']: if (key == 'rxdose'): pass elif key in self.ptdata.keys(): add = True else: add = False break # Plugin can view all DICOM data so always load it else: add = True # Initialize the plugin if add: # Load the main panel plugins if (props['plugin_type'] == 'main'): plugin = p.pluginLoader(self.notebook) self.notebook.AddPage(plugin, props['name']) # Load the menu plugins elif (props['plugin_type'] == 'menu'): if not len(self.menuDict): self.menuPlugins.AppendSeparator() self.menuDict[100 + i] = self.menuPlugins.Append( 100 + i, props['name'] + '...') plugin = p.plugin(self) wx.EVT_MENU(self, 100 + i, plugin.pluginMenu) # Load the export menu plugins elif (props['plugin_type'] == 'export'): if not len(self.menuExportDict): self.menuExportItem.Enable(True) self.menuExportDict[200 + i] = self.menuExport.Append( 200 + i, props['menuname']) plugin = p.plugin(self) wx.EVT_MENU(self, 200 + i, plugin.pluginMenu) # If a sub-plugin, mark it to be initialized later else: subplugins.append(p) continue # Add the plugin preferences if they exist if hasattr(plugin, 'preferences'): self.preftemplate.append( {props['name']: plugin.preferences}) pub.sendMessage('preferences.updated.template', self.preftemplate) # Load the subplugins and notify the parent plugins for s in subplugins: props = s.pluginProperties() msg = 'plugin.loaded.' + props['plugin_type'] + '.' + s.__name__ pub.sendMessage(msg, s) dlgProgress = guiutil.get_progress_dialog(self, "Loading Patient Data...") self.t = threading.Thread(target=self.LoadPatientDataThread, args=(self, self.ptdata, dlgProgress.OnUpdateProgress, self.OnUpdatePatientData)) self.t.start() dlgProgress.ShowModal() if dlgProgress: dlgProgress.Destroy() def LoadPatientDataThread(self, parent, ptdata, progressFunc, updateFunc): """Thread to load the patient data.""" # Call the progress function to update the gui wx.CallAfter(progressFunc, 0, 0, 'Processing patient data...') patient = {} if not ptdata.has_key('images'): patient.update(dp(ptdata.values()[0]).GetDemographics()) if ptdata.has_key('rtss'): wx.CallAfter(progressFunc, 20, 100, 'Processing RT Structure Set...') patient['structures'] = dp(ptdata['rtss']).GetStructures() if ptdata.has_key('rtplan'): wx.CallAfter(progressFunc, 40, 100, 'Processing RT Plan...') patient['plan'] = dp(ptdata['rtplan']).GetPlan() if ptdata.has_key('rtdose'): wx.CallAfter(progressFunc, 60, 100, 'Processing RT Dose...') patient['dvhs'] = dp(ptdata['rtdose']).GetDVHs() patient['dose'] = dp(ptdata['rtdose']) if ptdata.has_key('images'): wx.CallAfter(progressFunc, 80, 100, 'Processing Images...') if not patient.has_key('id'): patient.update(dp(ptdata['images'][0]).GetDemographics()) patient['images'] = [] for image in ptdata['images']: patient['images'].append(dp(image)) if ptdata.has_key('rxdose'): if not patient.has_key('plan'): patient['plan'] = {} patient['plan']['rxdose'] = ptdata['rxdose'] # if the min/max/mean dose was not present, calculate it and save it for each structure wx.CallAfter(progressFunc, 90, 100, 'Processing DVH data...') if ('dvhs' in patient) and ('structures' in patient): # If the DVHs are not present, calculate them i = 0 for key, structure in patient['structures'].iteritems(): # Only calculate DVHs if they are not present for the structure # or recalc all DVHs if the preference is set if ((not (key in patient['dvhs'].keys())) or (self.dvhRecalc == 'Always Recalculate DVH')): # Only calculate DVHs for structures, not applicators # and only if the dose grid is present if ((structure['name'].startswith('Applicator')) or (not "PixelData" in patient['dose'].ds)): continue wx.CallAfter( progressFunc, 10 * i / len(patient['structures']) + 90, 100, 'Calculating DVH for ' + structure['name'] + '...') # Limit DVH bins to 500 Gy due to high doses in brachy dvh = dvhcalc.get_dvh(structure, patient['dose'], 50000) if len(dvh['data']): patient['dvhs'][key] = dvh i += 1 for key, dvh in patient['dvhs'].iteritems(): self.CalculateDoseStatistics(dvh, ptdata['rxdose']) wx.CallAfter(progressFunc, 100, 100, 'Done') wx.CallAfter(updateFunc, patient) def CalculateDoseStatistics(self, dvh, rxdose): """Calculate the dose statistics for the given DVH and rx dose.""" sfdict = { 'min': dvhdoses.get_dvh_min, 'mean': dvhdoses.get_dvh_mean, 'max': dvhdoses.get_dvh_max } for stat, func in sfdict.iteritems(): # Only calculate stat if the stat was not calculated previously (-1) if dvh[stat] == -1: dvh[stat] = 100 * func(dvh['data'] * dvh['scaling']) / rxdose return dvh def OnUpdatePatientData(self, patient): """Update the patient data in the main program interface.""" self.PopulateDemographics(patient) self.structures = {} if patient.has_key('structures'): self.structures = patient['structures'] self.PopulateStructures() if patient.has_key('plan'): self.PopulatePlan(patient['plan']) else: self.PopulatePlan({}) if (patient.has_key('dose') and patient.has_key('plan')): self.PopulateIsodoses(patient.has_key('images'), patient['plan'], patient['dose']) else: self.PopulateIsodoses(patient.has_key('images'), {}, {}) if patient.has_key('dvhs'): self.dvhs = patient['dvhs'] else: self.dvhs = {} # Re-publish the raw data pub.sendMessage('patient.updated.raw_data', self.ptdata) # Publish the parsed data pub.sendMessage('patient.updated.parsed_data', patient) def PopulateStructures(self): """Populate the structure list.""" self.cclbStructures.Clear() self.structureList = {} for id, structure in iter(sorted(self.structures.iteritems())): # Only append structures, don't include applicators if not (structure['name'].startswith('Applicator')): self.cclbStructures.Append(structure['name'], structure, structure['color'], refresh=False) # Refresh the structure list manually since we didn't want it to refresh # after adding each structure self.cclbStructures.Layout() self.choiceStructure.Clear() self.choiceStructure.Enable(False) self.OnStructureUnselect() def PopulateIsodoses(self, has_images, plan, dose): """Populate the isodose list.""" self.cclbIsodoses.Clear() self.isodoseList = {} if (has_images and len(plan) and "PixelData" in dose.ds): dosedata = dose.GetDoseData() dosemax = int(dosedata['dosemax'] * dosedata['dosegridscaling'] * 10000 / plan['rxdose']) self.isodoses = [{ 'level': dosemax, 'color': wx.Colour(120, 0, 0), 'name': 'Max' }, { 'level': 102, 'color': wx.Colour(170, 0, 0) }, { 'level': 100, 'color': wx.Colour(238, 69, 0) }, { 'level': 98, 'color': wx.Colour(255, 165, 0) }, { 'level': 95, 'color': wx.Colour(255, 255, 0) }, { 'level': 90, 'color': wx.Colour(0, 255, 0) }, { 'level': 80, 'color': wx.Colour(0, 139, 0) }, { 'level': 70, 'color': wx.Colour(0, 255, 255) }, { 'level': 50, 'color': wx.Colour(0, 0, 255) }, { 'level': 30, 'color': wx.Colour(0, 0, 128) }] for isodose in self.isodoses: # Calculate the absolute dose value name = ' / ' + unicode("%.6g" % (float(isodose['level']) * float(plan['rxdose']) / 100)) + \ ' cGy' if isodose.has_key('name'): name = name + ' [' + isodose['name'] + ']' self.cclbIsodoses.Append(str(isodose['level']) + ' %' + name, isodose, isodose['color'], refresh=False) # Refresh the isodose list manually since we didn't want it to refresh # after adding each isodose self.cclbIsodoses.Layout() def PopulateDemographics(self, demographics): """Populate the patient demographics.""" self.lblPatientName.SetLabel(demographics['name']) self.lblPatientID.SetLabel(demographics['id']) self.lblPatientGender.SetLabel(demographics['gender']) self.lblPatientDOB.SetLabel(demographics['dob']) def PopulatePlan(self, plan): """Populate the patient's plan information.""" if (len(plan) and not plan['rxdose'] == 1): if plan.has_key('name'): if len(plan['name']): self.lblPlanName.SetLabel(plan['name']) elif len(plan['label']): self.lblPlanName.SetLabel(plan['label']) self.lblRxDose.SetLabel(unicode("%.6g" % plan['rxdose'])) else: self.lblPlanName.SetLabel('-') self.lblRxDose.SetLabel('-') ############################## Structure Functions ############################# def OnStructureCheck(self, msg): """Load the properties of the currently checked structures.""" structure = msg.data # Get the structure number id = structure['data']['id'] structure['data']['color'] = structure['color'].Get() # Make sure that the volume has been calculated for each structure # before setting it if not self.structures[id].has_key('volume'): # Use the volume units from the DVH if they are absolute volume if self.dvhs.has_key(id) and (self.dvhs[id]['volumeunits'] == 'CM3'): self.structures[id]['volume'] = self.dvhs[id]['data'][0] # Otherwise calculate the volume from the structure data else: self.structures[id]['volume'] = dvhdata.CalculateVolume( self.structures[id]) structure['data']['volume'] = self.structures[id]['volume'] self.structureList[id] = structure['data'] # Populate the structure choice box with the checked structures self.choiceStructure.Enable() i = self.choiceStructure.Append(structure['data']['name']) self.choiceStructure.SetClientData(i, id) # Select the first structure self.OnStructureSelect() pub.sendMessage('structures.checked', self.structureList) def OnStructureUncheck(self, msg): """Remove the unchecked structures.""" structure = msg.data # Get the structure number id = structure['data']['id'] # Remove the structure from the structure list if self.structureList.has_key(id): del self.structureList[id] # Remove the structure from the structure choice box for n in range(self.choiceStructure.GetCount()): if (id == self.choiceStructure.GetClientData(n)): # Save if the currently selected item's position currSelection = self.choiceStructure.GetSelection() self.choiceStructure.Delete(n) break # If the currently selected item will be deleted, # select the last item instead if (n == currSelection): if (self.choiceStructure.GetCount() >= 1): self.OnStructureSelect() # Disable the control if it is the last item if (self.choiceStructure.GetCount() == 0): self.choiceStructure.Enable(False) self.OnStructureUnselect() pub.sendMessage('structures.checked', self.structureList) def OnStructureSelect(self, evt=None): """Load the properties of the currently selected structure.""" if (evt == None): self.choiceStructure.SetSelection(0) choiceItem = 0 else: choiceItem = evt.GetInt() # Load the structure id chosen from the choice control id = self.choiceStructure.GetClientData(choiceItem) pub.sendMessage('structure.selected', {'id': id}) self.lblStructureVolume.SetLabel( str(self.structures[id]['volume'])[0:7]) # make sure that the dvh has been calculated for each structure # before setting it if self.dvhs.has_key(id): self.lblStructureMinDose.SetLabel("%.3f" % self.dvhs[id]['min']) self.lblStructureMaxDose.SetLabel("%.3f" % self.dvhs[id]['max']) self.lblStructureMeanDose.SetLabel("%.3f" % self.dvhs[id]['mean']) else: self.lblStructureMinDose.SetLabel('-') self.lblStructureMaxDose.SetLabel('-') self.lblStructureMeanDose.SetLabel('-') def OnStructureUnselect(self): """Clear the properties of the selected structure.""" pub.sendMessage('structures.selected', {'id': None}) self.lblStructureVolume.SetLabel('-') self.lblStructureMinDose.SetLabel('-') self.lblStructureMaxDose.SetLabel('-') self.lblStructureMeanDose.SetLabel('-') ############################### Isodose Functions ############################## def OnIsodoseCheck(self, msg): """Load the properties of the currently checked isodoses.""" isodose = msg.data self.isodoseList[isodose['data']['level']] = isodose pub.sendMessage('isodoses.checked', self.isodoseList) def OnIsodoseUncheck(self, msg): """Remove the unchecked isodoses.""" isodose = msg.data id = isodose['data']['level'] # Remove the isodose from the isodose list if self.isodoseList.has_key(id): del self.isodoseList[id] pub.sendMessage('isodoses.checked', self.isodoseList) ################################ Other Functions ############################### def InstallThreadExcepthook(self): """Workaround for sys.excepthook thread bug from Jonathan Ellis (http://bugs.python.org/issue1230540). Call once from __main__ before creating any threads. If using psyco, call psyco.cannotcompile(threading.Thread.run) since this replaces a new-style class method.""" run_old = threading.Thread.run def Run(*args, **kwargs): try: run_old(*args, **kwargs) except (KeyboardInterrupt, SystemExit): raise except: sys.excepthook(*sys.exc_info()) threading.Thread.run = Run def OnUpdatePreferences(self, msg): """When the preferences change, update the values.""" if (msg.topic[1] == 'calculation') and (msg.topic[2] == 'dvh_recalc'): self.dvhRecalc = msg.data elif (msg.topic[1] == 'advanced') and \ (msg.topic[2] == 'detailed_logging'): # Enable logging at the debug level if the value is set if msg.data: self.fh.setLevel(logging.DEBUG) if not util.main_is_frozen(): self.ch.setLevel(logging.DEBUG) else: self.fh.setLevel(logging.WARNING) if not util.main_is_frozen(): self.ch.setLevel(logging.WARNING) elif (msg.topic[1] == 'plugins') and (msg.topic[2] == 'disabled_list'): self.pluginsDisabled = msg.data elif (msg.topic[1] == 'window'): if msg.topic[2] == 'maximized': self.Maximize(msg.data) elif msg.topic[2] == 'size': if not self.IsMaximized(): self.SetSize(tuple(msg.data)) elif msg.topic[2] == 'position': if not self.IsMaximized(): self.SetPosition(tuple(msg.data)) def OnUpdatePlugins(self, msg): """Update the location of the user plugins and load all plugins.""" self.userpluginpath = msg.data # Load the plugins only if they haven't been loaded previously if not len(self.plugins): self.plugins = plugin.import_plugins(self.userpluginpath) self.menuDict = {} self.menuImportDict = {} self.menuExportDict = {} # Set up the import plugins for dicompyler for i, pg in enumerate(self.plugins): # Skip plugin if it doesn't contain the required dictionary # or actually is a proper Python module or is disabled p = pg['plugin'] if not hasattr(p, 'pluginProperties') or \ (p.__name__ in self.pluginsDisabled): continue props = p.pluginProperties() # Only load plugin versions that are qualified if ((props['plugin_version'] == 1) and (props['plugin_type'] == 'import')): self.menuImportItem.Enable(True) # Load the import menu plugins if not len(self.menuImportDict): self.menuImportItem.Enable(True) self.menuImportDict[300 + i] = self.menuImport.Append( 300 + i, props['menuname']) pi = p.plugin(self) self.Bind(wx.EVT_MENU, pi.pluginMenu, id=300 + i) # If the import plugin has toolbar items, display them if hasattr(pi, 'tools'): for t, tool in enumerate(pi.tools): self.maintools.append(tool) self.toolbar.AddLabelTool( (300 + i) * 10 + t, tool['label'], tool['bmp'], shortHelp=tool['shortHelp']) self.Bind(wx.EVT_TOOL, tool['eventhandler'], id=(300 + i) * 10 + t) self.toolbar.Realize() def OnUpdateStatusBar(self, msg): """Update the status bar text.""" for k, v in msg.data.iteritems(): self.sb.SetStatusText(unicode(v), k) def OnPageChanged(self, evt): """Notify each notebook tab whether it has the focus or not.""" # Determine the new tab new = evt.GetSelection() page = self.notebook.GetPage(new) # Notify the new tab that it has focus if hasattr(page, 'OnFocus'): page.OnFocus() # If the tab has toolbar items, display them if hasattr(page, 'tools'): for t, tool in enumerate(page.tools): self.toolbar.AddLabelTool((new + 1) * 10 + t, tool['label'], tool['bmp'], shortHelp=tool['shortHelp']) self.Bind(wx.EVT_TOOL, tool['eventhandler'], id=(new + 1) * 10 + t) self.toolbar.Realize() # For all other tabs, notify that they don't have focus anymore for i in range(self.notebook.GetPageCount()): if not (new == i): page = self.notebook.GetPage(i) if hasattr(page, 'OnUnfocus'): page.OnUnfocus() # Delete all other toolbar items if hasattr(page, 'tools'): for t, tool in enumerate(page.tools): self.toolbar.DeleteTool((i + 1) * 10 + t) evt.Skip() def OnKeyDown(self, evt): """Capture the keypress when the notebook tab is focused. Currently this is used to workaround a bug in Windows since the notebook tab instead of the panel receives focus.""" if guiutil.IsMSWindows(): pub.sendMessage('main.key_down', evt) def OnMouseWheel(self, evt): """Capture the mousewheel event when the notebook tab is focused. Currently this is used to workaround a bug in Windows since the notebook tab instead of the panel receives focus.""" if guiutil.IsMSWindows(): pub.sendMessage('main.mousewheel', evt) def OnPreferences(self, evt): """Load and show the Preferences dialog box.""" self.prefmgr.Show() def OnShowLogs(self, evt): """Open and display the logs folder.""" util.open_path(os.path.join(guiutil.get_data_dir(), 'logs')) def OnPluginManager(self, evt): """Load and show the Plugin Manager dialog box.""" self.pm = plugin.PluginManager(self, self.plugins, self.pluginsDisabled) def OnAbout(self, evt): # First we create and fill the info object info = wx.AboutDialogInfo() info.Name = "dicompyler" info.Version = __version__ info.Copyright = u"© 2009-2014 Aditya Panchal" credits = util.get_credits() info.Developers = credits['developers'] info.Artists = credits['artists'] desc = "Extensible radiation therapy research platform and viewer for DICOM and DICOM RT." + \ "\n\ndicompyler is released under a BSD license.\n" + \ "See the Help menu for license information." info.Description = desc if guiutil.IsGtk(): info.WebSite = "http://code.google.com/p/dicompyler/" # Then we call wx.AboutBox giving it that info object wx.AboutBox(info) def OnHomepage(self, evt): """Show the homepage for dicompyler.""" webbrowser.open_new_tab("http://code.google.com/p/dicompyler/") def OnLicense(self, evt): """Show the license document in a new dialog.""" f = open(util.get_text_resources("license.txt"), "rU") msg = f.read() f.close() if guiutil.IsMSWindows(): dlg = wx.lib.dialogs.ScrolledMessageDialog(self, msg, "dicompyler License") else: dlg = wx.lib.dialogs.ScrolledMessageDialog(self, msg, "dicompyler License", size=(650, 550)) dlg.ShowModal() dlg.Destroy() def OnActivate(self, evt): """Show the import dialog if command-line arguments have been passed.""" # Check if a folder is provided via command-line arguments if (len(sys.argv) == 2): path = sys.argv[1] if not os.path.isdir(path): path = os.path.split(path)[0] pub.sendMessage('preferences.updated.value', {'general.dicom.import_location': path}) sys.argv.pop() self.OnOpenPatient(None) evt.Skip() def OnClose(self, _): pub.sendMessage('preferences.updated.value', {'general.window.maximized': self.IsMaximized()}) if not self.IsMaximized(): pub.sendMessage('preferences.updated.value', {'general.window.size': tuple(self.GetSize())}) pub.sendMessage( 'preferences.updated.value', {'general.window.position': tuple(self.GetPosition())}) self.Destroy()
class PngScraperFrame(wx.Frame): """ Frame for browsing through a list/directory of images, running a script on each. """ def __init__(self, *args, **kwargs): wx.Frame.__init__(self, *args, **kwargs) self.im: Image.Image = None self.pngs = [] self.cursor = Cursor(ListView(self.pngs)) self.coords: wx.TextCtrl = None self.color: wx.TextCtrl = None self.scroll: wx.ScrolledWindow = None self.image: wx.Image = None self.which: wx.TextCtrl = None self.locals = {} self.shell: py.shell.Shell = None def init(self): self.coords = XRCCTRL(self, 'txt_coords') self.color = XRCCTRL(self, 'txt_color') self.scroll = XRCCTRL(self, 'box_scroll') self.image = XRCCTRL(self, 'bmp_image') self.image.Bind(wx.EVT_LEFT_DOWN, self.on_left_down) self.image.Bind(wx.EVT_RIGHT_DOWN, self.on_right_down) #self.update_image() self.which = XRCCTRL(self, 'txt_which') self.update_which() # button handlers for btn in 'btn_first btn_prev btn_next btn_last'.split(): self.Bind(wx.EVT_BUTTON, self.on_cursor_button, id=XRCID(btn)) # menu handlers self.Bind(wx.EVT_MENU, self.on_exit, id=XRCID('cmd_exit')) self.Bind(wx.EVT_MENU, self.on_open_file, id=XRCID('cmd_open_file')) self.Bind(wx.EVT_MENU, self.on_open_dir, id=XRCID('cmd_open_dir')) self.Bind(wx.EVT_CLOSE, self.on_close_window) # -- manually add pyshell panel = XRCCTRL(self, 'shell_panel') self.locals = { 'self': self, 'wx': wx, 'hook': lambda: None, 'gc': self.get_dc() } self.shell = py.shell.Shell(panel, locals=self.locals) sizer = wx.BoxSizer() sizer.Add(self.shell, 4, wx.EXPAND) self.shell.SetFocus() panel.SetSizer(sizer) sizer.Fit(panel) def on_cursor_button(self, evt): button_map = { "<<": "moveToStart", "<": "movePrevious", ">": "moveNext", ">>": "moveToEnd" } try: getattr(self.cursor, button_map[evt.GetEventObject().Label])() self.update_which() self.update_image() self.shell.run('hook()') except StopIteration: pass def on_open_file(self, _evt): dlg = wx.FileDialog(self, message="Choose a file", defaultDir=os.getcwd(), defaultFile="", wildcard='*.png', style=wx.FD_OPEN | wx.FD_CHANGE_DIR) if dlg.ShowModal() == wx.ID_OK: path = dlg.GetPath() self.pngs.insert(self.cursor.position, path) self.update_which() self.update_image() def on_open_dir(self, _evt): dlg = wx.DirDialog(self, "Choose a directory:", defaultPath=os.getcwd(), style=wx.DD_DEFAULT_STYLE | wx.DD_NEW_DIR_BUTTON) if dlg.ShowModal() == wx.ID_OK: path = dlg.GetPath() while self.pngs: self.pngs.pop() self.pngs.extend(png_list(path)) self.cursor.moveToStart() self.update_which() self.update_image() self.shell.run('hook()') def on_exit(self, _evt): self.Close() def update_which(self): self.which.Value = "%s/%s" % (self.cursor.position, len(self.pngs)) def update_image(self): if len(self.pngs): # set the visible bitmap img = wx.Image(self.pngs[self.cursor.position], wx.BITMAP_TYPE_PNG) self.image.SetBitmap(wx.Bitmap(img)) self.scroll.SetVirtualSize(self.image.Size) # and make the pil image self.im = Image.new('RGB', tuple(self.image.Size)) self.im.frombytes( bytes(self.image.GetBitmap().ConvertToImage().GetData())) def on_right_down(self, _e): self.image.Refresh() def on_left_down(self, e): point = (e.X, e.Y) self.coords.Value = "(%s,%s)" % point self.color.Value = "0x%s" % "".join( hex(v)[2:].upper() for v in self.im.getpixel(point)) # draw crosshairs: dc = wx.ClientDC(self.image) dc.SetPen(wx.Pen("RED")) dc.DrawLine(e.X - 2, e.Y, e.X, e.Y) dc.DrawLine(e.X, e.Y - 2, e.X, e.Y) dc.DrawLine(e.X + 2, e.Y, e.X, e.Y) dc.DrawLine(e.X, e.Y + 2, e.X, e.Y) def get_dc(self): """return the client drawing context""" gc = wx.GCDC(wx.ClientDC(self.image)) ink = wx.Colour(0x99, 0xcc, 0xff, 0x88) gc.SetPen(wx.Pen(ink)) gc.SetBrush(wx.Brush(ink)) return gc def draw_words(self): # @TODO: re-enable draw_words. it was cool. :) raise NotImplementedError # chars = self.chars() # inks = wx.Color(0x99, 0xcc, 0xff, 0x88) , wx.Color(0x99, 0xff, 0xcc, 0x88) # # gc = self.getGC() # # i = 0 # for c in chars: # if c[4] not in ('', ' '): # # ink = inks[i % 2] # i += 1 # # gc.SetPen(wx.Pen(ink)) # gc.SetBrush(wx.Brush(ink)) # # gc.DrawRectangle(*c[:4]) # def drawFirstUnkowns(self, cutoff=200, mode='L'): # "I *THINK* this was to show a new char to learn in context." # chars = self.chars(mode, cutoff) # # seen = {} # # gc = self.getGC() # ink = wx.Color(0xff, 0x00, 0x00, 0x88) # gc.SetPen(wx.Pen(ink)) # gc.SetBrush(wx.Brush(ink)) # # i = 0 # for c in chars: # if type(c[4]) in (int,long): # if c[4] not in seen: # gc.DrawRectangle(*c[:4]) # seen[c[4]]=True # # def chars(self, mode='L', cutoff=200): # return list(scrape.letters(self.im.convert(mode), cutoff)) def draw_baselines(self, baseline_color='#99CCFF', linegap_color='#eeeeee'): img_out = self.im dc = self.get_dc() y = 0 w, h = img_out.size for (top, base, bottom) in scrape.guess_lines(img_out): # draw the baseline dc.SetPen(wx.Pen(baseline_color)) if not base: base = bottom - 2 dc.SetPen(wx.RED_PEN) dc.DrawLines([(0, base), (w, base)]) # shade out the other stuff dc.SetPen(wx.Pen(linegap_color)) dc.SetBrush(wx.Brush(linegap_color)) dc.DrawRectangle(0, y, w, top - y) y = bottom # shade bottom area dc.DrawRectangle(0, y, w, h - y) def on_close_window(self, _evt): self.Destroy()
class EditorFrame(wx.Frame): """ A wx.Panel for displaying and editing Categories """ def __init__(self, parent, block_store, id=wx.ID_ANY, style=wx.EXPAND): #load from XRC, need to use two-stage create res = gui.XrcUtilities.XmlResource('./gui/xrc/TpclEditorFrame.xrc') pre = wx.PreFrame() res.LoadOnFrame(pre, parent, "editor") self.PostCreate(pre) self.block_store = block_store self.OnCreate() def OnCreate(self): self.SetSize((600, 400)) #widgets self.code_stc = XRCCTRL(self, "code_stc") self.code_stc.Bind(wx.EVT_LEFT_UP, self.ContextMenuHandler) self.block_tree = XRCCTRL(self, "block_tree") self.block_tree.SetBlockstore(self.block_store) self.preview_ctrl = XRCCTRL(self, "preview_ctrl") self.block_tree.Bind(wx.EVT_TREE_SEL_CHANGED, self.OnSelectionChanged) #buttons self.clear_button = XRCCTRL(self, "clear_button") self.Bind(wx.EVT_BUTTON, self.OnClear, self.clear_button) self.remove_button = XRCCTRL(self, "remove_button") self.Bind(wx.EVT_BUTTON, self.OnRemove, self.remove_button) self.save_button = XRCCTRL(self, "save_button") self.Bind(wx.EVT_BUTTON, self.OnSave, self.save_button) self.info_button = XRCCTRL(self, "info_button") self.Bind(wx.EVT_BUTTON, self.OnInfo, self.info_button) #fill the block_tree #tpcl.IO.LoadBlockIntoTree(self.block_tree) self.root_expression = None self.CreateQuickInsertMenu() def CreateQuickInsertMenu(self): self.quick_insert_menu = wx.Menu() submenus = [ "Procedure Definitions", "Literal Expression", "Numerical Functions", "Flow Control" ] for sm in submenus: cat_id = self.block_store.FindCategory(sm) smenu = wx.Menu() for child_id in self.block_store.GetChildBlocks(cat_id): node = self.block_store.GetNode(child_id) mitem = smenu.Append(wx.ID_ANY, node.name) self.Bind(wx.EVT_MENU, MakeInserter(self, node.block), mitem) self.quick_insert_menu.AppendMenu(wx.ID_ANY, sm, smenu) def OnInfo(self, event): """\ Opens a dialog without a parent for the moment """ sel_id = self.block_tree.GetSelection() if sel_id.IsOk(): block = self.block_tree.GetPyData(sel_id) if block: info_dialog = BlockInfoDialog(self, block.name, block.display, block.description) info_dialog.ShowModal() event.Skip() def OnClear(self, event): """\ Clears the code window, deleting all unsaved changes. """ self.code_stc.ClearAll() #for now we'll put the root expression back in place self.root_expression = None event.Skip() def OnRemove(self, event): """\ Removes the current tpcl code block that is selected. """ pos = self.code_stc.GetCurrentPos() try: self.root_expression.RemoveExpression(pos) self.code_stc.SetText(str(self.root_expression)) except ValueError: #text in top level expression, get rid of it self.root_expression = None self.code_stc.ClearAll() event.Skip() def OnSave(self, event): """\ Saves the current work. """ event.Skip() def OnSelectionChanged(self, event): """\ Selection change in the block tree We need to fill the preview control here """ print "Handling selection changed event." sel_id = self.block_tree.GetSelection() if sel_id.IsOk(): block = self.block_tree.GetPyData(sel_id) if block: self.preview_ctrl.SetValue(block.display) else: self.preview_ctrl.SetValue("") else: self.preview_ctrl.SetValue("") event.Skip() def OnInsert(self, event, block=None): """\ Inserts the currently selected code block into the currently selected code socket. """ pos = self.code_stc.GetCurrentPos() if not block: sel_id = self.block_tree.GetSelection() if sel_id.IsOk(): block = self.block_tree.GetPyData(sel_id) if block: print "Trying to insert block..." try: #provide the OnInsert function of the block # access to us as a parent frame for when they # need to show a dialog parent_frame = self expression = TpclExpression(block) insert_ok = True if block.on_insert: print "Trying to use OnInsert function of block" exec(block.on_insert) insert_ok = OnInsert(expression) if insert_ok: if not self.root_expression: self.root_expression = expression else: self.root_expression.InsertExpression(pos, expression) self.code_stc.SetText(str(self.root_expression)) except ValueError: print "Tried to insert in a place where there's no expansion point" event.Skip() def ContextMenuHandler(self, event): """\ Processes a left click on the STC """ event.Skip() print "Trying to show context menu at pos:", self.code_stc.GetCurrentPos( ) try: if wx.GetMouseState().ControlDown(): print "Trying to show popup menu..." menu = wx.Menu() offset = self.code_stc.GetCurrentPos() if not self.root_expression: is_insertion_point = True is_added_insertion_point = False is_expansion_point = False else: is_insertion_point = self.root_expression.IsInsertionPoint( offset)[0] is_added_insertion_point = self.root_expression.IsAddedInsertionPoint( offset)[0] is_expansion_point = self.root_expression.IsExpansionPoint( offset)[0] print "is_insertion_point", is_insertion_point print "is_expansion_point", is_expansion_point sel_id = self.block_tree.GetSelection() if sel_id.IsOk(): block = self.block_tree.GetPyData(sel_id) else: block = None #insert item insert_item = menu.Append(wx.ID_ANY, "Insert") self.Bind(wx.EVT_MENU, self.OnInsert, insert_item) #only enable if we're on an expansion point insert_item.Enable(block != None and is_insertion_point) #quick insert quick_insert = menu.AppendMenu(wx.ID_ANY, "Quick Insert...", self.quick_insert_menu) quick_insert.Enable(is_insertion_point) #remove item remove_item = menu.Append(wx.ID_ANY, "Remove") self.Bind(wx.EVT_MENU, self.OnRemove, remove_item) remove_item.Enable( (not is_insertion_point and not is_expansion_point) or is_added_insertion_point) #check for expansion menu if is_expansion_point: exp_menu = wx.Menu() i = 0 for option in self.root_expression.GetExpansionOptions( offset): opt_item = exp_menu.Append(wx.ID_ANY, option) self.Bind(wx.EVT_MENU, MakeExpander(self, offset, i), opt_item) i += 1 menu.AppendMenu(wx.ID_ANY, "Expansion Point...", exp_menu) self.code_stc.PopupMenu(menu, event.GetPosition()) except ValueError: print "Index out of range..." def ShowModal(self): print "TPCLEE Showing itself Modally" self.MakeModal(True) self.old_top_window = wx.GetApp().GetTopWindow() wx.GetApp().SetTopWindow(self) self.Bind(wx.EVT_CLOSE, self.OnClose) self.Show(True) def OnClose(self, event): self.MakeModal(False) self.Unbind(wx.EVT_CLOSE) wx.GetApp().SetTopWindow(self.old_top_window) event.Skip()
class TripVoiDialog(wx.Dialog): def __init__(self): pre = wx.PreDialog() self.PostCreate(pre) pub.subscribe(self.patient_data_updated, "patient.loaded") pub.sendMessage("patient.request", {}) def patient_data_updated(self, msg): self.data = msg.data def select_drop_by_value(self, drop, value): for i, item in enumerate(drop.GetItems()): if item == value: drop.SetSelection(i) def Init(self, voi): self.voi = voi wx.EVT_BUTTON(self, XRCID('btn_ok'), self.save_and_close) wx.EVT_BUTTON(self, XRCID('btn_close'), self.close) self.label_name = XRCCTRL(self, "label_name") self.label_name.SetLabel(voi.get_name()) self.txt_dose = XRCCTRL(self, "txt_dose") self.txt_dose.SetValue("%.2f" % (voi.get_dose())) self.check_target = XRCCTRL(self, "check_target") self.check_target.SetValue(voi.is_target()) self.check_target.Bind(wx.EVT_CHECKBOX, self.on_check_target_changed) self.check_oar = XRCCTRL(self, "check_oar") self.check_oar.SetValue(voi.is_oar()) self.check_oar.Bind(wx.EVT_CHECKBOX, self.on_check_oar_changed) self.txt_max_dose_fraction = XRCCTRL(self, "txt_max_dose_fraction") self.txt_max_dose_fraction.SetValue("%.2f" % (voi.get_max_dose_fraction())) self.txt_max_dose_fraction.Enable(False) self.txt_dose.Enable(False) if voi.is_target(): self.check_oar.Enable(False) self.txt_dose.Enable(True) if voi.is_oar(): self.txt_max_dose_fraction.Enable(True) self.check_target.Enable(False) self.txt_hu_value = XRCCTRL(self, "txt_hu_value") self.txt_hu_offset = XRCCTRL(self, "txt_hu_offset") if not voi.get_hu_value() is None: self.txt_hu_value.SetValue("%d" % voi.get_hu_value()) if not voi.get_hu_offset() is None: self.txt_hu_offset.SetValue("%d" % voi.get_hu_offset()) self.drop_projectile = XRCCTRL(self, "drop_projectile") self.drop_projectile.Append("H") self.drop_projectile.Append("C") self.txt_dose_percent = XRCCTRL(self, "txt_dose_percent") wx.EVT_BUTTON(self, XRCID('btn_set_dosepercent'), self.set_dose_percent) wx.EVT_CHOICE(self, XRCID('drop_projectile'), self.on_projectile_changed) def on_projectile_changed(self, evt): projectile = self.drop_projectile.GetStringSelection() dose_percent = self.voi.get_dose_percent(projectile) if dose_percent is None: self.txt_dose_percent.SetValue("") else: self.txt_dose_percent.SetValue("%d" % dose_percent) def set_dose_percent(self, evt): if not self.drop_projectile.GetStringSelection() == "": self.voi.set_dose_percent(self.drop_projectile.GetStringSelection(), self.txt_dose_percent.GetValue()) def on_check_target_changed(self, evt): if evt.Checked(): self.check_oar.Enable(False) self.txt_dose.Enable(True) else: self.check_oar.Enable(True) self.txt_dose.Enable(False) def on_check_oar_changed(self, evt): if evt.Checked(): self.txt_max_dose_fraction.Enable(True) self.check_target.Enable(False) else: self.check_target.Enable(True) self.txt_max_dose_fraction.Enable(False) def save_and_close(self, evt): voi = self.voi voi.set_dose(self.txt_dose.GetValue()) if voi.is_target() is not self.check_target.IsChecked(): voi.toogle_target() if voi.is_oar() is not self.check_oar.IsChecked(): voi.toogle_oar() voi.set_max_dose_fraction(self.txt_max_dose_fraction.GetValue()) voi.set_hu_offset(self.txt_hu_offset.GetValue()) voi.set_hu_value(self.txt_hu_value.GetValue()) self.Close() def close(self, evt): self.Close()
class Panel(ObjectPanel.Panel): """ A wx.Panel for displaying and editing Components """ def __init__(self, parent, id=wx.ID_ANY, style=wx.EXPAND): #load from XRC, need to use two-stage create pre = wx.PrePanel() res = gui.XrcUtilities.XmlResource('./gui/xrc/ComponentPanel.xrc') res.LoadOnPanel(pre, parent, "ComponentPanel") self.PostCreate(pre) ObjectPanel.Panel.Setup(self) self.OnCreate() def OnCreate(self): self.name_field = XRCCTRL(self, "name_field") self.desc_field = XRCCTRL(self, "desc_field") self.tpcl_req_stc = XRCCTRL(self, "tpcl_req_stc") self.cat_choice = XRCCTRL(self, "cat_choice") self.prop_list = XRCCTRL(self, "prop_list") self.tpcl_cost_stc = XRCCTRL(self, "tpcl_cost_stc") self.tpcl_cost_stc.Enable(False) add_button = XRCCTRL(self, "add_button") self.Bind(wx.EVT_BUTTON, self.OnAddProperty, add_button) remove_button = XRCCTRL(self, "remove_button") self.Bind(wx.EVT_BUTTON, self.OnRemoveProperty, remove_button) #bind event handlers self.desc_field.Bind(wx.EVT_TEXT, self.CreateAttributeMonitor('description')) self.tpcl_req_stc.Bind(wx.stc.EVT_STC_CHANGE, self.CreateAttributeMonitor('tpcl_requirements')) self.tpcl_cost_stc.Bind(wx.stc.EVT_STC_CHANGE, self.OnCostEdit) self.filling_tpcl_cost = False self.Bind(wx.EVT_LISTBOX, self.OnPropListSelect, self.prop_list) self.cat_choice.Bind(wx.EVT_CHECKLISTBOX, self.CreateAttributeMonitor('categories')) #self.BindEditWatchers([self.desc_field, self.tpcl_req_stc]) self.loaded = False def LoadObject(self, node): print "ComponentPanel loading %s" % node.name #VERY IMPORTANT TO MARK OURSELVES AS LOADING HERE # THIS WAY WE AVOID PROGRAMMATIC CHANGES BEING MARKED AS USER CHANGES self.loading = True self.node = node self.object = node.GetObject() print "\tErrors:", self.object.errors self.node.visible = True self.name_field.SetLabel(str(self.object.name)) self.desc_field.SetValue(str(self.object.description)) if self.object.errors.has_key('description'): print "Error in description!" self.SetErrorLabel('description', self.object.errors['description']) self.tpcl_req_stc.SetText(self.object.tpcl_requirements) if self.object.errors.has_key('tpcl_requirements'): print "Error in tpcl_requirements!" self.SetErrorLabel('tpcl_requirements', self.object.errors['tpcl_requirements']) self.filling_tpcl_cost = True self.tpcl_cost_stc.SetText("") self.filling_tpcl_cost = False self.tpcl_cost_stc.Enable(False) if self.object.errors.has_key('properties'): print "Error in properties!" self.SetErrorLabel('properties', self.object.errors['properties']) #fill the category choice box self.cat_choice.Clear() for catnode in self.node.object_database.getObjectsOfType('Category'): idx = self.cat_choice.Append(catnode.name) if catnode.name in self.object.categories: self.cat_choice.Check(idx) if self.object.errors.has_key('categories'): print "Error in categories!" self.SetErrorLabel('categories', self.object.errors['categories']) #create the property list self.prop_sel = -1 prop_names = [pname for pname in self.object.properties.keys()] self.prop_list.Set(prop_names) self.node.object_database.Emphasize(prop_names, "BLUE") self.loaded = True self.loading = False self.Show() return self def OnDClickProperty(self, event): """\ Should open a window to edit the TPCL cost function here. """ pass def OnPropListSelect(self, event): sel_idx = self.prop_list.GetSelection() if sel_idx != wx.NOT_FOUND: self.tpcl_cost_stc.Enable(True) self.prop_sel = sel_idx self.filling_tpcl_cost = True self.tpcl_cost_stc.SetText(self.object.properties[ self.prop_list.GetString(sel_idx)]) self.filling_tpcl_cost = False def OnCostEdit(self, event): """\ Saves changes made to the TPCL cost functions """ print "Handling a cost edit event!" idx = self.prop_list.GetSelection() if idx == wx.NOT_FOUND or self.filling_tpcl_cost: pass else: self.object.properties[self.prop_list.GetString(idx)] = \ self.tpcl_cost_stc.GetText() self.node.SetModified(True) event.Skip() def OnAddProperty(self, event): print "On Add Property" loose_props = filter(lambda x: not x in self.object.properties.keys(), [n.name for n in self.node.object_database.getObjectsOfType('Property')]) choice_diag = wx.MultiChoiceDialog(self, "Choose the Properties to add...", "Add Properties...", loose_props) choice_diag.ShowModal() if len(choice_diag.GetSelections()) > 0: print "Selection OK" prop_names = [] for i in choice_diag.GetSelections(): print "\t" + loose_props[i] prop_names.append(loose_props[i]) self.object.properties[loose_props[i]] = "(lambda (design) #)" self.prop_list.Append(loose_props[i]) self.node.SetModified(True) self.node.object_database.Emphasize(prop_names, "BLUE") else: #cancelled print "CANCELED!" print "Selections: ", choice_diag.GetSelections() pass choice_diag.Destroy() def OnRemoveProperty(self, event): #remove the selected properties if self.prop_list.GetSelections() != []: ridx = [] prop_names = [] for idx in self.prop_list.GetSelections(): prop_name = self.prop_list.GetString(idx) prop_names.append(prop_name) del self.object.properties[prop_name] ridx.insert(0, idx) for i in ridx: self.prop_list.Delete(i) self.node.object_database.UnEmphasize(prop_names) self.tpcl_cost_stc.SetText("") self.node.SetModified(True) def Destroy(self): self.Hide() def ReallyDestroy(self): wx.Panel.Destroy(self) def cleanup(self): print "Cleaning up Component Panel" self.CleanupErrorLabels() self.node.object_database.UnEmphasize( [self.prop_list.GetString(i) for i in range(0, self.prop_list.GetCount())]) self.node.visible = False self.Hide() self.node.ClearObject() self.object = None self.loaded = False
class FieldDialog(wx.Dialog): def __init__(self): pre = wx.PreDialog() self.PostCreate(pre) def Init(self, field): self.field = field self.btn_ok = XRCCTRL(self, 'btn_ok') wx.EVT_BUTTON(self, XRCID('btn_ok'), self.save_and_close) self.btn_cancel = XRCCTRL(self, 'btn_close') wx.EVT_BUTTON(self, XRCID('btn_close'), self.close) self.label_fieldname = XRCCTRL(self, 'label_fieldname') self.label_fieldname.SetLabel(field.get_name()) self.check_isocenter = XRCCTRL(self, 'check_isocenter') target = field.get_target() if len(target) > 0: self.check_isocenter.SetValue(True) self.check_isocenter.Bind(wx.EVT_CHECKBOX, self.on_check_isocenter_changed) self.txt_targetx = XRCCTRL(self, 'txt_targetx') self.txt_targety = XRCCTRL(self, 'txt_targety') self.txt_targetz = XRCCTRL(self, 'txt_targetz') if len(target) > 0: self.txt_targetx.SetValue("%.2f" % (target[0])) self.txt_targety.SetValue("%.2f" % (target[1])) self.txt_targetz.SetValue("%.2f" % (target[2])) else: self.txt_targetx.Enable(False) self.txt_targety.Enable(False) self.txt_targetz.Enable(False) self.txt_gantry = XRCCTRL(self, 'txt_gantry') self.txt_gantry.SetValue("%.2f" % field.get_gantry()) self.txt_couch = XRCCTRL(self, 'txt_couch') self.txt_couch.SetValue("%.2f" % field.get_couch()) self.txt_fwhm = XRCCTRL(self, 'txt_fwhm') self.txt_fwhm.SetValue("%.2f" % field.get_fwhm()) self.txt_zsteps = XRCCTRL(self, 'txt_zsteps') self.txt_zsteps.SetValue("%.2f" % field.get_zsteps()) self.txt_doseextension = XRCCTRL(self, 'txt_doseext') self.txt_doseextension.SetValue("%.2f" % field.get_doseextension()) self.txt_contourextension = XRCCTRL(self, 'txt_contourext') self.txt_contourextension.SetValue("%.2f" % field.get_contourextension()) self.txt_raster1 = XRCCTRL(self, 'txt_raster1') self.txt_raster2 = XRCCTRL(self, 'txt_raster2') raster = field.get_rasterstep() self.txt_raster1.SetValue("%.2f" % raster[0]) self.txt_raster2.SetValue("%.2f" % raster[1]) self.drop_projectile = XRCCTRL(self, 'drop_projectile') self.drop_projectile.SetSelection( self.drop_projectile.GetItems().index(field.projectile)) def on_check_isocenter_changed(self, evt): if self.check_isocenter.IsChecked(): self.txt_targetx.Enable(True) self.txt_targety.Enable(True) self.txt_targetz.Enable(True) else: self.txt_targetx.Enable(False) self.txt_targety.Enable(False) self.txt_targetz.Enable(False) def save_and_close(self, evt): self.field.set_couch(self.txt_couch.GetValue()) self.field.set_gantry(self.txt_gantry.GetValue()) self.field.set_fwhm(self.txt_fwhm.GetValue()) if self.check_isocenter.IsChecked(): self.field.set_target(self.txt_targetx.GetValue() + "," + self.txt_targety.GetValue() + "," + self.txt_targetz.GetValue()) else: self.field.set_target("") self.field.set_zsteps(self.txt_zsteps.GetValue()) self.field.set_doseextension(self.txt_doseextension.GetValue()) self.field.set_contourextension(self.txt_contourextension.GetValue()) self.field.set_rasterstep(self.txt_raster1.GetValue(), self.txt_raster2.GetValue()) self.field.set_projectile(self.drop_projectile.GetStringSelection()) self.Close() def close(self, evt): self.Close()