Ian Panzica
Ian Panzica

Reputation: 354

Interacting with Windows Form controls in derived IronPython class

I'm learning how to use IronPython in conjunction with C#. I want to incorporate a simple windows form into my application so I decided to throw one together in a separate C# project and compile it to a dll which I have referenced in the python project. I want to create a subclass of the referenced windows form class so that I can create the event handlers in python. Right now I have the first steps:

import clr
import sys
import threading 
import random
import time


clr.AddReference('System.Drawing')
clr.AddReference('System.Windows.Forms')
clr.AddReference('CustomForm')

from System.Drawing import *
from System.Windows.Forms import * 
from System.IO import *
from System import EventHandler
from CustomForm import *
from threading import Lock, Semaphore, Thread
from Queue import PriorityQueue


#Set up some global variables
NUM_DOCTORS = 3
MAX_CAPACITY = 10
PRIORITY_HIGH = 1
PRIORITY_MEDIUM = 2
PRIORITY_LOW = 3
PRIORITY_NONE = -1

#TODO: Fix all print statements once form can be referenced 

class MyForm(Form1):

    def __init__(self):
        # Create child controls and initialize form
        super(MyForm, self).__init__()
        self.InitializeComponent()
        self.input_button.Click += self.input_button_Click
        print "Here right now"
        self.admin = None
        self.started = False

    def setAdmin(self, ad):
        self.admin = ad

    def input_button_Click(self, sender, e):
        print "hello click"
        if not self.started:
            self.admin.start()
            self.started = not self.started

        name = self.input_box.Text
        #acquire the lock here, can't do it in the method or it will block
        self.admin.lock.acquire()
        self.admin.addPatient(name)
        self.admin.lock.release()
        print "is this working?"
        self.input_box.Clear()




class Patient():

    def __init__(self, patient_name):
        #set up the severity of the illness   
        self.name = patient_name
        #set up illness -> random time from 1-60 seconds
        self.illness = random.randint(1,60)
        self.priority = random.randint(1,3)


    def getIllness(self):
        return self.illness

    def getName(self):
        return self.name

    def getPriority(self):
        return self.priority


class Doctor(Thread):

    def __init__(self, administrator, patient = None):
        self.curr_patient = patient
        self.admin = administrator

    def healPatient(self):
        sleep_time = self.curr_patient.getIllness()
        #heal the patient by sleeping with how long it takes to cure illness 
        time.sleep(sleep_time)


    def setPatient(self, patient):
        self.curr_patient = patient

    def run(self):
        #grab a patient, heal them, and report the results 
        self.curr_patient = self.admin.getPatientFromQueue()
        healPatient()
        self.admin.reportResults(self.curr_patient)




class Administrator(Thread):

    def __init__(self, form):
        self.num_doctors = NUM_DOCTORS
        self.lock = Lock()
        self.patient_queue = PriorityQueue()
        self.hospital_status = []
        self.parent = form
        self.waiting_room = []
        #potential replacement for pq.qsize()
        self.num_patients = 0

    def run(self):
        #first time
        for i in range(NUM_DOCTORS):
            d = Doctor(self)
            d.start()
            num_doctors -= 1


    def getPatientFromQueue(self):
        #grab a patient from the queue safely 
        if self.patient_queue.qsize() >= 1:
            return patient_queue.get()
        else: 
            return None


    def reportResults(self, patient):
        #update master list
        with self.lock:
            self.hospital_status.remove(patient)
            self.admitPatient()
            #change this for outcome
            self.parent.status_box.Text += "\n" + patient.getName() + " is released"
            printStatus()
            self.num_doctors += 1

            while self.num_doctors < 0:
                d = Doctor(self)
                d.start()
                self.num_docters -= 1



    def addPatient(self, name):
        #add a patient if possible
        p = Patient(name)
        if len(self.hospital_status) < MAX_CAPACITY:
            self.hospital_status.append(p)
            self.patient_queue.put((p, p.getPriority()))
            self.parent.status_box.Text += "\nPlease come in."    
        else:
            #check the waiting list
            self.waiting_room.append(p)
            self.parent.status_box.Text += "\nPlease take a seat in the waiting room."

    def admitPatient(self):
        #check the waiting room -> admit patient if there otherwise do nothing
        if len(self.waiting_room) > 0:
            p = self.waiting_room.pop()
            self.addPatient(p)



    def printStatus(self):       
        #only called when lock is had
        for p in self.hospital_status:
            self.parent.status_box.Text += p.getName() + "-Priority-" + p.getPriority()
            #print p.getName() + "-Priority-" + p.getPriority()



Application.EnableVisualStyles()
Application.SetCompatibleTextRenderingDefault(False)

form = MyForm()
admin = Administrator(form)
form.admin = admin



Application.Run(form)

Where my own form is simply Form1 and is inherited by the python class MyForm. When I run this, the form I made in C# displays properly but I can't find how I should be interacting with the controls. The form displays as:

enter image description here

I would like to be able to listen to the button press and read from the text box on top. Status will be printed in the larger rich text box. I would really appreciate any input. Thanks in advance!

UPDATE 1: I think the answer to my question is something like what is found here. I've changed the access modifiers of my components and methods to public and now they're showing up. I'll update this if my problem is solved.

UPDATE 2: I can see these components now but I'm not exactly sure how I'm supposed to interact with them. Should I bind them to variables in the python init() function for MyForm?

UPDATE 3: I seem to have referenced these components just fine (SEEM TO) but now I've noticed that the Event handling seems to be off. I've included much more of the code I'm working with. Please excuse any potential issues with threading logic etc. for now as I have yet to get through that and can figure it out myself. What I can't seem to get is the event handling. As always, I'd really appreciate any input and thanks in advance!

Upvotes: 2

Views: 2800

Answers (1)

Jashaszun
Jashaszun

Reputation: 9270

Here's what worked for me:

  1. I made a Windows Forms Project (called FormProject) that looks exactly like the one in your screenshot. I called the input text box input_box, the button input_button, and the status text box output_box. Each of these controls is completely passive (no events subscribed to in the C#).

  2. I made all of these controls public by going to each of their properties in the Design view, and selecting Modifiers = Public (the default is Private). Make sure that your form's class is also public.

  3. I built the project as a DLL, as you already know how to do.

  4. I wrote the following IronPython code: (I had this code in a separate project within the same solution as the FormProject project, but I'm sure that whatever setup you already have is working just fine)


Remember that your CustomForm is my FormProject.

import clr
import sys

clr.AddReference('System.Windows.Forms')
clr.AddReferenceToFileAndPath('C:/Full/Path/To/Compiled/FormProject.dll')

from System.Windows.Forms import *
from FormProject import *

class MyForm(Form1):
    def __init__(self):
        super(MyForm, self).__init__()
        self.input_button.Click += self.input_button_Click

    def input_button_Click(self, sender, e):
        self.output_box.Text += "Status: Tried to sign in with Name: \"" + self.input_box.Text + "\"\r\n"


Application.EnableVisualStyles()
Application.SetCompatibleTextRenderingDefault(False)

form = MyForm()
Application.Run(form)

I'm not sure why in your MyForm constructor you try to call self.InitializeComponent(), since (1) Form1.InitializeComponent() is private, and is therefore not accessible to derived classes, and (2) calling super(MyForm, self).__init__() already goes through Form1's constructor, which calls Form1.InitializeComponent. So in my code I just took it out (my code works just fine).

If you run this Python code, clicking on the button should add text to the output box (according to the text of the input box), demonstrating that I wrote an event handler and subscribed it to a control's event, all in Python.

I hope this helps!

Upvotes: 1

Related Questions