Author Topic: Flyte  (Read 1918 times)

0 Members and 1 Guest are viewing this topic.

WILL HATCH

  • Bull Frog
  • Posts: 450
Flyte
« on: October 03, 2014, 03:58:28 PM »
I'd like to take this discussion away from the library thread.

Hi,

Here's a little example for attributes extraction.

I try to write it in a "declarative" style using the Linq extension methods and some others defined in the Extension class (thanks to Thorsten 'kaefer' @ TheSwamp for its nice 'evil' ones).

[...snip..]
The Extensions class provides extension methods that allows to write code in a more "declarative" style
and methods to build a DataTable from a BlockAttribute collection and convert this DataTable into xls, csv files or AutoCAD Table.
Code: [Select]
using System;
using System.Collections;
using System.Collections.Generic;
using System.Data;
using System.IO;
using System.Linq;
using Autodesk.AutoCAD.DatabaseServices;
using AcDataTable = Autodesk.AutoCAD.DatabaseServices.DataTable;

namespace AttributeExtraction
{
    public static class Extensions
    {
        // Opens a DBObject in ForRead mode (kaefer @ TheSwamp)
        public static T GetObject<T>(this ObjectId id) where T : DBObject
        {
            return id.GetObject<T>(OpenMode.ForRead);
        }

        // Opens a DBObject in the given mode (kaefer @ TheSwamp)
        public static T GetObject<T>(this ObjectId id, OpenMode mode) where T : DBObject
        {
            return id.GetObject(mode) as T;
        }
 
        // Opens a collection of DBObject in ForRead mode (kaefer @ TheSwamp)       
        public static IEnumerable<T> GetObjects<T>(this IEnumerable ids) where T : DBObject
        {
            return ids.GetObjects<T>(OpenMode.ForRead);
        }

        // Opens a collection of DBObject in the given mode (kaefer @ TheSwamp)
        public static IEnumerable<T> GetObjects<T>(this IEnumerable ids, OpenMode mode) where T : DBObject
        {
            return ids
                .Cast<ObjectId>()
                .Select(id => id.GetObject<T>(mode))
                .Where(res => res != null);
        }

        // Applies the given Action to each element of the collection (mimics the F# Seq.iter function).
        public static void Iterate<T>(this IEnumerable<T> collection, Action<T> action)
        {
            foreach (T item in collection) action(item);
        }

        // Applies the given Action to each element of the collection (mimics the F# Seq.iteri function).
        // The integer passed to the Action indicates the index of element.
        public static void Iterate<T>(this IEnumerable<T> collection, Action<T, int> action)
        {
            int i = 0;
            foreach (T item in collection) action(item, i++);
        }

        // Gets the block effective name (anonymous dynamic blocs).
        public static string GetEffectiveName(this BlockReference br)
        {
            if (br.IsDynamicBlock)
                return br.DynamicBlockTableRecord.GetObject<BlockTableRecord>().Name;
            return br.Name;
        }

        // Creates a System.Data.DataTable from a BlockAttribute collection.
        public static System.Data.DataTable ToDataTable(this IEnumerable<BlockAttribute> blockAtts, string name)
        {
            System.Data.DataTable dTable = new System.Data.DataTable(name);
            dTable.Columns.Add("Name", typeof(string));
            dTable.Columns.Add("Quantity", typeof(int));
            blockAtts
                .GroupBy(blk => blk, (blk, blks) => new { Block = blk, Count = blks.Count() }, new BlockAttributeEqualityComparer())
                .Iterate(row =>
                {
                    System.Data.DataRow dRow = dTable.Rows.Add(row.Block.Name, row.Count);
                    row.Block.Attributes.Iterate(att =>
                    {
                        if (!dTable.Columns.Contains(att.Key))
                            dTable.Columns.Add(att.Key);
                        dRow[att.Key] = att.Value;
                    });
                });
            return dTable;
        }

        // Gets the column names collection of the datatable
        public static IEnumerable<string> GetColumnNames(this System.Data.DataTable dataTbl)
        {
            return dataTbl.Columns.Cast<System.Data.DataColumn>().Select(col => col.ColumnName);
        }

        // Writes an Excel file from the datatable (using late binding)
        public static void WriteXls(this System.Data.DataTable dataTbl, string filename, string sheetName, bool visible)
        {
            object mis = Type.Missing;
            object xlApp = LateBinding.GetOrCreateInstance("Excel.Application");
            xlApp.Set("DisplayAlerts", false);
            object workbooks = xlApp.Get("Workbooks");
            object workbook, worksheet;
            if (File.Exists(filename))
                workbook = workbooks.Invoke("Open", filename);
            else
                workbook = workbooks.Invoke("Add", mis);
            if (string.IsNullOrEmpty(sheetName))
                worksheet = workbook.Get("Activesheet");
            else
            {
                object worksheets = workbook.Get("Worksheets");
                try
                {
                    worksheet = worksheets.Get("Item", sheetName);
                    worksheet.Get("Cells").Invoke("Clear");
                }
                catch
                {
                    worksheet = worksheets.Invoke("Add", mis);
                    worksheet.Set("Name", sheetName);
                }
            }
            object range = worksheet.Get("Cells");
            dataTbl.GetColumnNames()
                .Iterate((name, i) => range.Get("Item", 1, i + 1).Set("Value2", name));
            dataTbl.Rows
                .Cast<DataRow>()
                .Iterate((row, i) => row.ItemArray
                    .Iterate((item, j) => range.Get("Item", i + 2, j + 1).Set("Value2", item)));
            xlApp.Set("DisplayAlerts", true);
            if (visible)
            {
                xlApp.Set("Visible", true);
            }
            else
            {
                if (File.Exists(filename))
                    workbook.Invoke("Save");
                else
                {
                    int fileFormat =
                        string.Compare("11.0", (string)xlApp.Get("Version")) < 0 &&
                        filename.EndsWith(".xlsx", StringComparison.CurrentCultureIgnoreCase) ?
                        51 : -4143;
                    workbook.Invoke("Saveas", filename, fileFormat, string.Empty, string.Empty, false, false, 1, 1);
                }
                workbook.Invoke("Close");
                workbook = null;
                xlApp.ReleaseInstance();
                xlApp = null;
            }
        }

