def verify_prescription(): plan = lib.get_current_plan() beamset = lib.get_current_beamset() # Check if beamset dose is dependent try: bkgdose_name = plan.PlanOptimizations[ beamset.Number - 1].BackgroundDose.ForBeamSet.DicomPlanLabel except: bkgdose_name = "None" # Determine prescription dose, fractions and target try: presc_text = str( (beamset.Prescription.PrimaryDosePrescription.DoseValue) / 100.0) + "Gy" presc_text += " en %dfx " % beamset.FractionationPattern.NumberOfFractions # Display prescription type and dose if beamset.Prescription.PrimaryDosePrescription.PrescriptionType == 'DoseAtPoint': presc_text += "au point " + beamset.Prescription.PrimaryDosePrescription.OnStructure.Name elif beamset.Prescription.PrimaryDosePrescription.PrescriptionType == 'DoseAtVolume': presc_text += "à " + str( beamset.Prescription.PrimaryDosePrescription.DoseVolume ) + r'% du volume du ' + beamset.Prescription.PrimaryDosePrescription.OnStructure.Name if bkgdose_name != "None": presc_text += "\n (dépendente sur beamset " + bkgdose_name + ")" presc_text = presc_text.replace(".0Gy", "Gy") presc_text = presc_text.replace(".0%", "%") except: presc_text = "Prescription non définie" presc_text = "Prescription: " + presc_text return presc_text
def add_opt_obj_prostate_A1(plan=None): """ Ajoute les objectifs d'optimisation pour les cas de prostate (plan A1). =================== ================ =========== ========= ========= ROI Type de critère Poids Valeur 1 Valeur 2 =================== ================ =========== ========= ========= PTV A1 ``mindose`` 100 76 Gy PTV A1 ``maxdose`` 10 83 Gy RECTUM ``maxdvh` ` 10 75 Gy 15 % RECTUM ``maxdvh` ` 10 70 Gy 25 % RECTUM ``maxdvh` ` 1 65 Gy 35 % RECTUM ``maxdvh` ` 1 60 Gy 50 % RECTUM ``maxdose`` 10 79.5 Gy VESSIE ``maxdose`` 1 82 Gy BodyRS ``dosefalloff`` 0 76-40 Gy 3 cm =================== ================ =========== ========= ========= .. seealso:: fonctions :py:func:`hmrlib.hmrlib.optim.add_mindose_objective`, :py:func:`hmrlib.hmrlib.optim.add_maxdose_objective` """ if plan is None: plan = lib.get_current_plan() nb_fx = plan.BeamSets[0].FractionationPattern.NumberOfFractions if nb_fx == 40: #Plan to 80Gy optim.add_mindose_objective('PTV A1', 7600, weight=100, plan=plan) optim.add_maxdose_objective('PTV A1', 8300, weight=10, plan=plan) optim.add_maxdvh_objective('RECTUM', 7500, 15, weight=10, plan=plan) optim.add_maxdvh_objective('RECTUM', 7000, 25, weight=10, plan=plan) optim.add_maxdvh_objective('RECTUM', 6500, 35, weight=1, plan=plan) optim.add_maxdvh_objective('RECTUM', 6000, 50, weight=1, plan=plan) optim.add_maxdose_objective('RECTUM+3mm', 7900, weight=10, plan=plan) optim.add_maxdose_objective('VESSIE', 8200, weight=1, plan=plan) optim.add_dosefalloff_objective('BodyRS', 7600, 4000, 3, weight=0, plan=plan) elif nb_fx == 33: #Plan to 66 Gy optim.add_mindose_objective('PTV A1', 6270, weight=100, plan=plan) optim.add_maxdose_objective('PTV A1', 6850, weight=10, plan=plan) optim.add_maxdvh_objective('RECTUM', 6500, 35, weight=10, plan=plan) optim.add_maxdvh_objective('RECTUM', 6000, 50, weight=10, plan=plan) optim.add_maxdose_objective('RECTUM+3mm', 6800, weight=10, plan=plan) optim.add_maxdose_objective('VESSIE', 6900, weight=1, plan=plan) optim.add_dosefalloff_objective('BodyRS', 6270, 3300, 3, weight=0, plan=plan) elif nb_fx == 22: #Plan to 66 Gy at 3Gy per fraction optim.add_mindose_objective('PTV A1', 6270, weight=100, plan=plan) optim.add_maxdose_objective('PTV A1', 6850, weight=10, plan=plan) optim.add_maxdvh_objective('RECTUM', 5420, 35, weight=10, plan=plan) optim.add_maxdvh_objective('RECTUM', 5000, 50, weight=10, plan=plan) optim.add_maxdose_objective('RECTUM+3mm', 6500, weight=10, plan=plan) optim.add_maxdose_objective('VESSIE', 6900, weight=1, plan=plan) optim.add_dosefalloff_objective('BodyRS', 6270, 3300, 3, weight=0, plan=plan) elif nb_fx == 20 or nb_fx == 24: #Plan to 60 Gy at 3Gy per fraction (or PACE 62Gy in 20fx) if roi.roi_exists('PTV_6200'): optim.add_mindose_objective('PTV_6200', 5890, weight=100, plan=plan) optim.add_maxdose_objective('PTV_6200', 6450, weight=10, plan=plan) optim.add_maxdvh_objective('Rectum', 6000, 5, weight=10, plan=plan) optim.add_maxdvh_objective('Rectum', 5600, 25, weight=10, plan=plan) optim.add_maxdvh_objective('Rectum', 5200, 30, weight=1, plan=plan) optim.add_maxdvh_objective('Rectum', 4800, 50, weight=1, plan=plan) optim.add_maxdose_objective('Rectum+3mm', 6100, weight=10, plan=plan) optim.add_maxdose_objective('Bladder', 6400, weight=1, plan=plan) optim.add_dosefalloff_objective('BodyRS', 5890, 3100, 3, weight=0, plan=plan) else: optim.add_mindose_objective('PTV A1', 5700, weight=100, plan=plan) optim.add_maxdose_objective('PTV A1', 6225, weight=10, plan=plan) optim.add_maxdvh_objective('RECTUM', 5700, 15, weight=10, plan=plan) optim.add_maxdvh_objective('RECTUM', 5280, 30, weight=10, plan=plan) optim.add_maxdvh_objective('RECTUM', 4860, 50, weight=1, plan=plan) optim.add_maxdvh_objective('RECTUM', 3240, 70, weight=1, plan=plan) optim.add_maxdose_objective('RECTUM+3mm', 6000, weight=10, plan=plan) optim.add_maxdose_objective('VESSIE', 6300, weight=1, plan=plan) optim.add_dosefalloff_objective('BodyRS', 5700, 3000, 3, weight=0, plan=plan) elif nb_fx == 39: #PACE 78Gy en 29fx optim.add_mindose_objective('PTV_7800', 7410, weight=100, plan=plan) optim.add_maxdose_objective('PTV_7800', 8100, weight=10, plan=plan) optim.add_maxdvh_objective('Rectum', 7500, 5, weight=10, plan=plan) optim.add_maxdvh_objective('Rectum', 7000, 25, weight=10, plan=plan) optim.add_maxdvh_objective('Rectum', 6500, 30, weight=1, plan=plan) optim.add_maxdvh_objective('Rectum', 6000, 50, weight=1, plan=plan) optim.add_maxdose_objective('Rectum+3mm', 7700, weight=10, plan=plan) optim.add_maxdose_objective('Bladder', 8000, weight=1, plan=plan) optim.add_dosefalloff_objective('BodyRS', 7410, 3900, 3, weight=0, plan=plan) elif nb_fx == 5: #PACE 36.25Gy en 5fx optim.add_mindose_objective('PTV_3625', 3444, weight=100, plan=plan) optim.add_maxdose_objective('PTV_3625', 3760, weight=10, plan=plan) optim.add_maxdvh_objective('Rectum', 2500, 20, weight=10, plan=plan) optim.add_maxdvh_objective('Rectum', 1810, 50, weight=10, plan=plan) optim.add_maxdose_objective('Rectum+3mm', 3575, weight=10, plan=plan) optim.add_maxdose_objective('Bladder', 3700, weight=1, plan=plan) optim.add_dosefalloff_objective('BodyRS', 3444, 1813, 3, weight=0, plan=plan)
def add_opt_obj_lung_stereo(contralateral_lung=None, patient_plan=None): """ Ajoute les objectifs d'optimisation pour les cas de stéréo de poumon. Assume que les PTV sont nommés avec leur niveau de prescription. Ex. : PTV48 Si le nom du ROI du poumon contralatéral n'est pas spécifié, détecte si le PTV est situé dans POUMON D ou POUMON G et ajuste en conséquence. Args: contralateral_lung (str, optional): nom du ROI du poumon contralatéral .. rubric:: Objectifs pour prescription de 48 Gy - 4 Fx : ===================== ================ =========== ======== ROI Type de critère Poids Valeur ===================== ================ =========== ======== PTV48 ``mindose`` 50 48 Gy PTV48 ``maxdose`` 1 72 Gy RING_1 ``maxdose`` 1 48.5 Gy RING_2 ``maxdose`` 1 42 Gy RING_3 ``maxdose`` 1 28 Gy PEAU ``maxdose`` 1 27 Gy BR SOUCHE ``maxdose`` 1 33.9 Gy COTES ``maxdose`` 1 48 Gy MOELLE ``maxdose`` 1 18 Gy COEUR ``maxdose`` 1 33.9 Gy OESOPHAGE ``maxdose`` 1 11 Gy <poumon contralat.>\* ``maxdose`` 1 5 Gy PLEXUS BRACHIAL ``maxdose`` 1 27 Gy TRACHEE ``maxdose`` 1 33.9 Gy PRV MOELLE ``maxdose`` 1 22.4 Gy PRV PLEXUS ``maxdose`` 1 30.5 Gy ===================== ================ =========== ======== .. rubric:: Objectifs pour prescription de 54 Gy - 3 Fx : ===================== ================ =========== ======== ROI Type de critère Poids Valeur ===================== ================ =========== ======== PTV54 ``mindose`` 50 54 Gy PTV54 ``maxdose`` 1 81 Gy RING_1 ``maxdose`` 1 54.5 Gy RING_2 ``maxdose`` 1 47 Gy RING_3 ``maxdose`` 1 32 Gy PEAU ``maxdose`` 1 24 Gy BR SOUCHE ``maxdose`` 1 30 Gy COTES ``maxdose`` 1 54 Gy MOELLE ``maxdose`` 1 18 Gy COEUR ``maxdose`` 1 30 Gy OESOPHAGE ``maxdose`` 1 10 Gy <poumon contralat.>\* ``maxdose`` 1 5 Gy PLEXUS BRACHIAL ``maxdose`` 1 24 Gy TRACHEE ``maxdose`` 1 30 Gy PRV MOELLE ``maxdose`` 1 20 Gy PRV PLEXUS ``maxdose`` 1 27 Gy ===================== ================ =========== ======== .. rubric:: Objectifs pour prescription de 60 Gy - 8 Fx : ===================== ================ =========== ======== ROI Type de critère Poids Valeur ===================== ================ =========== ======== PTV60 ``mindose`` 50 60 Gy PTV60 ``maxdose`` 1 90 Gy RING_1 ``maxdose`` 1 60.5 Gy RING_2 ``maxdose`` 1 52 Gy RING_3 ``maxdose`` 1 35.5 Gy PEAU ``maxdose`` 1 35.5 Gy BR SOUCHE ``maxdose`` 1 45.1 Gy BR SOUCHE ``maxdose`` 1 61 Gy COTES ``maxdose`` 1 60 Gy MOELLE ``maxdose`` 1 24 Gy COEUR ``maxdose`` 1 45 Gy OESOPHAGE ``maxdose`` 1 13.5 Gy <poumon contralat.>\* ``maxdose`` 1 5 Gy PLEXUS BRACHIAL ``maxdose`` 1 35.5 Gy TRACHEE ``maxdose`` 1 45 Gy PRV MOELLE ``maxdose`` 1 29.1 Gy PRV PLEXUS ``maxdose`` 1 40.3 Gy ===================== ================ =========== ======== .. note:: \*<poumon contralat.> représente le nom du ROI du poumon contralatéral, soit *POUMON G* ou *POUMON D*. .. seealso:: fonctions :py:func:`hmrlib.hmrlib.optim.add_mindose_objective`, :py:func:`hmrlib.hmrlib.optim.add_maxdose_objective` """ if patient_plan is None: patient_plan = lib.get_current_plan() ptvs = roi.identify_ptvs2() if len(ptvs) > 1: logger.warning('More than one dose level found for PTVs. PTVs = %s', ptvs) # raise SystemError(u"Plus d'un niveau de dose trouvé pour les PTV.") if 'PTV48' in ptvs: optim.add_mindose_objective('PTV48', 4800, weight=50, plan=patient_plan) optim.add_maxdose_objective('PTV48', 7200, plan=patient_plan) optim.add_maxdose_objective('RING_1', 4850, plan=patient_plan) optim.add_maxdose_objective('RING_2', 4200, plan=patient_plan) optim.add_maxdose_objective('RING_3', 2800, plan=patient_plan) optim.add_maxdose_objective('PEAU', 2700, plan=patient_plan) optim.add_maxdose_objective('BR SOUCHE', 3390, plan=patient_plan) optim.add_maxdose_objective('COTES', 4800, plan=patient_plan) optim.add_maxdose_objective('MOELLE', 1800, plan=patient_plan) optim.add_maxdose_objective('COEUR', 3390, plan=patient_plan) optim.add_maxdose_objective('OESOPHAGE', 1100, plan=patient_plan) if contralateral_lung is None: if roi.find_most_intersecting_roi( 'PTV48', ['POUMON DRT', 'POUMON GCHE']) == 'POUMON GCHE': contralateral_lung = 'POUMON DRT' else: contralateral_lung = 'POUMON GCHE' optim.add_maxdose_objective(contralateral_lung, 500, plan=patient_plan) optim.add_maxdose_objective('PLEXUS BRACHIAL', 2700, plan=patient_plan) optim.add_maxdose_objective('TRACHEE', 3390, plan=patient_plan) optim.add_maxdose_objective('PRV MOELLE', 2240, plan=patient_plan) optim.add_maxdose_objective('PRV PLEXUS', 3050, plan=patient_plan) elif 'PTV54' in ptvs: optim.add_mindose_objective('PTV54', 5400, weight=50, plan=patient_plan) optim.add_maxdose_objective('PTV54', 8100, plan=patient_plan) optim.add_maxdose_objective('RING_1', 5450, plan=patient_plan) optim.add_maxdose_objective('RING_2', 4700, plan=patient_plan) optim.add_maxdose_objective('RING_3', 3200, plan=patient_plan) optim.add_maxdose_objective('PEAU', 2400, plan=patient_plan) optim.add_maxdose_objective('BR SOUCHE', 3000, plan=patient_plan) optim.add_maxdose_objective('COTES', 5400, plan=patient_plan) optim.add_maxdose_objective('MOELLE', 1800, plan=patient_plan) optim.add_maxdose_objective('COEUR', 3000, plan=patient_plan) optim.add_maxdose_objective('OESOPHAGE', 1000, plan=patient_plan) if contralateral_lung is None: if roi.find_most_intersecting_roi( 'PTV54', ['POUMON DRT', 'POUMON GCHE']) == 'POUMON GCHE': contralateral_lung = 'POUMON DRT' else: contralateral_lung = 'POUMON GCHE' optim.add_maxdose_objective(contralateral_lung, 500, plan=patient_plan) optim.add_maxdose_objective('PLEXUS BRACHIAL', 2400, plan=patient_plan) optim.add_maxdose_objective('TRACHEE', 3000, plan=patient_plan) optim.add_maxdose_objective('PRV MOELLE', 2000, plan=patient_plan) optim.add_maxdose_objective('PRV PLEXUS', 2700, plan=patient_plan) elif 'PTV60' in ptvs: optim.add_mindose_objective('PTV60', 6000, weight=50, plan=patient_plan) optim.add_maxdose_objective('PTV60', 9000, plan=patient_plan) optim.add_maxdose_objective('RING_1', 6050, plan=patient_plan) optim.add_maxdose_objective('RING_2', 5200, plan=patient_plan) optim.add_maxdose_objective('RING_3', 3550, plan=patient_plan) optim.add_maxdose_objective('PEAU', 3550, plan=patient_plan) optim.add_maxdose_objective('BR SOUCHE', 4510, plan=patient_plan) optim.add_maxdose_objective('BR SOUCHE', 6100, plan=patient_plan) optim.add_maxdose_objective('COTES', 6000, plan=patient_plan) optim.add_maxdose_objective('MOELLE', 2400, plan=patient_plan) optim.add_maxdose_objective('COEUR', 4500, plan=patient_plan) optim.add_maxdose_objective('OESOPHAGE', 1350, plan=patient_plan) if contralateral_lung is None: if roi.find_most_intersecting_roi( 'PTV60', ['POUMON DRT', 'POUMON GCHE']) == 'POUMON GCHE': contralateral_lung = 'POUMON DRT' else: contralateral_lung = 'POUMON GCHE' optim.add_maxdose_objective(contralateral_lung, 500, plan=patient_plan) optim.add_maxdose_objective('PLEXUS BRACHIAL', 3550, plan=patient_plan) optim.add_maxdose_objective('TRACHEE', 4500, plan=patient_plan) optim.add_maxdose_objective('PRV MOELLE', 2910, plan=patient_plan) optim.add_maxdose_objective('PRV PLEXUS', 4030, plan=patient_plan) else: logger.warning( 'No PTV with supported dose level found. Please name PTV with prescription in name (ex. PTV48).' )
def add_opt_obj_brain_stereo(patient_plan=None): """ Ajoute les objectifs d'optimisation pour les cas de stéréo de poumon. Assume que les PTV sont nommés avec leur niveau de prescription. Ex. : PTV15 Les prescriptions supportées sont : * 15 Gy * 18 Gy * 20 Gy .. rubric:: Objectifs pour prescription de 15 Gy : =================== ================ =========== ========== ROI Type de critère Poids Valeur =================== ================ =========== ========== PTV15 ``mindose`` 50 15 Gy PTV15 ``maxdose`` 1 15 Gy RING_1_3mm ``maxdose`` 1 15.5 Gy RING_2_8mm ``maxdose`` 1 10 Gy RING_3_1cm ``maxdose`` 1 7.5 Gy =================== ================ =========== ========== .. rubric:: Objectifs pour prescription de 18 Gy : =================== ================ =========== ========== ROI Type de critère Poids Valeur =================== ================ =========== ========== PTV18 ``mindose`` 50 18 Gy PTV18 ``maxdose`` 1 27 Gy RING_1_3mm ``maxdose`` 1 18.5 Gy RING_2_8mm ``maxdose`` 1 12.5 Gy RING_3_1cm ``maxdose`` 1 8.5 Gy =================== ================ =========== ========== .. rubric:: Objectifs pour prescription de 20 Gy : =================== ================ =========== ========== ROI Type de critère Poids Valeur =================== ================ =========== ========== PTV20 ``mindose`` 50 20 Gy PTV20 ``maxdose`` 1 30 Gy RING_1_3mm ``maxdose`` 1 20.5 Gy RING_2_8mm ``maxdose`` 1 14 Gy RING_3_1cm ``maxdose`` 1 9.5 Gy =================== ================ =========== ========== .. seealso:: fonctions :py:func:`hmrlib.optim.add_mindose_objective`, :py:func:`hmrlib.optim.add_maxdose_objective` """ if patient_plan is None: patient_plan = lib.get_current_plan() ptvs = roi.identify_ptvs2() if len(ptvs) > 1: logger.warning('More than one dose level found for PTVs. PTVs = %s', ptvs) # raise SystemError('More than one dose level found.') if 'PTV15' in ptvs: optim.add_mindose_objective('PTV15', 1500, weight=50, plan=patient_plan) optim.add_maxdose_objective('PTV15', 2250, plan=patient_plan) optim.add_maxdose_objective('RING_1_3mm', 1550, plan=patient_plan) optim.add_maxdose_objective('RING_2_8mm', 1000, plan=patient_plan) optim.add_maxdose_objective('RING_3_1cm', 750, plan=patient_plan) optim.add_maxdose_objective('TISSUS SAINS', 650, plan=patient_plan) elif 'PTV18' in ptvs: optim.add_mindose_objective('PTV18', 1800, weight=50, plan=patient_plan) optim.add_maxdose_objective('PTV18', 2700, plan=patient_plan) optim.add_maxdose_objective('RING_1_3mm', 1850, plan=patient_plan) optim.add_maxdose_objective('RING_2_8mm', 1250, plan=patient_plan) optim.add_maxdose_objective('RING_3_1cm', 850, plan=patient_plan) optim.add_maxdose_objective('TISSUS SAINS', 725, plan=patient_plan) elif 'PTV20' in ptvs: optim.add_mindose_objective('PTV20', 2000, weight=50, plan=patient_plan) optim.add_maxdose_objective('PTV20', 3000, plan=patient_plan) optim.add_maxdose_objective('RING_1_3mm', 2050, plan=patient_plan) optim.add_maxdose_objective('RING_2_8mm', 1400, plan=patient_plan) optim.add_maxdose_objective('RING_3_1cm', 950, plan=patient_plan) else: logger.warning( 'No PTV with supported dose level found. Please name PTV with prescription in name (ex. PTV18).' )
def verification_finale(): class Verif1Window(Form): def __init__(self): self.Text = "Vérification 2" self.Width = 450 self.Height = 1000 self.setupHeaderWindow() self.setupMainWindow() self.setupOKButtons() self.Controls.Add(self.HeaderWindow) self.Controls.Add(self.MainWindow) self.Controls.Add(self.OKbuttonPanel) #Create the dictionary that will be used to pass the verification info to the print function self.d = dict( patient_name=patient.PatientName.replace('^', ', '), plan_name=plan.Name, beamset_name=beamset.DicomPlanLabel, patient_number=patient.PatientID, #planned_by_name = lib.get_user_name(patient.ModificationInfo.UserName.Split('\\')[1]), planned_by_name=plan.PlannedBy, verified_by_name=lib.get_user_name(os.getenv('USERNAME')), ext_text="Script pas roulé", grid_text="Script pas roulé", DSP_text="Script pas roulé", iso_text="Script pas roulé", beam_text="Script pas roulé", presc_text="Script pas roulé", segments_text="Script pas roulé", check_billes="Pas vérifié", check_ext="Pas vérifié", check_isoOK="Pas vérifié", check_grid="Pas vérifié", check_beams_Rx="Pas vérifié", check_segments="Pas vérifié", check_distribution_dose="Pas vérifié", check_noteMD="Pas vérifié", check_DSP="Pas vérifié", check_mise_en_place="Pas vérifié", check_doctx="Pas vérifié", check_codestat="Pas vérifié") #Create dictionaries for window/level switching self.lung_dict = dict(x=-600, y=1600) self.lw_dict = dict(x=-exam.Series[0].LevelWindow.x, y=exam.Series[0].LevelWindow.y) #Display localisation point uis.display_loc_point() def Panel(self, x, y): panel = Panel() panel.Width = 450 panel.Height = 800 panel.Location = Point(x, y) panel.BorderStyle = BorderStyle.None return panel def miniPanel(self, x, y): panel = Panel() panel.Width = 450 panel.Height = 60 panel.Location = Point(x, y) panel.BorderStyle = BorderStyle.None return panel def setupHeaderWindow(self): self.HeaderWindow = self.miniPanel(0, 0) self.PatientIDHeader = Label() self.PatientIDHeader.Text = "Patient: " + patient.PatientName.replace( '^', ', ') + " Beamset: " + beamset.DicomPlanLabel self.PatientIDHeader.Location = Point(15, 25) self.PatientIDHeader.Font = Font("Arial", 11, FontStyle.Bold) self.PatientIDHeader.AutoSize = True self.HeaderWindow.Controls.Add(self.PatientIDHeader) def setupMainWindow(self): self.MainWindow = self.Panel(0, 60) vert_spacer = 32 offset = 15 self.label_billes = Label() self.label_billes.Text = "Billes sur point de localisation" self.label_billes.Location = Point(15, offset) self.label_billes.Font = Font("Arial", 10.25, FontStyle.Bold) self.label_billes.AutoSize = True self.check_billes = CheckBox() self.check_billes.Location = Point(410, offset - 2) self.check_billes.Width = 30 self.check_billes.Checked = False button_ext = Button() button_ext.Text = "Contour 'External' et overrides" button_ext.Font = Font("Arial", 10.25, FontStyle.Bold) button_ext.Location = Point(15, offset + vert_spacer) button_ext.Width = 375 button_ext.Click += self.button_ext_Clicked self.check_ext = CheckBox() self.check_ext.Location = Point(410, offset + vert_spacer - 2) self.check_ext.Width = 30 self.check_ext.Checked = False button_isoOK = Button() button_isoOK.Text = "Position de l'isocentre et point de localisation" button_isoOK.Font = Font("Arial", 10.25, FontStyle.Bold) button_isoOK.Location = Point(15, offset + vert_spacer * 2) button_isoOK.Width = 375 button_isoOK.Click += self.button_isoOK_Clicked self.check_isoOK = CheckBox() self.check_isoOK.Location = Point(410, offset + vert_spacer * 2 - 2) self.check_isoOK.Width = 30 self.check_isoOK.Checked = False button_segments = Button() button_segments.Text = "Les segments sont corrects/flashé au besoin" button_segments.Font = Font("Arial", 10.25, FontStyle.Bold) button_segments.Location = Point(15, offset + vert_spacer * 3) button_segments.Width = 375 button_segments.Click += self.button_segments_Clicked self.check_segments = CheckBox() self.check_segments.Location = Point(410, offset + vert_spacer * 3 - 2) self.check_segments.Width = 30 self.check_segments.Checked = False button_beams_Rx = Button() button_beams_Rx.Text = "Faisceaux et prescription" button_beams_Rx.Font = Font("Arial", 10.25, FontStyle.Bold) button_beams_Rx.Location = Point(15, offset + vert_spacer * 4) button_beams_Rx.Width = 375 button_beams_Rx.Click += self.button_beams_Clicked self.check_beams_Rx = CheckBox() self.check_beams_Rx.Location = Point(410, offset + vert_spacer * 4 - 2) self.check_beams_Rx.Width = 30 self.check_beams_Rx.Checked = False button_grid = Button() button_grid.Text = "La grille de dose est correcte" button_grid.Font = Font("Arial", 10.25, FontStyle.Bold) button_grid.Location = Point(15, offset + vert_spacer * 5) button_grid.Width = 375 button_grid.Click += self.button_grid_Clicked self.check_grid = CheckBox() self.check_grid.Location = Point(410, offset + vert_spacer * 5 - 2) self.check_grid.Width = 30 self.check_grid.Checked = False self.label_distribution_dose = Label() self.label_distribution_dose.Text = "Distribution de dose et clinical goals" self.label_distribution_dose.Location = Point( 15, offset + vert_spacer * 6) self.label_distribution_dose.Font = Font("Arial", 10.25, FontStyle.Bold) self.label_distribution_dose.AutoSize = True self.check_distribution_dose = CheckBox() self.check_distribution_dose.Location = Point( 410, offset + vert_spacer * 6 - 2) self.check_distribution_dose.Width = 30 self.check_distribution_dose.Checked = False self.label_noteMD = Label() self.label_noteMD.Text = "Vérif note de planif du MD NON" self.label_noteMD.Location = Point(15, offset + vert_spacer * 7) self.label_noteMD.Font = Font("Arial", 10.25, FontStyle.Bold) self.label_noteMD.AutoSize = True self.check_noteMD_non = CheckBox() self.check_noteMD_non.Location = Point( 340, offset + vert_spacer * 7 - 2) self.check_noteMD_non.Width = 30 self.check_noteMD_non.Checked = False self.label_OK1 = Label() self.label_OK1.Text = "OK" self.label_OK1.Location = Point(370, offset + vert_spacer * 7) self.label_OK1.Font = Font("Arial", 10.25, FontStyle.Bold) self.label_OK1.AutoSize = True self.check_noteMD_OK = CheckBox() self.check_noteMD_OK.Location = Point(410, offset + vert_spacer * 7 - 2) self.check_noteMD_OK.Width = 30 self.check_noteMD_OK.Checked = False self.label_DSP = Label() self.label_DSP.Text = "Vérif note positionnement: HT, DSPs, matching" self.label_DSP.Location = Point(15, offset + vert_spacer * 8) self.label_DSP.Font = Font("Arial", 10.25, FontStyle.Bold) self.label_DSP.AutoSize = True self.check_DSP = CheckBox() self.check_DSP.Location = Point(410, offset + vert_spacer * 8 - 2) self.check_DSP.Width = 30 self.check_DSP.Checked = False self.label_mise_en_place = Label() self.label_mise_en_place.Text = "Vérif mise en place: ISO DICOM, struct, phase" self.label_mise_en_place.Location = Point(15, offset + vert_spacer * 9) self.label_mise_en_place.Font = Font("Arial", 10.25, FontStyle.Bold) self.label_mise_en_place.AutoSize = True self.check_mise_en_place = CheckBox() self.check_mise_en_place.Location = Point( 410, offset + vert_spacer * 9 - 2) self.check_mise_en_place.Width = 30 self.check_mise_en_place.Checked = False self.label_doctx = Label() self.label_doctx.Text = "Vérification Document de Tx NON" self.label_doctx.Location = Point(15, offset + vert_spacer * 10) self.label_doctx.Font = Font("Arial", 10.25, FontStyle.Bold) self.label_doctx.AutoSize = True self.check_doctx_non = CheckBox() self.check_doctx_non.Location = Point( 340, offset + vert_spacer * 10 - 2) self.check_doctx_non.Width = 30 self.check_doctx_non.Checked = False self.label_OK2 = Label() self.label_OK2.Text = "OK" self.label_OK2.Location = Point(370, offset + vert_spacer * 10) self.label_OK2.Font = Font("Arial", 10.25, FontStyle.Bold) self.label_OK2.AutoSize = True self.check_doctx_OK = CheckBox() self.check_doctx_OK.Location = Point(410, offset + vert_spacer * 10 - 2) self.check_doctx_OK.Width = 30 self.check_doctx_OK.Checked = False self.label_codestat = Label() self.label_codestat.Text = "Vérification des codes statistiques" self.label_codestat.Location = Point(15, offset + vert_spacer * 11) self.label_codestat.Font = Font("Arial", 10.25, FontStyle.Bold) self.label_codestat.AutoSize = True self.check_codestat = CheckBox() self.check_codestat.Location = Point(410, offset + vert_spacer * 11 - 2) self.check_codestat.Width = 30 self.check_codestat.Checked = False #Count up beamsets bs_text = "" num_bs = 0 for bs in plan.BeamSets: bs_text += bs.DicomPlanLabel + ", " num_bs += 1 self.label_results_header = Label() self.label_results_header.Text = "Résultats" self.label_results_header.Location = Point( 15, offset + vert_spacer * 12.5) self.label_results_header.Font = Font("Arial", 11, FontStyle.Bold) self.label_results_header.AutoSize = True self.label_results = Label() self.label_results.Text = "Nombre de beamsets dans le plan: " + str( num_bs) + ' (' + bs_text[0:-2] + ')' self.label_results.Location = Point(15, offset + vert_spacer * 13.5) self.label_results.Font = Font( "Arial", 10, ) self.label_results.AutoSize = True self.label_reminder = Label() self.label_reminder.Text = "Rappel:\nRoulez le script de vérification pour chaque beamset" self.label_reminder.Location = Point(15, offset + vert_spacer * 14.5) self.label_reminder.Font = Font("Arial", 10.25, FontStyle.Bold) self.label_reminder.ForeColor = Color.Red self.label_reminder.AutoSize = True self.MainWindow.Controls.Add(self.label_billes) self.MainWindow.Controls.Add(self.check_billes) self.MainWindow.Controls.Add(button_ext) self.MainWindow.Controls.Add(self.check_ext) self.MainWindow.Controls.Add(button_isoOK) self.MainWindow.Controls.Add(self.check_isoOK) self.MainWindow.Controls.Add(button_segments) self.MainWindow.Controls.Add(self.check_segments) self.MainWindow.Controls.Add(button_beams_Rx) self.MainWindow.Controls.Add(self.check_beams_Rx) self.MainWindow.Controls.Add(button_grid) self.MainWindow.Controls.Add(self.check_grid) self.MainWindow.Controls.Add(self.label_distribution_dose) self.MainWindow.Controls.Add(self.check_distribution_dose) self.MainWindow.Controls.Add(self.label_noteMD) self.MainWindow.Controls.Add(self.check_noteMD_non) self.MainWindow.Controls.Add(self.label_OK1) self.MainWindow.Controls.Add(self.check_noteMD_OK) self.MainWindow.Controls.Add(self.label_DSP) self.MainWindow.Controls.Add(self.check_DSP) self.MainWindow.Controls.Add(self.label_mise_en_place) self.MainWindow.Controls.Add(self.check_mise_en_place) self.MainWindow.Controls.Add(self.label_doctx) self.MainWindow.Controls.Add(self.check_doctx_non) self.MainWindow.Controls.Add(self.label_OK2) self.MainWindow.Controls.Add(self.check_doctx_OK) self.MainWindow.Controls.Add(self.label_codestat) self.MainWindow.Controls.Add(self.check_codestat) self.MainWindow.Controls.Add(self.label_results_header) self.MainWindow.Controls.Add(self.label_results) self.MainWindow.Controls.Add(self.label_reminder) def button_ext_Clicked(self, sender, args): uis.show_patient_modeling() uis.select_roi_tab() self.message.ForeColor = Color.Black self.message.Text = "Vérification du contour External en cours" self.d['ext_text'] = verification.verify_external_and_overrides() self.label_results_header.Text = "Résultats" self.label_results.Text = self.d['ext_text'] self.label_reminder.Location = Point(self.label_results.Left, self.label_results.Top + 100) self.label_reminder.Text = "Rappel:\nVérifiez que la table (ou la planche ORL) est comprise dans\nle contour External avant de procéder à la prochaine étape" self.message.Text = "" def button_grid_Clicked(self, sender, args): self.message.ForeColor = Color.Black self.message.Text = "Vérification de la grille de dose en cours" self.d['grid_text'] = verification.verify_dose_grid_resolution() self.d['DSP_text'] = verification.verify_dose_specification_points( ) self.label_results_header.Text = "Résultats" self.label_results.Text = self.d['grid_text'] self.label_results.Text += "\n" + self.d['DSP_text'] self.label_reminder.Location = Point(self.label_results.Left, self.label_results.Top + 50) self.label_reminder.Text = "" self.message.Text = "" def button_isoOK_Clicked(self, sender, args): uis.show_plan_optimization() uis.show_po_bev() uis.show_po_cg() self.message.ForeColor = Color.Black self.message.Text = "Vérification de l'isocentre" a, b, c, d = verification.verify_isocenter() self.label_results_header.Text = "Résultats" self.d['iso_text'] = a + "\n" + b + "\n" + c + "\n\n" + d self.label_results.Text = self.d['iso_text'] self.label_reminder.Location = Point(self.label_results.Left, self.label_results.Top + 130) self.label_reminder.Text = "Rappel:\nVérifiez le placement de l'isocentre en mode BEV avant de\nprocéder à la prochaine étape" self.message.Text = "" def button_beams_Clicked(self, sender, args): uis.show_plan_optimization() uis.show_po_2D() self.label_reminder.Text = "" self.label_results.Text = "" self.label_results_header.Text = "Résultats" self.message.ForeColor = Color.Black self.message.Text = "Vérification de la prescription en cours" self.d['presc_text'] = verification.verify_prescription() self.label_results.Text = self.d['presc_text'] self.message.Text = "Vérification des faisceaux en cours" a, b, c, d, e = verification.verify_beams() self.d['beam_text'] = a + "\n\n" + d + "\n" + e + "\n\n" + c self.label_results.Text += "\n\nFaisceaux:\n" + self.d['beam_text'] self.label_reminder.Location = Point( self.label_results.Left, self.label_results.Top + 160 + 15 * b) #self.label_reminder.Text = "Rappel:\nS'il y a un prothèse, un pacemaker ou un membre qui\ndépasse le FOV du scan, vérifiez que les angles de gantry\nsont bien choisis" self.label_reminder.Text = "Rappel:\nVérifiez que les angles de gantry et collimateur sont bien\nchoisis avant de procéder à la prochaine étape" self.message.Text = "" def button_segments_Clicked(self, sender, args): self.message.ForeColor = Color.Black self.label_reminder.Text = "" self.label_results.Text = "" self.message.Text = "Vérification des segments en cours" self.d['segments_text'] = verification.verify_segments() self.label_results_header.Text = "Résultats" self.label_results.Text = self.d['segments_text'] self.label_reminder.Location = Point(self.label_results.Left, self.label_results.Top + 90) self.label_reminder.Text = "Rappel:\nVérifiez les segments en mode BEV avant de procéder" self.message.Text = "" #def cancelClicked(self, sender, args): # self.Close() def levelwindowClicked(self, sender, args): if exam.Series[0].LevelWindow.x == -600 and exam.Series[ 0].LevelWindow.y == 1600: exam.Series[0].LevelWindow = self.lw_dict else: exam.Series[0].LevelWindow = self.lung_dict def referencedoseClicked(self, sender, args): self.message.ForeColor = Color.Black self.message.Text = "En cours" prostate.toggle_reference_dose_verif2() self.message.Text = "" def nbfxClicked(self, sender, args): #uis.display_loc_point() uis.open_plan_settings() def printClicked(self, sender, args): #Verify that all boxes have been checked warning = False if self.check_billes.Checked: self.d['check_billes'] = 'OK' else: warning = True if self.check_ext.Checked: self.d['check_ext'] = 'OK' else: warning = True if self.check_isoOK.Checked: self.d['check_isoOK'] = 'OK' else: warning = True if self.check_grid.Checked: self.d['check_grid'] = 'OK' else: warning = True if self.check_beams_Rx.Checked: self.d['check_beams_Rx'] = 'OK' else: warning = True if self.check_segments.Checked: self.d['check_segments'] = 'OK' else: warning = True if self.check_distribution_dose.Checked: self.d['check_distribution_dose'] = 'OK' else: warning = True if self.check_noteMD_non.Checked: self.d['check_noteMD'] = 'A completer' elif self.check_noteMD_OK.Checked: self.d['check_noteMD'] = 'OK' else: warning = True if self.check_DSP.Checked: self.d['check_DSP'] = 'OK' else: warning = True if self.check_mise_en_place.Checked: self.d['check_mise_en_place'] = 'OK' else: warning = True if self.check_doctx_non.Checked: self.d['check_doctx'] = 'A approuver' elif self.check_doctx_OK.Checked: self.d['check_doctx'] = 'OK' else: warning = True if self.check_codestat.Checked: self.d['check_codestat'] = 'OK' else: warning = True self.message.ForeColor = Color.Black self.message.Text = "Impression en cours" report.create_verif2_report(data=self.d) if warning: self.message.ForeColor = Color.Red self.message.Text = "Impression terminé - vérification incomplète" else: self.message.ForeColor = Color.Green self.message.Text = "Impression terminé" def setupOKButtons(self): self.OKbuttonPanel = self.miniPanel(0, 900) printButton = Button() printButton.Text = "Imprimer" printButton.Location = Point(25, 28) #self.AcceptButton = okButton printButton.Click += self.printClicked #cancelButton = Button() #cancelButton.Text = "Annuler" #cancelButton.Location = Point(110,10) #self.CancelButton = cancelButton #cancelButton.Click += self.cancelClicked levelwindowButton = Button() levelwindowButton.Text = "Level/Window" levelwindowButton.Location = Point(108, 28) levelwindowButton.Width = 85 levelwindowButton.Click += self.levelwindowClicked referencedoseButton = Button() referencedoseButton.Text = "Toggle Reference Dose" referencedoseButton.Location = Point(200, 28) referencedoseButton.Width = 140 referencedoseButton.Click += self.referencedoseClicked nbfxButton = Button() nbfxButton.Text = "Nb. de fx" nbfxButton.Location = Point(345, 28) nbfxButton.Width = 70 nbfxButton.Click += self.nbfxClicked self.message = Label() self.message.Text = "" self.message.Location = Point(30, 0) self.message.Font = Font("Arial", 11, FontStyle.Bold) self.message.AutoSize = True self.OKbuttonPanel.Controls.Add(printButton) self.OKbuttonPanel.Controls.Add(levelwindowButton) self.OKbuttonPanel.Controls.Add(referencedoseButton) self.OKbuttonPanel.Controls.Add(nbfxButton) self.OKbuttonPanel.Controls.Add(self.message) #Check for common errors while importing patient, plan, beamset and examination try: patient = lib.get_current_patient() except: message.message_window('Aucun patient sélectionné') return try: plan = lib.get_current_plan() except: message.message_window('Aucun plan sélectionné') return try: beamset = lib.get_current_beamset() except: message.message_window('Aucun beamset sélectionné') return try: exam = lib.get_current_examination() except: message.message_window('Aucun examination trouvé') return try: temp = patient.ModificationInfo.UserName except: message.message_window( 'ATTENTION: Le plan a été modifié depuis la dernière sauvegarde.\n\nFermez cette fenêtre pour poursuivre avec la vérification.' ) #return form = Verif1Window() Application.Run(form)
def verification_initiale(): class Verif1Window(Form): def __init__(self): self.Text = "Vérification 1" self.Width = 450 self.Height = 1050 self.setupHeaderWindow() self.setupMainWindow() self.setupOKButtons() self.Controls.Add(self.HeaderWindow) self.Controls.Add(self.MainWindow) self.Controls.Add(self.OKbuttonPanel) #Create the dictionary that will be used to pass the verification info to the print function self.d = dict( patient_name=patient.PatientName.replace('^', ', '), plan_name=plan.Name, beamset_name=beamset.DicomPlanLabel, patient_number=patient.PatientID, #planned_by_name = lib.get_user_name(patient.ModificationInfo.UserName.Split('\\')[1]), planned_by_name=plan.PlannedBy, verified_by_name=lib.get_user_name(os.getenv('USERNAME')), ext_text="Script pas roulé", iso_text="Script pas roulé", beam_text="Script pas roulé", presc_text="Script pas roulé", opt_text="Script pas roulé", check_bonscan="Pas vérifié", check_scanOK="Pas vérifié", check_ext="Pas vérifié", check_isoOK="Pas vérifié", check_beams_Rx="Pas vérifié", check_contours="Pas vérifié", check_optimisation="Pas vérifié", check_distribution_dose="Pas vérifié") #Create dictionaries for window/level switching self.lung_dict = dict(x=-600, y=1600) self.lw_dict = dict(x=-exam.Series[0].LevelWindow.x, y=exam.Series[0].LevelWindow.y) def Panel(self, x, y): panel = Panel() panel.Width = 450 panel.Height = 860 panel.Location = Point(x, y) panel.BorderStyle = BorderStyle.None return panel def miniPanel(self, x, y): panel = Panel() panel.Width = 450 panel.Height = 60 panel.Location = Point(x, y) panel.BorderStyle = BorderStyle.None return panel def setupHeaderWindow(self): self.HeaderWindow = self.miniPanel(0, 0) self.PatientIDHeader = Label() self.PatientIDHeader.Text = "Patient: " + patient.PatientName.replace( '^', ', ') + " Beamset: " + beamset.DicomPlanLabel self.PatientIDHeader.Location = Point(15, 25) self.PatientIDHeader.Font = Font("Arial", 11, FontStyle.Bold) self.PatientIDHeader.AutoSize = True self.HeaderWindow.Controls.Add(self.PatientIDHeader) def setupMainWindow(self): self.MainWindow = self.Panel(0, 60) vert_spacer = 35 offset = 20 self.label_bonscan = Label() self.label_bonscan.Text = "Le bon scan est utilisé pour la planification" self.label_bonscan.Location = Point(15, offset) self.label_bonscan.Font = Font("Arial", 10.25, FontStyle.Bold) self.label_bonscan.AutoSize = True self.check_bonscan = CheckBox() self.check_bonscan.Location = Point(410, offset - 2) self.check_bonscan.Width = 30 self.check_bonscan.Checked = False self.label_scanOK = Label() self.label_scanOK.Text = "Scan OK (artéfactes, étendu du scan, objets sur la table)" self.label_scanOK.Location = Point(15, offset + vert_spacer) self.label_scanOK.Font = Font("Arial", 10.25, FontStyle.Bold) self.label_scanOK.AutoSize = True self.check_scanOK = CheckBox() self.check_scanOK.Location = Point(410, offset + vert_spacer - 2) self.check_scanOK.Width = 30 self.check_scanOK.Checked = False button_ext = Button() button_ext.Text = "Contour 'External' + overrides" button_ext.Font = Font("Arial", 10.25, FontStyle.Bold) button_ext.Location = Point(15, offset + vert_spacer * 2) button_ext.Width = 375 button_ext.Click += self.button_ext_Clicked self.check_ext = CheckBox() self.check_ext.Location = Point(410, offset + vert_spacer * 2 - 2) self.check_ext.Width = 30 self.check_ext.Checked = False self.label_contours = Label() self.label_contours.Text = "Les contours d'optimisation sont corrects" self.label_contours.Location = Point(15, offset + vert_spacer * 3) self.label_contours.Font = Font("Arial", 10.25, FontStyle.Bold) self.label_contours.AutoSize = True self.check_contours = CheckBox() self.check_contours.Location = Point(410, offset + vert_spacer * 3 - 2) self.check_contours.Width = 30 self.check_contours.Checked = False button_isoOK = Button() button_isoOK.Text = "Position de l'isocentre" button_isoOK.Font = Font("Arial", 10.25, FontStyle.Bold) button_isoOK.Location = Point(15, offset + vert_spacer * 4) button_isoOK.Width = 375 button_isoOK.Click += self.button_isoOK_Clicked self.check_isoOK = CheckBox() self.check_isoOK.Location = Point(410, offset + vert_spacer * 4 - 2) self.check_isoOK.Width = 30 self.check_isoOK.Checked = False button_beams_Rx = Button() button_beams_Rx.Text = "Faisceaux et prescription" button_beams_Rx.Font = Font("Arial", 10.25, FontStyle.Bold) button_beams_Rx.Location = Point(15, offset + vert_spacer * 5) button_beams_Rx.Width = 375 button_beams_Rx.Click += self.button_beams_Clicked self.check_beams_Rx = CheckBox() self.check_beams_Rx.Location = Point(410, offset + vert_spacer * 5 - 2) self.check_beams_Rx.Width = 30 self.check_beams_Rx.Checked = False button_optimisation = Button() button_optimisation.Text = "Objectifs et paramètres d'optimisation" button_optimisation.Font = Font("Arial", 10.25, FontStyle.Bold) #button_optimisation.TextAlign = HorizontalAlignment.Center button_optimisation.Location = Point(15, offset + vert_spacer * 6) button_optimisation.Width = 375 button_optimisation.Click += self.button_opt_Clicked self.check_optimisation = CheckBox() self.check_optimisation.Location = Point( 410, offset + vert_spacer * 6 - 2) self.check_optimisation.Width = 30 self.check_optimisation.Checked = False self.label_distribution_dose = Label() self.label_distribution_dose.Text = "Distribution de dose et clinical goals" self.label_distribution_dose.Location = Point( 15, offset + vert_spacer * 7) self.label_distribution_dose.Font = Font("Arial", 10.25, FontStyle.Bold) self.label_distribution_dose.AutoSize = True self.check_distribution_dose = CheckBox() self.check_distribution_dose.Location = Point( 410, offset + vert_spacer * 7 - 2) self.check_distribution_dose.Width = 30 self.check_distribution_dose.Checked = False #Count up beamsets bs_text = "" num_bs = 0 for bs in plan.BeamSets: bs_text += bs.DicomPlanLabel + ", " num_bs += 1 self.label_results_header = Label() self.label_results_header.Text = "Résultats" self.label_results_header.Location = Point( 15, offset + vert_spacer * 9) self.label_results_header.Font = Font("Arial", 11, FontStyle.Bold) self.label_results_header.AutoSize = True self.label_results = Label() self.label_results.Text = "Nombre de beamsets dans le plan: " + str( num_bs) + ' (' + bs_text[0:-2] + ')' self.label_results.Location = Point(15, offset + vert_spacer * 10) self.label_results.Font = Font( "Arial", 10, ) self.label_results.AutoSize = True self.label_reminder = Label() self.label_reminder.Text = "Rappel:\nRoulez le script de vérification pour chaque beamset" self.label_reminder.Location = Point(15, offset + vert_spacer * 11) self.label_reminder.Font = Font("Arial", 10.25, FontStyle.Bold) self.label_reminder.ForeColor = Color.Red self.label_reminder.AutoSize = True self.MainWindow.Controls.Add(self.label_bonscan) self.MainWindow.Controls.Add(self.check_bonscan) self.MainWindow.Controls.Add(self.label_scanOK) self.MainWindow.Controls.Add(self.check_scanOK) self.MainWindow.Controls.Add(button_ext) self.MainWindow.Controls.Add(self.check_ext) self.MainWindow.Controls.Add(button_isoOK) self.MainWindow.Controls.Add(self.check_isoOK) self.MainWindow.Controls.Add(button_beams_Rx) self.MainWindow.Controls.Add(self.check_beams_Rx) self.MainWindow.Controls.Add(self.label_contours) self.MainWindow.Controls.Add(self.check_contours) self.MainWindow.Controls.Add(button_optimisation) self.MainWindow.Controls.Add(self.check_optimisation) self.MainWindow.Controls.Add(self.label_distribution_dose) self.MainWindow.Controls.Add(self.check_distribution_dose) self.MainWindow.Controls.Add(self.label_results_header) self.MainWindow.Controls.Add(self.label_results) self.MainWindow.Controls.Add(self.label_reminder) def button_ext_Clicked(self, sender, args): self.message.Text = "Vérification du contour External en cours" self.d['ext_text'] = verification.verify_external_and_overrides() self.label_results_header.Text = "Résultats" self.label_results.Text = self.d['ext_text'] self.label_reminder.Location = Point(self.label_results.Left, self.label_results.Top + 100) self.label_reminder.Text = "Rappel:\nVérifiez que la table (ou la planche ORL) est comprise dans\nle contour External avant de procéder à la prochaine étape" self.message.Text = "" def button_isoOK_Clicked(self, sender, args): uis.show_plan_optimization() uis.show_po_bev() uis.show_po_cg() self.message.Text = "Vérification de l'isocentre" a, b, c, d = verification.verify_isocenter() self.label_results_header.Text = "Résultats" self.d['iso_text'] = a + "\n" + b + "\n" + c + "\n\n" + d self.label_results.Text = self.d['iso_text'] #self.label_results.Text = d['presc_text'] #d['iso_text'] = a #+ "\n" + b + "\n" + c + "\n\n" + d #self.label_results.Text = d['iso_text'] self.label_reminder.Location = Point(self.label_results.Left, self.label_results.Top + 130) self.label_reminder.Text = "Rappel:\nVérifiez le placement de l'isocentre en mode BEV avant de\nprocéder à la prochaine étape" self.message.Text = "" def button_beams_Clicked(self, sender, args): uis.show_plan_optimization() uis.show_po_2D() self.label_reminder.Text = "" self.label_results.Text = "" self.label_results_header.Text = "Résultats" self.message.Text = "Vérification de la prescription en cours" self.d['presc_text'] = verification.verify_prescription() self.label_results.Text = self.d['presc_text'] self.message.Text = "Vérification des faisceaux en cours" a, b, c, d, e = verification.verify_beams( ) #d and e are not needed for a first verification (machine type and energy) self.d['beam_text'] = a + "\n\n" + e + "\n\n" + c self.label_results.Text += "\n\nFaisceaux:\n" + self.d['beam_text'] self.label_reminder.Location = Point( self.label_results.Left, self.label_results.Top + 150 + 15 * b) self.label_reminder.Text = "Rappel:\nVérifiez que les angles de gantry et collimateur sont bien\nchoisis avant de procéder à la prochaine étape" self.message.Text = "" def button_opt_Clicked(self, sender, args): self.label_results_header.Text = "Résultats" self.message.Text = "Vérification des paramètres d'optimisation" a, b, c, d = verification.verify_opt_parameters() #self.d['opt_text'] = a + "\n" + b + "\n" + d + "\n" + c self.d['opt_text'] = c self.label_results.Text = self.d['opt_text'] #d['opt_text'] = a + "\n" + b + "\n" + d + "\n" + c #self.label_results.Text = d['opt_text'] self.label_reminder.Location = Point(self.label_results.Left, self.label_results.Top + 90) self.label_reminder.Text = "Rappel:\nVérifiez tous les objectifs d'optimisation avant de\nprocéder à la prochaine étape" self.message.Text = "" #def cancelClicked(self, sender, args): # self.Close() def levelwindowClicked(self, sender, args): if exam.Series[0].LevelWindow.x == -600 and exam.Series[ 0].LevelWindow.y == 1600: exam.Series[0].LevelWindow = self.lw_dict else: exam.Series[0].LevelWindow = self.lung_dict def referencedoseClicked(self, sender, args): self.message.ForeColor = Color.Black self.message.Text = "En cours" prostate.toggle_reference_dose() self.message.Text = "" def nbfxClicked(self, sender, args): #uis.display_loc_point() uis.open_plan_settings() def printClicked(self, sender, args): #Verify that all boxes have been checked warning = False if self.check_bonscan.Checked: self.d['check_bonscan'] = 'OK' else: warning = True if self.check_scanOK.Checked: self.d['check_scanOK'] = 'OK' else: warning = True if self.check_ext.Checked: self.d['check_ext'] = 'OK' else: warning = True if self.check_isoOK.Checked: self.d['check_isoOK'] = 'OK' else: warning = True if self.check_beams_Rx.Checked: self.d['check_beams_Rx'] = 'OK' else: warning = True if self.check_contours.Checked: self.d['check_contours'] = 'OK' else: warning = True if self.check_optimisation.Checked: self.d['check_optimisation'] = 'OK' else: warning = True if self.check_distribution_dose.Checked: self.d['check_distribution_dose'] = 'OK' else: warning = True self.message.ForeColor = Color.Black self.message.Text = "Impression en cours" report.create_verif1_report(data=self.d) if warning: self.message.ForeColor = Color.Red self.message.Text = "Impression terminé - vérification incomplète" else: self.message.ForeColor = Color.Green self.message.Text = "Impression terminé" def setupOKButtons(self): self.OKbuttonPanel = self.miniPanel(0, 950) printButton = Button() printButton.Text = "Imprimer" printButton.Location = Point(25, 28) #self.AcceptButton = okButton printButton.Click += self.printClicked #cancelButton = Button() #cancelButton.Text = "Annuler" #cancelButton.Location = Point(110,10) #self.CancelButton = cancelButton #cancelButton.Click += self.cancelClicked levelwindowButton = Button() levelwindowButton.Text = "Level/Window" levelwindowButton.Location = Point(25, 28) levelwindowButton.Width = 85 levelwindowButton.Click += self.levelwindowClicked referencedoseButton = Button() referencedoseButton.Text = "Toggle Reference Dose" referencedoseButton.Location = Point(117, 28) referencedoseButton.Width = 140 referencedoseButton.Click += self.referencedoseClicked nbfxButton = Button() nbfxButton.Text = "Nb. de fx" nbfxButton.Location = Point(263, 28) nbfxButton.Width = 70 nbfxButton.Click += self.nbfxClicked self.message = Label() self.message.Text = "" self.message.Location = Point(30, 0) self.message.Font = Font("Arial", 11, FontStyle.Bold) self.message.AutoSize = True #Printing has been disabled for verif 1, simply add the printButton back if you want to reactivate it self.OKbuttonPanel.Controls.Add(nbfxButton) self.OKbuttonPanel.Controls.Add(levelwindowButton) self.OKbuttonPanel.Controls.Add(referencedoseButton) self.OKbuttonPanel.Controls.Add(self.message) #Check for common errors while importing patient, plan, beamset and examination try: patient = lib.get_current_patient() except: message.message_window('Aucun patient sélectionné') return try: plan = lib.get_current_plan() except: message.message_window('Aucun plan sélectionné') return try: beamset = lib.get_current_beamset() except: message.message_window('Aucun beamset sélectionné') return try: exam = lib.get_current_examination() except: message.message_window('Aucun examination trouvé') return try: temp = patient.ModificationInfo.UserName except: message.message_window( 'ATTENTION: Le plan a été modifié depuis la dernière sauvegarde.\n\nFermez cette fenêtre pour poursuivre avec la vérification.' ) #return form = Verif1Window() Application.Run(form)
def finalize_beamset(original_beamset_name, rx_dose, nb_fx, site, ptv_name, color, skip_oars=False): #Note that I tried to pass the beamset and ptv objects directly into this function, but that seems to cause crashing, so now I pass strings instead. patient = lib.get_current_patient() plan = lib.get_current_plan() bs = plan.BeamSets[original_beamset_name] ptv = patient.PatientModel.RegionsOfInterest[ptv_name] beamset_name = ptv_name.split()[1] scan_name = "CT 1" if site == 'Crâne': try: #statistics.stereo_brain_statistics() crane.crane_kbp_write_final_results_to_file( rx_dose, nb_fx, ptv_name) except: #file_path = r'\\radonc.hmr\Departements\Physiciens\Clinique\IMRT\Statistiques\crane.txt' file_path = r'\\radonc.hmr\Departements\Physiciens\Clinique\IMRT\Statistiques\Crane KBP Plan Failure.txt' #Dump to a separate file in case exception was caused by main stats file being open elsewhere output = patient.PatientName + ',' + patient.PatientID + ',' + plan.Name + ',' + bs.DicomPlanLabel + ',' + 'Collecte de statistiques échouée' with open(file_path, 'a') as stat_file: stat_file.write(output + '\n') elif site == 'Poumon': try: statistics.stereo_lung_statistics() except: file_path = r'\\radonc.hmr\Departements\Physiciens\Clinique\IMRT\Statistiques\poumon.txt' output = patient.PatientName + ',' + patient.PatientID + ',' + plan.Name + ',' + bs.DicomPlanLabel + ',' + 'Collecte de statistiques échouée' with open(file_path, 'a') as stat_file: stat_file.write(output + '\n') #Check to see if PTV is drawn using contours (since prescription point placement will fail if the PTV is in voxel format) try: temp = patient.PatientModel.StructureSets[scan_name].RoiGeometries[ ptv_name].PrimaryShape.Contours[0] except: return "PTV en voxels" #Check to see if prescription dose exists in patient (otherwise it is impossible to place PT PRESC correctly) rx_dose_vol = bs.FractionDose.GetRelativeVolumeAtDoseValues( RoiName=ptv_name, DoseValues=[rx_dose * 100 / nb_fx]) if rx_dose_vol[ 0] == 0: #The volume in BodyRS that receives the prescription dose is 0 return "Impossible de placer PT PRESC pour beamset " + original_beamset_name + " car \ndose max inférieure à la dose de prescription" """ #WARNING: This script uses UI scripting. If the user switches focus to another piece of software during # execution, the UI commands will not be completed. The script will not give the desired results # and may crash. # Erase setup beams ui = lib.get_current("ui") #Select the beamset using the combobox on the top right of RayStation. Note that in plans with only one beamset, this combobox does not exist! So we have to use try. try: ui.SelectionBar.ComboBox_RadiationSets.ToggleButton.Click() ui.SelectionBar.ComboBox_RadiationSets.Popup.ComboBoxItem[original_beamset_name].Select() ui.SelectionBar.ComboBox_RadiationSets.ToggleButton.Click() except: no_big_deal = True ui.MenuItem[2].Button_PlanDesign.Click() #Select Plan Design tab try: ui.Workspace.TabControl['Beams'].TabItem['Setup Beams'].Select() for setup_beam in bs.PatientSetup.SetupBeams: ui.Workspace.TabControl['Beams'].Button_Delete.Click() except: no_big_deal = True """ if site == "Poumon": expi_scan = "CT 2" #Used for transfer of contours to expi scan for Mosaiq if not skip_oars: # Rename OAR contours for SuperBridge transfer roi_list = check_rois(site) if site == "Poumon": #Add ITV to transfer list and prepare to copy contours to expi scan itv_name = "I" + ptv_name[1:] roi_list.append(itv_name) for rois in patient.PatientModel.RegionsOfInterest: if rois.Name in roi_list: rois.Name += "*" if site == "Poumon": patient.PatientModel.CopyRoiGeometry( SourceExamination=patient.Examinations[scan_name], TargetExamination=patient.Examinations[expi_scan], RoiName=rois.Name) # Rename beamset bs.DicomPlanLabel = beamset_name # Rename isodose ROI and add * to it and PTV isodose_roi = patient.PatientModel.RegionsOfInterest["isodose " + beamset_name] if site == "Prostate" or site == "ORL": isodose_roi.Name = "ISO %s %.2fGy*" % (beamset_name, rx_dose * 0.95) else: isodose_roi.Name = "ISO %s %.2fGy*" % (beamset_name, rx_dose) isodose_roi.Name = isodose_roi.Name.replace('.00Gy', 'Gy') ptv.Name += '*' # Rename ISO MOELLE 48Gy (ORL only) if roi.roi_exists("isodose moelle"): patient.PatientModel.RegionsOfInterest[ 'isodose moelle'].Name = "ISO MOELLE 48Gy*" # Copy PTV and isodose ROIs to second beamset for patient positioning at treatment room (lung cases only) if site == "Poumon": patient.PatientModel.CopyRoiGeometry( SourceExamination=patient.Examinations[scan_name], TargetExamination=patient.Examinations[expi_scan], RoiName=ptv.Name) patient.PatientModel.CopyRoiGeometry( SourceExamination=patient.Examinations[scan_name], TargetExamination=patient.Examinations[expi_scan], RoiName=isodose_roi.Name) # Add comment for Superbridge transfer (except for prostate cases, since they are not measured with the ArcCheck) if site != "Prostate": bs.Prescription.Description = "VMAT" # Create DSP and PT PRESC poi_name = 'PT PRESC ' + beamset_name poi.create_poi({ 'x': 0, 'y': 0, 'z': 0 }, poi_name, color=color, examination=patient.Examinations[scan_name]) bs.CreateDoseSpecificationPoint(Name="DSP", Coordinates={ 'x': 0, 'y': 0, 'z': 0 }) # Move PT PRESC to a point that receives correct dose per fraction and prescribe poi.place_prescription_point(target_fraction_dose=rx_dose * 100 / nb_fx, ptv_name=ptv.Name, poi_name=poi_name, beamset=bs, exam=patient.Examinations[scan_name]) bs.AddDosePrescriptionToPoi(PoiName=poi_name, DoseValue=rx_dose * 100) # Move DSP to coordinates of PT PRESC and assign to all beams point = poi.get_poi_coordinates(poi_name) dsp = [x for x in bs.DoseSpecificationPoints][0] dsp.Name = "DSP " + beamset_name dsp.Coordinates = point.value for beam in bs.Beams: beam.SetDoseSpecificationPoint(Name=dsp.Name) bs.ComputeDose( ComputeBeamDoses=True, DoseAlgorithm="CCDose", ForceRecompute=True ) # Dose is recalculated to show beam dose at spec point (otherwise not displayed) return ("You just finalized the beamset named " + bs.DicomPlanLabel)
def final_launcher(): class FinalisationWindow(Form): def __init__(self): self.Text = "Finalisation des plans RayStation version 1.0" self.Width = 900 self.Height = 780 self.setupHeaderWindow() self.setupBeamset1Window() self.setupBeamset2Window() self.setupBeamset3Window() self.setupOKButtons() self.Controls.Add(self.HeaderWindow) self.Controls.Add(self.Beamset1Window) self.Controls.Add(self.Beamset2Window) self.Controls.Add(self.Beamset3Window) self.Controls.Add(self.OKbuttonPanel) def beamsetPanel(self, x, y): panel = Panel() panel.Width = 300 panel.Height = 600 panel.Location = Point(x, y) panel.BorderStyle = BorderStyle.FixedSingle return panel def medPanel(self, x, y): panel = Panel() panel.Width = 300 panel.Height = 300 panel.Location = Point(x, y) panel.BorderStyle = BorderStyle.FixedSingle return panel def miniPanel(self, x, y): panel = Panel() panel.Width = 900 panel.Height = 60 panel.Location = Point(x, y) panel.BorderStyle = BorderStyle.None return panel def setupHeaderWindow(self): self.HeaderWindow = self.miniPanel(0, 0) self.PatientIDHeader = Label() self.PatientIDHeader.Text = "Patient: " + patient.PatientName.replace( '^', ', ') + " Plan: " + plan.Name self.PatientIDHeader.Location = Point(25, 25) self.PatientIDHeader.Font = Font("Arial", 12, FontStyle.Bold) self.PatientIDHeader.AutoSize = True self.HeaderWindow.Controls.Add(self.PatientIDHeader) def setupBeamset1Window(self): self.Beamset1Window = self.beamsetPanel(0, 60) vert_spacer = 30 offset = 28 self.beamset1Header = Label() self.beamset1Header.Text = "Beamset 1" self.beamset1Header.Location = Point(25, 25) self.beamset1Header.Font = Font("Arial", 11, FontStyle.Bold) self.beamset1Header.AutoSize = True self.Label1 = Label() self.Label1.Text = "Beamset:" self.Label1.Location = Point(25, vert_spacer + offset) self.Label1.Font = Font("Arial", 10) self.Label1.AutoSize = True self.beamset1combo = ComboBox() self.beamset1combo.Parent = self self.beamset1combo.Size = Size(120, 40) self.beamset1combo.Location = Point(100, vert_spacer + offset) self.beamset1combo.Text = "Choisissez beamset" self.beamset1combo.TextChanged += self.beamset1selectionChanged self.Label2 = Label() self.Label2.Text = "Site:" self.Label2.Location = Point(25, 2 * vert_spacer + offset) self.Label2.Font = Font("Arial", 10) self.Label2.AutoSize = True self.site1combo = ComboBox() self.site1combo.Parent = self self.site1combo.Size = Size(120, 40) self.site1combo.Location = Point(100, 2 * vert_spacer + offset) self.site1combo.Items.Add("Prostate") self.site1combo.Items.Add("Poumon") self.site1combo.Items.Add("Crâne") self.site1combo.Items.Add("Foie") self.site1combo.Items.Add("Vertebre") self.site1combo.Items.Add("ORL") self.site1combo.Text = "Choisissez site" self.site1combo.TextChanged += self.site1selectionChanged self.Label3 = Label() self.Label3.Text = "Gy en" self.Label3.Location = Point(70, 3 * vert_spacer + offset) self.Label3.Font = Font("Arial", 10) self.Label3.AutoSize = True self.Label4 = Label() self.Label4.Text = "fractions" self.Label4.Location = Point(160, 3 * vert_spacer + offset) self.Label4.Font = Font("Arial", 10) self.Label4.AutoSize = True self.dosebox = TextBox() self.dosebox.Text = "----" self.dosebox.Location = Point(25, 3 * vert_spacer + offset) self.dosebox.Width = 40 self.fxbox = TextBox() self.fxbox.Text = "----" self.fxbox.Location = Point(115, 3 * vert_spacer + offset) self.fxbox.Width = 40 self.Label5 = Label() self.Label5.Text = "Nom du PTV: " self.Label5.Location = Point(25, 4.75 * vert_spacer + offset) self.Label5.Font = Font("Arial", 10) self.Label5.AutoSize = True self.Label6 = Label() self.Label6.Text = "Contour isodose: " self.Label6.Location = Point(25, 5.5 * vert_spacer + offset) self.Label6.Font = Font("Arial", 10) self.Label6.AutoSize = True self.Label7 = Label() self.Label7.Text = "Nom final du beamset: " self.Label7.Location = Point(25, 6.25 * vert_spacer + offset) self.Label7.Font = Font("Arial", 10) self.Label7.AutoSize = True self.Label8 = Label() self.Label8.Text = "OARs pour transfert SuperBridge: " self.Label8.Location = Point(25, 8 * vert_spacer + offset) self.Label8.Font = Font("Arial", 10) self.Label8.AutoSize = True self.Beamset1Window.Controls.Add(self.beamset1Header) self.Beamset1Window.Controls.Add(self.Label1) self.Beamset1Window.Controls.Add(self.beamset1combo) self.Beamset1Window.Controls.Add(self.Label2) self.Beamset1Window.Controls.Add(self.site1combo) self.Beamset1Window.Controls.Add(self.Label3) self.Beamset1Window.Controls.Add(self.Label4) self.Beamset1Window.Controls.Add(self.dosebox) self.Beamset1Window.Controls.Add(self.fxbox) self.Beamset1Window.Controls.Add(self.Label5) self.Beamset1Window.Controls.Add(self.Label6) self.Beamset1Window.Controls.Add(self.Label7) self.Beamset1Window.Controls.Add(self.Label8) def setupBeamset2Window(self): self.Beamset2Window = self.medPanel(300, 60) vert_spacer = 30 offset = 28 self.Beamset2Header = Label() self.Beamset2Header.Text = "Beamset 2" self.Beamset2Header.Location = Point(25, 25) self.Beamset2Header.Font = Font("Arial", 11, FontStyle.Bold) self.Beamset2Header.AutoSize = True self.Label21 = Label() self.Label21.Text = "Beamset:" self.Label21.Location = Point(25, vert_spacer + offset) self.Label21.Font = Font("Arial", 10) self.Label21.AutoSize = True self.beamset2combo = ComboBox() self.beamset2combo.Parent = self self.beamset2combo.Size = Size(120, 40) self.beamset2combo.Location = Point(100, vert_spacer + offset) self.beamset2combo.Text = "Choisissez beamset" self.beamset2combo.TextChanged += self.beamset2selectionChanged self.Label22 = Label() self.Label22.Text = "Site:" # Site for Beamset2 is copied from Beamset1 self.Label22.Location = Point(25, 2 * vert_spacer + offset) self.Label22.Font = Font("Arial", 10) self.Label22.AutoSize = True self.Label23 = Label() self.Label23.Text = "Gy en" self.Label23.Location = Point(70, 3 * vert_spacer + offset) self.Label23.Font = Font("Arial", 10) self.Label23.AutoSize = True self.Label24 = Label() self.Label24.Text = "fractions" self.Label24.Location = Point(160, 3 * vert_spacer + offset) self.Label24.Font = Font("Arial", 10) self.Label24.AutoSize = True self.dosebox2 = TextBox() self.dosebox2.Text = "----" self.dosebox2.Location = Point(25, 3 * vert_spacer + offset) self.dosebox2.Width = 40 self.fxbox2 = TextBox() self.fxbox2.Text = "----" self.fxbox2.Location = Point(115, 3 * vert_spacer + offset) self.fxbox2.Width = 40 self.Label25 = Label() self.Label25.Text = "Nom du PTV: " self.Label25.Location = Point(25, 4.75 * vert_spacer + offset) self.Label25.Font = Font("Arial", 10) self.Label25.AutoSize = True self.Label26 = Label() self.Label26.Text = "Contour isodose: " self.Label26.Location = Point(25, 5.5 * vert_spacer + offset) self.Label26.Font = Font("Arial", 10) self.Label26.AutoSize = True self.Label27 = Label() self.Label27.Text = "Nom final du beamset: " self.Label27.Location = Point(25, 6.25 * vert_spacer + offset) self.Label27.Font = Font("Arial", 10) self.Label27.AutoSize = True self.Beamset2Window.Controls.Add(self.Beamset2Header) self.Beamset2Window.Controls.Add(self.Label21) self.Beamset2Window.Controls.Add(self.beamset2combo) self.Beamset2Window.Controls.Add(self.Label22) self.Beamset2Window.Controls.Add(self.Label23) self.Beamset2Window.Controls.Add(self.Label24) self.Beamset2Window.Controls.Add(self.dosebox2) self.Beamset2Window.Controls.Add(self.fxbox2) self.Beamset2Window.Controls.Add(self.Label25) self.Beamset2Window.Controls.Add(self.Label26) self.Beamset2Window.Controls.Add(self.Label27) def setupBeamset3Window(self): self.Beamset3Window = self.medPanel(300, 360) vert_spacer = 30 offset = 28 self.Beamset3Header = Label() self.Beamset3Header.Text = "Beamset 3" self.Beamset3Header.Location = Point(25, 25) self.Beamset3Header.Font = Font("Arial", 11, FontStyle.Bold) self.Beamset3Header.AutoSize = True self.Label31 = Label() self.Label31.Text = "Beamset:" self.Label31.Location = Point(25, vert_spacer + offset) self.Label31.Font = Font("Arial", 10) self.Label31.AutoSize = True self.beamset3combo = ComboBox() self.beamset3combo.Parent = self self.beamset3combo.Size = Size(120, 40) self.beamset3combo.Location = Point(100, vert_spacer + offset) self.beamset3combo.Text = "Choisissez beamset" self.beamset3combo.TextChanged += self.beamset3selectionChanged self.Label32 = Label() self.Label32.Text = "Site:" # Site for Beamset3 is copied from Beamset1 self.Label32.Location = Point(25, 2 * vert_spacer + offset) self.Label32.Font = Font("Arial", 10) self.Label32.AutoSize = True self.Label33 = Label() self.Label33.Text = "Gy en" self.Label33.Location = Point(70, 3 * vert_spacer + offset) self.Label33.Font = Font("Arial", 10) self.Label33.AutoSize = True self.Label34 = Label() self.Label34.Text = "fractions" self.Label34.Location = Point(160, 3 * vert_spacer + offset) self.Label34.Font = Font("Arial", 10) self.Label34.AutoSize = True self.dosebox3 = TextBox() self.dosebox3.Text = "----" self.dosebox3.Location = Point(25, 3 * vert_spacer + offset) self.dosebox3.Width = 40 self.fxbox3 = TextBox() self.fxbox3.Text = "----" self.fxbox3.Location = Point(115, 3 * vert_spacer + offset) self.fxbox3.Width = 40 self.Label35 = Label() self.Label35.Text = "Nom du PTV: " self.Label35.Location = Point(25, 4.75 * vert_spacer + offset) self.Label35.Font = Font("Arial", 10) self.Label35.AutoSize = True self.Label36 = Label() self.Label36.Text = "Contour isodose: " self.Label36.Location = Point(25, 5.5 * vert_spacer + offset) self.Label36.Font = Font("Arial", 10) self.Label36.AutoSize = True self.Label37 = Label() self.Label37.Text = "Nom final du beamset: " self.Label37.Location = Point(25, 6.25 * vert_spacer + offset) self.Label37.Font = Font("Arial", 10) self.Label37.AutoSize = True self.Beamset3Window.Controls.Add(self.Beamset3Header) self.Beamset3Window.Controls.Add(self.Label31) self.Beamset3Window.Controls.Add(self.beamset3combo) self.Beamset3Window.Controls.Add(self.Label32) self.Beamset3Window.Controls.Add(self.Label33) self.Beamset3Window.Controls.Add(self.Label34) self.Beamset3Window.Controls.Add(self.dosebox3) self.Beamset3Window.Controls.Add(self.fxbox3) self.Beamset3Window.Controls.Add(self.Label35) self.Beamset3Window.Controls.Add(self.Label36) self.Beamset3Window.Controls.Add(self.Label37) def beamset1selectionChanged(self, sender, args): try: beamset = plan.BeamSets[self.beamset1combo.Text] except: return # Attempt to determine site and prescription dose_scale = 100.0 if roi.roi_exists("PROSTATE"): self.site1combo.Text = "Prostate" dose_scale = 95.0 elif roi.roi_exists("CAVITE ORALE"): self.site1combo.Text = "ORL" elif roi.roi_exists("CERVEAU"): self.site1combo.Text = "Crâne" elif roi.roi_exists("BR SOUCHE"): self.site1combo.Text = "Poumon" elif roi.roi_exists("FOIE EXPI-GTV"): self.site1combo.Text = "Foie" try: self.dosebox.Text = "%.2f" % ( (beamset.Prescription.PrimaryDosePrescription.DoseValue) / dose_scale) except: hello = True self.fxbox.Text = str( beamset.FractionationPattern.NumberOfFractions) # Determine PTV and isodose contours if beamset.Prescription.PrimaryDosePrescription.PrescriptionType == 'DoseAtVolume': ptv_name = beamset.Prescription.PrimaryDosePrescription.OnStructure.Name new_beamset_name = ptv_name.split()[1] self.Label5.Text = "Nom du PTV: " + ptv_name if roi.roi_exists("isodose " + new_beamset_name): self.Label6.Text = "Contour isodose: isodose " + new_beamset_name else: self.Label6.Text = "Contour isodose: pas trouvé" self.Label7.Text = "Nom final du beamset: " + new_beamset_name else: self.Label5.Text = "Nom du PTV: Rx pas sur un ROI" self.Label7.Text = "Nom final du beamset: impossible de continuer" def beamset2selectionChanged(self, sender, args): #Set text windows to default values if beamset selection is cleared if self.beamset2combo.Text == "Choisissez beamset": self.dosebox2.Text = "----" self.fxbox2.Text = "----" self.Label25.Text = "Nom du PTV: " self.Label26.Text = "Contour isodose: " self.Label27.Text = "Nom final du beamset: " return try: beamset = plan.BeamSets[self.beamset2combo.Text] except: return self.Label22.Text = "Site: " + self.site1combo.Text #Copy directly from first beamset # Attempt to determine site and prescription if roi.roi_exists("PROSTATE"): dose_scale = 95.0 else: dose_scale = 100.0 try: self.dosebox2.Text = "%.2f" % ( (beamset.Prescription.PrimaryDosePrescription.DoseValue) / dose_scale) except: hello = True self.fxbox2.Text = str( beamset.FractionationPattern.NumberOfFractions) # Determine PTV and isodose contours if beamset.Prescription.PrimaryDosePrescription.PrescriptionType == 'DoseAtVolume': ptv_name = beamset.Prescription.PrimaryDosePrescription.OnStructure.Name new_beamset_name = ptv_name.split()[1] self.Label25.Text = "Nom du PTV: " + ptv_name if roi.roi_exists("isodose " + new_beamset_name): self.Label26.Text = "Contour isodose: isodose " + new_beamset_name else: self.Label26.Text = "Contour isodose: pas trouvé" self.Label27.Text = "Nom final du beamset: " + new_beamset_name else: self.Label25.Text = "Nom du PTV: Rx pas sur un ROI" self.Label27.Text = "Nom final du beamset: impossible de continuer" def beamset3selectionChanged(self, sender, args): #Set text windows to default values if beamset selection is cleared if self.beamset3combo.Text == "Choisissez beamset": self.dosebox3.Text = "----" self.fxbox3.Text = "----" self.Label35.Text = "Nom du PTV: " self.Label36.Text = "Contour isodose: " self.Label37.Text = "Nom final du beamset: " return try: beamset = plan.BeamSets[self.beamset3combo.Text] except: return self.Label32.Text = "Site: " + self.site1combo.Text #Copy directly from first beamset # Attempt to determine site and prescription if roi.roi_exists("PROSTATE"): dose_scale = 95.0 else: dose_scale = 100.0 try: self.dosebox3.Text = "%.2f" % ( (beamset.Prescription.PrimaryDosePrescription.DoseValue) / dose_scale) except: hello = True self.fxbox3.Text = str( beamset.FractionationPattern.NumberOfFractions) # Determine PTV and isodose contours if beamset.Prescription.PrimaryDosePrescription.PrescriptionType == 'DoseAtVolume': ptv_name = beamset.Prescription.PrimaryDosePrescription.OnStructure.Name new_beamset_name = ptv_name.split()[1] self.Label35.Text = "Nom du PTV: " + ptv_name if roi.roi_exists("isodose " + new_beamset_name): self.Label36.Text = "Contour isodose: isodose " + new_beamset_name else: self.Label36.Text = "Contour isodose: pas trouvé" self.Label37.Text = "Nom final du beamset: " + new_beamset_name else: self.Label35.Text = "Nom du PTV: Rx pas sur un ROI" self.Label37.Text = "Nom final du beamset: impossible de continuer" def site1selectionChanged(self, sender, args): site_list = [ "Prostate", "Poumon", "Crâne", "Foie", "Vertebre", "ORL" ] if self.site1combo.Text in site_list: roi_list = check_rois(self.site1combo.Text) else: return found_rois = "" for roi in patient.PatientModel.RegionsOfInterest: if roi.Name in roi_list: found_rois += "\n " + roi.Name self.Label8.Text = "OARs pour transfert SuperBridge: " + found_rois if self.beamset2combo.Text != "Choisissez beamset": self.Label22.Text = "Site: " + self.site1combo.Text #Copy from first beamset if self.beamset3combo.Text != "Choisissez beamset": self.Label32.Text = "Site: " + self.site1combo.Text #Copy from first beamset def cancelClicked(self, sender, args): self.Close() def okClicked(self, sender, args): #Check for errors site_list = [ "Prostate", "Poumon", "Crâne", "Foie", "Vertebre", "ORL" ] self.message.ForeColor = Color.Black error_text = None if self.beamset1combo.Text == "Choisissez beamset": error_text = "Aucun beamset choisi pour Beamset1" elif self.beamset1combo.Text == self.beamset2combo.Text or self.beamset1combo.Text == self.beamset3combo.Text: error_text = "Même beamset choisi deux fois" elif self.beamset2combo.Text == self.beamset3combo.Text and self.beamset2combo.Text != "Choisissez beamset": error_text = "Même beamset choisi pour Beamset 2 et Beamset 3" elif self.site1combo.Text == "Choisissez site": error_text = "Il faut indiquer le site" elif self.site1combo.Text not in site_list: error_text = "Le site indiqué n'est pas supporté" elif "Rx pas sur un ROI" in self.Label5.Text: error_text = "Impossible d'identifier PTV pour Beamset 1" elif self.beamset2combo.Text != "Choisissez beamset" and "Rx pas sur un ROI" in self.Label25.Text: error_text = "Impossible d'identifier PTV pour Beamset 2" elif self.beamset3combo.Text != "Choisissez beamset" and "Rx pas sur un ROI" in self.Label35.Text: error_text = "Impossible d'identifier PTV pour Beamset 3" elif "pas trouvé" in self.Label6.Text: error_text = "Contour isodose pas trouvé pour Beamset 1" elif self.beamset2combo.Text != "Choisissez beamset" and "pas trouvé" in self.Label26.Text: error_text = "Contour isodose pas trouvé pour Beamset 2" elif self.beamset3combo.Text != "Choisissez beamset" and "pas trouvé" in self.Label36.Text: error_text = "Contour isodose pas trouvé pour Beamset 3" elif self.dosebox.Text == "----": error_text = "Dose pas définie pour Beamset 1" elif self.dosebox2.Text == "----" and self.beamset2combo.Text != "Choisissez beamset": error_text = "Dose pas définie pour Beamset 2" elif self.dosebox3.Text == "----" and self.beamset3combo.Text != "Choisissez beamset": error_text = "Dose pas définie pour Beamset 3" elif self.fxbox.Text == "----": error_text = "Dose pas définie pour Beamset 1" elif self.fxbox2.Text == "----" and self.beamset2combo.Text != "Choisissez beamset": error_text = "Nombre de fx pas définie pour Beamset 2" elif self.fxbox3.Text == "----" and self.beamset3combo.Text != "Choisissez beamset": error_text = "Nombre de fx pas définie pour Beamset 3" try: temp = float(self.dosebox.Text) except: error_text = "Dose de prescription indiquée pour Beamset 1 n'est pas un chiffre" if self.beamset2combo.Text != "Choisissez beamset": try: temp = float(self.dosebox2.Text) except: error_text = "Dose de prescription indiquée pour Beamset 2 n'est pas un chiffre" if self.beamset3combo.Text != "Choisissez beamset": try: temp = float(self.dosebox3.Text) except: error_text = "Dose de prescription indiquée pour Beamset 3 n'est pas un chiffre" try: temp = int(self.fxbox.Text) except: error_text = "Nombre de fractions pour Beamset 1 n'est pas un chiffre" if self.beamset2combo.Text != "Choisissez beamset": try: temp = int(self.fxbox2.Text) except: error_text = "Nombre de fractions pour Beamset 2 n'est pas un chiffre" if self.beamset3combo.Text != "Choisissez beamset": try: temp = int(self.fxbox3.Text) except: error_text = "Nombre de fractions pour Beamset 3 n'est pas un chiffre" if self.message.Text == "Finalisation terminée" or self.message.Text == "La finalisation est déjà terminée! Cliquez sur Annuler.": error_text = "La finalisation est déjà terminée! Cliquez sur Annuler." if error_text != None: self.message.Text = error_text self.message.ForeColor = Color.Red return beamset_name = self.beamset1combo.Text beamset = plan.BeamSets[beamset_name] rx_dose = float(self.dosebox.Text) nb_fx = int(self.fxbox.Text) site = self.site1combo.Text ptv_name = beamset.Prescription.PrimaryDosePrescription.OnStructure.Name #Finalize one or more beamsets self.message.Text = "Finalisation du beamset %s en cours" % beamset_name result_text = finalize_beamset(beamset_name, rx_dose, nb_fx, site, ptv_name, color="Red") if "Impossible" in result_text: #Dose max too low to place PT PRESC self.message.Text = result_text self.message.ForeColor = Color.Red return if self.beamset2combo.Text != "Choisissez beamset": beamset_name = self.beamset2combo.Text beamset = plan.BeamSets[beamset_name] rx_dose = float(self.dosebox2.Text) nb_fx = int(self.fxbox2.Text) site = self.site1combo.Text ptv_name = beamset.Prescription.PrimaryDosePrescription.OnStructure.Name self.message.Text = "Finalisation du beamset %s en cours" % beamset_name result_text = finalize_beamset(beamset_name, rx_dose, nb_fx, site, ptv_name, color="Green", skip_oars=True) if "Impossible" in result_text: #Dose max too low to place PT PRESC self.message.Text = result_text self.message.ForeColor = Color.Red return if self.beamset3combo.Text != "Choisissez beamset": beamset_name = self.beamset3combo.Text beamset = plan.BeamSets[beamset_name] rx_dose = float(self.dosebox3.Text) nb_fx = int(self.fxbox3.Text) site = self.site1combo.Text ptv_name = beamset.Prescription.PrimaryDosePrescription.OnStructure.Name self.message.Text = "Finalisation du beamset %s en cours" % beamset_name result_text = finalize_beamset(beamset_name, rx_dose, nb_fx, site, ptv_name, color="Skyblue", skip_oars=True) if "Impossible" in result_text: #Dose max too low to place PT PRESC self.message.Text = result_text self.message.ForeColor = Color.Red return if result_text == "PTV en voxels": self.message.Text = "PTV doit être en contours (pas en voxels).\nUtiliser l'outil Simplify Contours pour le convertir et reessayer." self.message.ForeColor = Color.Red else: self.message.Text = "Finalisation terminée" self.message.ForeColor = Color.Green def setupOKButtons(self): self.OKbuttonPanel = self.miniPanel(0, 660) okButton = Button() okButton.Text = "Finaliser" okButton.Location = Point(100, 25) self.AcceptButton = okButton okButton.Click += self.okClicked cancelButton = Button() cancelButton.Text = "Annuler" cancelButton.Location = Point(200, 25) self.CancelButton = cancelButton cancelButton.Click += self.cancelClicked self.message = Label() self.message.Text = "" self.message.Location = Point(300, 28) self.message.Font = Font("Arial", 11, FontStyle.Bold) self.message.AutoSize = True self.OKbuttonPanel.Controls.Add(okButton) self.OKbuttonPanel.Controls.Add(cancelButton) self.OKbuttonPanel.Controls.Add(self.message) #Automatically populate beamset selection comboboxes self.beamset2combo.Items.Add("Choisissez beamset") self.beamset3combo.Items.Add("Choisissez beamset") num_beamsets = 0 for bs in plan.BeamSets: num_beamsets += 1 self.beamset1combo.Items.Add(bs.DicomPlanLabel) self.beamset2combo.Items.Add(bs.DicomPlanLabel) self.beamset3combo.Items.Add(bs.DicomPlanLabel) if num_beamsets > 0: self.beamset1combo.Text = plan.BeamSets[0].DicomPlanLabel if num_beamsets > 1: self.beamset2combo.Text = plan.BeamSets[1].DicomPlanLabel if num_beamsets > 2: self.beamset3combo.Text = plan.BeamSets[2].DicomPlanLabel #Check for common errors while importing patient, plan, beamset and examination try: patient = lib.get_current_patient() except: message.message_window('Aucun patient sélectionné') return try: plan = lib.get_current_plan() except: message.message_window('Aucun plan sélectionné') return try: exam = lib.get_current_examination() except: message.message_window('Aucun examination trouvé') return try: beamset = lib.get_current_beamset() ptv_name = beamset.Prescription.PrimaryDosePrescription.OnStructure.Name new_beamset_name = ptv_name.split()[1] except: message.message_window( "Le nom du PTV (%s) n'est pas dans le format PTV A1 15Gy.\nRenommez le PTV ou changez la prescription avant de lancer le script de finalisation." % ptv_name) return form = FinalisationWindow() Application.Run(form)
def verify_isocenter(): exam = lib.get_current_examination() plan = lib.get_current_plan() beamset = lib.get_current_beamset() loc_coords = None iso_coords = None # Isocenter and localization point try: loc_point_name = beamset.PatientSetup.LocalizationPoiGeometrySource.LocalizationPoiGeometry.OfPoi.Name loc_coords = poi.get_poi_coordinates(loc_point_name, exam) except: loc_point_name = "Aucun point de localisation trouvé" num_iso = 0 if poi.poi_exists("ISO", exam): iso_point_name = "ISO" num_iso += 1 if poi.poi_exists("ISO B1", exam): iso_point_name = "ISO B1" num_iso += 1 if num_iso == 0: iso_point_name = "Aucun point trouvé pour l'isocentre" elif num_iso > 1: iso_point_name = "Plus qu'un candidat trouvé pour l'isocentre" elif num_iso == 1: iso_coords = poi.get_poi_coordinates(iso_point_name, exam) if loc_coords is None: loc_poi_text = "Aucun point de localisation trouvé" else: if loc_point_name == "REF SCAN": loc_poi_text = "POI localization: %s (%.2f, %.2f, %.2f)" % ( loc_point_name, loc_coords.x, loc_coords.z, -1 * loc_coords.y ) #Fewer spaces to maintain alignment of results else: loc_poi_text = "POI localization: %s (%.2f, %.2f, %.2f)" % ( loc_point_name, loc_coords.x, loc_coords.z, -1 * loc_coords.y) if iso_coords is None: iso_poi_text = iso_point_name else: iso_poi_text = "Coordonnées POI %s (%.2f, %.2f, %.2f)" % ( iso_point_name, iso_coords.x, iso_coords.z, -1 * iso_coords.y) shift_text = "" #Check to find shift from reference point to isocenter if loc_coords != None and iso_coords != None: if iso_point_name != loc_point_name: if (loc_coords.x - iso_coords.x == 0) and (loc_coords.y - iso_coords.y == 0) and (loc_coords.z - iso_coords.z == 0): shift_text += "Le point de localisation et le point " + iso_point_name + " ont les mêmes coordonnés" else: shift_text += "Shift de %.2fcm, %.2fcm, %.2fcm entre point de localisation\n et point %s" % ( loc_coords.x - iso_coords.x, loc_coords.z - iso_coords.z, iso_coords.y - loc_coords.y, iso_point_name) # Beam isocenters beam_iso_text = "Coordonnées faisceaux " mismatch = False try: for i, beam in enumerate( beamset.Beams ): #Verify that coordinates are the same for all beams if i > 0: old_iso_poi = beam_iso_poi beam_iso_poi = [ x for x in beam.PatientToBeamMapping.IsocenterPoint ] if i > 0: if abs(beam_iso_poi[0].Value - old_iso_poi[0].Value ) > 0.005 or abs(beam_iso_poi[1].Value - old_iso_poi[1].Value) > 0.005 or abs( beam_iso_poi[2].Value - old_iso_poi[2].Value) > 0.005: beam_iso_text = "Coordonnées faisceaux: Coordonnées différentes pour faisceaux " + beamset.Beams[ i - 1].Name + " et " + beam.Name + "!" mismatch = True break if not mismatch: beam_iso_text += "(%.2f, %.2f, %.2f)" % ( beam_iso_poi[0].Value, beam_iso_poi[2].Value, beam_iso_poi[1].Value * -1) except: beam_iso_text += "Coordonnées pas trouvés" # Verify if beams are centered on isocenter point if beam_iso_poi != None and iso_coords != None and not mismatch: iso_shift_x = iso_coords.x - beam_iso_poi[0].Value iso_shift_y = iso_coords.y - beam_iso_poi[1].Value iso_shift_z = iso_coords.z - beam_iso_poi[2].Value if abs(iso_shift_x) > 0.005 or abs(iso_shift_y) > 0.005 or abs( iso_shift_z) > 0.005: shift_text += "\nShift de %.2fcm, %.2fcm, %.2fcm entre les faisceaux et\n le point %s" % ( -1 * iso_shift_x, -1 * iso_shift_z, iso_shift_y, iso_point_name) else: shift_text += "\nTous les faisceaux partagent les coordonnés du point " + iso_point_name return loc_poi_text, iso_poi_text, beam_iso_text, shift_text
def verify_segments(): plan = lib.get_current_plan() beamset = lib.get_current_beamset() opt = plan.PlanOptimizations[beamset.Number - 1] segment_results = "" area_results = "" MU_results = "" incompatible_MLC_results = "" segment_error = False # If one or more beams does not have valid segments area_error = False # If one or more segments has an area below the requested minimum segment are MU_error = False # If one or more segments has a number of MU inferior to requested minimum segment MU incompatible_MLC = False # If the MLC is non-standard and its width or number of leaf pairs is unknown output = "" # The actual output returned by the function min_seg_area = opt.OptimizationParameters.SegmentConversion.MinSegmentArea num_segments = 0 for beam in beamset.Beams: #Check to see if beam has segments try: segment = beam.Segments[0] verify_leaves = True except: segment_results += beam.Name + " " segment_error = True verify_leaves = False if beamset.DeliveryTechnique != "Arc": #We only check if all beams have segments for VMAT plans #Check which kind of MLC is used for area calculations if beam.MachineReference.MachineName == 'BeamMod': leaf_width = 0.4 num_leaf_pairs = 40 elif beam.MachineReference.MachineName == 'Infinity': leaf_width = 0.5 num_leaf_pairs = 80 else: area_results = "Impossible de vérifier l'aire des segments pour ce linac" verify_leaves = False incompatible_MLC = True incompatible_MLC_results = "Impossible de vérifier le faisceau " + beam.Name + " car la machine est inconnue" #Verify segment area and MU if verify_leaves: for segment in beam.Segments: num_segments += 1 segment_area = 0 for i in range(num_leaf_pairs - 1): #Area calculation segment_area += ( segment.LeafPositions[1][i] - segment.LeafPositions[0][i]) * leaf_width if segment_area < min_seg_area: area_error = True area_results += " %s segment %d (%.2fcm^2)\n" % ( beam.Name, segment.SegmentNumber + 1, segment_area) if beam.BeamMU * segment.RelativeWeight < opt.OptimizationParameters.SegmentConversion.MinSegmentMUPerFraction: MU_error = True MU_results += " %s segment %d (%.2f MUs)\n" % ( beam.Name, segment.SegmentNumber + 1, beam.BeamMU * segment.RelativeWeight) #Once the area and MU verifications are complete, output results if segment_error: output = "Les champs suivants n'ont pas de segments valides:\n " + segment_results return output elif beamset.DeliveryTechnique == "Arc": output = "Tous les faisceaux ont des segments valides." return output elif incompatible_MLC: output = incompatible_MLC_results return output else: #For IMRT plans that have compatible MLCs output += "Tous les faisceaux ont des segments valides\n" output += "Il y a un total de %d segments (nb. demandé: %d)\n" % ( num_segments, opt.OptimizationParameters.SegmentConversion.MaxNumberOfSegments) #Report any errors in segment size or min MUs if area_error: output += "Les segments suivants sont plus petits que " + str( min_seg_area) + "cm^2: \n" + area_results else: output += "Tous les segments sont plus grand que %dcm^2\n" % min_seg_area if MU_error: output += "Les segments suivants ont moins que " + str( opt.OptimizationParameters.SegmentConversion. MinSegmentMUPerFraction) + "UMs: \n" + MU_results else: output += "Tous les segments ont plus que %dUMs\n" % opt.OptimizationParameters.SegmentConversion.MinSegmentMUPerFraction return output
def verify_segments_old(): plan = lib.get_current_plan() beamset = lib.get_current_beamset() opt = plan.PlanOptimizations[beamset.Number - 1] segment_results = "" area_results = "" MU_results = "" leaf_gap_results = "" incompatible_MLC_results = "" segment_error = False # If one or more beams does not have valid segments area_error = False # If one or more segments has an area below the requested minimum segment are MU_error = False # If one or more segments has a number of MU inferior to requested minimum segment MU leaf_gap_error = False # If one or more leaf pairs is open less than 2cm (and is not parked) incompatible_MLC = False # If the MLC is non-standard and its width or number of leaf pairs is unknown results = "" # Used to create a list of all segment areas for debugging purposes output = "" # The actual output returned by the function min_seg_area = opt.OptimizationParameters.SegmentConversion.MinSegmentArea num_segments = 0 for beam in beamset.Beams: #Check to see if beam has segments try: segment = beam.Segments[0] verify_leaves = True except: segment_results += beam.Name + " " segment_error = True verify_leaves = False #continue #Move on to next beam if beam.MachineReference.MachineName == 'BeamMod': leaf_width = 0.4 num_leaf_pairs = 40 elif beam.MachineReference.MachineName == 'Infinity': leaf_width = 0.5 num_leaf_pairs = 80 else: area_results = "Impossible de vérifier l'aire des segments pour ce linac" leaf_gap_results = "Impossible de vérifier l'ouverture des lames pour ce linac" verify_leaves = False incompatible_MLC = True incompatible_MLC_results = "Impossible de vérifier le faisceau " + beam.Name + " car la machine est inconnue" #Calculate the area of each segment #results += beam.Name + "\n" if verify_leaves: for segment in beam.Segments: num_segments += 1 """ The leaf gap restiction set during optimization is ignored for certain leaves that are near the edge of the field. Any leaf pairs that fall within 1cm of the field edge WITH ROOM TO SPARE may ignore the minimum leaf gap setting. Leaf pairs that finish flush at the end of the 1cm area must obey the minimum leaf gap. BEAM MODULATOR: The edge of the field is defined by the first and last pairs of open leaves. As such, there are always exactly 2 pairs of leaves on either side that may ignore the leaf gap restriction. This does not change if a segment has multiple distinct openings, a maximum of 4 leaf pairs can ignore the restriction. AGILITY/INFINITY: If a jaw position falls exactly on the line between two leaf pairs, then only the first pair of leaves may ignore the leaf gap restriction (since the second pair of leaves ends at the exact boundary of the 1cm area). If a jaw falls between two leaf gaps, then there will be two leaf pairs that can ignore the restriction: the partially blocked pair and the first pair that is completely inside the field. NOTE: After writing all that down, I checked and RayStation seems to permit 2 pairs of leaves to do whatever if the jaw falls on a leaf edge. Is that different from Pinnacle? SO I WILL ADD ONE TO ALL LOWEST LEAF PAIR VALUES AND SUBTRACT ONE FROM ALL HIGHEST LEAF PAIR VALUES FOR THE INFINITY MACHINE """ #Determine which leaves to consider for leaf gap verification #Note that lowest_leaf_pair_index and highest_leaf_pair_index are those of the first leaf pairs that need to be examined for leaf gap violations if beam.MachineReference.MachineName == 'BeamMod': #This part seems to work! for i in range(num_leaf_pairs - 1): if segment.LeafPositions[1][i] - segment.LeafPositions[ 0][i] > 0: lowest_leaf_pair_index = i + 2 break i = num_leaf_pairs - 1 while i >= 0: if segment.LeafPositions[1][i] - segment.LeafPositions[ 0][i] > 0: highest_leaf_pair_index = i - 2 break else: i -= 1 elif beam.MachineReference.MachineName == 'Infinity': #Only leaves inside the backup jaws should be considered - NEEDS WORK import math lowest_leaf_pair_index = int( math.floor(40 + 2 * segment.JawPositions[2])) highest_leaf_pair_index = int( math.ceil(39 + 2 * segment.JawPositions[3])) if ( 40 + 2 * segment.JawPositions[2] ) % 0.5 == 0: #Jaw is parked on border between two leaves lowest_leaf_pair_index += 2 else: #Jaw position is in the middle of a leaf lowest_leaf_pair_index += 3 if (39 + 2 * segment.JawPositions[3]) % 0.5 == 0: highest_leaf_pair_index -= 2 else: highest_leaf_pair_index -= 3 #output += "Segment %d\n First open pair of leaves: %d\n First considered pair of leaves: %d\n Last open pair of leaves: %d\n Last considered pair of leaves: %d\n\n" % (segment.SegmentNumber, lowest_leaf_pair_index-1,lowest_leaf_pair_index+1,highest_leaf_pair_index+3,highest_leaf_pair_index+1) #output += "Segment %d\n First pair of leaves to evaluate: %d\n Last pair of leaves to evaluate: %d\n\n" % (segment.SegmentNumber,lowest_leaf_pair_index+1,highest_leaf_pair_index+1) #continue #This will skip the rest, remove after debugging! segment_area = 0 for i in range(num_leaf_pairs - 1): leaf_gap = segment.LeafPositions[1][ i] - segment.LeafPositions[0][i] segment_area += leaf_gap * leaf_width if leaf_gap >= 0.5 and leaf_gap < opt.OptimizationParameters.SegmentConversion.MinLeafEndSeparation and i in range( lowest_leaf_pair_index, highest_leaf_pair_index + 1): leaf_gap_error = True leaf_gap_results += " %s segment %d leafpair %d (%.2f) \n" % ( beam.Name, segment.SegmentNumber + 1, i + 1, leaf_gap) if segment_area < min_seg_area: area_error = True area_results += " %s segment %d (%.2fcm^2)\n" % ( beam.Name, segment.SegmentNumber + 1, segment_area) if beamset.DeliveryTechnique != "Arc": #Don't check MU per segment in VMAT plans if beam.BeamMU * segment.RelativeWeight < opt.OptimizationParameters.SegmentConversion.MinSegmentMUPerFraction: MU_error = True MU_results += " %s segment %d (%.2f MUs)\n" % ( beam.Name, segment.SegmentNumber + 1, beam.BeamMU * segment.RelativeWeight) #results += " Segment %d: %.2fcm^2\n" % (segment.SegmentNumber+1, segment_area) #results += "\n" if segment_error: output = "Les champs suivants n'ont pas de segments valides: " + segment_results return output elif incompatible_MLC: output = incompatible_MLC_results return output else: output += "Tous les faisceaux ont des segments valides\n" if beamset.DeliveryTechnique != "Arc": output += "Il y a un total de %d segments (nb. demandé: %d)\n" % ( num_segments, opt.OptimizationParameters.SegmentConversion. MaxNumberOfSegments) if area_error: output += "Les segments suivants sont plus petits que " + str( min_seg_area) + "cm^2: \n" + area_results else: output += "Tous les segments sont plus grand que %dcm^2\n" % min_seg_area if MU_error: output += "Les segments suivants ont moins que " + str( opt.OptimizationParameters.SegmentConversion. MinSegmentMUPerFraction) + "UMs: \n" + MU_results else: output += "Tous les segments ont plus que %dUMs\n" % opt.OptimizationParameters.SegmentConversion.MinSegmentMUPerFraction if leaf_gap_error: output += "Les lames sont à <%.1fcm dans les segments suivants: \n%s\n" % ( opt.OptimizationParameters.SegmentConversion. MinLeafEndSeparation, leaf_gap_results) else: output += "Toutes les paires de lames sont à >%.1fcm\n" % opt.OptimizationParameters.SegmentConversion.MinLeafEndSeparation return output
def verify_opt_parameters(): plan = lib.get_current_plan() beamset = lib.get_current_beamset() opt = plan.PlanOptimizations[beamset.Number - 1] # Number of optimizations and tolerance iterations_text = "Optimization: " iterations_text += "%d iterations / %d avant la conversion" % ( opt.OptimizationParameters.Algorithm.MaxNumberOfIterations, opt. OptimizationParameters.DoseCalculation.IterationsInPreparationsPhase) iterations_text += ", Stopping Tolerance " + str( opt.OptimizationParameters.Algorithm.OptimalityTolerance) # Computation of intermediate/final dose dose_calc_text = "Compute Intermediate Dose / Final Dose: " if opt.OptimizationParameters.DoseCalculation.ComputeIntermediateDose == True: dose_calc_text += "oui / " else: dose_calc_text += "non / " if opt.OptimizationParameters.DoseCalculation.ComputeFinalDose == True: dose_calc_text += "oui" else: dose_calc_text += "non" #Beam optimization parameters (these depend on whether the plan is VMAT or static) time_mismatch = False spacing_mismatch = False opt_types_mismatch = False if beamset.DeliveryTechnique == "Arc": settings_text = "Constrain Leaf Motion: " if beamset.DeliveryTechnique == "Arc": if opt.OptimizationParameters.SegmentConversion.ArcConversionProperties.UseMaxLeafTravelDistancePerDegree == True: settings_text += "%.1fcm/deg\n" % opt.OptimizationParameters.SegmentConversion.ArcConversionProperties.MaxLeafTravelDistancePerDegree else: settings_text += "pas coché\n" settings_text += "Gantry Spacing / Max Delivery Time: " old_time = 0 new_time = 0 old_spacing = 0 new_spacing = 0 old_opt_types = "" new_opt_types = "" for ts in opt.OptimizationParameters.TreatmentSetupSettings: for i, beam_setting in enumerate(ts.BeamSettings): new_spacing = beam_setting.ArcConversionPropertiesPerBeam.FinalArcGantrySpacing new_time = beam_setting.ArcConversionPropertiesPerBeam.MaxArcDeliveryTime new_opt_types = "" for opt_type in beam_setting.OptimizationTypes: new_opt_types += opt_type if i > 0: if old_spacing != new_spacing: spacing_mismatch = True if old_time != new_time: time_mismatch = True if old_opt_types != new_opt_types: opt_types_mismatch = True old_spacing = new_spacing old_time = new_time old_opt_types = new_opt_types if spacing_mismatch: settings_text += "pas pareil pour tous les faisceaux / " else: settings_text += "%d degrés / " % new_spacing if time_mismatch: settings_text += "pas pareil pour tous les faisceaux" else: settings_text += "%ds" % new_time else: #For IMRT and 3DC #settings_text = "Segment MU / Segment Area / Leaf Pairs / Leaf End Separation / Nb Segments" #settings_text += "\n %dUMs / %d cm2 / %d / %dcm / %d" % (opt.OptimizationParameters.SegmentConversion.MinSegmentMUPerFraction, opt.OptimizationParameters.SegmentConversion.MinSegmentArea, opt.OptimizationParameters.SegmentConversion.MinNumberOfOpenLeafPairs, opt.OptimizationParameters.SegmentConversion.MinLeafEndSeparation, opt.OptimizationParameters.SegmentConversion.MaxNumberOfSegments) settings_text = "Segment MU: %dUMs\n" % opt.OptimizationParameters.SegmentConversion.MinSegmentMUPerFraction settings_text += "Segment Area: %d cm2\n" % opt.OptimizationParameters.SegmentConversion.MinSegmentArea settings_text += "Leaf Pairs: %d\n" % opt.OptimizationParameters.SegmentConversion.MinNumberOfOpenLeafPairs settings_text += "Leaf End Separation: %dcm\n" % opt.OptimizationParameters.SegmentConversion.MinLeafEndSeparation settings_text += "Nb total de segments: %d" % opt.OptimizationParameters.SegmentConversion.MaxNumberOfSegments old_opt_types = "" new_opt_types = "" for ts in opt.OptimizationParameters.TreatmentSetupSettings: for i, beam_setting in enumerate(ts.BeamSettings): new_opt_types = "" for opt_type in beam_setting.OptimizationTypes: new_opt_types += opt_type if i > 0 and old_opt_types != new_opt_types: opt_types_mismatch = True old_opt_types = new_opt_types opt_types_text = "Optimize Segment Shapes / Segment MU: " if opt_types_mismatch: opt_types_text += "pas pareil pour tous les faisceaux" else: if new_opt_types == "SegmentOptSegmentMU" or new_opt_types == "SegmentMUSegmentOpt": opt_types_text += "oui / oui" elif new_opt_types == "SegmentOpt": opt_types_text += "oui / non" elif new_opt_types == "SegmentMU": opt_types_text += "non / oui" elif new_opt_types == "": opt_types_text += "non / non" return iterations_text, dose_calc_text, settings_text, opt_types_text
def verify_beams(): #patient = lib.get_current_patient() exam = lib.get_current_examination() plan = lib.get_current_plan() beamset = lib.get_current_beamset() #Beam details number_of_beams = 0 beam_info = "Nom / Description / Gantry / Sens / Colli / Couch" beams = True try: beam_name = beamset.Beams[0].Name except: beams = False beam_info = "Aucun faisceau trouvé" if beams: #Creat table of beam info machine_mismatch = False energy_mismatch = False for i, beam in enumerate(beamset.Beams): number_of_beams += 1 beam_info += "\n" + beam.Name + " / " beam_info += beam.Description #Pad out beam description to try to keep columns aligned if name is short if len(beam.Description) < 6: beam_info += " " elif len(beam.Description) < 7: beam_info += " " elif len(beam.Description) < 9: beam_info += " " beam_info += " / " try: #Checks whether beam has a stop angle (for arcs) stop_angle = beam.ArcStopGantryAngle beam_info += " %d-%d / " % (beam.GantryAngle, beam.ArcStopGantryAngle) except: if beam.GantryAngle < 10: beam_info += " " beam_info += " %d / " % (beam.GantryAngle) if beam.ArcRotationDirection == "Clockwise": beam_info += " CW " elif beam.ArcRotationDirection == "CounterClockwise": beam_info += " CCW " else: beam_info += "Statique" beam_info += " / " + str(beam.InitialCollimatorAngle) if beam.InitialCollimatorAngle >= 0 and beam.InitialCollimatorAngle < 10: beam_info += " " #Adds a spacer to keep the columns aligned elif beam.InitialCollimatorAngle >= 10 and beam.InitialCollimatorAngle < 100: beam_info += " " #Adds a spacer to keep the columns aligned beam_info += " / %d " % (beam.CouchAngle) if i > 0: if beam.MachineReference.MachineName != machine_type: machine_mismatch = True if beam.MachineReference.Energy != energy: energy_mismatch = True machine_type = beam.MachineReference.MachineName energy = beam.MachineReference.Energy #Check if first and last leaf pairs are open (anywhere) first_leaf_open = False last_leaf_open = False leaf_open_text = "" skip_leaf_check = False for beam in beamset.Beams: try: temp = beam.Segments[ 0].CollimatorAngle #Just to see if there are segments defined for the beam for seg in beam.Segments: if first_leaf_open == False: #Stop checking if a segment is found where leaves are open if machine_type == "BeamMod": if seg.LeafPositions[0][0] != seg.LeafPositions[1][ 0]: first_leaf_open = True elif machine_type == "Infinity": if abs( seg.LeafPositions[0][0] - seg.LeafPositions[1][0] ) > 0.4: #4mm is the minimum leaf gap for Infinity machines as of October 2017 first_leaf_open = True #first_leaf_open_seg = seg.SegmentNumber #first_leaf_open_name = beam.Name if last_leaf_open == False: if machine_type == "BeamMod": if seg.LeafPositions[0][39] != seg.LeafPositions[ 1][39]: last_leaf_open = True elif machine_type == "Infinity": if abs(seg.LeafPositions[0][79] - seg.LeafPositions[1][79]) > 0.4: last_leaf_open = True #last_leaf_open_seg = seg.SegmentNumber #last_leaf_open_name = beam.Name except: leaf_open_text += "Impossible de vérifier les segments car\nau moins un faisceau n'a pas de segments valides" skip_leaf_check = True if not skip_leaf_check: if machine_type not in ['BeamMod', 'Infinity']: leaf_open_text += "Vérification des lames SUP/INF pas possible sur l'appareil " + machine_type elif first_leaf_open and last_leaf_open: leaf_open_text += "Première et dernère paire de lames ouvertes dans au moins\nun segment, PTV potentiellement trop large pour collimateur" elif first_leaf_open and not last_leaf_open: leaf_open_text += "Première paire de lames ouverte dans au moins un segment,\nil est peut-être nécessaire de déplacer l'isocentre" elif not first_leaf_open and last_leaf_open: leaf_open_text += "Dernière paire de lames ouverte dans au moins un segment,\nil est peut-être nécessaire de déplacer l'isocentre" elif not first_leaf_open and not last_leaf_open: leaf_open_text += "Première/dernière paires de lames fermées pour tous les segments" if machine_mismatch: machine_text = "Machine pas pareil pour tous les faisceaux" else: machine_text = "Machine: " + machine_type + " (tous les faisceaux)" if energy_mismatch: energy_text = "Énergie pas pareil pour tous les faisceaux" else: energy_text = "Énergie: %dMV (tous les faisceaux)" % energy return beam_info, number_of_beams, leaf_open_text, machine_text, energy_text
def addplanClicked(self, sender, args): self.status.ForeColor = Color.Black self.status.Text = "Compilation des données du plan" d,error_message = self.compile_plan_data() if error_message != "": #If an error is noticed, cancel script execution self.status.Text = error_message self.status.ForeColor = Color.Red return rx = d['rx'] ptv_names = d['ptv_names'] technique = d['technique'] site = d['site_name'] #Predict dose to brain (and generate ROIs) self.status.Text = "Estimation de la dose au cerveau" predicted_vol = crane.crane_stereo_kbp_predict_dose(plan_data = d) cerv_ptv_vol = patient.PatientModel.StructureSets[d['exam'].Name].RoiGeometries["CERVEAU-PTV_"+d['site_name']].GetRoiVolume() #Display predicted results self.message.Text = 'Volumes prédits dans le cerveau-PTV:\n V100%%: %.2fcc\n V90%%: %.2fcc\n V80%%: %.2fcc\n V70%%: %.2fcc\n V60%%: %.2fcc\n V50%%: %.2fcc\n V40%%: %.2fcc' % (predicted_vol[0],predicted_vol[1],predicted_vol[2],predicted_vol[3],predicted_vol[4],predicted_vol[5],predicted_vol[6]) v10 = crane.estimate_vx(predicted_vol=predicted_vol,rx_dose=max(rx),dose_level=1000) v12 = crane.estimate_vx(predicted_vol=predicted_vol,rx_dose=max(rx),dose_level=1200) self.message.Text += '\n\nV10 Cerveau-PTV estimé: %s\nV12 Cerveau-PTV estimé: %s' % (v10,v12) if max(rx) >= 2000: v20 = crane.estimate_vx(predicted_vol=predicted_vol,rx_dose=max(rx),dose_level=2000) self.message.Text += '\nV20 Cerveau-PTV estimé: %s' % (v20) self.status.Text = "Estimation de la dose max au tronc cerebral" tronc_max = crane.crane_stereo_kbp_predict_oar_dose(plan_data = d) #Check which steps of the script are to be performed if self.stepcombo.Text == "Rouler le script au complet": add_plan = True optimize_collimator_angles = True optimize_plan = True elif self.stepcombo.Text == "Multi-PTV: Arrêtez avant optimization collimateur": add_plan = True optimize_collimator_angles = False optimize_plan = False elif self.stepcombo.Text == "Multi-PTV: Reprendre après optimization collimateur": add_plan = False optimize_collimator_angles = False optimize_plan = True if add_plan: if patient.BodySite == '': patient.BodySite = 'Crâne' if self.isodosecombo.Text == "Créer": self.status.Text = "Ajout du dose color table" crane.crane_stereo_create_isodose_lines(plan_data = d) #Create/assign types to POIs and ROIs (only if this is the first plan for the patient) try: existing_plan = patient.TreatmentPlans[0] except: if d['iso_name'] == 'REF SCAN': #Need to skip this step if planner is intentionally using a different isocenter self.status.Text = "Création de l'isocentre à partir du REF SCAN" poi.create_iso() self.status.Text = "Gestion des POIs" poi.auto_assign_poi_types() self.status.Text = "Suppression des overrides de densité" for rois in patient.PatientModel.RegionsOfInterest: rois.SetRoiMaterial(Material=None) self.status.Text = "Création du contour externe" roi.generate_BodyRS_using_threshold() #Create TISSU SAINS à 1cm if not roi.roi_exists("TISSU SAIN 1cm "+site): patient.PatientModel.CreateRoi(Name="TISSU SAIN 1cm "+site, Color="Magenta", Type="Organ", TissueName=None, RoiMaterial=None) patient.PatientModel.RegionsOfInterest["TISSU SAIN 1cm "+site].SetAlgebraExpression(ExpressionA={'Operation': "Union", 'SourceRoiNames': ["BodyRS"], 'MarginSettings': {'Type': "Expand", 'Superior': 0, 'Inferior': 0, 'Anterior': 0, 'Posterior': 0, 'Right': 0, 'Left': 0}}, ExpressionB={'Operation': "Union", 'SourceRoiNames': ['sum_ptvs_'+site], 'MarginSettings': {'Type': "Expand", 'Superior': 1, 'Inferior': 1, 'Anterior': 1, 'Posterior': 1, 'Right': 1, 'Left': 1}}, ResultOperation="Subtraction", ResultMarginSettings={'Type': "Expand", 'Superior': 0, 'Inferior': 0, 'Anterior': 0, 'Posterior': 0, 'Right': 0, 'Left': 0}) patient.PatientModel.RegionsOfInterest["TISSU SAIN 1cm "+site].UpdateDerivedGeometry(Examination=exam) #Assign proper contour type to all PTVs self.status.Text = "Assignation du statut PTV" for ptv in ptv_names: try: roi.set_roi_type(ptv, 'Ptv', 'Target') except: pass #In a perfect world, I would copy the ROI, replace the PTV with the copy in ptv_names and then change its type #Add plan, beamset and beams self.status.Text = "Ajout du plan, beamset et faisceaux" if technique == 'VMAT': #Only case where we don't need a 3DC plan at all plan,beamset = crane.crane_stereo_kbp_add_VMAT_plan_and_beamset(plan_data = d) elif technique == 'IMRT' and len(ptv_names) == 1: plan,beamset = crane.crane_stereo_kbp_add_IMRT_plan_and_beamset(plan_data = d) elif technique == 'IMRT' and len(ptv_names) > 1: if optimize_collimator_angles: self.status.Text = "Ajout du plan, beamset et faisceaux (touchez pas à l'ordinateur SVP)" plan,beamset = crane.crane_stereo_kbp_add_3DC_plan(plan_data = d) self.status.Text = "Optimisation angles collimateur (touchez pas à l'ordinateur SVP)" crane.optimize_collimator_angles() self.status.Text = "Conversion du plan 3DC > IMRT (touchez pas à l'ordinateur SVP)" crane.crane_stereo_convert_3DC_IMRT(plan=plan,beamset=beamset) else: self.status.Text = "Ajout du plan, beamset et faisceaux" plan,beamset = crane.crane_stereo_kbp_add_IMRT_plan_and_beamset(plan_data = d) self.status.Text = "Prêt pour optimisation manuelle des angles de collimateur" self.status.ForeColor = Color.Green return elif technique == '3DC': self.status.Text = "Ajout du plan, beamset et faisceaux (touchez pas à l'ordinateur SVP)" plan,beamset = crane.crane_stereo_kbp_add_3DC_plan(plan_data = d) #if len(ptv_names)>1: if optimize_collimator_angles and len(ptv_names) > 1: self.status.Text = "Optimisation angles collimateur (touchez pas à l'ordinateur SVP)" crane.optimize_collimator_angles() elif not optimize_collimator_angles: self.status.Text = "Prêt pour optimisation manuelle des angles de collimateur" self.status.ForeColor = Color.Green return if add_plan == False: plan = lib.get_current_plan() beamset = lib.get_current_beamset() if optimize_plan: # Add clinical goals (conveniently the same for all types of plan) self.status.Text = "Ajout des clinical goals" clinical_goals.add_dictionary_cg('Crane Stereo', 15, 1, plan = plan) eval.add_clinical_goal("CERVEAU-PTV_"+d['site_name'], 1000, 'AtMost', 'AbsoluteVolumeAtDose', 10, plan=plan) eval.add_clinical_goal("CERVEAU-PTV_"+d['site_name'], 1200, 'AtMost', 'AbsoluteVolumeAtDose', 8, plan=plan) if max(rx) >= 2000: eval.add_clinical_goal("CERVEAU-PTV_"+d['site_name'], 2000, 'AtMost', 'AbsoluteVolumeAtDose', 20, plan=plan) for i,ptv in enumerate(ptv_names): eval.add_clinical_goal(ptv, rx[i], 'AtLeast', 'VolumeAtDose', 99, plan=plan) eval.add_clinical_goal(ptv, 1.5 * rx[i], 'AtMost', 'DoseAtAbsoluteVolume', 0.1, plan=plan) #if technique == '3DC': # optim.copy_clinical_goals(old_plan = plan,new_plan = patient.TreatmentPlans[d['site_name']+' 3DC optimised']) #Add objectives and optimize plan if technique == '3DC': self.status.Text = "Optimisation du plan 3DC (touchez pas à l'ordinateur SVP)" plan,beamset = crane.crane_stereo_kbp_optimize_3DC_plan(plan_data=d,plan=plan,beamset=beamset) obtained_vol,initial_ptv_cov = crane.crane_stereo_kbp_scale_dose(plan_data=d,beamset=beamset,reset_dose=False) else: self.status.Text = "Ajout des objectifs d'optimisation" crane.crane_stereo_kbp_initial_optimization_objectives(plan_data=d,plan=plan,predicted_vol=predicted_vol,tronc_max=tronc_max) #Make a copy of plan before optimizing for dosimetrists patient.CopyPlan(PlanName=plan.Name, NewPlanName=plan.Name + ' non-optimisé') self.status.Text = "Optimization du plan initial" plan.PlanOptimizations[beamset.Number-1].ResetOptimization() if len(ptv_names) == 1: optim.triple_optimization(plan=plan,beamset=beamset) elif len(ptv_names) > 1: optim.optimization_90_30(plan=plan,beamset=beamset) self.status.Text = "Modification du plan" if len(ptv_names) == 1: crane.crane_stereo_kbp_modify_plan_single_ptv(plan_data=d,plan=plan,beamset=beamset,predicted_vol=predicted_vol,tronc_max=tronc_max) self.status.Text = "Optimization du plan modifié" optim.triple_optimization(plan=plan,beamset=beamset) self.status.Text = "Scaling couverture à la prescription" obtained_vol,initial_ptv_cov = crane.crane_stereo_kbp_scale_dose(plan_data=d,beamset=beamset,reset_dose=False) elif len(ptv_names) > 1: obtained_vol,initial_ptv_cov = crane.crane_stereo_kbp_scale_dose(plan_data=d,beamset=beamset,reset_dose=True) self.message.Text += '\n\nV10 obtenu (plan initial): %.2fcc\nV12 obtenu (plan initial): %.2fcc' % (obtained_vol[0]*cerv_ptv_vol,obtained_vol[1]*cerv_ptv_vol) continue_optimization = True best_vol = 100000 for i in range(4): #i IS EQUAL TO THE NUMBER OF COMPLETED ITERATIONS! if continue_optimization: self.status.Text = "Modification du plan et réoptimisation (étape %d/4)" % (i+1) continue_optimization = crane.crane_stereo_kbp_modify_plan_multi_ptv(plan_data=d,plan=plan,beamset=beamset) #Evaluates PTV coverage, adjusts and reoptimizes if necessary obtained_vol,initial_ptv_cov = crane.crane_stereo_kbp_scale_dose(plan_data=d,beamset=beamset,reset_dose=True) if (obtained_vol[0] + obtained_vol[1]) < best_vol: best_vol = obtained_vol[0] + obtained_vol[1] best_iteration = i #if continue_optimization: #If crane_stereo_kbp_modify_plan_multi_ptv returns False, then the plan hasn't changed since last time and we don't need to print these values again self.message.Text += '\n\nV10 obtenu (après %d révision(s)): %.2fcc\nV12 obtenu (après %d révision(s)): %.2fcc' % (i+1,obtained_vol[0]*cerv_ptv_vol,i+1,obtained_vol[1]*cerv_ptv_vol) self.status.Text = "Scaling de la couverture à la prescription" obtained_vol,initial_ptv_cov = crane.crane_stereo_kbp_scale_dose(plan_data=d,beamset=beamset,reset_dose=False) #Now we have to check if the final plan is better than the previous plans. If not, we will reoptimize and stop and the correct point. self.status.Text = "Meilleur plan: plan initial avec %d itérations" % best_iteration #if (obtained_vol[0]+obtained_vol[1]*0.9) > best_vol: if (obtained_vol[0]+obtained_vol[1]) > best_vol: self.status.Text = "Retour vers le meilleur plan, veuillez patientez svp" optim.erase_objectives(plan,beamset) plan.PlanOptimizations[beamset.Number-1].ResetOptimization() crane.crane_stereo_kbp_initial_optimization_objectives(plan_data=d,plan=plan,predicted_vol=predicted_vol,tronc_max=tronc_max) optim.optimization_90_30(plan=plan,beamset=beamset) for i in range(best_iteration+1): self.status.Text = "Modification du plan et réoptimisation (étape %d/%d)" % (i+1,best_iteration+1) continue_optimization = crane.crane_stereo_kbp_modify_plan_multi_ptv(plan_data=d,plan=plan,beamset=beamset) self.status.Text = "Scaling de la couverture à la prescription" obtained_vol,initial_ptv_cov = crane.crane_stereo_kbp_scale_dose(plan_data=d,beamset=beamset,reset_dose=False) #Display results of plan self.message.Text += '\n\nV10 obtenu: %.2fcc\nV12 obtenu: %.2fcc' % (obtained_vol[0]*cerv_ptv_vol,obtained_vol[1]*cerv_ptv_vol) #Write results to file (this is put into a try because it will crash if someone has the destination file open when it tries to write to it) try: crane.crane_kbp_write_results_to_file(plan_data=d,plan=plan,beamset=beamset,predicted_vol=predicted_vol,initial_ptv_cov=initial_ptv_cov,obtained_vol=obtained_vol) except: pass #If the plan is VMAT or IMRT, copy it and set it up using the old technique for comparison if technique != '3DC': kbp_plan_name = site + ' ' + technique old_style_plan_name = site + ' ' + technique + ' RINGS' self.status.Text = "Ajout du plan RINGS" patient.CopyPlan(PlanName=kbp_plan_name, NewPlanName=old_style_plan_name) plan = patient.TreatmentPlans[old_style_plan_name] self.status.Text = "Nom du nouveau plan: " + plan.Name beamset = plan.BeamSets[old_style_plan_name] self.status.Text = "Préparation des ROIs et objectifs pour plan RINGS" crane.crane_add_old_plan(plan_data=d,plan=plan,beamset=beamset) #Create TISSU SAINS à 1cm (because sometimes I guess this wasn't happening for some reason) if not roi.roi_exists("TISSU SAIN 1cm "+site): patient.PatientModel.CreateRoi(Name="TISSU SAIN 1cm "+site, Color="Magenta", Type="Organ", TissueName=None, RoiMaterial=None) patient.PatientModel.RegionsOfInterest["TISSU SAIN 1cm "+site].SetAlgebraExpression(ExpressionA={'Operation': "Union", 'SourceRoiNames': ["BodyRS"], 'MarginSettings': {'Type': "Expand", 'Superior': 0, 'Inferior': 0, 'Anterior': 0, 'Posterior': 0, 'Right': 0, 'Left': 0}}, ExpressionB={'Operation': "Union", 'SourceRoiNames': ['sum_ptvs_'+site], 'MarginSettings': {'Type': "Expand", 'Superior': 1, 'Inferior': 1, 'Anterior': 1, 'Posterior': 1, 'Right': 1, 'Left': 1}}, ResultOperation="Subtraction", ResultMarginSettings={'Type': "Expand", 'Superior': 0, 'Inferior': 0, 'Anterior': 0, 'Posterior': 0, 'Right': 0, 'Left': 0}) patient.PatientModel.RegionsOfInterest["TISSU SAIN 1cm "+site].UpdateDerivedGeometry(Examination=exam) plan.PlanOptimizations[beamset.Number-1].ResetOptimization() self.status.Text = "Optimization du plan RINGS" optim.optimization_90_30(plan=plan,beamset=beamset) self.isodosecombo.Text = 'Ne pas créer' self.status.Text = "Terminé avec succès!" self.status.ForeColor = Color.Green