Exemplo n.º 1
0
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)
Exemplo n.º 2
0
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)
Exemplo n.º 3
0
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 crane_launcher():

    class CraneLauncher(Form):
        def __init__(self):
            self.Text = "Plan de crâne stéréo"

            self.Width = 700
            self.Height = 900

            self.setupHeaderWindow()
            self.setupMainWindow()

            self.Controls.Add(self.HeaderWindow)
            self.Controls.Add(self.MainWindow)
            
            #Automatically populate ROI selection comboboxes
            self.PTV1combo.Items.Add("Choisissez ROI")
            self.PTV2combo.Items.Add("Choisissez ROI")
            self.PTV3combo.Items.Add("Choisissez ROI")    
            self.OAR1combo.Items.Add("Choisissez OAR")
            self.OAR2combo.Items.Add("Choisissez OAR")
            self.OAR3combo.Items.Add("Choisissez OAR")
            for roi in patient.PatientModel.RegionsOfInterest:       
                if 'PTV' in roi.Name.upper():
                    self.PTV1combo.Items.Add(roi.Name)
                    self.PTV2combo.Items.Add(roi.Name)
                    self.PTV3combo.Items.Add(roi.Name)      
                if 'PTV' not in roi.Name.upper():
                    self.OAR1combo.Items.Add(roi.Name)
                    self.OAR2combo.Items.Add(roi.Name)
                    self.OAR3combo.Items.Add(roi.Name)
                    
            #Determine whether to add dose color table by looking for existing plans
            try:
                existing_plan = patient.TreatmentPlans[0]
                self.isodosecombo.SelectedIndex = self.isodosecombo.FindStringExact('Ne pas créer')
            except:
                self.isodosecombo.SelectedIndex = self.isodosecombo.FindStringExact('Créer')
                
            
        def Panel(self, x, y):
            panel = Panel()
            panel.Width = 700
            panel.Height = 800
            panel.Location = Point(x, y)
            panel.BorderStyle = BorderStyle.None
            return panel

        def miniPanel(self, x, y):
            panel = Panel()
            panel.Width = 700
            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('^', ', ')
            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 setupMainWindow(self):
            self.MainWindow = self.Panel(0, 60)
            
            vert_spacer = 30
            offset = 50   
            
            self.toplabel = Label()
            self.toplabel.Text = "PTV                      Dose (Gy)"
            self.toplabel.Font = Font("Arial", 10, FontStyle.Bold)
            self.toplabel.Location = Point(60, 20)
            self.toplabel.AutoSize = True  
                   
            self.PTV1combo = ComboBox()
            self.PTV1combo.Parent = self
            self.PTV1combo.Size = Size(120,40)
            self.PTV1combo.Location = Point(25, offset)
            self.PTV1combo.Text = "Choisissez ROI" 
     
            self.dose1_value = TextBox()
            self.dose1_value.Text = ""
            self.dose1_value.Location = Point(180, offset)
            self.dose1_value.Width = 50              

            self.PTV2combo = ComboBox()
            self.PTV2combo.Parent = self
            self.PTV2combo.Size = Size(120,40)
            self.PTV2combo.Location = Point(25, offset + vert_spacer)
            self.PTV2combo.Text = "Choisissez ROI" 

            self.dose2_value = TextBox()
            self.dose2_value.Text = ""
            self.dose2_value.Location = Point(180, offset + vert_spacer)
            self.dose2_value.Width = 50                                

            self.PTV3combo = ComboBox()
            self.PTV3combo.Parent = self
            self.PTV3combo.Size = Size(120,40)
            self.PTV3combo.Location = Point(25, offset + 2*vert_spacer)
            self.PTV3combo.Text = "Choisissez ROI" 
                      
            self.dose3_value = TextBox()
            self.dose3_value.Text = ""
            self.dose3_value.Location = Point(180, offset + 2*vert_spacer)
            self.dose3_value.Width = 50                   
         
         
            self.fxlabel = Label()
            self.fxlabel.Text = "Nb de fx"
            self.fxlabel.Location = Point(25, offset + 3.5*vert_spacer)
            self.fxlabel.Font = Font("Arial", 10, FontStyle.Bold)
            self.fxlabel.AutoSize = True              
            
            self.fxbox = TextBox()
            self.fxbox.Parent = self
            self.fxbox.Size = Size(50,40)
            self.fxbox.Location = Point(150, offset + 3.5*vert_spacer)
            self.fxbox.Text = "1"                 
            
            self.techlabel = Label()
            self.techlabel.Text = "Technique"
            self.techlabel.Location = Point(25, offset + 4.5*vert_spacer)
            self.techlabel.Font = Font("Arial", 10, FontStyle.Bold)
            self.techlabel.AutoSize = True              
            
            self.techcombo = ComboBox()
            self.techcombo.Parent = self
            self.techcombo.Size = Size(90,40)
            self.techcombo.Location = Point(150, offset + 4.5*vert_spacer)
            self.techcombo.Text = "VMAT" 
            self.techcombo.Items.Add('VMAT')
            self.techcombo.Items.Add('IMRT')
            self.techcombo.Items.Add('3DC')        
            
            self.sitelabel = Label()
            self.sitelabel.Text = "Nom du site"
            self.sitelabel.Location = Point(25, offset + 5.5*vert_spacer)
            self.sitelabel.Font = Font("Arial", 10, FontStyle.Bold)
            self.sitelabel.AutoSize = True              
            
            self.sitebox = TextBox()
            self.sitebox.Parent = self
            self.sitebox.Size = Size(50,40)
            self.sitebox.Location = Point(150, offset + 5.5*vert_spacer)
            self.sitebox.Text = "A1"    
            
            
            self.isolabel = Label()
            self.isolabel.Text = "Isocentre"
            self.isolabel.Location = Point(25, offset + 6.5*vert_spacer)
            self.isolabel.Font = Font("Arial", 10, FontStyle.Bold)
            self.isolabel.AutoSize = True              
            
            self.isocombo = ComboBox()
            self.isocombo.Parent = self
            self.isocombo.Size = Size(90,40)
            self.isocombo.Location = Point(150, offset + 6.5*vert_spacer)
            for poi in patient.PatientModel.PointsOfInterest:
                self.isocombo.Items.Add(poi.Name)
                if poi.Name == 'ISO':
                    self.isocombo.Text = 'ISO'
                elif self.isocombo.Text != 'ISO' and poi.Name == 'REF SCAN':
                    self.isocombo.Text = 'REF SCAN'
            
            
            self.scanlabel = Label()
            self.scanlabel.Text = "CT de planif"
            self.scanlabel.Location = Point(25, offset + 7.5*vert_spacer)
            self.scanlabel.Font = Font("Arial", 10, FontStyle.Bold)
            self.scanlabel.AutoSize = True              
            
            self.scancombo = ComboBox()
            self.scancombo.Parent = self
            self.scancombo.Size = Size(90,40)
            self.scancombo.Location = Point(150, offset + 7.5*vert_spacer)
            for ct in patient.Examinations:
                self.scancombo.Items.Add(ct.Name)         
                if ct.Name == 'CT 1':
                    self.scancombo.Text = 'CT 1'
            
            
            self.machinelabel = Label()
            self.machinelabel.Text = "Appareil"
            self.machinelabel.Location = Point(25, offset + 8.5*vert_spacer)
            self.machinelabel.Font = Font("Arial", 10, FontStyle.Bold)
            self.machinelabel.AutoSize = True              
            
            self.machinecombo = ComboBox()
            self.machinecombo.Parent = self
            self.machinecombo.Size = Size(90,40)
            self.machinecombo.Location = Point(150, offset + 8.5*vert_spacer)
            self.machinecombo.Text = "BeamMod"                  
            self.machinecombo.Items.Add('BeamMod')
            self.machinecombo.Items.Add('Infinity')
            

            self.isodoselabel = Label()
            self.isodoselabel.Text = "Dose table"
            self.isodoselabel.Location = Point(25, offset + 9.5*vert_spacer)
            self.isodoselabel.Font = Font("Arial", 10, FontStyle.Bold)
            self.isodoselabel.AutoSize = True              
            
            self.isodosecombo = ComboBox()
            self.isodosecombo.Parent = self
            self.isodosecombo.Size = Size(90,40)
            self.isodosecombo.Location = Point(150, offset + 9.5*vert_spacer)
            self.isodosecombo.Text = "Créer"                  
            self.isodosecombo.Items.Add('Créer')            
            self.isodosecombo.Items.Add('Ne pas créer')            


            self.couchlabel = Label()
            self.couchlabel.Text = "Couch table"
            self.couchlabel.Location = Point(25, offset + 10.5*vert_spacer)
            self.couchlabel.Font = Font("Arial", 10, FontStyle.Bold)
            self.couchlabel.AutoSize = True              
            
            self.couchcombo = ComboBox()
            self.couchcombo.Parent = self
            self.couchcombo.Size = Size(90,40)
            self.couchcombo.Location = Point(150, offset + 10.5*vert_spacer)
            self.couchcombo.Text = "Ne pas ajouter"                  
            self.couchcombo.Items.Add('Ne pas ajouter')            
            #self.couchcombo.Items.Add('Ajouter')                  

            
            self.message = Label()
            self.message.Text = "Sélectionnez le(s) ROI(s) à traiter (chaque\ncontour distinct devrait être indiqué\nséparément). Seulement les ROIs avec\nPTV dans leurs noms sont disponibles.\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\nLes plans IMRT/3DC avec plusieurs PTVs\ndistincts auront une optimisation automatique\ndu collimateur. SVP ne touchez pas à\nl'ordinateur pendant cette optimisation\n(4 à 5 minutes environ)"
            self.message.Location = Point(300, offset)
            self.message.Font = Font("Arial", 11, FontStyle.Italic)
            self.message.AutoSize = True                
            
            self.status = Label()
            self.status.Text = ""
            self.status.Location = Point(25, 760)
            self.status.Font = Font("Arial", 11, FontStyle.Bold)
            self.status.AutoSize = True    
            

            self.OARlabel = Label()
            self.OARlabel.Text = "Max doses custom (Gy)"
            self.OARlabel.Location = Point(25, offset + 13*vert_spacer)
            self.OARlabel.Font = Font("Arial", 10, FontStyle.Bold)
            self.OARlabel.AutoSize = True              
            
            self.OAR1combo = ComboBox()
            self.OAR1combo.Parent = self
            self.OAR1combo.Size = Size(120,40)
            self.OAR1combo.Location = Point(25, offset + 14*vert_spacer)
            self.OAR1combo.Text = "Choisissez OAR"     

            self.OAR1_value = TextBox()
            self.OAR1_value.Text = ""
            self.OAR1_value.Location = Point(160, offset + 14*vert_spacer)
            self.OAR1_value.Width = 50                  
            
            self.OAR2combo = ComboBox()
            self.OAR2combo.Parent = self
            self.OAR2combo.Size = Size(120,40)
            self.OAR2combo.Location = Point(25, offset + 15*vert_spacer)
            self.OAR2combo.Text = "Choisissez OAR"     
            
            self.OAR2_value = TextBox()
            self.OAR2_value.Text = ""
            self.OAR2_value.Location = Point(160, offset + 15*vert_spacer)
            self.OAR2_value.Width = 50                
            
            self.OAR3combo = ComboBox()
            self.OAR3combo.Parent = self
            self.OAR3combo.Size = Size(120,40)
            self.OAR3combo.Location = Point(25, offset + 16*vert_spacer)
            self.OAR3combo.Text = "Choisissez OAR"                 
            
            self.OAR3_value = TextBox()
            self.OAR3_value.Text = ""
            self.OAR3_value.Location = Point(160, offset + 16*vert_spacer)
            self.OAR3_value.Width = 50    

            
            evalButton = Button()
            evalButton.Text = "Évaluer les PTVs"
            evalButton.Location = Point(25, offset + 18 * vert_spacer)
            evalButton.Width = 200
            evalButton.Click += self.evalClicked                           
            
            addplanButton = Button()
            addplanButton.Text = "Ajouter plan"
            addplanButton.Location = Point(25, offset + 19 * vert_spacer)
            addplanButton.Width = 200
            addplanButton.Click += self.addplanClicked   


            self.stepcombo = ComboBox()
            self.stepcombo.Parent = self
            self.stepcombo.Size = Size(250,40)
            self.stepcombo.Location = Point(25, offset + 20 * vert_spacer)
            self.stepcombo.Text = "Rouler le script au complet"                  
            self.stepcombo.Items.Add('Rouler le script au complet')            
            self.stepcombo.Items.Add('Multi-PTV: Arrêtez avant optimization collimateur')            
            self.stepcombo.Items.Add('Multi-PTV: Reprendre après optimization collimateur')                   
            
            
            eraseROIButton = Button()
            eraseROIButton.Text = "Effacer les ROIs de prédiction"
            eraseROIButton.Location = Point(25, offset + 21.5 * vert_spacer)
            eraseROIButton.Width = 200
            eraseROIButton.Click += self.eraseROIClicked   
            
            
            
            self.MainWindow.Controls.Add(self.toplabel)
            
            self.MainWindow.Controls.Add(self.PTV1combo)
            self.MainWindow.Controls.Add(self.dose1_value)
            self.MainWindow.Controls.Add(self.PTV2combo)
            self.MainWindow.Controls.Add(self.dose2_value)
            self.MainWindow.Controls.Add(self.PTV3combo)
            self.MainWindow.Controls.Add(self.dose3_value)          

            self.MainWindow.Controls.Add(self.fxlabel)
            self.MainWindow.Controls.Add(self.fxbox)   
            
            self.MainWindow.Controls.Add(self.techlabel)          
            self.MainWindow.Controls.Add(self.techcombo)          
         
            self.MainWindow.Controls.Add(self.sitelabel)
            self.MainWindow.Controls.Add(self.sitebox)            

            self.MainWindow.Controls.Add(self.isolabel)
            self.MainWindow.Controls.Add(self.isocombo) 
            
            self.MainWindow.Controls.Add(self.scanlabel)          
            self.MainWindow.Controls.Add(self.scancombo)    
            
            self.MainWindow.Controls.Add(self.machinelabel)          
            self.MainWindow.Controls.Add(self.machinecombo)          

            self.MainWindow.Controls.Add(self.isodoselabel)          
            self.MainWindow.Controls.Add(self.isodosecombo)     

            self.MainWindow.Controls.Add(self.couchlabel)          
            self.MainWindow.Controls.Add(self.couchcombo)             
            
            self.MainWindow.Controls.Add(self.OARlabel)
            self.MainWindow.Controls.Add(self.OAR1combo)
            self.MainWindow.Controls.Add(self.OAR1_value)
            self.MainWindow.Controls.Add(self.OAR2combo)
            self.MainWindow.Controls.Add(self.OAR2_value)
            self.MainWindow.Controls.Add(self.OAR3combo)
            self.MainWindow.Controls.Add(self.OAR3_value)
            
            self.MainWindow.Controls.Add(self.message)            
            self.MainWindow.Controls.Add(self.status)     

            self.MainWindow.Controls.Add(evalButton)
            self.MainWindow.Controls.Add(addplanButton)            
            
            self.MainWindow.Controls.Add(self.stepcombo) 
            
            self.MainWindow.Controls.Add(eraseROIButton) 

            
            #Label empty contours
            exam_list = []
            for CT in patient.Examinations:
                exam_list.append(CT.Name)
                
            for contour in patient.PatientModel.RegionsOfInterest:
                VolCT1 = roi.get_roi_volume(contour.Name, exam=patient.Examinations["CT 1"])
                if "CT 2" in exam_list:
                    VolCT2 = roi.get_roi_volume(contour.Name, exam=patient.Examinations["CT 2"])
                else:
                    VolCT2 = 0

                if VolCT1 == 0 and VolCT2 == 0:
                    contour.Name = ("vide_" + contour.Name)               
            
            
            
        def compile_plan_data(self):            
                               
            self.status.Text = "Compilation des données du plan"      
            
            ptv_names = []
            rx = []  
            custom_max = []
            error_message = ""
            
            if roi.roi_exists(self.PTV1combo.Text):       
                ptv_names.append(self.PTV1combo.Text)
                try:
                    rx.append(int(float(self.dose1_value.Text) * 100))
                except:
                    error_message = "Dose du PTV 1 illisible"                    

            if roi.roi_exists(self.PTV2combo.Text):       
                ptv_names.append(self.PTV2combo.Text)
                try:
                    rx.append(int(float(self.dose2_value.Text) * 100))
                except:
                    error_message = "Dose du PTV 2 illisible"

            if roi.roi_exists(self.PTV3combo.Text):       
                ptv_names.append(self.PTV3combo.Text)
                try:
                    rx.append(int(float(self.dose3_value.Text) * 100))
                except:
                    error_message = "Dose du PTV 3 illisible"              
            
            if len(ptv_names) == 0:
                error_message = "Aucun PTV sélectionné"
            
            try:
                nb_fx = int(self.fxbox.Text)
            except:
                error_message = "Nb de fractions illisible"
                
            technique = self.techcombo.Text
                    
            if self.isocombo.Text == '':
                error_message = "Choisissez un isocentre avant de continuer"

            if self.scancombo.Text == '':
                error_message = "Choisissez un scan avant de continuer"
                
            if self.couchcombo.Text == 'Ajouter':
                couch = True
            else:
                couch = False                    
                                  
            name = self.sitebox.Text + ' ' + technique
            if couch:
                name += ' Couch'    
            if self.stepcombo.Text != "Multi-PTV: Reprendre après optimization collimateur": #We need to skip this step if completing a plan that was started earlier           
                try:    
                    existing_plan = patient.TreatmentPlans[name]
                    error_message = "Un plan avec le nom %s exist déjà, SVP le renommez avant de commencer" % name
                except:
                    pass                    
                
            if self.stepcombo.Text == "Multi-PTV: Arrêtez avant optimization collimateur" or self.stepcombo.Text == "Multi-PTV: Reprendre après optimization collimateur":
                if technique == 'VMAT':
                    error_message = "Le script partiel devrait seulement être utilisé pour les cas d'IMRT et 3DC"
                elif len(ptv_names) == 1:
                    error_message = "Le script partiel devrait seulement être utilisé pour les cas avec plus qu'un PTV"
                
            if roi.roi_exists(self.OAR1combo.Text):
                try:
                    custom_max.append((self.OAR1combo.Text,float(self.OAR1_value.Text))) #Yes, you need all those parentheses for this to work
                except:
                    error_message = "Impossible de lire custom max dose 1"
            if roi.roi_exists(self.OAR2combo.Text):
                try:
                    custom_max.append((self.OAR2combo.Text,float(self.OAR2_value.Text)))
                except:
                    error_message = "Impossible de lire custom max dose 2"
            if roi.roi_exists(self.OAR3combo.Text):
                try:
                    custom_max.append((self.OAR3combo.Text,float(self.OAR3_value.Text)))
                except:
                    error_message = "Impossible de lire custom max dose 3"         
                
                
            oar_list = crane.crane_stereo_kbp_identify_rois(patient)
            if oar_list[0] == 'ERROR':
                error_message = 'OAR essentiel pas trouvé: ' + oar_list[1]                     

            if error_message != '': #In case of any error, abort and send error message back
                d = []
                return d,error_message
            
            #Compile plan data to send to scripts
            d = dict(patient = patient,
                     site_name = self.sitebox.Text,
                     exam = patient.Examinations[self.scancombo.Text],
                     iso_name = self.isocombo.Text,
                     machine = self.machinecombo.Text,
                     nb_fx = nb_fx,
                     rx = rx,
                     rx_dose = max(rx), #Needed for isodose creation
                     ptv_names = ptv_names,
                     oar_list = oar_list,
                     technique = technique,
                     couch = couch,
                     custom_max = custom_max)      
            
            return d,error_message

            
        def evalClicked(self, sender, args):

            self.status.ForeColor = Color.Black
            self.status.Text = "Évaluation en cours, veuillez patienter"        
            
            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']
            
            #Predict dose to brain (and generate ROIs)
            self.status.ForeColor = Color.Black
            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 = "Terminé"
            
            #Use bounding boxes to determine PTV diameters
            small_ptvs = 0
            medium_ptvs = 0
            for ptv in d['ptv_names']:
                bb = patient.PatientModel.StructureSets[d['exam'].Name].RoiGeometries[ptv].GetBoundingBox()
                ptv_vol = patient.PatientModel.StructureSets[d['exam'].Name].RoiGeometries[ptv].GetRoiVolume()
                dia = [abs(bb[0].x-bb[1].x),abs(bb[0].y-bb[1].y),abs(bb[0].z-bb[1].z)]
                self.message.Text += "\n\nCible: %s\n   Vol (cc): %.2f\n   Dimensions: %.2fcm x %.2fcm x %.2fcm" % (ptv,ptv_vol,dia[0],dia[1],dia[2])
                if min(dia)<1:
                    self.message.Text += "\nCe PTV est plus petit que 1cm, consultez la physique"
                elif min(dia)<2:
                    small_ptvs += 1
                elif min(dia)<3:
                    medium_ptvs +=1
                    
            if len(ptv_names) == 1 and small_ptvs == 0:                
                if medium_ptvs == 0:
                    self.message.Text += "\n\nLe VMAT devrait être utilisé pour ce plan"
                    self.techcombo.Text = 'VMAT'
                elif medium_ptvs == 1:
                    self.message.Text += "\n\nComme le PTV est relativement petit, faites une\ncomparaison 3DC/VMAT"
                    self.techcombo.Text = '3DC'
            elif len(ptv_names) == 1 and small_ptvs > 0:
                    self.message.Text += "\n\nLe PTV est trop petit pour un plan VMAT, utilisez le 3DC"
                    self.techcombo.Text = '3DC'
            elif len(ptv_names) > 1:
                if small_ptvs == 0:
                    self.message.Text += "\n\nL'IMRT devrait être utilisé pour ce plan"
                    self.techcombo.Text = 'IMRT'
                else:
                    self.message.Text += "\n\nAu moins un PTV <2cm, faites une comparaison\n3DC/IMRT et consultez la physique"
                    self.techcombo.Text = '3DC'            
   
   
        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
        
    
    
        def eraseROIClicked(self, sender, args):

            self.status.ForeColor = Color.Black
            self.status.Text = "En cours, veuillez patienter"        
            
            erased_rois = 0
            
            for roi in patient.PatientModel.RegionsOfInterest:
                if 'predicted_r' in roi.Name or 'ptvs_smooth' in roi.Name:
                    try:
                        patient.PatientModel.RegionsOfInterest[roi.Name].DeleteRoi()
                        erased_rois += 1
                    except:
                        pass
    
            self.status.ForeColor = Color.Green
            self.status.Text = "%d ROIs effacés" % erased_rois
    
    
    #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:
        exam = lib.get_current_examination()
    except:
        message.message_window('Aucun examination trouvé')
        return
        
    form = CraneLauncher()
    Application.Run(form)   
