def verify_prescription():

    plan = lib.get_current_plan()
    beamset = lib.get_current_beamset()

    # Check if beamset dose is dependent
    try:
        bkgdose_name = plan.PlanOptimizations[
            beamset.Number - 1].BackgroundDose.ForBeamSet.DicomPlanLabel
    except:
        bkgdose_name = "None"

    # Determine prescription dose, fractions and target
    try:
        presc_text = str(
            (beamset.Prescription.PrimaryDosePrescription.DoseValue) /
            100.0) + "Gy"
        presc_text += " en %dfx " % beamset.FractionationPattern.NumberOfFractions
        # Display prescription type and dose
        if beamset.Prescription.PrimaryDosePrescription.PrescriptionType == 'DoseAtPoint':
            presc_text += "au point " + beamset.Prescription.PrimaryDosePrescription.OnStructure.Name
        elif beamset.Prescription.PrimaryDosePrescription.PrescriptionType == 'DoseAtVolume':
            presc_text += "à " + str(
                beamset.Prescription.PrimaryDosePrescription.DoseVolume
            ) + r'% du volume du ' + beamset.Prescription.PrimaryDosePrescription.OnStructure.Name
        if bkgdose_name != "None":
            presc_text += "\n     (dépendente sur beamset " + bkgdose_name + ")"
        presc_text = presc_text.replace(".0Gy", "Gy")
        presc_text = presc_text.replace(".0%", "%")
    except:
        presc_text = "Prescription non définie"
    presc_text = "Prescription: " + presc_text

    return presc_text
Esempio n. 2
0
def add_opt_obj_prostate_A1(plan=None):
    """
    Ajoute les objectifs d'optimisation pour les cas de prostate (plan A1).

    ===================  ================  ===========  =========  =========
    ROI                  Type de critère   Poids        Valeur 1   Valeur 2
    ===================  ================  ===========  =========  =========
    PTV A1               ``mindose``       100          76 Gy
    PTV A1               ``maxdose``       10           83 Gy
    RECTUM               ``maxdvh` `       10           75 Gy      15 %
    RECTUM               ``maxdvh` `       10           70 Gy      25 %
    RECTUM               ``maxdvh` `       1            65 Gy      35 %
    RECTUM               ``maxdvh` `       1            60 Gy      50 %
    RECTUM               ``maxdose``       10           79.5 Gy
    VESSIE               ``maxdose``       1            82 Gy
    BodyRS               ``dosefalloff``   0            76-40 Gy   3 cm
    ===================  ================  ===========  =========  =========

    .. seealso::
      fonctions :py:func:`hmrlib.hmrlib.optim.add_mindose_objective`, :py:func:`hmrlib.hmrlib.optim.add_maxdose_objective`
    """

    if plan is None:
        plan = lib.get_current_plan()

    nb_fx = plan.BeamSets[0].FractionationPattern.NumberOfFractions

    if nb_fx == 40:  #Plan to 80Gy
        optim.add_mindose_objective('PTV A1', 7600, weight=100, plan=plan)
        optim.add_maxdose_objective('PTV A1', 8300, weight=10, plan=plan)
        optim.add_maxdvh_objective('RECTUM', 7500, 15, weight=10, plan=plan)
        optim.add_maxdvh_objective('RECTUM', 7000, 25, weight=10, plan=plan)
        optim.add_maxdvh_objective('RECTUM', 6500, 35, weight=1, plan=plan)
        optim.add_maxdvh_objective('RECTUM', 6000, 50, weight=1, plan=plan)
        optim.add_maxdose_objective('RECTUM+3mm', 7900, weight=10, plan=plan)
        optim.add_maxdose_objective('VESSIE', 8200, weight=1, plan=plan)
        optim.add_dosefalloff_objective('BodyRS',
                                        7600,
                                        4000,
                                        3,
                                        weight=0,
                                        plan=plan)
    elif nb_fx == 33:  #Plan to 66 Gy
        optim.add_mindose_objective('PTV A1', 6270, weight=100, plan=plan)
        optim.add_maxdose_objective('PTV A1', 6850, weight=10, plan=plan)
        optim.add_maxdvh_objective('RECTUM', 6500, 35, weight=10, plan=plan)
        optim.add_maxdvh_objective('RECTUM', 6000, 50, weight=10, plan=plan)
        optim.add_maxdose_objective('RECTUM+3mm', 6800, weight=10, plan=plan)
        optim.add_maxdose_objective('VESSIE', 6900, weight=1, plan=plan)
        optim.add_dosefalloff_objective('BodyRS',
                                        6270,
                                        3300,
                                        3,
                                        weight=0,
                                        plan=plan)
    elif nb_fx == 22:  #Plan to 66 Gy at 3Gy per fraction
        optim.add_mindose_objective('PTV A1', 6270, weight=100, plan=plan)
        optim.add_maxdose_objective('PTV A1', 6850, weight=10, plan=plan)
        optim.add_maxdvh_objective('RECTUM', 5420, 35, weight=10, plan=plan)
        optim.add_maxdvh_objective('RECTUM', 5000, 50, weight=10, plan=plan)
        optim.add_maxdose_objective('RECTUM+3mm', 6500, weight=10, plan=plan)
        optim.add_maxdose_objective('VESSIE', 6900, weight=1, plan=plan)
        optim.add_dosefalloff_objective('BodyRS',
                                        6270,
                                        3300,
                                        3,
                                        weight=0,
                                        plan=plan)
    elif nb_fx == 20 or nb_fx == 24:  #Plan to 60 Gy at 3Gy per fraction (or PACE 62Gy in 20fx)
        if roi.roi_exists('PTV_6200'):
            optim.add_mindose_objective('PTV_6200',
                                        5890,
                                        weight=100,
                                        plan=plan)
            optim.add_maxdose_objective('PTV_6200', 6450, weight=10, plan=plan)
            optim.add_maxdvh_objective('Rectum', 6000, 5, weight=10, plan=plan)
            optim.add_maxdvh_objective('Rectum',
                                       5600,
                                       25,
                                       weight=10,
                                       plan=plan)
            optim.add_maxdvh_objective('Rectum', 5200, 30, weight=1, plan=plan)
            optim.add_maxdvh_objective('Rectum', 4800, 50, weight=1, plan=plan)
            optim.add_maxdose_objective('Rectum+3mm',
                                        6100,
                                        weight=10,
                                        plan=plan)
            optim.add_maxdose_objective('Bladder', 6400, weight=1, plan=plan)
            optim.add_dosefalloff_objective('BodyRS',
                                            5890,
                                            3100,
                                            3,
                                            weight=0,
                                            plan=plan)
        else:
            optim.add_mindose_objective('PTV A1', 5700, weight=100, plan=plan)
            optim.add_maxdose_objective('PTV A1', 6225, weight=10, plan=plan)
            optim.add_maxdvh_objective('RECTUM',
                                       5700,
                                       15,
                                       weight=10,
                                       plan=plan)
            optim.add_maxdvh_objective('RECTUM',
                                       5280,
                                       30,
                                       weight=10,
                                       plan=plan)
            optim.add_maxdvh_objective('RECTUM', 4860, 50, weight=1, plan=plan)
            optim.add_maxdvh_objective('RECTUM', 3240, 70, weight=1, plan=plan)
            optim.add_maxdose_objective('RECTUM+3mm',
                                        6000,
                                        weight=10,
                                        plan=plan)
            optim.add_maxdose_objective('VESSIE', 6300, weight=1, plan=plan)
            optim.add_dosefalloff_objective('BodyRS',
                                            5700,
                                            3000,
                                            3,
                                            weight=0,
                                            plan=plan)

    elif nb_fx == 39:  #PACE 78Gy en 29fx
        optim.add_mindose_objective('PTV_7800', 7410, weight=100, plan=plan)
        optim.add_maxdose_objective('PTV_7800', 8100, weight=10, plan=plan)
        optim.add_maxdvh_objective('Rectum', 7500, 5, weight=10, plan=plan)
        optim.add_maxdvh_objective('Rectum', 7000, 25, weight=10, plan=plan)
        optim.add_maxdvh_objective('Rectum', 6500, 30, weight=1, plan=plan)
        optim.add_maxdvh_objective('Rectum', 6000, 50, weight=1, plan=plan)
        optim.add_maxdose_objective('Rectum+3mm', 7700, weight=10, plan=plan)
        optim.add_maxdose_objective('Bladder', 8000, weight=1, plan=plan)
        optim.add_dosefalloff_objective('BodyRS',
                                        7410,
                                        3900,
                                        3,
                                        weight=0,
                                        plan=plan)
    elif nb_fx == 5:  #PACE 36.25Gy en 5fx
        optim.add_mindose_objective('PTV_3625', 3444, weight=100, plan=plan)
        optim.add_maxdose_objective('PTV_3625', 3760, weight=10, plan=plan)
        optim.add_maxdvh_objective('Rectum', 2500, 20, weight=10, plan=plan)
        optim.add_maxdvh_objective('Rectum', 1810, 50, weight=10, plan=plan)
        optim.add_maxdose_objective('Rectum+3mm', 3575, weight=10, plan=plan)
        optim.add_maxdose_objective('Bladder', 3700, weight=1, plan=plan)
        optim.add_dosefalloff_objective('BodyRS',
                                        3444,
                                        1813,
                                        3,
                                        weight=0,
                                        plan=plan)