        // Writes a csv file from the datatable.
        public static void WriteCsv(this System.Data.DataTable dataTbl, string filename)
        {
            using (StreamWriter writer = new StreamWriter(filename))
            {
                writer.WriteLine(dataTbl.GetColumnNames().Aggregate((s1, s2) => string.Format("{0},{1}", s1, s2)));
                dataTbl.Rows
                    .Cast<DataRow>()
                    .Select(row => row.ItemArray.Aggregate((s1, s2) => string.Format("{0},{1}", s1, s2)))
                    .Iterate(line => writer.WriteLine(line));
            }
        }

        // Creates an AutoCAD Table from the datatable.
        public static Table ToAcadTable(this System.Data.DataTable dataTbl, double rowHeight, double columnWidth)
        {
            //return dataTbl.Rows.Cast<DataRow>().ToAcadTable(dataTbl.TableName, dataTbl.GetColumnNames(), rowHeight, columnWidth);
            Table tbl = new Table();
            tbl.Rows[0].Height = rowHeight;
            tbl.Columns[0].Width = columnWidth;
            tbl.InsertColumns(0, columnWidth, dataTbl.Columns.Count - 1);
            tbl.InsertRows(0, rowHeight, dataTbl.Rows.Count + 1);
            tbl.Cells[0, 0].Value = dataTbl.TableName;
            dataTbl.GetColumnNames()
                .Iterate((name, i) => tbl.Cells[1, i].Value = name);
            dataTbl.Rows
                .Cast<DataRow>()
                .Iterate((row, i) =>
                    row.ItemArray.Iterate((item, j) =>
                        tbl.Cells[i + 2, j].Value = item));
            return tbl;
        }
    }
}

wow.. this post blew me away and I signed up to the swamp just for this.

Does anyone know if this can obtain attributes from nested blocks? When I try to test it out, it doesn't appear to find nested blocks.

Is the only way to obtain the attributes of nested blocks is to do it by recursion? I'm stuck using AutoCAD 2010, and thus .NET 3.5, so any nifty dynamic solutions won't work for me.

Any help would be greatly appreciated.

What you're missing is in Gile's testing command:

Hi,
....

A testing command
Code - C#: [Select]
  1. using System.Linq;
  2. using Autodesk.AutoCAD.ApplicationServices;
  3. using Autodesk.AutoCAD.DatabaseServices;
  4. using Autodesk.AutoCAD.EditorInput;
  5. using Autodesk.AutoCAD.Runtime;
  6. using AcAp = Autodesk.AutoCAD.ApplicationServices.Application;
  7.  
  8. namespace AttributeExtraction
  9. {
  10.     public class Commands
  11.     {
  12.         [CommandMethod("Test")]
  13.         public void Test()
  14.         {
  15.             Document doc = AcAp.DocumentManager.MdiActiveDocument;
  16.             Database db = doc.Database;
  17.             Editor ed = doc.Editor;
  18.             TypedValue[] filter = { new TypedValue(0, "INSERT") };
  19.             PromptSelectionResult psr = ed.GetSelection(new SelectionFilter(filter));
  20.             if (psr.Status != PromptStatus.OK) return;
  21.             PromptPointResult ppr = ed.GetPoint("\nInsertion point: ");
  22.             if (ppr.Status != PromptStatus.OK) return;
  23.             using (Transaction tr = db.TransactionManager.StartTransaction())
  24.             {
  25.                 System.Data.DataTable dataTable = psr.Value.GetObjectIds()
  26.                     .Select(id => new BlockAttribute(id.GetObject<BlockReference>()))
  27.                     .ToDataTable("Extraction");
  28.                 Table tbl = dataTable.ToAcadTable(9.0, 40.0);
  29.                 tbl.Position = ppr.Value.TransformBy(ed.CurrentUserCoordinateSystem);
  30.                 BlockTableRecord btr = db.CurrentSpaceId.GetObject<BlockTableRecord>(OpenMode.ForWrite);
  31.                 btr.AppendEntity(tbl);
  32.                 tr.AddNewlyCreatedDBObject(tbl, true);
  33.                 try
  34.                 {
  35.                     string filename = (string)AcAp.GetSystemVariable("dwgprefix") + "Extraction.xls";
  36.                     dataTable.WriteXls(filename, null, true);
  37.                 }
  38.                 catch
  39.                 {
  40.                     AcAp.ShowAlertDialog("Failed to open Excel");
  41.                 }
  42.                 tr.Commit();
  43.             }
  44.         }
  45.     }
  46. }
  47.  

On line 25 where is where he sends the block ids to the data table.  Collect the ObjectIds of the nested blocks then send to table.