Exemplo n.º 5
0
def preparation_qa():

    #QA preparation script for dosimetrists
    # - Creates QA plan for the currently selected beamset on the ArcCheck phantom
    # - Notes dose to MicroLion
    # - Notes max dose and its location
    # - Shifts the isocenter to put the point of max dose on the MicroLion (most of the time)
    # - Recomputes dose and then exports dose to MicroLion before and after shift, along with instructions for the shift

    patient = lib.get_current_patient()
    plan = lib.get_current_plan()
    beamset = lib.get_current_beamset()
    
    output = "Patient: %s\nPlan: %s\nBeamset: %s\n" % (patient.PatientName.replace("^", ", "),plan.Name,beamset.DicomPlanLabel)
    
    #Create QA plan centered on ISO AC
    create_ac_qa_plans()
    ui = get_current("ui")
    ui.MessageBoxWindowContent.Button['OK'].Click() #Clears away dialogue after exporting DICOM files (or failing to)
    vp = find_current_verification_plan(plan,beamset)
    output += "Plan QA: " + vp.BeamSet.DicomPlanLabel + '\n'
    
    #Find coordinates of current isocenter (rounded to a tenth of a millimeter)
    iso_x = round(vp.BeamSet.Beams[0].PatientToBeamMapping.IsocenterPoint.x, 2)
    iso_y = round(vp.BeamSet.Beams[0].PatientToBeamMapping.IsocenterPoint.y, 2)
    iso_z = round(vp.BeamSet.Beams[0].PatientToBeamMapping.IsocenterPoint.z, 2)
    
    #Get total dose at isocenter as well as each beam's contribution
    isocenter_rsp = lib.RSPoint(iso_x, iso_y, iso_z)
    isocenter_dose = vp.BeamSet.FractionDose.InterpolateDoseInPoint(Point=isocenter_rsp.value)
    output += "\nPLAN CENTRÉ\nDose à l'isocentre: %.2fGy" % (isocenter_dose/100.0)
    for beamdose in vp.BeamSet.FractionDose.BeamDoses:
        dose_per_beam = beamdose.InterpolateDoseInPoint(Point=isocenter_rsp.value)
        output += "\n     %s : %.2fGy" % (beamdose.ForBeam.Name, dose_per_beam / 100.0)

    #Determine location of max dose point and the dose it receives
    fraction_dose = vp.BeamSet.FractionDose
    grid = vp.DoseGrid
    max_dose,max_coords = poi.get_max_dose_coordinates(fraction_dose,grid)
    
    #Create a DSP at this point
    vp.BeamSet.CreateDoseSpecificationPoint(Name="DSP", Coordinates={ 'x': max_coords.x, 'y': max_coords.y, 'z': max_coords.z })
    
    # Formula for new isocenter: 2*(iso coordinate) - dose max coordinate (rounded)
    new_iso = lib.RSPoint(0, 0, 0)
    new_iso.x = 2 * iso_x - round(max_coords.x, 2)
    new_iso.y = 2 * iso_y - round(max_coords.y, 2)
    new_iso.z = 2 * iso_z - round(max_coords.z, 2)

    # Round DSP coordinates to avoid sub-millimeter table displacements
    new_iso.x = round(new_iso.x, 1) + (iso_x - round(iso_x, 1))
    new_iso.y = round(new_iso.y, 1) + (iso_y - round(iso_y, 1))  # If iso_y = 2.75, then you get (2.75-2.8) = -0.05
    new_iso.z = round(new_iso.z, 1) + (iso_z - round(iso_z, 1))

    # Eliminate displacements that are 2mm or less
    if abs(new_iso.x - iso_x) < 0.21:
        new_iso.x = iso_x
    if abs(new_iso.y - iso_y) < 0.21:
        new_iso.y = iso_y
    if abs(new_iso.z - iso_z) < 0.21:
        new_iso.z = iso_z
    
    #Set all beams to the new isocenter
    for beam in vp.BeamSet.Beams:
        beam.PatientToBeamMapping.IsocenterPoint = {'x': new_iso.x, 'y': new_iso.y, 'z': new_iso.z}    
    
    #Now that we're done with our DSP, set it back to the original isocenter coordinates to get dose to MicroLion
    dsp = vp.BeamSet.DoseSpecificationPoints[0]
    dsp.Name = "ISO MicroLion"
    dsp.Coordinates = {'x': iso_x, 'y': iso_y, 'z': iso_z}        

    for beam in vp.BeamSet.Beams:
        beam.SetDoseSpecificationPoint(Name="ISO MicroLion")

    # Compute dose (this is done last, because otherwise changing beam spec points erases dose)
    vp.BeamSet.ComputeDose(ComputeBeamDoses=True, DoseAlgorithm="CCDose", ForceRecompute=False)
    
    #Calculate shift
    shift_x = new_iso.x - iso_x    
    shift_y = new_iso.y - iso_y
    shift_z = new_iso.z - iso_z
    
    if shift_x == 0 and shift_y == 0 and shift_z == 0:
        output += "\n\nL'isocentre se trouve sur le point de dose max. Aucun shift automatique a été calculé."
    else:
        if (isocenter_dose/max_dose) < 0.75:
            output += "\n\nLa dose à l'isocentre est inférieure à 75% de la dose maximale.\nIl est nécessaire de mesurer la dose au MicroLion avec le shift suivant:\n\n"
        else:
            output += "\n\nLa dose à l'isocentre est supérieure à 75% de la dose maximale.\nIl n'est probablement pas nécessaire de faire un shift, mais voici une possibilité de shift au cas ou:\n\n"
        
        if shift_x > 0:
            direction_x = 'vers A'
        elif shift_x < 0:
            direction_x = 'vers B'
        if shift_x != 0:
            output += '          LATERAL: ' + str(abs(shift_x)) + 'cm ' + direction_x + '\n'
        else:
            output += '          LATERAL: Aucun shift\n'

        if shift_y > 0:
            direction_y = 'UP'
        elif shift_y < 0:
            direction_y = 'DOWN'
        if shift_y != 0:
            output += '          HAUTEUR: ' + str(abs(shift_y)) + 'cm ' + direction_y + '\n'
        else:
            output += '          HAUTEUR: Aucun shift\n'
            
        if shift_z < 0:
            direction_z = 'IN'
        else:
            direction_z = 'OUT' #We have to account for 0 shift because I call on this variable later (which isn't true for the other axes)
        if shift_z != 0:
            output += '     LONGITUDINAL: ' + str(abs(shift_z)) + 'cm ' + direction_z + '\n'
        else:
            output += '     LONGITUDINAL: Aucun shift\n'            
            
        if direction_z == 'IN':
            output += "ATTENTION: Suite au déplacement vers IN, assurez-vous que le faisceau n'irradierais pas l'électronique du ArcCheck\n"

        # Print dose to MicroLion with AC shift
        isocenter_dose = vp.BeamSet.FractionDose.InterpolateDoseInPoint(Point=isocenter_rsp.value)
        output += "\nPLAN AVEC SHIFT\nDose à l'isocentre: %.2fGy" % (isocenter_dose/100.0)
        for beamdose in vp.BeamSet.FractionDose.BeamDoses:
            dose_per_beam = beamdose.InterpolateDoseInPoint(Point=isocenter_rsp.value)
            output += "\n     %s : %.2fGy" % (beamdose.ForBeam.Name, dose_per_beam / 100.0)  
            
    
    #For IMRT plans, make a second QA plan to create EPIDStan files
    if vp.BeamSet.DeliveryTechnique == "SMLC":
        num_beams = 0
        num_segments = 0
        for beam in vp.BeamSet.Beams:
            num_beams += 1
            for seg in beam.Segments:
                num_segments += 1
        
        if num_segments > num_beams: #If this is an IMRT plan and not a 3DC plan
            if lib.check_version(4.7):
                phantom_name = '48x48x48 FANTOME'
            elif lib.check_version(4.6):
                phantom_name = '48x48x48 FANTOME'
            create_ac_qa_plans(plan=None,phantom_name=phantom_name,iso_name='2cm EPID')
            ui = get_current("ui")
            ui.MessageBoxWindowContent.Button['OK'].Click() #Clears away dialogue after exporting DICOM files (or failing to)
            output += "\n\nLe plan est fait en IMRT, n'oubliez pas de vérifier la dose 2D à l'aide de EPIDStan\nLes fichiers EPID se trouvent sur MAGGIE"
    
    #Print all results to text file
    try:
        patient_ID = patient.PatientID
        patient_name = patient.PatientName.replace("^", ", ")
        file_path = r'\\radonc.hmr\Departements\Physiciens\Clinique\IMRT\QA'
        file_path += '\\' + patient_name + '~' + patient_ID + '-TESTSUPERBRIDGE'    
        with open(file_path + '\\QA patient ' + vp.BeamSet.DicomPlanLabel + ".txt", 'w') as dvh_file:
            dvh_file.write(output)
    except Exception as e:
        texte = 'Impossible de créer le fichier texte avec les résultats.\n%s\nSVP, notez les valeurs ci-dessous:\n\n' % e
        message.message_window(texte + output)