Esempio n. 3
0
def add_opt_obj_lung_stereo(contralateral_lung=None, patient_plan=None):
    """
    Ajoute les objectifs d'optimisation pour les cas de stéréo de poumon.

    Assume que les PTV sont nommés avec leur niveau de prescription. Ex. : PTV48

    Si le nom du ROI du poumon contralatéral n'est pas spécifié, détecte si le
    PTV est situé dans POUMON D ou POUMON G et ajuste en conséquence.

    Args:
        contralateral_lung (str, optional): nom du ROI du poumon contralatéral

    .. rubric::
      Objectifs pour prescription de 48 Gy - 4 Fx :

    =====================  ================  ===========  ========
    ROI                    Type de critère   Poids        Valeur
    =====================  ================  ===========  ========
    PTV48                  ``mindose``       50           48 Gy
    PTV48                  ``maxdose``       1            72 Gy
    RING_1                 ``maxdose``       1            48.5 Gy
    RING_2                 ``maxdose``       1            42 Gy
    RING_3                 ``maxdose``       1            28 Gy
    PEAU                   ``maxdose``       1            27 Gy
    BR SOUCHE              ``maxdose``       1            33.9 Gy
    COTES                  ``maxdose``       1            48 Gy
    MOELLE                 ``maxdose``       1            18 Gy
    COEUR                  ``maxdose``       1            33.9 Gy
    OESOPHAGE              ``maxdose``       1            11 Gy
    <poumon contralat.>\*  ``maxdose``       1            5 Gy
    PLEXUS BRACHIAL        ``maxdose``       1            27 Gy
    TRACHEE                ``maxdose``       1            33.9 Gy
    PRV MOELLE             ``maxdose``       1            22.4 Gy
    PRV PLEXUS             ``maxdose``       1            30.5 Gy
    =====================  ================  ===========  ========

    .. rubric::
      Objectifs pour prescription de 54 Gy - 3 Fx :

    =====================  ================  ===========  ========
    ROI                    Type de critère   Poids        Valeur
    =====================  ================  ===========  ========
    PTV54                  ``mindose``       50           54 Gy
    PTV54                  ``maxdose``       1            81 Gy
    RING_1                 ``maxdose``       1            54.5 Gy
    RING_2                 ``maxdose``       1            47 Gy
    RING_3                 ``maxdose``       1            32 Gy
    PEAU                   ``maxdose``       1            24 Gy
    BR SOUCHE              ``maxdose``       1            30 Gy
    COTES                  ``maxdose``       1            54 Gy
    MOELLE                 ``maxdose``       1            18 Gy
    COEUR                  ``maxdose``       1            30 Gy
    OESOPHAGE              ``maxdose``       1            10 Gy
    <poumon contralat.>\*  ``maxdose``       1            5 Gy
    PLEXUS BRACHIAL        ``maxdose``       1            24 Gy
    TRACHEE                ``maxdose``       1            30 Gy
    PRV MOELLE             ``maxdose``       1            20 Gy
    PRV PLEXUS             ``maxdose``       1            27 Gy
    =====================  ================  ===========  ========

    .. rubric::
      Objectifs pour prescription de 60 Gy - 8 Fx :

    =====================  ================  ===========  ========
    ROI                    Type de critère   Poids        Valeur
    =====================  ================  ===========  ========
    PTV60                  ``mindose``       50           60 Gy
    PTV60                  ``maxdose``       1            90 Gy
    RING_1                 ``maxdose``       1            60.5 Gy
    RING_2                 ``maxdose``       1            52 Gy
    RING_3                 ``maxdose``       1            35.5 Gy
    PEAU                   ``maxdose``       1            35.5 Gy
    BR SOUCHE              ``maxdose``       1            45.1 Gy
    BR SOUCHE              ``maxdose``       1            61 Gy
    COTES                  ``maxdose``       1            60 Gy
    MOELLE                 ``maxdose``       1            24 Gy
    COEUR                  ``maxdose``       1            45 Gy
    OESOPHAGE              ``maxdose``       1            13.5 Gy
    <poumon contralat.>\*  ``maxdose``       1            5 Gy
    PLEXUS BRACHIAL        ``maxdose``       1            35.5 Gy
    TRACHEE                ``maxdose``       1            45 Gy
    PRV MOELLE             ``maxdose``       1            29.1 Gy
    PRV PLEXUS             ``maxdose``       1            40.3 Gy
    =====================  ================  ===========  ========

    .. note::
      \*<poumon contralat.> représente le nom du ROI du poumon contralatéral, soit *POUMON G* ou *POUMON D*.

    .. seealso::
      fonctions :py:func:`hmrlib.hmrlib.optim.add_mindose_objective`, :py:func:`hmrlib.hmrlib.optim.add_maxdose_objective`
    """

    if patient_plan is None:
        patient_plan = lib.get_current_plan()

    ptvs = roi.identify_ptvs2()

    if len(ptvs) > 1:
        logger.warning('More than one dose level found for PTVs.  PTVs = %s',
                       ptvs)
        # raise SystemError(u"Plus d'un niveau de dose trouvé pour les PTV.")

    if 'PTV48' in ptvs:
        optim.add_mindose_objective('PTV48',
                                    4800,
                                    weight=50,
                                    plan=patient_plan)
        optim.add_maxdose_objective('PTV48', 7200, plan=patient_plan)

        optim.add_maxdose_objective('RING_1', 4850, plan=patient_plan)
        optim.add_maxdose_objective('RING_2', 4200, plan=patient_plan)
        optim.add_maxdose_objective('RING_3', 2800, plan=patient_plan)

        optim.add_maxdose_objective('PEAU', 2700, plan=patient_plan)
        optim.add_maxdose_objective('BR SOUCHE', 3390, plan=patient_plan)
        optim.add_maxdose_objective('COTES', 4800, plan=patient_plan)
        optim.add_maxdose_objective('MOELLE', 1800, plan=patient_plan)
        optim.add_maxdose_objective('COEUR', 3390, plan=patient_plan)
        optim.add_maxdose_objective('OESOPHAGE', 1100, plan=patient_plan)

        if contralateral_lung is None:
            if roi.find_most_intersecting_roi(
                    'PTV48', ['POUMON DRT', 'POUMON GCHE']) == 'POUMON GCHE':
                contralateral_lung = 'POUMON DRT'
            else:
                contralateral_lung = 'POUMON GCHE'

        optim.add_maxdose_objective(contralateral_lung, 500, plan=patient_plan)

        optim.add_maxdose_objective('PLEXUS BRACHIAL', 2700, plan=patient_plan)
        optim.add_maxdose_objective('TRACHEE', 3390, plan=patient_plan)
        optim.add_maxdose_objective('PRV MOELLE', 2240, plan=patient_plan)
        optim.add_maxdose_objective('PRV PLEXUS', 3050, plan=patient_plan)

    elif 'PTV54' in ptvs:
        optim.add_mindose_objective('PTV54',
                                    5400,
                                    weight=50,
                                    plan=patient_plan)
        optim.add_maxdose_objective('PTV54', 8100, plan=patient_plan)

        optim.add_maxdose_objective('RING_1', 5450, plan=patient_plan)
        optim.add_maxdose_objective('RING_2', 4700, plan=patient_plan)
        optim.add_maxdose_objective('RING_3', 3200, plan=patient_plan)

        optim.add_maxdose_objective('PEAU', 2400, plan=patient_plan)
        optim.add_maxdose_objective('BR SOUCHE', 3000, plan=patient_plan)
        optim.add_maxdose_objective('COTES', 5400, plan=patient_plan)
        optim.add_maxdose_objective('MOELLE', 1800, plan=patient_plan)
        optim.add_maxdose_objective('COEUR', 3000, plan=patient_plan)
        optim.add_maxdose_objective('OESOPHAGE', 1000, plan=patient_plan)

        if contralateral_lung is None:
            if roi.find_most_intersecting_roi(
                    'PTV54', ['POUMON DRT', 'POUMON GCHE']) == 'POUMON GCHE':
                contralateral_lung = 'POUMON DRT'
            else:
                contralateral_lung = 'POUMON GCHE'

        optim.add_maxdose_objective(contralateral_lung, 500, plan=patient_plan)

        optim.add_maxdose_objective('PLEXUS BRACHIAL', 2400, plan=patient_plan)
        optim.add_maxdose_objective('TRACHEE', 3000, plan=patient_plan)
        optim.add_maxdose_objective('PRV MOELLE', 2000, plan=patient_plan)
        optim.add_maxdose_objective('PRV PLEXUS', 2700, plan=patient_plan)

    elif 'PTV60' in ptvs:
        optim.add_mindose_objective('PTV60',
                                    6000,
                                    weight=50,
                                    plan=patient_plan)
        optim.add_maxdose_objective('PTV60', 9000, plan=patient_plan)

        optim.add_maxdose_objective('RING_1', 6050, plan=patient_plan)
        optim.add_maxdose_objective('RING_2', 5200, plan=patient_plan)
        optim.add_maxdose_objective('RING_3', 3550, plan=patient_plan)

        optim.add_maxdose_objective('PEAU', 3550, plan=patient_plan)
        optim.add_maxdose_objective('BR SOUCHE', 4510, plan=patient_plan)
        optim.add_maxdose_objective('BR SOUCHE', 6100, plan=patient_plan)
        optim.add_maxdose_objective('COTES', 6000, plan=patient_plan)
        optim.add_maxdose_objective('MOELLE', 2400, plan=patient_plan)
        optim.add_maxdose_objective('COEUR', 4500, plan=patient_plan)
        optim.add_maxdose_objective('OESOPHAGE', 1350, plan=patient_plan)

        if contralateral_lung is None:
            if roi.find_most_intersecting_roi(
                    'PTV60', ['POUMON DRT', 'POUMON GCHE']) == 'POUMON GCHE':
                contralateral_lung = 'POUMON DRT'
            else:
                contralateral_lung = 'POUMON GCHE'

        optim.add_maxdose_objective(contralateral_lung, 500, plan=patient_plan)

        optim.add_maxdose_objective('PLEXUS BRACHIAL', 3550, plan=patient_plan)
        optim.add_maxdose_objective('TRACHEE', 4500, plan=patient_plan)
        optim.add_maxdose_objective('PRV MOELLE', 2910, plan=patient_plan)
        optim.add_maxdose_objective('PRV PLEXUS', 4030, plan=patient_plan)

    else:
        logger.warning(
            'No PTV with supported dose level found.  Please name PTV with prescription in name (ex. PTV48).'
        )
