[Users] [IronPython] IronPython Attribute Workaround

Tim Riley riltim at gmail.com
Mon Jul 23 19:11:17 PDT 2007


Michael:

Earlier today I was doing some digging using Lutz Roeder's .NET
Reflector and it seems that the CommandMethod attribute registers the
command using the acmgd.Autodesk.AutoCAD.Runtime.ICommandLineCallable
interface. As I can tap into any other aspect of AutoCAD's .NET api
via ironpython I'm going to assume that I can somehow tap into this
and register a command. The only problem is I don't have a clue
how...yet.

If you want to take a look at what C# code I have so far you can find
it @ http://pyacaddotnet.googlecode.com/svn/trunk/PyAcadDotNet.cs

Also I have some (lousy) samples that I use for testing IronPython in
AutoCAD. You can find them here:
http://pyacaddotnet.googlecode.com/svn/trunk/Samples/

Tim

On 7/23/07, Michael Foord <fuzzyman at voidspace.org.uk> wrote:
> I'm pretty sure that it is impossible to dynamically generate classes
> decorated with attributes at runtime using 'pure C#'.
>
> The two alternatives are either to use the Reflection.Emit API - which
> is probably the best way but requires more digging than I have time for
> at this point in time.
>
> Another alternative is to dynamically generate C# and compile to in
> memory assemblies. This is less memory efficient because it requires an
> assembly and type object for every command you wish to add. It is a lot
> faster than you might expect.
>
> Attached are two files. One is 'generate.py'. It has a function called
> 'Generate' which compiles C# using the CodeDom API with the
> CSharpCodeProvider.
>
> As an example I have provided another file - DllImport.py. This uses
> Generate to compile the C# from my screenshot example :
>
> http://www.voidspace.org.uk/ironpython/winforms/part10.shtml
>
> It works fine. :-)
>
> The specific call is :
>
> assembly = Generate(unmanaged_code, 'UnamangedCode', inMemory=True)
> clr.AddReference(assembly)
> from UnmanagedCode import User32, GDI32
>
> (You can also use it to save assemblies to disk.)
>
> For your use you will need to pass in a list of references (absolute
> file paths) to the AutoCAD DLLs that your C# code is dependent on.
>
> I think you need to generate C# code that looks something like:
>
>
> using Autodesk.AutoCAD.ApplicationServices;
> using Autodesk.AutoCAD.DatabaseServices;
> using Autodesk.AutoCAD.EditorInput;
> using Autodesk.AutoCAD.Runtime;
>
> namespace AutoCADStuff
>
> {
>   public class AutoCADStuffBase
>   {
>     [CommandMethod("myname")]
>     public void method
>     {
>        _method();
>     }
>
>     public virtual void _method
>     {
>        return null;
>     }
> }
>
> In IronPython you can call 'Generate', then import and subclass
> AutoCADStuffBase and override '_method':
>
> class AutoCADStuff(AutoCADStuffBase):
>     def _method(self):
>        some code ...
>
> I have used this approach with Silverlight assemblies for creating
> classes with methods marked with attributes.
>
> I hope this helps.
>
> Please no one post this code as is - I'll write up and post an article
> shortly. :-)
>
> Michael Foord
>
> Michael Foord wrote:
> > Tim Riley wrote:
> > [snip..]
> >
> >>  AutoCAD's .NET API allows
> >> you to register a command that is callable from the AutoCAD via a CommandMethod attribute[1]. Since IP doesn't support attributes I'd like to develop some sort of decorator that would clone the functionality of the CommandMethod attribute. So I could execute
> >> python code like:
> >>
> >> @commandmethod("test")
> >> def tester:
> >>     print "test worked"
> >>
> >> and it would register the test command so  I could call it from the
> >> command line.
> >>
> >>
> >>
> >
> > Right - so you want to dynamically create  methods (or functions) with
> > attributes. The attribute takes the name that the command will be
> > exposed with.
> >
> > I wonder if AutoCAD even supports these being dynamically created?
> >
> > If it does I wonder if we can do it by creating an inner class in C#.
> > The method marked with the attribute would call down to another method
> > (marked as virtual) that we can override from IronPython.
> >
> > I'm not exactly sure of the C# syntax/semantics for inner classes - (a
> > class factory) - so I'll have to do some digging. (i.e. will it allow us
> > to set the argument to the attribute at runtime rather than compile time
> > and can we return classes or only instances.)
> >
> > AFAIK, attributes are normally bound at compile time rather than
> > runtime, so using C# to create classes in this way may not work.
> >
> > The proper way (*sigh*) is to use the Reflection.Emit API to add
> > attributes. I've experimented Reflection.Emit - but never got as far as
> > adding attributes dynamically. (You need to 'program in bytecode' using
> > this API - which isn't too bad but has a bit of a learning curve
> > associated.)
> >
> > A *third* approach is to dynamically generate C# and compile to in
> > memory assemblies (using the CodeDOM API if I remember correctly). That
> > turns out to be surprisingly easy.
> >
> > Michael Foord
> > http://www.voidspace.org.uk/ironpython/index.shtml
> >
> >
> >> [1] As an example:
> >> http://through-the-interface.typepad.com/through_the_interface/2007/07/updating-a-spec.html
> >>
> >>
> >> Tim
> >>
> >> On 7/23/07, Michael Foord <fuzzyman at voidspace.org.uk> wrote:
> >>
> >>
> >>> Tim Riley wrote:
> >>>
> >>>
> >>>> Michael:
> >>>>
> >>>> Thanks for the reply. However when reading it my eyes glazed over. Is
> >>>> there any way you could provide a simple man like myself with some
> >>>> example code for me to peruse?
> >>>>
> >>>>
> >>>>
> >>> What do you want to achieve? Write the IronPython code you would like -
> >>> and I will try and provide a C# stub that you can subclass. (You will
> >>> need a C# compiler - Visual Studio Express C# is probably the most
> >>> straightforward if you are using Windows.)
> >>>
> >>> Michael Foord
> >>> http://www.voidspace.org.uk/ironpython/index.shtml
> >>>
> >>>
> >>>
> >>>> Tim
> >>>>
> >>>> On 7/23/07, Michael Foord <fuzzyman at voidspace.org.uk> wrote:
> >>>>
> >>>>
> >>>>
> >>>>> Tim Riley wrote:
> >>>>>
> >>>>>
> >>>>>
> >>>>>> I know that IronPython doesn't support attributes but does anyone know
> >>>>>> of a workaround that will allow IP code to use them?
> >>>>>>
> >>>>>>
> >>>>>>
> >>>>>>
> >>>>> The IronPython team are strangely quiet every time someone asks this... ;-)
> >>>>>
> >>>>> A lot of people would like an answer to this question. Currently the
> >>>>> only way is to use stub C# classes.
> >>>>>
> >>>>> Michael Foord
> >>>>> http://www.voidspace.org.uk/ironpython/index.shtml
> >>>>>
> >>>>>
> >>>>>
> >>>>>
> >>>>>
> >>>>>> _______________________________________________
> >>>>>> users mailing list
> >>>>>> users at lists.ironpython.com
> >>>>>> http://lists.ironpython.com/listinfo.cgi/users-ironpython.com
> >>>>>>
> >>>>>>
> >>>>>>
> >>>>>>
> >>>>>>
> >>>>> _______________________________________________
> >>>>> users mailing list
> >>>>> users at lists.ironpython.com
> >>>>> http://lists.ironpython.com/listinfo.cgi/users-ironpython.com
> >>>>>
> >>>>>
> >>>>>
> >>>>>
> >>>> _______________________________________________
> >>>> Users mailing list
> >>>> Users at lists.ironpython.com
> >>>> http://lists.ironpython.com/listinfo.cgi/users-ironpython.com
> >>>>
> >>>>
> >>>>
> >>>>
> >>> _______________________________________________
> >>> Users mailing list
> >>> Users at lists.ironpython.com
> >>> http://lists.ironpython.com/listinfo.cgi/users-ironpython.com
> >>>
> >>>
> >>>
> >> _______________________________________________
> >> Users mailing list
> >> Users at lists.ironpython.com
> >> http://lists.ironpython.com/listinfo.cgi/users-ironpython.com
> >>
> >>
> >>
> >
> > _______________________________________________
> > Users mailing list
> > Users at lists.ironpython.com
> > http://lists.ironpython.com/listinfo.cgi/users-ironpython.com
> >
> >
>
>
> import clr
> clr.AddReference('System.Drawing')
>
> from System.Drawing import Bitmap, Image
>
> from generate import Generate, LoadAssembly
>
>
> unmanaged_code = """
> using System;
> using System.Collections.Generic;
> using System.Text;
> using System.Runtime.InteropServices;
>
> namespace UnmanagedCode
> {
>     public class GDI32
>     {
>         [DllImport("GDI32.dll")]
>         public static extern IntPtr CreateCompatibleDC(IntPtr hdc);
>
>         [DllImport("GDI32.dll")]
>         public static extern IntPtr CreateCompatibleBitmap(IntPtr hdc, int nWidth,
>             int nHeight);
>
>         [DllImport("GDI32.dll")]
>         public static extern IntPtr SelectObject(IntPtr hdc, IntPtr hgdiobj);
>
>         [DllImport("GDI32.dll")]
>         public static extern bool BitBlt(IntPtr hdcDest, int nXDest, int nYDest,
>                                          int nWidth, int nHeight, IntPtr hdcSrc,
>                                          int nXSrc, int nYSrc, int dwRop);
>
>         [DllImport("GDI32.dll")]
>         public static extern bool DeleteDC(IntPtr hdc);
>
>         [DllImport("GDI32.dll")]
>         public static extern bool DeleteObject(IntPtr hObject);
>     }
>
>     public class User32
>     {
>         [DllImport("user32.dll")]
>         public static extern IntPtr GetDesktopWindow();
>
>         [DllImport("user32.dll")]
>         public static extern IntPtr GetTopWindow(IntPtr hWnd);
>
>         [DllImport("user32.dll")]
>         public static extern IntPtr GetWindow(IntPtr hWnd, uint wCmd);
>
>         [DllImport("User32.dll")]
>         public static extern IntPtr GetWindowDC(IntPtr hWnd);
>
>         [DllImport("User32.dll")]
>         public static extern IntPtr ReleaseDC(IntPtr hWnd, IntPtr hDC);
>
>     }
> }
> """
>
> assembly = Generate(unmanaged_code, 'UnamangedCode', inMemory=True)
>
> clr.AddReference(assembly)
>
> from UnmanagedCode import User32, GDI32
>
> def ScreenCapture(x, y, width, height):
>     hdcSrc = User32.GetWindowDC(User32.GetDesktopWindow())
>     hdcDest = GDI32.CreateCompatibleDC(hdcSrc)
>     hBitmap = GDI32.CreateCompatibleBitmap(hdcSrc, width, height)
>     GDI32.SelectObject(hdcDest, hBitmap)
>
>     # 0x00CC0020 is the magic number for a copy raster operation
>     GDI32.BitBlt(hdcDest, 0, 0, width, height, hdcSrc, x, y, 0x00CC0020)
>     result = Bitmap(Image.FromHbitmap(hBitmap))
>     User32.ReleaseDC(User32.GetDesktopWindow(), hdcSrc)
>     GDI32.DeleteDC(hdcDest)
>     GDI32.DeleteObject(hBitmap)
>     return result
>
> image = ScreenCapture(0, 0, 50, 400)
> for y in range(image.Height):
>     row = []
>     for x in range(image.Width):
>         color = image.GetPixel(x, y)
>         value = color.R + color.G + color.B
>         if value > 384:
>             row.append(' ')
>         else:
>             row.append('X')
>     print ''.join(row)
>
> import clr
>
> from System.Environment import CurrentDirectory
> from System.IO import Path, Directory
>
> from System.CodeDom import Compiler
> from Microsoft.CSharp import CSharpCodeProvider
>
>
> def Generate(code, name, references=None, outputDirectory=None, inMemory=False):
>     CompilerParams = Compiler.CompilerParameters()
>
>     if outputDirectory is None:
>         outputDirectory = Directory.GetCurrentDirectory()
>     if not inMemory:
>         CompilerParams.OutputAssembly = Path.Combine(outputDirectory, name + ".dll")
>         CompilerParams.GenerateInMemory = False
>     else:
>         CompilerParams.GenerateInMemory = True
>
>     CompilerParams.TreatWarningsAsErrors = False
>     CompilerParams.GenerateExecutable = False
>     CompilerParams.CompilerOptions = "/optimize"
>
>     for reference in references or []:
>         CompilerParams.ReferencedAssemblies.Add(reference)
>
>     provider = CSharpCodeProvider()
>     compile = provider.CompileAssemblyFromSource(CompilerParams, code)
>
>     if compile.Errors.HasErrors:
>         raise Exception("Compile error: %r" % list(compile.Errors.List))
>
>     if inMemory:
>         return compile.CompiledAssembly
>     return compile.PathToAssembly
>
>
> def LoadAssembly(name, namespace=None):
>     clr.AddReference(name)
>     namespace = __import__(namespace or name)
>     return namespace
>
> _______________________________________________
> Users mailing list
> Users at lists.ironpython.com
> http://lists.ironpython.com/listinfo.cgi/users-ironpython.com
>
>


More information about the Users mailing list