/usr/share/dicompyler/baseplugins/dvh.py is in dicompyler 0.4~a2-1.
This file is owned by root:root, with mode 0o755.
The actual contents of the file can be viewed below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 | #!/usr/bin/env python
# -*- coding: ISO-8859-1 -*-
# dvh.py
"""dicompyler plugin that displays a dose volume histogram (DVH)
with adjustable constraints via wxPython and matplotlib."""
# Copyright (c) 2009-2010 Aditya Panchal
# This file is part of dicompyler, relased under a BSD license.
# See the file license.txt included with this distribution, also
# available at http://code.google.com/p/dicompyler/
#
# It is assumed that the reference (prescription) dose is in cGy.
import wx
from wx.xrc import XmlResource, XRCCTRL, XRCID
from wx.lib.pubsub import Publisher as pub
import guiutil, util
import wxmpl
import numpy as np
import dvhdata, guidvh
def pluginProperties():
"""Properties of the plugin."""
props = {}
props['name'] = 'DVH'
props['description'] = "Display and evaluate dose volume histogram (DVH) data"
props['author'] = 'Aditya Panchal'
props['version'] = 0.3
props['plugin_type'] = 'main'
props['plugin_version'] = 1
props['min_dicom'] = ['rtss', 'rtdose']
props['recommended_dicom'] = ['rtss', 'rtdose', 'rtplan']
return props
def pluginLoader(parent):
"""Function to load the plugin."""
# Load the XRC file for our gui resources
res = XmlResource(util.GetBasePluginsPath('dvh.xrc'))
panelDVH = res.LoadPanel(parent, 'pluginDVH')
panelDVH.Init(res)
return panelDVH
class pluginDVH(wx.Panel):
"""Plugin to display DVH data with adjustable constraints."""
def __init__(self):
pre = wx.PrePanel()
# the Create step is done by XRC.
self.PostCreate(pre)
def Init(self, res):
"""Method called after the panel has been initialized."""
self.guiDVH = guidvh.guiDVH(self)
res.AttachUnknownControl('panelDVH', self.guiDVH.panelDVH, self)
# Initialize the Constraint selector controls
self.lblType = XRCCTRL(self, 'lblType')
self.choiceConstraint = XRCCTRL(self, 'choiceConstraint')
self.txtConstraint = XRCCTRL(self, 'txtConstraint')
self.sliderConstraint = XRCCTRL(self, 'sliderConstraint')
self.lblResultType = XRCCTRL(self, 'lblResultType')
self.lblConstraintUnits = XRCCTRL(self, 'lblConstraintUnits')
self.lblConstraintTypeUnits = XRCCTRL(self, 'lblConstraintTypeUnits')
# Initialize the result labels
self.lblConstraintType = XRCCTRL(self, 'lblConstraintType')
self.lblResultDivider = XRCCTRL(self, 'lblResultDivider')
self.lblConstraintPercent = XRCCTRL(self, 'lblConstraintPercent')
# Modify the control and font size on Mac
controls = [self.lblType, self.choiceConstraint, self.sliderConstraint,
self.lblResultType, self.lblConstraintUnits, self.lblConstraintPercent,
self.lblConstraintType, self.lblConstraintTypeUnits, self.lblResultDivider]
# Add children of composite controls to modification list
compositecontrols = [self.txtConstraint]
for control in compositecontrols:
for child in control.GetChildren():
controls.append(child)
# Add the constraint static box to the modification list
controls.append(self.lblType.GetContainingSizer().GetStaticBox())
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)
# Adjust the control size for the result value labels
te = self.lblType.GetTextExtent('0')
self.lblConstraintUnits.SetMinSize((te[0]*10, te[1]))
self.lblConstraintPercent.SetMinSize((te[0]*6, te[1]))
self.Layout()
# Bind ui events to the proper methods
wx.EVT_CHOICE(self, XRCID('choiceConstraint'), self.OnToggleConstraints)
wx.EVT_SPINCTRL(self, XRCID('txtConstraint'), self.OnChangeConstraint)
wx.EVT_COMMAND_SCROLL_THUMBTRACK(self, XRCID('sliderConstraint'), self.OnChangeConstraint)
wx.EVT_COMMAND_SCROLL_CHANGED(self, XRCID('sliderConstraint'), self.OnChangeConstraint)
self.Bind(wx.EVT_WINDOW_DESTROY, self.OnDestroy)
# Initialize variables
self.structures = {} # structures from initial DICOM data
self.checkedstructures = {} # structures that need to be shown
self.dvhs = {} # raw dvhs from initial DICOM data
self.dvhdata = {} # dict of dvh constraint functions
self.dvharray = {} # dict of dvh data processed from dvhdata
self.dvhscaling = {} # dict of dvh scaling data
self.plan = {} # used for rx dose
self.structureid = 1 # used to indicate current constraint structure
# Set up pubsub
pub.subscribe(self.OnUpdatePatient, 'patient.updated.parsed_data')
pub.subscribe(self.OnStructureCheck, 'structures.checked')
pub.subscribe(self.OnStructureSelect, 'structure.selected')
def OnUpdatePatient(self, msg):
"""Update and load the patient data."""
self.structures = msg.data['structures']
self.dvhs = msg.data['dvhs']
self.plan = msg.data['plan']
# show an empty plot when (re)loading a patient
self.guiDVH.Replot()
self.EnableConstraints(False)
def OnDestroy(self, evt):
"""Unbind to all events before the plugin is destroyed."""
pub.unsubscribe(self.OnUpdatePatient)
pub.unsubscribe(self.OnStructureCheck)
pub.unsubscribe(self.OnStructureSelect)
def OnStructureCheck(self, msg):
"""When a structure changes, update the interface and plot."""
# Make sure that the volume has been calculated for each structure
# before setting it
self.checkedstructures = msg.data
for id, structure in self.checkedstructures.iteritems():
if not self.structures[id].has_key('volume'):
self.structures[id]['volume'] = structure['volume']
# make sure that the dvh has been calculated for each structure
# before setting it
if self.dvhs.has_key(id):
self.EnableConstraints(True)
# Create an instance of the dvhdata class to can access its functions
self.dvhdata[id] = dvhdata.DVH(self.dvhs[id])
# Create an instance of the dvh arrays so that guidvh can plot it
self.dvharray[id] = dvhdata.DVH(self.dvhs[id]).dvh
# Create an instance of the dvh scaling data for guidvh
self.dvhscaling[id] = self.dvhs[id]['scaling']
# 'Toggle' the choice box to refresh the dose data
self.OnToggleConstraints(None)
if not len(self.checkedstructures):
self.EnableConstraints(False)
# Make an empty plot on the DVH
self.guiDVH.Replot(None, None)
def OnStructureSelect(self, msg):
"""Load the constraints for the currently selected structure."""
if (msg.data['id'] == None):
self.EnableConstraints(False)
else:
self.structureid = msg.data['id']
if self.dvhs.has_key(self.structureid):
# Create an instance of the dvhdata class to can access its functions
self.dvhdata[self.structureid] = dvhdata.DVH(self.dvhs[self.structureid])
# Create an instance of the dvh scaling data for guidvh
self.dvhscaling[self.structureid] = self.dvhs[self.structureid]['scaling']
# 'Toggle' the choice box to refresh the dose data
self.OnToggleConstraints(None)
else:
self.EnableConstraints(False)
self.guiDVH.Replot(self.dvharray, self.dvhscaling, self.checkedstructures)
def EnableConstraints(self, value):
"""Enable or disable the constraint selector."""
self.choiceConstraint.Enable(value)
self.txtConstraint.Enable(value)
self.sliderConstraint.Enable(value)
if not value:
self.lblConstraintUnits.SetLabel('- ')
self.lblConstraintPercent.SetLabel('- ')
self.txtConstraint.SetValue(0)
self.sliderConstraint.SetValue(0)
def OnToggleConstraints(self, evt):
"""Switch between different constraint modes."""
# Replot the remaining structures and disable the constraints
# if a structure that has no DVH calculated is selected
if not self.dvhs.has_key(self.structureid):
self.guiDVH.Replot(self.dvharray, self.dvhscaling, self.checkedstructures)
self.EnableConstraints(False)
return
else:
self.EnableConstraints(True)
dvh = self.dvhs[self.structureid]
# Check if the function was called via an event or not
if not (evt == None):
constrainttype = evt.GetInt()
else:
constrainttype = self.choiceConstraint.GetSelection()
constraintrange = 0
# Volume constraint
if (constrainttype == 0):
self.lblConstraintType.SetLabel(' Dose:')
self.lblConstraintTypeUnits.SetLabel('% ')
self.lblResultType.SetLabel('Volume:')
rxDose = float(self.plan['rxdose'])
dvhdata = len(dvh['data'])*dvh['scaling']
constraintrange = int(dvhdata*100/rxDose)
# never go over the max dose as data does not exist
if (constraintrange > int(dvh['max'])):
constraintrange = int(dvh['max'])
# Volume constraint in Gy
elif (constrainttype == 1):
self.lblConstraintType.SetLabel(' Dose:')
self.lblConstraintTypeUnits.SetLabel('Gy ')
self.lblResultType.SetLabel('Volume:')
constraintrange = self.plan['rxdose']/100
maxdose = int(dvh['max']*self.plan['rxdose']/10000)
# never go over the max dose as data does not exist
if (constraintrange*100 > maxdose):
constraintrange = maxdose
# Dose constraint
elif (constrainttype == 2):
self.lblConstraintType.SetLabel('Volume:')
self.lblConstraintTypeUnits.SetLabel(u'% ')
self.lblResultType.SetLabel(' Dose:')
constraintrange = 100
# Dose constraint in cc
elif (constrainttype == 3):
self.lblConstraintType.SetLabel('Volume:')
self.lblConstraintTypeUnits.SetLabel(u'cm³')
self.lblResultType.SetLabel(' Dose:')
constraintrange = int(self.structures[self.structureid]['volume'])
self.sliderConstraint.SetRange(0, constraintrange)
self.sliderConstraint.SetValue(constraintrange)
self.txtConstraint.SetRange(0, constraintrange)
self.txtConstraint.SetValue(constraintrange)
self.OnChangeConstraint(None)
def OnChangeConstraint(self, evt):
"""Update the results when the constraint value changes."""
# Check if the function was called via an event or not
if not (evt == None):
slidervalue = evt.GetInt()
else:
slidervalue = self.sliderConstraint.GetValue()
self.txtConstraint.SetValue(slidervalue)
self.sliderConstraint.SetValue(slidervalue)
rxDose = self.plan['rxdose']
id = self.structureid
constrainttype = self.choiceConstraint.GetSelection()
# Volume constraint
if (constrainttype == 0):
absDose = rxDose * slidervalue / 100
volume = self.structures[id]['volume']
cc = self.dvhdata[id].GetVolumeConstraintCC(absDose, volume)
constraint = self.dvhdata[id].GetVolumeConstraint(absDose)
self.lblConstraintUnits.SetLabel("%.1f" % cc + u' cm³')
self.lblConstraintPercent.SetLabel("%.1f" % constraint + " %")
self.guiDVH.Replot(self.dvharray, self.dvhscaling,
self.checkedstructures, ([absDose], [constraint]), id)
# Volume constraint in Gy
elif (constrainttype == 1):
absDose = slidervalue*100
volume = self.structures[id]['volume']
cc = self.dvhdata[id].GetVolumeConstraintCC(absDose, volume)
constraint = self.dvhdata[id].GetVolumeConstraint(absDose)
self.lblConstraintUnits.SetLabel("%.1f" % cc + u' cm³')
self.lblConstraintPercent.SetLabel("%.1f" % constraint + " %")
self.guiDVH.Replot(self.dvharray, self.dvhscaling,
self.checkedstructures, ([absDose], [constraint]), id)
# Dose constraint
elif (constrainttype == 2):
dose = self.dvhdata[id].GetDoseConstraint(slidervalue)
self.lblConstraintUnits.SetLabel("%.1f" % dose + u' cGy')
self.lblConstraintPercent.SetLabel("%.1f" % (dose*100/rxDose) + " %")
self.guiDVH.Replot(self.dvharray, self.dvhscaling,
self.checkedstructures, ([dose], [slidervalue]), id)
# Dose constraint in cc
elif (constrainttype == 3):
volumepercent = slidervalue*100/self.structures[id]['volume']
dose = self.dvhdata[id].GetDoseConstraint(volumepercent)
self.lblConstraintUnits.SetLabel("%.1f" % dose + u' cGy')
self.lblConstraintPercent.SetLabel("%.1f" % (dose*100/rxDose) + " %")
self.guiDVH.Replot(self.dvharray, self.dvhscaling,
self.checkedstructures, ([dose], [volumepercent]), id)
|