Esempio n. 4
0
def add_opt_obj_brain_stereo(patient_plan=None):
    """
    Ajoute les objectifs d'optimisation pour les cas de stéréo de poumon.

    Assume que les PTV sont nommés avec leur niveau de prescription. Ex. : PTV15

    Les prescriptions supportées sont :

    * 15 Gy
    * 18 Gy
    * 20 Gy

    .. rubric::
      Objectifs pour prescription de 15 Gy :

    ===================  ================  ===========  ==========
    ROI                  Type de critère   Poids        Valeur
    ===================  ================  ===========  ==========
    PTV15                ``mindose``       50           15 Gy
    PTV15                ``maxdose``       1            15 Gy
    RING_1_3mm           ``maxdose``       1            15.5 Gy
    RING_2_8mm           ``maxdose``       1            10 Gy
    RING_3_1cm           ``maxdose``       1            7.5 Gy
    ===================  ================  ===========  ==========

    .. rubric::
      Objectifs pour prescription de 18 Gy :

    ===================  ================  ===========  ==========
    ROI                  Type de critère   Poids        Valeur
    ===================  ================  ===========  ==========
    PTV18                ``mindose``       50           18 Gy
    PTV18                ``maxdose``       1            27 Gy
    RING_1_3mm           ``maxdose``       1            18.5 Gy
    RING_2_8mm           ``maxdose``       1            12.5 Gy
    RING_3_1cm           ``maxdose``       1            8.5 Gy
    ===================  ================  ===========  ==========

    .. rubric::
      Objectifs pour prescription de 20 Gy :

    ===================  ================  ===========  ==========
    ROI                  Type de critère   Poids        Valeur
    ===================  ================  ===========  ==========
    PTV20                ``mindose``       50           20 Gy
    PTV20                ``maxdose``       1            30 Gy
    RING_1_3mm           ``maxdose``       1            20.5 Gy
    RING_2_8mm           ``maxdose``       1            14 Gy
    RING_3_1cm           ``maxdose``       1            9.5 Gy
    ===================  ================  ===========  ==========

    .. seealso::
      fonctions :py:func:`hmrlib.optim.add_mindose_objective`, :py:func:`hmrlib.optim.add_maxdose_objective`
    """
    if patient_plan is None:
        patient_plan = lib.get_current_plan()

    ptvs = roi.identify_ptvs2()

    if len(ptvs) > 1:
        logger.warning('More than one dose level found for PTVs.  PTVs = %s',
                       ptvs)
        # raise SystemError('More than one dose level found.')

    if 'PTV15' in ptvs:
        optim.add_mindose_objective('PTV15',
                                    1500,
                                    weight=50,
                                    plan=patient_plan)
        optim.add_maxdose_objective('PTV15', 2250, plan=patient_plan)

        optim.add_maxdose_objective('RING_1_3mm', 1550, plan=patient_plan)
        optim.add_maxdose_objective('RING_2_8mm', 1000, plan=patient_plan)
        optim.add_maxdose_objective('RING_3_1cm', 750, plan=patient_plan)
        optim.add_maxdose_objective('TISSUS SAINS', 650, plan=patient_plan)

    elif 'PTV18' in ptvs:
        optim.add_mindose_objective('PTV18',
                                    1800,
                                    weight=50,
                                    plan=patient_plan)
        optim.add_maxdose_objective('PTV18', 2700, plan=patient_plan)

        optim.add_maxdose_objective('RING_1_3mm', 1850, plan=patient_plan)
        optim.add_maxdose_objective('RING_2_8mm', 1250, plan=patient_plan)
        optim.add_maxdose_objective('RING_3_1cm', 850, plan=patient_plan)
        optim.add_maxdose_objective('TISSUS SAINS', 725, plan=patient_plan)

    elif 'PTV20' in ptvs:
        optim.add_mindose_objective('PTV20',
                                    2000,
                                    weight=50,
                                    plan=patient_plan)
        optim.add_maxdose_objective('PTV20', 3000, plan=patient_plan)

        optim.add_maxdose_objective('RING_1_3mm', 2050, plan=patient_plan)
        optim.add_maxdose_objective('RING_2_8mm', 1400, plan=patient_plan)
        optim.add_maxdose_objective('RING_3_1cm', 950, plan=patient_plan)

    else:
        logger.warning(
            'No PTV with supported dose level found.  Please name PTV with prescription in name (ex. PTV18).'
        )
Esempio n. 5
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)
Esempio n. 6
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)
Esempio n. 7
0
def finalize_beamset(original_beamset_name,
                     rx_dose,
                     nb_fx,
                     site,
                     ptv_name,
                     color,
                     skip_oars=False):
    #Note that I tried to pass the beamset and ptv objects directly into this function, but that seems to cause crashing, so now I pass strings instead.
    patient = lib.get_current_patient()
    plan = lib.get_current_plan()

    bs = plan.BeamSets[original_beamset_name]
    ptv = patient.PatientModel.RegionsOfInterest[ptv_name]
    beamset_name = ptv_name.split()[1]
    scan_name = "CT 1"

    if site == 'Crâne':
        try:
            #statistics.stereo_brain_statistics()
            crane.crane_kbp_write_final_results_to_file(
                rx_dose, nb_fx, ptv_name)
        except:
            #file_path = r'\\radonc.hmr\Departements\Physiciens\Clinique\IMRT\Statistiques\crane.txt'
            file_path = r'\\radonc.hmr\Departements\Physiciens\Clinique\IMRT\Statistiques\Crane KBP Plan Failure.txt'  #Dump to a separate file in case exception was caused by main stats file being open elsewhere
            output = patient.PatientName + ',' + patient.PatientID + ',' + plan.Name + ',' + bs.DicomPlanLabel + ',' + 'Collecte de statistiques échouée'
            with open(file_path, 'a') as stat_file:
                stat_file.write(output + '\n')
    elif site == 'Poumon':
        try:
            statistics.stereo_lung_statistics()
        except:
            file_path = r'\\radonc.hmr\Departements\Physiciens\Clinique\IMRT\Statistiques\poumon.txt'
            output = patient.PatientName + ',' + patient.PatientID + ',' + plan.Name + ',' + bs.DicomPlanLabel + ',' + 'Collecte de statistiques échouée'
            with open(file_path, 'a') as stat_file:
                stat_file.write(output + '\n')

    #Check to see if PTV is drawn using contours (since prescription point placement will fail if the PTV is in voxel format)
    try:
        temp = patient.PatientModel.StructureSets[scan_name].RoiGeometries[
            ptv_name].PrimaryShape.Contours[0]
    except:
        return "PTV en voxels"

    #Check to see if prescription dose exists in patient (otherwise it is impossible to place PT PRESC correctly)
    rx_dose_vol = bs.FractionDose.GetRelativeVolumeAtDoseValues(
        RoiName=ptv_name, DoseValues=[rx_dose * 100 / nb_fx])
    if rx_dose_vol[
            0] == 0:  #The volume in BodyRS that receives the prescription dose is 0
        return "Impossible de placer PT PRESC pour beamset " + original_beamset_name + " car \ndose max inférieure à la dose de prescription"
    """
    #WARNING: This script uses UI scripting. If the user switches focus to another piece of software during 
    #         execution, the UI commands will not be completed. The script will not give the desired results
    #         and may crash.
    
    # Erase setup beams
    ui = lib.get_current("ui")
    #Select the beamset using the combobox on the top right of RayStation. Note that in plans with only one beamset, this combobox does not exist! So we have to use try.
    try:
        ui.SelectionBar.ComboBox_RadiationSets.ToggleButton.Click()
        ui.SelectionBar.ComboBox_RadiationSets.Popup.ComboBoxItem[original_beamset_name].Select()
        ui.SelectionBar.ComboBox_RadiationSets.ToggleButton.Click()
    except:
        no_big_deal = True        
    ui.MenuItem[2].Button_PlanDesign.Click() #Select Plan Design tab
    try:
        ui.Workspace.TabControl['Beams'].TabItem['Setup Beams'].Select()
        for setup_beam in bs.PatientSetup.SetupBeams:
            ui.Workspace.TabControl['Beams'].Button_Delete.Click()
    except:
        no_big_deal = True    
    """

    if site == "Poumon":
        expi_scan = "CT 2"  #Used for transfer of contours to expi scan for Mosaiq

    if not skip_oars:
        # Rename OAR contours for SuperBridge transfer
        roi_list = check_rois(site)
        if site == "Poumon":  #Add ITV to transfer list and prepare to copy contours to expi scan
            itv_name = "I" + ptv_name[1:]
            roi_list.append(itv_name)
        for rois in patient.PatientModel.RegionsOfInterest:
            if rois.Name in roi_list:
                rois.Name += "*"
                if site == "Poumon":
                    patient.PatientModel.CopyRoiGeometry(
                        SourceExamination=patient.Examinations[scan_name],
                        TargetExamination=patient.Examinations[expi_scan],
                        RoiName=rois.Name)

    # Rename beamset
    bs.DicomPlanLabel = beamset_name

    # Rename isodose ROI and add * to it and PTV
    isodose_roi = patient.PatientModel.RegionsOfInterest["isodose " +
                                                         beamset_name]
    if site == "Prostate" or site == "ORL":
        isodose_roi.Name = "ISO %s %.2fGy*" % (beamset_name, rx_dose * 0.95)
    else:
        isodose_roi.Name = "ISO %s %.2fGy*" % (beamset_name, rx_dose)
    isodose_roi.Name = isodose_roi.Name.replace('.00Gy', 'Gy')
    ptv.Name += '*'

    # Rename ISO MOELLE 48Gy (ORL only)
    if roi.roi_exists("isodose moelle"):
        patient.PatientModel.RegionsOfInterest[
            'isodose moelle'].Name = "ISO MOELLE 48Gy*"

    # Copy PTV and isodose ROIs to second beamset for patient positioning at treatment room (lung cases only)
    if site == "Poumon":
        patient.PatientModel.CopyRoiGeometry(
            SourceExamination=patient.Examinations[scan_name],
            TargetExamination=patient.Examinations[expi_scan],
            RoiName=ptv.Name)
        patient.PatientModel.CopyRoiGeometry(
            SourceExamination=patient.Examinations[scan_name],
            TargetExamination=patient.Examinations[expi_scan],
            RoiName=isodose_roi.Name)

    # Add comment for Superbridge transfer (except for prostate cases, since they are not measured with the ArcCheck)
    if site != "Prostate":
        bs.Prescription.Description = "VMAT"

    # Create DSP and PT PRESC
    poi_name = 'PT PRESC ' + beamset_name
    poi.create_poi({
        'x': 0,
        'y': 0,
        'z': 0
    },
                   poi_name,
                   color=color,
                   examination=patient.Examinations[scan_name])
    bs.CreateDoseSpecificationPoint(Name="DSP",
                                    Coordinates={
                                        'x': 0,
                                        'y': 0,
                                        'z': 0
                                    })

    # Move PT PRESC to a point that receives correct dose per fraction and prescribe
    poi.place_prescription_point(target_fraction_dose=rx_dose * 100 / nb_fx,
                                 ptv_name=ptv.Name,
                                 poi_name=poi_name,
                                 beamset=bs,
                                 exam=patient.Examinations[scan_name])
    bs.AddDosePrescriptionToPoi(PoiName=poi_name, DoseValue=rx_dose * 100)

    # Move DSP to coordinates of PT PRESC and assign to all beams
    point = poi.get_poi_coordinates(poi_name)
    dsp = [x for x in bs.DoseSpecificationPoints][0]
    dsp.Name = "DSP " + beamset_name
    dsp.Coordinates = point.value

    for beam in bs.Beams:
        beam.SetDoseSpecificationPoint(Name=dsp.Name)
    bs.ComputeDose(
        ComputeBeamDoses=True, DoseAlgorithm="CCDose", ForceRecompute=True
    )  # Dose is recalculated to show beam dose at spec point (otherwise not displayed)

    return ("You just finalized the beamset named " + bs.DicomPlanLabel)