Exemplo n.º 6
0
def shift_plans_QA(print_results=True):
    """
    Checks all existing QA plans to see if they have a dose specification
    point (which should correspond to the point of dose max).

    If a DSP is found, the isocenter is shifted and the plan is recalculated,
    which should usually move the MicroLion to the point of dose max.

    This script should only be used if the beams are currently on the phantom isocenter point
    (where the MicroLion is situated). If not, then the displacements printed to file may be incorrect!
    """
    patient = lib.get_current_patient()
    plan = lib.get_current_plan()
    beamset = lib.get_current_beamset()
    grid = {'x': 0.2, 'y': 0.2, 'z': 0.2}

    #Determine which verification plan we are currently
    vp = None
    for vpl in plan.VerificationPlans:
        if vpl.ForTreatmentPlan.Name == plan.Name and vpl.OfRadiationSet.DicomPlanLabel == beamset.DicomPlanLabel:
            vp = vpl
            break
    
    #If no appropriate verification plan is found, do nothing
    if vp == None:
        message.message_window('Aucun plan QA trouvé')
        return
    
    message.message_window('Le nom du plan QA trouvé est ' + vpl.BeamSet.DicomPlanLabel)
    
    #for vp in plan.VerificationPlans:
    try:
        p = lib.RSPoint(point=vp.BeamSet.DoseSpecificationPoints[0].Coordinates)  # get coords of first DSP (if it exists)
    except:
        message.message_window('Aucun DSP trouvé dans le plan QA')
        return

    # Find coordinates of current isocenter (rounded to a tenth of a millimeter)
    iso_x = round(vp.BeamSet.Beams[0].PatientToBeamMapping.IsocenterPoint.x, 2)
    iso_y = round(vp.BeamSet.Beams[0].PatientToBeamMapping.IsocenterPoint.y, 2)
    iso_z = round(vp.BeamSet.Beams[0].PatientToBeamMapping.IsocenterPoint.z, 2)

    # Formula for new isocenter: 2*(iso coordinate) - dose max coordinate (rounded)
    p.x = 2 * iso_x - round(p.x, 2)
    p.y = 2 * iso_y - round(p.y, 2)  # difference between DICOM and patient coordinate systems, y=-z
    p.z = 2 * iso_z - round(p.z, 2)  # difference between DICOM and patient coordinate systems, z=y

    # Round DSP coordinates to avoid sub-millimeter table displacements
    p.x = round(p.x, 1) + (iso_x - round(iso_x, 1))
    p.y = round(p.y, 1) + (iso_y - round(iso_y, 1))  # If iso_y = 2.75, then you get (2.75-2.8) = -0.05
    p.z = round(p.z, 1) + (iso_z - round(iso_z, 1))

    # Eliminate displacements that are 2mm or less
    if abs(p.x - iso_x) < 0.21:
        p.x = iso_x
    if abs(p.y - iso_y) < 0.21:
        p.y = iso_y
    if abs(p.z - iso_z) < 0.21:
        p.z = iso_z

    # Add SHIFT to plan name (truncate name if resulting name will be >16 characters)
    if len(vp.BeamSet.DicomPlanLabel) > 9:  # To prevent crashes if resulting name will be > 16 characters long
        name = vp.BeamSet.DicomPlanLabel[:9] + ' SHIFT'
    else:
        name = vp.BeamSet.DicomPlanLabel + ' SHIFT'
    vp.BeamSet.DicomPlanLabel = name

    # Shift isocenter
    #plan.BeamSets[vp.OfRadiationSet.Number-1].CreateQAPlan(PhantomName='QA VMAT ARCCHECK', QAPlanName=name, IsoCenter=p.value, DoseGrid=grid, ComputeDoseWhenPlanIsCreated=True)
    for beam in vp.BeamSet.Beams:
        beam.PatientToBeamMapping.IsocenterPoint = {'x': p.x, 'y': p.y, 'z': p.z}

    # Move DSP to isocenter coordinates and assign as spec point for all beams
    try:
        dsp = vp.BeamSet.DoseSpecificationPoints[0]
        dsp.Name = "ISO MicroLion"
        dsp.Coordinates = {'x': iso_x, 'y': iso_y, 'z': iso_z}
    except IndexError as e:
        logger.error('You must create a dose specification point manually before executing this script.')
        logger.exception(e)
        raise
    except Exception as e:
        logger.exception(e)
        raise

    for beam in vp.BeamSet.Beams:
        beam.SetDoseSpecificationPoint(Name="ISO MicroLion")

    # Compute dose (this is done last, because otherwise changing beam spec points erases dose)
    vp.BeamSet.ComputeDose(ComputeBeamDoses=True, DoseAlgorithm="CCDose", ForceRecompute=False)

    # Write displacement to file
    # Get demographic information
    patient_ID = patient.PatientID
    patient_name = patient.PatientName.replace("^", ", ")

    if print_results is True:        
        try:
            file_path = r'\\radonc.hmr\Departements\Physiciens\Clinique\IMRT\QA'
            file_path += '\\' + patient_name + '~' + patient_ID + '-TESTSUPERBRIDGE'

            # Write to file

            with open(file_path + '\\Déplacement ArcCheck ' + name + ".txt", 'w') as dvh_file:
                dvh_file.write('Patient:                    ' + patient_name + '\n')
                dvh_file.write('No. HMR:                    ' + patient_ID + '\n\n\n')
                dvh_file.write('Déplacement ArcCheck pour QA plan: ' + name + '\n\n')

                shift_x = p.x - iso_x
                if shift_x > 0:
                    direction_x = 'vers A'
                elif shift_x < 0:
                    direction_x = 'vers B'
                if shift_x != 0:
                    dvh_file.write('          LATERAL: ' + str(abs(shift_x)) + 'cm ' + direction_x + '\n')
                else:
                    dvh_file.write('          LATERAL: Aucun shift\n')

                shift_z = p.z - iso_z
                if shift_z > 0:
                    direction_z = 'OUT'
                elif shift_z < 0:
                    direction_z = 'IN'
                if shift_z != 0:
                    dvh_file.write('     LONGITUDINAL: ' + str(abs(shift_z)) + 'cm ' + direction_z + '\n')
                else:
                    dvh_file.write('     LONGITUDINAL: Aucun shift\n')

                shift_y = p.y - iso_y
                if shift_y > 0:
                    direction_y = 'UP'
                elif shift_y < 0:
                    direction_y = 'DOWN'
                if shift_y != 0:
                    dvh_file.write('          HAUTEUR: ' + str(abs(shift_y)) + 'cm ' + direction_y + '\n')
                else:
                    dvh_file.write('          HAUTEUR: Aucun shift\n')

                # Print dose to MicroLion with AC shift
                dsp_temp = lib.RSPoint(dsp.Coordinates.x, dsp.Coordinates.y, dsp.Coordinates.z)
                dsp_dose = vp.BeamSet.FractionDose.InterpolateDoseInPoint(Point=dsp_temp.value)
                dvh_file.write("\n\nNouveau dose au point %s : %.3fGy" % (dsp.Name, dsp_dose / 100.0))
                dvh_file.write("\n\nDose par faisceau: ")
                for beamdose in vp.BeamSet.FractionDose.BeamDoses:
                    dose_per_beam = beamdose.InterpolateDoseInPoint(Point=dsp_temp.value)
                    dvh_file.write("\n     %s : %.3fGy" % (beamdose.ForBeam.Name, dose_per_beam / 100.0))
    
        except:
            texte = 'Impossible de créer le fichier texte avec les déplacements. SVP, notez les valeurs ci-dessous:\n\n'
            shift_x = p.x - iso_x
            if shift_x > 0:
                direction_x = 'vers A'
            elif shift_x < 0:
                direction_x = 'vers B'
            if shift_x != 0:
                texte += '          LATERAL: ' + str(abs(shift_x)) + 'cm ' + direction_x + '\n'
            else:
                texte += '          LATERAL: Aucun shift\n'

            shift_z = p.z - iso_z
            if shift_z > 0:
                direction_z = 'OUT'
            elif shift_z < 0:
                direction_z = 'IN'
            if shift_z != 0:
                texte += '     LONGITUDINAL: ' + str(abs(shift_z)) + 'cm ' + direction_z + '\n'
            else:
                texte += '     LONGITUDINAL: Aucun shift\n'

            shift_y = p.y - iso_y
            if shift_y > 0:
                direction_y = 'UP'
            elif shift_y < 0:
                direction_y = 'DOWN'
            if shift_y != 0:
                texte += '          HAUTEUR: ' + str(abs(shift_y)) + 'cm ' + direction_y + '\n'
            else:
                texte += '          HAUTEUR: Aucun shift\n'

            message.message_window(texte)