Skip to content

Commit

Permalink
Add compatibility with Python3 and CPython
Browse files Browse the repository at this point in the history
Merge pull request #31 from mghro/rs12
  • Loading branch information
ferdymercury authored Apr 24, 2024
2 parents 8647821 + 566fdbe commit 0581a14
Show file tree
Hide file tree
Showing 3 changed files with 63 additions and 31 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ It loads a 3D model (STL file) of a LINAC and/or couch into a 3D window, that ca

The (currently available) interface layers of the RadCollision platform are:
* <a href="https://github.com/SlicerRt/SlicerRT">SlicerRT</a> (C++) -- full integration into <a href="http://perk.cs.queensu.ca/sites/perkd7.cs.queensu.ca/files/Suriyakumar2017a.pdf">open-source TPS</a>
* <a href="RayStation">RayStation</a> (IronPython) -- full integration into proprietary TPS
* <a href="RayStation">RayStation</a> (IronPython2.7/CPython2.7/CPython3.6) -- full integration into proprietary TPS
* <a href="Python3">Python 3</a> -- independent of TPS
* <a href="https://github.com/jlhue/rad-collision-matlab/">MATLAB</a> -- independent of TPS

Expand Down
5 changes: 3 additions & 2 deletions RayStation/README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[//]: # (RadCollision-RayStation)

This folder contains one flavor (or interface layer) of RadCollision. For other languages, consult https://github.com/mghro/rad-collision
This folder contains one flavor (or interface layer) of RadCollision. It supports both IronPython 2.7 as well as CPython 2.7 and 3.6, the latter being the only option starting from RayStation version 12B. For other languages, consult https://github.com/mghro/rad-collision

Licensing
---------
Expand Down Expand Up @@ -49,4 +49,5 @@ Known issues
Authors
-------

- F. Hueso-González
- F. Hueso-González, MGH, now at IFIC (CSIC-UV)
- J. Morin, CISSS-CA
87 changes: 59 additions & 28 deletions RayStation/collision_detection.py
Original file line number Diff line number Diff line change
Expand Up @@ -128,10 +128,11 @@
import clr
clr.AddReference("System.Windows.Forms")
clr.AddReference("System.Drawing")
import threading
import platform
from System.Windows.Forms import Application, Form, Label, ComboBox, Button, TextBox, TrackBar, FormStartPosition, TickStyle, Keys, CheckBox, GroupBox#, DataGridView
from System.Drawing import Point, Size, Color#, SolidBrush, Graphics
from System.Threading import ParameterizedThreadStart, ThreadStart, Thread, ThreadInterruptedException, ThreadAbortException, SpinWait
from System.Environment import ProcessorCount


class Part:
Expand Down Expand Up @@ -205,7 +206,7 @@ def __init__(self, lst, description):

# Add a ComboBox that will display the items of this list
self.combobox = ComboBox()
self.combobox.DataSource = lst.keys()
self.combobox.DataSource = list(lst.keys())
self.combobox.Location = Point(15, 60)
self.combobox.AutoSize = True
self.Controls.Add(self.combobox)
Expand Down Expand Up @@ -463,7 +464,7 @@ def __init__(self):
col_box = GroupBox()
col_box.Text = 'Collision report (increases CPU load of server)'
col_box.Location = Point(15, lastpos + colmargin)
col_box.Size = Size(450, colheight + colmargin / 2)
col_box.Size = Size(450, int(colheight + colmargin / 2))

self.col_pairs = []
self.col_cb = []
Expand Down Expand Up @@ -655,13 +656,19 @@ def exit_button_clicked(self, _sender, _event):
:param _sender: ignore
:param _event: ignore
"""

if 'colthreads' in globals():
for th in colthreads:
if th.IsAlive:
th.Interrupt()
if th.IsAlive and not th.Join(100):
th.Abort()
if platform.python_implementation()=="IronPython":
for th in colthreads:
if th.IsAlive:
th.Interrupt()
if th.IsAlive and not th.Join(100):
th.Abort()
else:
for th in colthreads:
if th.is_alive():
th.Interrupt()
if th.is_alive() and not th.join(100):
th.Abort()

self.Close()

Expand Down Expand Up @@ -695,14 +702,21 @@ def beamset_button_clicked(self, _sender, _event):
if 'beamthread' not in globals():
global beamthread
beamthread = Thread(ParameterizedThreadStart(await_col_report))
elif beamthread.IsAlive:
beamthread.Interrupt()
if beamthread.IsAlive and not beamthread.Join(100):
beamthread.Abort()

else:
if platform.python_implementation() == 'IronPython':
if beamthread.IsAlive:
beamthread.Interrupt()
if beamthread.IsAlive and not beamthread.Join(100):
beamthread.Abort()
else:
if beamthread.is_alive():
beamthread.Interrupt()
if beamthread.is_alive() and not beamthread.join(100):
beamthread.Abort()
beamthread = Thread(ParameterizedThreadStart(await_col_report))
beamthread.Start(self)


def transform(self):
"""
Slot function called whenever the Apply button is clicked, or when entered is clicked on text box,
Expand Down Expand Up @@ -1006,11 +1020,18 @@ def transform_models():
if 'colthreads' not in globals():
global colthreads
else:
for th in colthreads:
if th.IsAlive:
th.Interrupt()
if th.IsAlive and not th.Join(100):
th.Abort()
if platform.python_implementation()=="IronPython":
for th in colthreads:
if th.IsAlive:
th.Interrupt()
if th.IsAlive and not th.Join(100):
th.Abort()
else:
for th in colthreads:
if th.is_alive():
th.Interrupt()
if th.is_alive() and not th.join(100):
th.Abort()

if len(coltag) == maxColThreads * 6: # If nothing selected, just separators " \t \t0\n" for each row, remove everything
for labels in aform.reports:
Expand Down Expand Up @@ -1074,9 +1095,14 @@ def await_col_report(arg):
arg.tboxC.Text = str(couch_angle)
arg.transform()
if 'colthreads' in globals():
while any([th.IsAlive for th in colthreads]):
print([th.IsAlive for th in colthreads])
Thread.SpinWait(100000)# Thread.Sleep seems not to be available in this NET version
if platform.python_implementation()=="IronPython":
while any([th.IsAlive for th in colthreads]):
print([th.IsAlive for th in colthreads])
Thread.SpinWait(100000)# Thread.Sleep seems not to be available in this NET version
else:
while any([th.is_alive() for th in colthreads]):
print([th.is_alive() for th in colthreads])
Thread.SpinWait(100000)# Thread.Sleep seems not to be available in this NET version
await_user_input('Collision report is ready for beam "' + beam.Description + '", gantry angle '+str(sgangle)+'. Click OK to verify 3D geometry. Then click on Play Script to continue')
except ThreadInterruptedException:
print('Beamset interrupted')
Expand Down Expand Up @@ -1239,7 +1265,7 @@ def main():

# Select first which treatment head to use
global linac
if linacs.has_key(machineName):
if machineName in linacs:
# Select it based on treatment plan info
linac = linacs[machineName]
else:
Expand All @@ -1254,7 +1280,7 @@ def main():

# Select now which couch to use
global couch
if couches.has_key(couchName):
if couchName in couches:
# Select it based on treatment plan info
couch = couches[couchName]
else:
Expand Down Expand Up @@ -1444,14 +1470,19 @@ def main():

# Check the maximum number of threads (roiA : roiB combinations) to allow for collision detection
global maxColThreads
maxColThreads = ProcessorCount - 2 # 1 for GUI, 1 for TuneForm
if platform.python_implementation() == 'IronPython':
from System.Environment import ProcessorCount
maxColThreads = ProcessorCount - 2 # 1 for GUI, 1 for TuneForm
else:
import multiprocessing
maxColThreads = multiprocessing.cpu_count() - 2 # 1 for GUI, 1 for TuneForm
maxColThreads = min(maxColThreads, 6) # Do not use more than 6 threads
maxColThreads = max(maxColThreads, 1) # Use at least 1 thread

# Tuning form thread
thread = Thread(ThreadStart(tune_models))
thread.Start()
thread.Join()
tune_thread = Thread(ThreadStart(tune_models))
tune_thread.Start()
tune_thread.Join()


if __name__ == '__main__':
Expand Down

0 comments on commit 0581a14

Please sign in to comment.