Esempio n. 8
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 verify_isocenter():

    exam = lib.get_current_examination()
    plan = lib.get_current_plan()
    beamset = lib.get_current_beamset()

    loc_coords = None
    iso_coords = None

    # Isocenter and localization point
    try:
        loc_point_name = beamset.PatientSetup.LocalizationPoiGeometrySource.LocalizationPoiGeometry.OfPoi.Name
        loc_coords = poi.get_poi_coordinates(loc_point_name, exam)
    except:
        loc_point_name = "Aucun point de localisation trouvé"

    num_iso = 0
    if poi.poi_exists("ISO", exam):
        iso_point_name = "ISO"
        num_iso += 1
    if poi.poi_exists("ISO B1", exam):
        iso_point_name = "ISO B1"
        num_iso += 1

    if num_iso == 0:
        iso_point_name = "Aucun point trouvé pour l'isocentre"
    elif num_iso > 1:
        iso_point_name = "Plus qu'un candidat trouvé pour l'isocentre"
    elif num_iso == 1:
        iso_coords = poi.get_poi_coordinates(iso_point_name, exam)

    if loc_coords is None:
        loc_poi_text = "Aucun point de localisation trouvé"
    else:
        if loc_point_name == "REF SCAN":
            loc_poi_text = "POI localization: %s   (%.2f, %.2f, %.2f)" % (
                loc_point_name, loc_coords.x, loc_coords.z, -1 * loc_coords.y
            )  #Fewer spaces to maintain alignment of results
        else:
            loc_poi_text = "POI localization: %s            (%.2f, %.2f, %.2f)" % (
                loc_point_name, loc_coords.x, loc_coords.z, -1 * loc_coords.y)

    if iso_coords is None:
        iso_poi_text = iso_point_name
    else:
        iso_poi_text = "Coordonnées POI %s            (%.2f, %.2f, %.2f)" % (
            iso_point_name, iso_coords.x, iso_coords.z, -1 * iso_coords.y)

    shift_text = ""

    #Check to find shift from reference point to isocenter
    if loc_coords != None and iso_coords != None:
        if iso_point_name != loc_point_name:
            if (loc_coords.x - iso_coords.x
                    == 0) and (loc_coords.y - iso_coords.y
                               == 0) and (loc_coords.z - iso_coords.z == 0):
                shift_text += "Le point de localisation et le point " + iso_point_name + " ont les mêmes coordonnés"
            else:
                shift_text += "Shift de %.2fcm, %.2fcm, %.2fcm entre point de localisation\n     et point %s" % (
                    loc_coords.x - iso_coords.x, loc_coords.z - iso_coords.z,
                    iso_coords.y - loc_coords.y, iso_point_name)

    # Beam isocenters
    beam_iso_text = "Coordonnées faisceaux          "
    mismatch = False
    try:
        for i, beam in enumerate(
                beamset.Beams
        ):  #Verify that coordinates are the same for all beams
            if i > 0:
                old_iso_poi = beam_iso_poi
            beam_iso_poi = [
                x for x in beam.PatientToBeamMapping.IsocenterPoint
            ]
            if i > 0:
                if abs(beam_iso_poi[0].Value - old_iso_poi[0].Value
                       ) > 0.005 or abs(beam_iso_poi[1].Value -
                                        old_iso_poi[1].Value) > 0.005 or abs(
                                            beam_iso_poi[2].Value -
                                            old_iso_poi[2].Value) > 0.005:
                    beam_iso_text = "Coordonnées faisceaux: Coordonnées différentes pour faisceaux " + beamset.Beams[
                        i - 1].Name + " et " + beam.Name + "!"
                    mismatch = True
                    break
        if not mismatch:
            beam_iso_text += "(%.2f, %.2f, %.2f)" % (
                beam_iso_poi[0].Value, beam_iso_poi[2].Value,
                beam_iso_poi[1].Value * -1)
    except:
        beam_iso_text += "Coordonnées pas trouvés"

    # Verify if beams are centered on isocenter point
    if beam_iso_poi != None and iso_coords != None and not mismatch:
        iso_shift_x = iso_coords.x - beam_iso_poi[0].Value
        iso_shift_y = iso_coords.y - beam_iso_poi[1].Value
        iso_shift_z = iso_coords.z - beam_iso_poi[2].Value
        if abs(iso_shift_x) > 0.005 or abs(iso_shift_y) > 0.005 or abs(
                iso_shift_z) > 0.005:
            shift_text += "\nShift de %.2fcm, %.2fcm, %.2fcm entre les faisceaux et\n     le point %s" % (
                -1 * iso_shift_x, -1 * iso_shift_z, iso_shift_y,
                iso_point_name)
        else:
            shift_text += "\nTous les faisceaux partagent les coordonnés du point " + iso_point_name

    return loc_poi_text, iso_poi_text, beam_iso_text, shift_text
def verify_segments():
    plan = lib.get_current_plan()
    beamset = lib.get_current_beamset()
    opt = plan.PlanOptimizations[beamset.Number - 1]

    segment_results = ""
    area_results = ""
    MU_results = ""
    incompatible_MLC_results = ""

    segment_error = False  # If one or more beams does not have valid segments
    area_error = False  # If one or more segments has an area below the requested minimum segment are
    MU_error = False  # If one or more segments has a number of MU inferior to requested minimum segment MU
    incompatible_MLC = False  # If the MLC is non-standard and its width or number of leaf pairs is unknown

    output = ""  # The actual output returned by the function

    min_seg_area = opt.OptimizationParameters.SegmentConversion.MinSegmentArea
    num_segments = 0

    for beam in beamset.Beams:
        #Check to see if beam has segments
        try:
            segment = beam.Segments[0]
            verify_leaves = True
        except:
            segment_results += beam.Name + " "
            segment_error = True
            verify_leaves = False

        if beamset.DeliveryTechnique != "Arc":  #We only check if all beams have segments for VMAT plans

            #Check which kind of MLC is used for area calculations
            if beam.MachineReference.MachineName == 'BeamMod':
                leaf_width = 0.4
                num_leaf_pairs = 40
            elif beam.MachineReference.MachineName == 'Infinity':
                leaf_width = 0.5
                num_leaf_pairs = 80
            else:
                area_results = "Impossible de vérifier l'aire des segments pour ce linac"
                verify_leaves = False
                incompatible_MLC = True
                incompatible_MLC_results = "Impossible de vérifier le faisceau " + beam.Name + " car la machine est inconnue"

            #Verify segment area and MU
            if verify_leaves:
                for segment in beam.Segments:
                    num_segments += 1
                    segment_area = 0
                    for i in range(num_leaf_pairs - 1):  #Area calculation
                        segment_area += (
                            segment.LeafPositions[1][i] -
                            segment.LeafPositions[0][i]) * leaf_width
                    if segment_area < min_seg_area:
                        area_error = True
                        area_results += "   %s segment %d (%.2fcm^2)\n" % (
                            beam.Name, segment.SegmentNumber + 1, segment_area)
                    if beam.BeamMU * segment.RelativeWeight < opt.OptimizationParameters.SegmentConversion.MinSegmentMUPerFraction:
                        MU_error = True
                        MU_results += "   %s segment %d (%.2f MUs)\n" % (
                            beam.Name, segment.SegmentNumber + 1,
                            beam.BeamMU * segment.RelativeWeight)

    #Once the area and MU verifications are complete, output results
    if segment_error:
        output = "Les champs suivants n'ont pas de segments valides:\n      " + segment_results
        return output
    elif beamset.DeliveryTechnique == "Arc":
        output = "Tous les faisceaux ont des segments valides."
        return output
    elif incompatible_MLC:
        output = incompatible_MLC_results
        return output
    else:  #For IMRT plans that have compatible MLCs
        output += "Tous les faisceaux ont des segments valides\n"
        output += "Il y a un total de %d segments (nb. demandé: %d)\n" % (
            num_segments,
            opt.OptimizationParameters.SegmentConversion.MaxNumberOfSegments)
        #Report any errors in segment size or min MUs
        if area_error:
            output += "Les segments suivants sont plus petits que " + str(
                min_seg_area) + "cm^2: \n" + area_results
        else:
            output += "Tous les segments sont plus grand que %dcm^2\n" % min_seg_area
        if MU_error:
            output += "Les segments suivants ont moins que " + str(
                opt.OptimizationParameters.SegmentConversion.
                MinSegmentMUPerFraction) + "UMs: \n" + MU_results
        else:
            output += "Tous les segments ont plus que %dUMs\n" % opt.OptimizationParameters.SegmentConversion.MinSegmentMUPerFraction

    return output
