Reputation: 354
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:
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
Reputation: 9270
Here's what worked for me:
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#).
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.
I built the project as a DLL, as you already know how to do.
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