def verify_segments_old():
    plan = lib.get_current_plan()
    beamset = lib.get_current_beamset()
    opt = plan.PlanOptimizations[beamset.Number - 1]

    segment_results = ""
    area_results = ""
    MU_results = ""
    leaf_gap_results = ""
    incompatible_MLC_results = ""

    segment_error = False  # If one or more beams does not have valid segments
    area_error = False  # If one or more segments has an area below the requested minimum segment are
    MU_error = False  # If one or more segments has a number of MU inferior to requested minimum segment MU
    leaf_gap_error = False  # If one or more leaf pairs is open less than 2cm (and is not parked)
    incompatible_MLC = False  # If the MLC is non-standard and its width or number of leaf pairs is unknown

    results = ""  # Used to create a list of all segment areas for debugging purposes
    output = ""  # The actual output returned by the function

    min_seg_area = opt.OptimizationParameters.SegmentConversion.MinSegmentArea
    num_segments = 0

    for beam in beamset.Beams:
        #Check to see if beam has segments
        try:
            segment = beam.Segments[0]
            verify_leaves = True
        except:
            segment_results += beam.Name + " "
            segment_error = True
            verify_leaves = False
            #continue #Move on to next beam

        if beam.MachineReference.MachineName == 'BeamMod':
            leaf_width = 0.4
            num_leaf_pairs = 40
        elif beam.MachineReference.MachineName == 'Infinity':
            leaf_width = 0.5
            num_leaf_pairs = 80
        else:
            area_results = "Impossible de vérifier l'aire des segments pour ce linac"
            leaf_gap_results = "Impossible de vérifier l'ouverture des lames pour ce linac"
            verify_leaves = False
            incompatible_MLC = True
            incompatible_MLC_results = "Impossible de vérifier le faisceau " + beam.Name + " car la machine est inconnue"

        #Calculate the area of each segment
        #results += beam.Name + "\n"
        if verify_leaves:
            for segment in beam.Segments:
                num_segments += 1
                """
                The leaf gap restiction set during optimization is ignored for certain leaves that are near the edge of the field. Any leaf pairs that fall within 1cm of the field edge
                WITH ROOM TO SPARE may ignore the minimum leaf gap setting. Leaf pairs that finish flush at the end of the 1cm area must obey the minimum leaf gap.
                
                BEAM MODULATOR: The edge of the field is defined by the first and last pairs of open leaves. As such, there are always exactly 2 pairs of leaves on either side that
                                may ignore the leaf gap restriction. This does not change if a segment has multiple distinct openings, a maximum of 4 leaf pairs can ignore the restriction.
                                
                AGILITY/INFINITY: If a jaw position falls exactly on the line between two leaf pairs, then only the first pair of leaves may ignore the leaf gap restriction (since the
                                  second pair of leaves ends at the exact boundary of the 1cm area). If a jaw falls between two leaf gaps, then there will be two leaf pairs that can ignore
                                  the restriction: the partially blocked pair and the first pair that is completely inside the field.
                                  NOTE: After writing all that down, I checked and RayStation seems to permit 2 pairs of leaves to do whatever if the jaw falls on a leaf edge. Is that different from Pinnacle?
                                  SO I WILL ADD ONE TO ALL LOWEST LEAF PAIR VALUES AND SUBTRACT ONE FROM ALL HIGHEST LEAF PAIR VALUES FOR THE INFINITY MACHINE
                """
                #Determine which leaves to consider for leaf gap verification
                #Note that lowest_leaf_pair_index and highest_leaf_pair_index are those of the first leaf pairs that need to be examined for leaf gap violations
                if beam.MachineReference.MachineName == 'BeamMod':  #This part seems to work!
                    for i in range(num_leaf_pairs - 1):
                        if segment.LeafPositions[1][i] - segment.LeafPositions[
                                0][i] > 0:
                            lowest_leaf_pair_index = i + 2
                            break
                    i = num_leaf_pairs - 1
                    while i >= 0:
                        if segment.LeafPositions[1][i] - segment.LeafPositions[
                                0][i] > 0:
                            highest_leaf_pair_index = i - 2
                            break
                        else:
                            i -= 1
                elif beam.MachineReference.MachineName == 'Infinity':  #Only leaves inside the backup jaws should be considered - NEEDS WORK
                    import math
                    lowest_leaf_pair_index = int(
                        math.floor(40 + 2 * segment.JawPositions[2]))
                    highest_leaf_pair_index = int(
                        math.ceil(39 + 2 * segment.JawPositions[3]))
                    if (
                            40 + 2 * segment.JawPositions[2]
                    ) % 0.5 == 0:  #Jaw is parked on border between two leaves
                        lowest_leaf_pair_index += 2
                    else:  #Jaw position is in the middle of a leaf
                        lowest_leaf_pair_index += 3
                    if (39 + 2 * segment.JawPositions[3]) % 0.5 == 0:
                        highest_leaf_pair_index -= 2
                    else:
                        highest_leaf_pair_index -= 3

                #output += "Segment %d\n   First open pair of leaves: %d\n   First considered pair of leaves: %d\n   Last open pair of leaves: %d\n   Last considered pair of leaves: %d\n\n" % (segment.SegmentNumber, lowest_leaf_pair_index-1,lowest_leaf_pair_index+1,highest_leaf_pair_index+3,highest_leaf_pair_index+1)
                #output += "Segment %d\n   First pair of leaves to evaluate: %d\n   Last pair of leaves to evaluate: %d\n\n" % (segment.SegmentNumber,lowest_leaf_pair_index+1,highest_leaf_pair_index+1)
                #continue #This will skip the rest, remove after debugging!

                segment_area = 0
                for i in range(num_leaf_pairs - 1):
                    leaf_gap = segment.LeafPositions[1][
                        i] - segment.LeafPositions[0][i]
                    segment_area += leaf_gap * leaf_width
                    if leaf_gap >= 0.5 and leaf_gap < opt.OptimizationParameters.SegmentConversion.MinLeafEndSeparation and i in range(
                            lowest_leaf_pair_index,
                            highest_leaf_pair_index + 1):
                        leaf_gap_error = True
                        leaf_gap_results += "   %s segment %d leafpair %d (%.2f) \n" % (
                            beam.Name, segment.SegmentNumber + 1, i + 1,
                            leaf_gap)

                if segment_area < min_seg_area:
                    area_error = True
                    area_results += "   %s segment %d (%.2fcm^2)\n" % (
                        beam.Name, segment.SegmentNumber + 1, segment_area)

                if beamset.DeliveryTechnique != "Arc":  #Don't check MU per segment in VMAT plans
                    if beam.BeamMU * segment.RelativeWeight < opt.OptimizationParameters.SegmentConversion.MinSegmentMUPerFraction:
                        MU_error = True
                        MU_results += "   %s segment %d (%.2f MUs)\n" % (
                            beam.Name, segment.SegmentNumber + 1,
                            beam.BeamMU * segment.RelativeWeight)

                #results += "     Segment %d: %.2fcm^2\n" % (segment.SegmentNumber+1, segment_area)
            #results += "\n"

    if segment_error:
        output = "Les champs suivants n'ont pas de segments valides: " + segment_results
        return output
    elif incompatible_MLC:
        output = incompatible_MLC_results
        return output
    else:
        output += "Tous les faisceaux ont des segments valides\n"
        if beamset.DeliveryTechnique != "Arc":
            output += "Il y a un total de %d segments (nb. demandé: %d)\n" % (
                num_segments, opt.OptimizationParameters.SegmentConversion.
                MaxNumberOfSegments)
            if area_error:
                output += "Les segments suivants sont plus petits que " + str(
                    min_seg_area) + "cm^2: \n" + area_results
            else:
                output += "Tous les segments sont plus grand que %dcm^2\n" % min_seg_area
            if MU_error:
                output += "Les segments suivants ont moins que " + str(
                    opt.OptimizationParameters.SegmentConversion.
                    MinSegmentMUPerFraction) + "UMs: \n" + MU_results
            else:
                output += "Tous les segments ont plus que %dUMs\n" % opt.OptimizationParameters.SegmentConversion.MinSegmentMUPerFraction
        if leaf_gap_error:
            output += "Les lames sont à <%.1fcm dans les segments suivants: \n%s\n" % (
                opt.OptimizationParameters.SegmentConversion.
                MinLeafEndSeparation, leaf_gap_results)
        else:
            output += "Toutes les paires de lames sont à >%.1fcm\n" % opt.OptimizationParameters.SegmentConversion.MinLeafEndSeparation

    return output
def verify_opt_parameters():
    plan = lib.get_current_plan()
    beamset = lib.get_current_beamset()

    opt = plan.PlanOptimizations[beamset.Number - 1]

    # Number of optimizations and tolerance
    iterations_text = "Optimization: "
    iterations_text += "%d iterations / %d avant la conversion" % (
        opt.OptimizationParameters.Algorithm.MaxNumberOfIterations, opt.
        OptimizationParameters.DoseCalculation.IterationsInPreparationsPhase)
    iterations_text += ", Stopping Tolerance " + str(
        opt.OptimizationParameters.Algorithm.OptimalityTolerance)

    # Computation of intermediate/final dose
    dose_calc_text = "Compute Intermediate Dose / Final Dose: "
    if opt.OptimizationParameters.DoseCalculation.ComputeIntermediateDose == True:
        dose_calc_text += "oui / "
    else:
        dose_calc_text += "non / "
    if opt.OptimizationParameters.DoseCalculation.ComputeFinalDose == True:
        dose_calc_text += "oui"
    else:
        dose_calc_text += "non"

    #Beam optimization parameters (these depend on whether the plan is VMAT or static)
    time_mismatch = False
    spacing_mismatch = False
    opt_types_mismatch = False

    if beamset.DeliveryTechnique == "Arc":
        settings_text = "Constrain Leaf Motion: "
        if beamset.DeliveryTechnique == "Arc":
            if opt.OptimizationParameters.SegmentConversion.ArcConversionProperties.UseMaxLeafTravelDistancePerDegree == True:
                settings_text += "%.1fcm/deg\n" % opt.OptimizationParameters.SegmentConversion.ArcConversionProperties.MaxLeafTravelDistancePerDegree
            else:
                settings_text += "pas coché\n"

        settings_text += "Gantry Spacing / Max Delivery Time: "

        old_time = 0
        new_time = 0
        old_spacing = 0
        new_spacing = 0
        old_opt_types = ""
        new_opt_types = ""

        for ts in opt.OptimizationParameters.TreatmentSetupSettings:
            for i, beam_setting in enumerate(ts.BeamSettings):
                new_spacing = beam_setting.ArcConversionPropertiesPerBeam.FinalArcGantrySpacing
                new_time = beam_setting.ArcConversionPropertiesPerBeam.MaxArcDeliveryTime
                new_opt_types = ""
                for opt_type in beam_setting.OptimizationTypes:
                    new_opt_types += opt_type
                if i > 0:
                    if old_spacing != new_spacing:
                        spacing_mismatch = True
                    if old_time != new_time:
                        time_mismatch = True
                    if old_opt_types != new_opt_types:
                        opt_types_mismatch = True
                old_spacing = new_spacing
                old_time = new_time
                old_opt_types = new_opt_types
        if spacing_mismatch:
            settings_text += "pas pareil pour tous les faisceaux / "
        else:
            settings_text += "%d degrés / " % new_spacing
        if time_mismatch:
            settings_text += "pas pareil pour tous les faisceaux"
        else:
            settings_text += "%ds" % new_time

    else:  #For IMRT and 3DC
        #settings_text = "Segment MU / Segment Area / Leaf Pairs / Leaf End Separation / Nb Segments"
        #settings_text += "\n       %dUMs      /        %d cm2       /        %d        /              %dcm              /          %d" % (opt.OptimizationParameters.SegmentConversion.MinSegmentMUPerFraction, opt.OptimizationParameters.SegmentConversion.MinSegmentArea, opt.OptimizationParameters.SegmentConversion.MinNumberOfOpenLeafPairs, opt.OptimizationParameters.SegmentConversion.MinLeafEndSeparation, opt.OptimizationParameters.SegmentConversion.MaxNumberOfSegments)

        settings_text = "Segment MU: %dUMs\n" % opt.OptimizationParameters.SegmentConversion.MinSegmentMUPerFraction
        settings_text += "Segment Area: %d cm2\n" % opt.OptimizationParameters.SegmentConversion.MinSegmentArea
        settings_text += "Leaf Pairs: %d\n" % opt.OptimizationParameters.SegmentConversion.MinNumberOfOpenLeafPairs
        settings_text += "Leaf End Separation: %dcm\n" % opt.OptimizationParameters.SegmentConversion.MinLeafEndSeparation
        settings_text += "Nb total de segments: %d" % opt.OptimizationParameters.SegmentConversion.MaxNumberOfSegments

        old_opt_types = ""
        new_opt_types = ""

        for ts in opt.OptimizationParameters.TreatmentSetupSettings:
            for i, beam_setting in enumerate(ts.BeamSettings):
                new_opt_types = ""
                for opt_type in beam_setting.OptimizationTypes:
                    new_opt_types += opt_type
                if i > 0 and old_opt_types != new_opt_types:
                    opt_types_mismatch = True
                old_opt_types = new_opt_types

    opt_types_text = "Optimize Segment Shapes / Segment MU: "
    if opt_types_mismatch:
        opt_types_text += "pas pareil pour tous les faisceaux"
    else:
        if new_opt_types == "SegmentOptSegmentMU" or new_opt_types == "SegmentMUSegmentOpt":
            opt_types_text += "oui / oui"
        elif new_opt_types == "SegmentOpt":
            opt_types_text += "oui / non"
        elif new_opt_types == "SegmentMU":
            opt_types_text += "non / oui"
        elif new_opt_types == "":
            opt_types_text += "non / non"

    return iterations_text, dose_calc_text, settings_text, opt_types_text
def verify_beams():
    #patient = lib.get_current_patient()
    exam = lib.get_current_examination()
    plan = lib.get_current_plan()
    beamset = lib.get_current_beamset()

    #Beam details
    number_of_beams = 0
    beam_info = "Nom    /    Description    /    Gantry    /    Sens    /   Colli   /  Couch"
    beams = True
    try:
        beam_name = beamset.Beams[0].Name
    except:
        beams = False
        beam_info = "Aucun faisceau trouvé"
    if beams:  #Creat table of beam info
        machine_mismatch = False
        energy_mismatch = False
        for i, beam in enumerate(beamset.Beams):
            number_of_beams += 1
            beam_info += "\n" + beam.Name + "    /   "
            beam_info += beam.Description
            #Pad out beam description to try to keep columns aligned if name is short
            if len(beam.Description) < 6:
                beam_info += "        "
            elif len(beam.Description) < 7:
                beam_info += "      "
            elif len(beam.Description) < 9:
                beam_info += "    "
            beam_info += "   /  "
            try:  #Checks whether beam has a stop angle (for arcs)
                stop_angle = beam.ArcStopGantryAngle
                beam_info += "  %d-%d   /  " % (beam.GantryAngle,
                                                beam.ArcStopGantryAngle)
            except:
                if beam.GantryAngle < 10:
                    beam_info += "  "
                beam_info += "     %d      /  " % (beam.GantryAngle)
            if beam.ArcRotationDirection == "Clockwise":
                beam_info += "  CW    "
            elif beam.ArcRotationDirection == "CounterClockwise":
                beam_info += "  CCW   "
            else:
                beam_info += "Statique"
            beam_info += " /   " + str(beam.InitialCollimatorAngle)
            if beam.InitialCollimatorAngle >= 0 and beam.InitialCollimatorAngle < 10:
                beam_info += "  "  #Adds a spacer to keep the columns aligned
            elif beam.InitialCollimatorAngle >= 10 and beam.InitialCollimatorAngle < 100:
                beam_info += " "  #Adds a spacer to keep the columns aligned
            beam_info += "   /      %d     " % (beam.CouchAngle)

            if i > 0:
                if beam.MachineReference.MachineName != machine_type:
                    machine_mismatch = True
                if beam.MachineReference.Energy != energy:
                    energy_mismatch = True
            machine_type = beam.MachineReference.MachineName
            energy = beam.MachineReference.Energy

        #Check if first and last leaf pairs are open (anywhere)
        first_leaf_open = False
        last_leaf_open = False
        leaf_open_text = ""
        skip_leaf_check = False

        for beam in beamset.Beams:
            try:
                temp = beam.Segments[
                    0].CollimatorAngle  #Just to see if there are segments defined for the beam
                for seg in beam.Segments:
                    if first_leaf_open == False:  #Stop checking if a segment is found where leaves are open
                        if machine_type == "BeamMod":
                            if seg.LeafPositions[0][0] != seg.LeafPositions[1][
                                    0]:
                                first_leaf_open = True
                        elif machine_type == "Infinity":
                            if abs(
                                    seg.LeafPositions[0][0] -
                                    seg.LeafPositions[1][0]
                            ) > 0.4:  #4mm is the minimum leaf gap for Infinity machines as of October 2017
                                first_leaf_open = True
                                #first_leaf_open_seg = seg.SegmentNumber
                                #first_leaf_open_name = beam.Name
                    if last_leaf_open == False:
                        if machine_type == "BeamMod":
                            if seg.LeafPositions[0][39] != seg.LeafPositions[
                                    1][39]:
                                last_leaf_open = True
                        elif machine_type == "Infinity":
                            if abs(seg.LeafPositions[0][79] -
                                   seg.LeafPositions[1][79]) > 0.4:
                                last_leaf_open = True
                                #last_leaf_open_seg = seg.SegmentNumber
                                #last_leaf_open_name = beam.Name
            except:
                leaf_open_text += "Impossible de vérifier les segments car\nau moins un faisceau n'a pas de segments valides"
                skip_leaf_check = True

        if not skip_leaf_check:
            if machine_type not in ['BeamMod', 'Infinity']:
                leaf_open_text += "Vérification des lames SUP/INF pas possible sur l'appareil " + machine_type
            elif first_leaf_open and last_leaf_open:
                leaf_open_text += "Première et dernère paire de lames ouvertes dans au moins\nun segment, PTV potentiellement trop large pour collimateur"
            elif first_leaf_open and not last_leaf_open:
                leaf_open_text += "Première paire de lames ouverte dans au moins un segment,\nil est peut-être nécessaire de déplacer l'isocentre"
            elif not first_leaf_open and last_leaf_open:
                leaf_open_text += "Dernière paire de lames ouverte dans au moins un segment,\nil est peut-être nécessaire de déplacer l'isocentre"
            elif not first_leaf_open and not last_leaf_open:
                leaf_open_text += "Première/dernière paires de lames fermées pour tous les segments"

        if machine_mismatch:
            machine_text = "Machine pas pareil pour tous les faisceaux"
        else:
            machine_text = "Machine: " + machine_type + " (tous les faisceaux)"

        if energy_mismatch:
            energy_text = "Énergie pas pareil pour tous les faisceaux"
        else:
            energy_text = "Énergie: %dMV (tous les faisceaux)" % energy

    return beam_info, number_of_beams, leaf_open_text, machine_text, energy_text
        def addplanClicked(self, sender, args):
            self.status.ForeColor = Color.Black
            self.status.Text = "Compilation des données du plan"
            
            d,error_message = self.compile_plan_data()
            
            if error_message != "": #If an error is noticed, cancel script execution
                self.status.Text = error_message
                self.status.ForeColor = Color.Red
                return                  
        
            rx = d['rx']       
            ptv_names = d['ptv_names']
            technique = d['technique']
            site = d['site_name']
                                            
            #Predict dose to brain (and generate ROIs)
            self.status.Text = "Estimation de la dose au cerveau"
            predicted_vol = crane.crane_stereo_kbp_predict_dose(plan_data = d)
            cerv_ptv_vol = patient.PatientModel.StructureSets[d['exam'].Name].RoiGeometries["CERVEAU-PTV_"+d['site_name']].GetRoiVolume()            
            
            #Display predicted results
            self.message.Text = 'Volumes prédits dans le cerveau-PTV:\n   V100%%: %.2fcc\n   V90%%:  %.2fcc\n   V80%%:  %.2fcc\n   V70%%:  %.2fcc\n   V60%%:  %.2fcc\n   V50%%:  %.2fcc\n   V40%%:  %.2fcc' % (predicted_vol[0],predicted_vol[1],predicted_vol[2],predicted_vol[3],predicted_vol[4],predicted_vol[5],predicted_vol[6])
            v10 = crane.estimate_vx(predicted_vol=predicted_vol,rx_dose=max(rx),dose_level=1000)
            v12 = crane.estimate_vx(predicted_vol=predicted_vol,rx_dose=max(rx),dose_level=1200)
            self.message.Text += '\n\nV10 Cerveau-PTV estimé: %s\nV12 Cerveau-PTV estimé: %s' % (v10,v12)
            if max(rx) >= 2000:
                v20 = crane.estimate_vx(predicted_vol=predicted_vol,rx_dose=max(rx),dose_level=2000)
                self.message.Text += '\nV20 Cerveau-PTV estimé: %s' % (v20)
            
            self.status.Text = "Estimation de la dose max au tronc cerebral"
            tronc_max = crane.crane_stereo_kbp_predict_oar_dose(plan_data = d)    
            
            #Check which steps of the script are to be performed
            if self.stepcombo.Text == "Rouler le script au complet":
                add_plan = True
                optimize_collimator_angles = True
                optimize_plan = True               
            elif self.stepcombo.Text == "Multi-PTV: Arrêtez avant optimization collimateur":
                add_plan = True
                optimize_collimator_angles = False
                optimize_plan = False              
            elif self.stepcombo.Text == "Multi-PTV: Reprendre après optimization collimateur":
                add_plan = False
                optimize_collimator_angles = False
                optimize_plan = True            
            
            if add_plan:
                
                if patient.BodySite == '':
                    patient.BodySite = 'Crâne'  
                
                if self.isodosecombo.Text == "Créer":
                    self.status.Text = "Ajout du dose color table"
                    crane.crane_stereo_create_isodose_lines(plan_data = d)           
                    
                #Create/assign types to POIs and ROIs (only if this is the first plan for the patient)
                try:
                    existing_plan = patient.TreatmentPlans[0]
                except:                
                    if d['iso_name'] == 'REF SCAN': #Need to skip this step if planner is intentionally using a different isocenter
                        self.status.Text = "Création de l'isocentre à partir du REF SCAN"
                        poi.create_iso()
                    self.status.Text = "Gestion des POIs"
                    poi.auto_assign_poi_types()
                    
                    self.status.Text = "Suppression des overrides de densité"
                    for rois in patient.PatientModel.RegionsOfInterest:
                        rois.SetRoiMaterial(Material=None)    
                    
                    self.status.Text = "Création du contour externe"
                    roi.generate_BodyRS_using_threshold()
                    
                    #Create TISSU SAINS à 1cm
                    if not roi.roi_exists("TISSU SAIN 1cm "+site):
                        patient.PatientModel.CreateRoi(Name="TISSU SAIN 1cm "+site, Color="Magenta", Type="Organ", TissueName=None, RoiMaterial=None)
                        patient.PatientModel.RegionsOfInterest["TISSU SAIN 1cm "+site].SetAlgebraExpression(ExpressionA={'Operation': "Union", 'SourceRoiNames': ["BodyRS"], 'MarginSettings': {'Type': "Expand", 'Superior': 0, 'Inferior': 0, 'Anterior': 0, 'Posterior': 0, 'Right': 0, 'Left': 0}}, ExpressionB={'Operation': "Union", 'SourceRoiNames': ['sum_ptvs_'+site], 'MarginSettings': {'Type': "Expand", 'Superior': 1, 'Inferior': 1, 'Anterior': 1, 'Posterior': 1, 'Right': 1, 'Left': 1}}, ResultOperation="Subtraction", ResultMarginSettings={'Type': "Expand", 'Superior': 0, 'Inferior': 0, 'Anterior': 0, 'Posterior': 0, 'Right': 0, 'Left': 0})
                        patient.PatientModel.RegionsOfInterest["TISSU SAIN 1cm "+site].UpdateDerivedGeometry(Examination=exam)                    
                
                #Assign proper contour type to all PTVs
                self.status.Text = "Assignation du statut PTV"
                for ptv in ptv_names:
                    try:
                        roi.set_roi_type(ptv, 'Ptv', 'Target')
                    except:
                        pass #In a perfect world, I would copy the ROI, replace the PTV with the copy in ptv_names and then change its type
                
                #Add plan, beamset and beams
                self.status.Text = "Ajout du plan, beamset et faisceaux"
                if technique == 'VMAT': #Only case where we don't need a 3DC plan at all
                    plan,beamset = crane.crane_stereo_kbp_add_VMAT_plan_and_beamset(plan_data = d)
                
                elif technique == 'IMRT' and len(ptv_names) == 1:
                    plan,beamset = crane.crane_stereo_kbp_add_IMRT_plan_and_beamset(plan_data = d)
            
                elif technique == 'IMRT' and len(ptv_names) > 1:
                    if optimize_collimator_angles:
                        self.status.Text = "Ajout du plan, beamset et faisceaux (touchez pas à l'ordinateur SVP)"
                        plan,beamset = crane.crane_stereo_kbp_add_3DC_plan(plan_data = d)
                        
                        self.status.Text = "Optimisation angles collimateur (touchez pas à l'ordinateur SVP)"
                        crane.optimize_collimator_angles()
                        
                        self.status.Text = "Conversion du plan 3DC > IMRT (touchez pas à l'ordinateur SVP)"
                        crane.crane_stereo_convert_3DC_IMRT(plan=plan,beamset=beamset)
                    
                    else:
                        self.status.Text = "Ajout du plan, beamset et faisceaux"
                        plan,beamset = crane.crane_stereo_kbp_add_IMRT_plan_and_beamset(plan_data = d)
                        self.status.Text = "Prêt pour optimisation manuelle des angles de collimateur"
                        self.status.ForeColor = Color.Green
                        return
                    
                elif technique == '3DC':
                    self.status.Text = "Ajout du plan, beamset et faisceaux (touchez pas à l'ordinateur SVP)"
                    plan,beamset = crane.crane_stereo_kbp_add_3DC_plan(plan_data = d)
                    
                    #if len(ptv_names)>1:
                    if optimize_collimator_angles and len(ptv_names) > 1:
                        self.status.Text = "Optimisation angles collimateur (touchez pas à l'ordinateur SVP)"
                        crane.optimize_collimator_angles()  
                    elif not optimize_collimator_angles:
                        self.status.Text = "Prêt pour optimisation manuelle des angles de collimateur"
                        self.status.ForeColor = Color.Green
                        return

            if add_plan == False:
                plan = lib.get_current_plan()
                beamset = lib.get_current_beamset()
                
            if optimize_plan:
                            
                # Add clinical goals (conveniently the same for all types of plan)
                self.status.Text = "Ajout des clinical goals"
                clinical_goals.add_dictionary_cg('Crane Stereo', 15, 1, plan = plan)
                eval.add_clinical_goal("CERVEAU-PTV_"+d['site_name'], 1000, 'AtMost', 'AbsoluteVolumeAtDose', 10, plan=plan)
                eval.add_clinical_goal("CERVEAU-PTV_"+d['site_name'], 1200, 'AtMost', 'AbsoluteVolumeAtDose', 8, plan=plan)
                if max(rx) >= 2000:
                    eval.add_clinical_goal("CERVEAU-PTV_"+d['site_name'], 2000, 'AtMost', 'AbsoluteVolumeAtDose', 20, plan=plan)
                for i,ptv in enumerate(ptv_names):
                    eval.add_clinical_goal(ptv, rx[i], 'AtLeast', 'VolumeAtDose', 99, plan=plan)
                    eval.add_clinical_goal(ptv, 1.5 * rx[i], 'AtMost', 'DoseAtAbsoluteVolume', 0.1, plan=plan)
                    #if technique == '3DC':
                    #    optim.copy_clinical_goals(old_plan = plan,new_plan = patient.TreatmentPlans[d['site_name']+' 3DC optimised'])                    
                        
                #Add objectives and optimize plan
                if technique == '3DC':
                    self.status.Text = "Optimisation du plan 3DC (touchez pas à l'ordinateur SVP)"
                    plan,beamset = crane.crane_stereo_kbp_optimize_3DC_plan(plan_data=d,plan=plan,beamset=beamset) 
                    obtained_vol,initial_ptv_cov = crane.crane_stereo_kbp_scale_dose(plan_data=d,beamset=beamset,reset_dose=False)            
                else:
                    self.status.Text = "Ajout des objectifs d'optimisation"
                    crane.crane_stereo_kbp_initial_optimization_objectives(plan_data=d,plan=plan,predicted_vol=predicted_vol,tronc_max=tronc_max)
                    
                    #Make a copy of plan before optimizing for dosimetrists
                    patient.CopyPlan(PlanName=plan.Name, NewPlanName=plan.Name + ' non-optimisé')
                    
                    self.status.Text = "Optimization du plan initial"
                    plan.PlanOptimizations[beamset.Number-1].ResetOptimization() 
                    if len(ptv_names) == 1:
                        optim.triple_optimization(plan=plan,beamset=beamset)
                    elif len(ptv_names) > 1:
                        optim.optimization_90_30(plan=plan,beamset=beamset)  
                        
                    self.status.Text = "Modification du plan"
                    if len(ptv_names) == 1:
                        crane.crane_stereo_kbp_modify_plan_single_ptv(plan_data=d,plan=plan,beamset=beamset,predicted_vol=predicted_vol,tronc_max=tronc_max)
                        self.status.Text = "Optimization du plan modifié"
                        optim.triple_optimization(plan=plan,beamset=beamset)
                        self.status.Text = "Scaling couverture à la prescription"
                        obtained_vol,initial_ptv_cov = crane.crane_stereo_kbp_scale_dose(plan_data=d,beamset=beamset,reset_dose=False)
                    elif len(ptv_names) > 1:     
                        obtained_vol,initial_ptv_cov = crane.crane_stereo_kbp_scale_dose(plan_data=d,beamset=beamset,reset_dose=True)
                        self.message.Text += '\n\nV10 obtenu (plan initial): %.2fcc\nV12 obtenu (plan initial): %.2fcc' % (obtained_vol[0]*cerv_ptv_vol,obtained_vol[1]*cerv_ptv_vol)
                        continue_optimization = True
                        best_vol = 100000
                        for i in range(4): #i IS EQUAL TO THE NUMBER OF COMPLETED ITERATIONS!
                            if continue_optimization:
                                self.status.Text = "Modification du plan et réoptimisation (étape %d/4)" % (i+1)
                                continue_optimization = crane.crane_stereo_kbp_modify_plan_multi_ptv(plan_data=d,plan=plan,beamset=beamset) #Evaluates PTV coverage, adjusts and reoptimizes if necessary
                                obtained_vol,initial_ptv_cov = crane.crane_stereo_kbp_scale_dose(plan_data=d,beamset=beamset,reset_dose=True)
                                if (obtained_vol[0] + obtained_vol[1]) < best_vol:
                                    best_vol = obtained_vol[0] + obtained_vol[1]
                                    best_iteration = i
                                #if continue_optimization: #If crane_stereo_kbp_modify_plan_multi_ptv returns False, then the plan hasn't changed since last time and we don't need to print these values again
                                self.message.Text += '\n\nV10 obtenu (après %d révision(s)): %.2fcc\nV12 obtenu (après %d révision(s)): %.2fcc' % (i+1,obtained_vol[0]*cerv_ptv_vol,i+1,obtained_vol[1]*cerv_ptv_vol)                            
                        self.status.Text = "Scaling de la couverture à la prescription"
                        obtained_vol,initial_ptv_cov = crane.crane_stereo_kbp_scale_dose(plan_data=d,beamset=beamset,reset_dose=False)
                        
                        #Now we have to check if the final plan is better than the previous plans. If not, we will reoptimize and stop and the correct point.
                        self.status.Text = "Meilleur plan: plan initial avec %d itérations" % best_iteration
                        
                        #if (obtained_vol[0]+obtained_vol[1]*0.9) > best_vol:
                        if (obtained_vol[0]+obtained_vol[1]) > best_vol:
                            self.status.Text = "Retour vers le meilleur plan, veuillez patientez svp"
                            optim.erase_objectives(plan,beamset)
                            plan.PlanOptimizations[beamset.Number-1].ResetOptimization() 
                            crane.crane_stereo_kbp_initial_optimization_objectives(plan_data=d,plan=plan,predicted_vol=predicted_vol,tronc_max=tronc_max)
                            optim.optimization_90_30(plan=plan,beamset=beamset)
                            for i in range(best_iteration+1):
                                self.status.Text = "Modification du plan et réoptimisation (étape %d/%d)" % (i+1,best_iteration+1)
                                continue_optimization = crane.crane_stereo_kbp_modify_plan_multi_ptv(plan_data=d,plan=plan,beamset=beamset)
                            self.status.Text = "Scaling de la couverture à la prescription"
                            obtained_vol,initial_ptv_cov = crane.crane_stereo_kbp_scale_dose(plan_data=d,beamset=beamset,reset_dose=False)                            
                
                
                #Display results of plan
                self.message.Text += '\n\nV10 obtenu: %.2fcc\nV12 obtenu: %.2fcc' % (obtained_vol[0]*cerv_ptv_vol,obtained_vol[1]*cerv_ptv_vol)
                
                #Write results to file (this is put into a try because it will crash if someone has the destination file open when it tries to write to it)
                try:
                    crane.crane_kbp_write_results_to_file(plan_data=d,plan=plan,beamset=beamset,predicted_vol=predicted_vol,initial_ptv_cov=initial_ptv_cov,obtained_vol=obtained_vol)
                except:
                    pass
                
                #If the plan is VMAT or IMRT, copy it and set it up using the old technique for comparison
                if technique != '3DC':
                    kbp_plan_name = site + ' ' + technique
                    old_style_plan_name = site + ' ' + technique + ' RINGS'
                    self.status.Text = "Ajout du plan RINGS"
                    patient.CopyPlan(PlanName=kbp_plan_name, NewPlanName=old_style_plan_name)
                    plan = patient.TreatmentPlans[old_style_plan_name]
                    self.status.Text = "Nom du nouveau plan: " + plan.Name
                    beamset = plan.BeamSets[old_style_plan_name]
                    self.status.Text = "Préparation des ROIs et objectifs pour plan RINGS"
                    crane.crane_add_old_plan(plan_data=d,plan=plan,beamset=beamset)
                    #Create TISSU SAINS à 1cm (because sometimes I guess this wasn't happening for some reason)
                    if not roi.roi_exists("TISSU SAIN 1cm "+site):
                        patient.PatientModel.CreateRoi(Name="TISSU SAIN 1cm "+site, Color="Magenta", Type="Organ", TissueName=None, RoiMaterial=None)
                        patient.PatientModel.RegionsOfInterest["TISSU SAIN 1cm "+site].SetAlgebraExpression(ExpressionA={'Operation': "Union", 'SourceRoiNames': ["BodyRS"], 'MarginSettings': {'Type': "Expand", 'Superior': 0, 'Inferior': 0, 'Anterior': 0, 'Posterior': 0, 'Right': 0, 'Left': 0}}, ExpressionB={'Operation': "Union", 'SourceRoiNames': ['sum_ptvs_'+site], 'MarginSettings': {'Type': "Expand", 'Superior': 1, 'Inferior': 1, 'Anterior': 1, 'Posterior': 1, 'Right': 1, 'Left': 1}}, ResultOperation="Subtraction", ResultMarginSettings={'Type': "Expand", 'Superior': 0, 'Inferior': 0, 'Anterior': 0, 'Posterior': 0, 'Right': 0, 'Left': 0})
                        patient.PatientModel.RegionsOfInterest["TISSU SAIN 1cm "+site].UpdateDerivedGeometry(Examination=exam)     
                    plan.PlanOptimizations[beamset.Number-1].ResetOptimization()
                    self.status.Text = "Optimization du plan RINGS"
                    optim.optimization_90_30(plan=plan,beamset=beamset)
                
            self.isodosecombo.Text = 'Ne pas créer'
            self.status.Text = "Terminé avec succès!"
            self.status.ForeColor = Color.Green