Pie Chart to Drafting View

Hi all,
Is there a way to push the Default Pie Charts from Dynamo 3.2.1 onto a Drafting View?
Something like Data-Shapes does for older versions of Revit?

I’m not aware of a Data-Shapes node that creates charts in Revit, but you can always draw the chart yourself (via Dynamo of course) based on the chart data. A pie chart is just a circle split into representative sections based on percentage of the total. Those are all things you can calculate and draw in Dynamo/Revit.

EDIT: Here’s a starting point.

2 Likes

I actually used to have a Revit family for this… yes I am a nerd. :rofl:

2 Likes

The number of weird dynamic families I’ve created over the years for stuff like this is crazy. Unfortunately I almost never use them more than a few times.

1 Like

I used mine every work day for like a year… then joined Autodesk never thought about it again…

A example using WPF and LiveCharts (Core) library (Accessible from the Dynamo environment )

code Python

code
import clr    
import sys
import System
from System.IO import StringReader, FileStream, FileMode, MemoryStream, File, Path
from System.Collections.Generic import List

clr.AddReference('RevitAPI')
import Autodesk
from Autodesk.Revit.DB import *
import Autodesk.Revit.DB as DB

#import transactionManager and DocumentManager (RevitServices is specific to Dynamo)
clr.AddReference('RevitServices')
import RevitServices
from RevitServices.Persistence import DocumentManager
from RevitServices.Transactions import TransactionManager
doc = DocumentManager.Instance.CurrentDBDocument
uiapp = DocumentManager.Instance.CurrentUIApplication
uidoc = uiapp.ActiveUIDocument


clr.AddReference("LiveChartsCore")
clr.AddReference("LiveChartsCore.SkiaSharpView")
clr.AddReference("LiveChartsCore.SkiaSharpView.WPF")
clr.AddReference('SkiaSharp')
import LiveChartsCore
from LiveChartsCore import *
from LiveChartsCore.SkiaSharpView import *
from LiveChartsCore.SkiaSharpView import PieSeries
from LiveChartsCore.SkiaSharpView.WPF import *
from SkiaSharp import SKColors
from LiveChartsCore.SkiaSharpView.VisualElements import LabelVisual
from LiveChartsCore.SkiaSharpView.Painting import SolidColorPaint

clr.AddReference("System.Xml")
clr.AddReference("PresentationFramework")
clr.AddReference("System.Xml")
clr.AddReference("PresentationCore")
clr.AddReference("System.Windows")
import System.Windows.Controls 
from System.Windows.Controls import *
import System.Windows.Controls.Primitives 
from System.IO import StringReader
from System.Xml import XmlReader
from System.Windows import LogicalTreeHelper 
from System.Windows.Markup import XamlReader, XamlWriter
from System.Windows import Window, Application
from System.Windows.Media.Imaging import RenderTargetBitmap, PngBitmapEncoder, BitmapFrame 

import traceback
import time
 
# https://v0.lvcharts.com/App/examples/v1/Wpf/Pie%20or%20Doughnut
    
class MainWindow(Window):
        
    LAYOUT2 = '''
        <Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
         xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
         xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
         xmlns:lvc="clr-namespace:LiveChartsCore.SkiaSharpView.WPF;assembly=LiveChartsCore.SkiaSharpView.WPF"
            Title="LiveCharts"
            Width="404"
            Height="500">
            <Grid>
                <Grid.RowDefinitions>
                    <RowDefinition Height="*"/>
                    <RowDefinition Height="Auto"/>
                </Grid.RowDefinitions>
                
                <UserControl Grid.Row="0" mc:Ignorable="d" d:DesignHeight="300" d:DesignWidth="500">
                    <Grid>
                        <lvc:PieChart Name="Chart" LegendPosition="Bottom" />
                    </Grid>
                </UserControl>

                <!-- Add the Save Chart button at the bottom -->
                <Button Grid.Row="1" Name="SaveButton" Content="Push on a draft view" Height="30" Margin="10"/>
            </Grid>
        </Window>'''
    def __init__(self, title="MyTitle", keys=["A","B", "C"], values=[10,20,30]):
        self.title = title
        self._keys = keys
        self._values = values
        self.img_stream = None
        xr = XmlReader.Create(StringReader(MainWindow.LAYOUT2))
        self.winLoad = XamlReader.Load(xr) 
        # initialize a SeriesCollection and bind it to the PieChart
        self.seriesCollection = List[ISeries]()
        # populate the PieChart with data
        for key, value in zip(keys, values):
            pie_series = PieSeries[float]()
            pie_series.Values = [value]
            pie_series.Name = key
            self.seriesCollection.Add(pie_series)
        self.InitializeComponent()
        
    def InitializeComponent(self):
        #
        try:
            self.SaveButton = LogicalTreeHelper.FindLogicalNode(self.winLoad, "SaveButton")
            self.SaveButton.Click += self.save_button_clicked
            #
            self.Chart = LogicalTreeHelper.FindLogicalNode(self.winLoad, "Chart")
            self.Chart.Series = self.seriesCollection
            # set the title using LabelVisual
            lb_title = LabelVisual()
            lb_title.Text=self.title
            lb_title.TextSize=20
            lb_title.Paint=SolidColorPaint(SKColors.Black)
            self.Chart.Title = lb_title
        except Exception as ex:
            print(str(ex))
            
    def save_button_clicked(self, sender, args):
        """Event handler for the Save Chart button click"""
        try:
            self.save_chart_to_memory()
            self.winLoad.Close()
        except Exception as e:
            print(f"Error saving chart as image: {e}")
            
    def save_chart_to_memory(self):
        try:
            # use RenderTargetBitmap to capture the visual content of the chart
            dpi = 300
            size = self.Chart.RenderSize
            width = int(size.Width * dpi / 96)  
            height = int(size.Height * dpi / 96)  
            render_bitmap = RenderTargetBitmap(width, height, dpi, dpi, System.Windows.Media.PixelFormats.Pbgra32)
            # Render the chart's visual tree to the RenderTargetBitmap
            render_bitmap.Render(self.Chart)
            # Prepare to save the rendered bitmap to a file
            encoder = PngBitmapEncoder()
            frames = List[BitmapFrame](encoder.Frames)
            frames.Add(System.Windows.Media.Imaging.BitmapFrame.Create(render_bitmap))
            encoder.Frames = frames
            # save image to memoryStream
            self.img_stream = MemoryStream()
            encoder.Save(self.img_stream)
            # reset the memory stream position to the beginning
            self.img_stream.Seek(0, System.IO.SeekOrigin.Begin)
            print(f"Chart successfully saved to memomry")
        except Exception as e:
            print(f"Error saving chart as image: {e}")
    
def create_draft_view(view_name):
    filter_Name = System.Predicate[System.Object](lambda x : x.ViewFamily == ViewFamily.Drafting )
    view_family_type = List[Element](FilteredElementCollector(doc).OfClass(ViewFamilyType).ToElements()).Find(filter_Name)
    #
    drafting_view = ViewDrafting.Create(doc, view_family_type.Id)
    try:
        drafting_view.Name = view_name
    except:
        drafting_view.Name = f"{view_name}_{int(time.time())}"
    return drafting_view
    
def create_image_on_draft_view(img_stream, view_name):
    TransactionManager.Instance.EnsureInTransaction(doc)
    #
    dft_view = create_draft_view(view_name)
    #
    temp_file_path = Path.GetTempFileName()
    temp_file_path_png = Path.ChangeExtension(temp_file_path, ".png")
    with File.OpenWrite(temp_file_path_png) as temp_file_stream:
        img_stream.CopyTo(temp_file_stream)
    #
    image_options = ImageTypeOptions(temp_file_path_png, False, ImageTypeSource.Import)
    placement_options = ImagePlacementOptions()
    placement_options.PlacementPoint = BoxPlacement.Center
    placement_options.Location = XYZ.Zero
    #
    image_type = ImageType.Create(doc, image_options)
    image_instance = ImageInstance.Create(doc, dft_view, image_type.Id, placement_options)
    #
    TransactionManager.Instance.TransactionTaskDone()
    TransactionManager.Instance.ForceCloseTransaction()
    uidoc.RequestViewChange(dft_view)
    return image_instance

my_window = MainWindow(IN[0], IN[1], IN[2])
my_window.winLoad.ShowDialog()
if my_window.img_stream is not None:
    OUT = create_image_on_draft_view(my_window.img_stream, my_window.title)

(compatible Revit 2025+ with all python engines ‘except the last… :face_with_monocle: :face_with_diagonal_mouth:)

EDIT 20/09/2024

It’s works now :grinning:

6 Likes

Thank you for the options guys. I will test them when I have some free time :slight_smile:

2 Likes

Thank you c.poupin. This is great.
Is there a way to input my own colours in it? I’ve been looking at the LiveCharts documentation but I can’t find anything about that :frowning:

showme-show

After some faffing around I’ve changed the code to this:

import clr    
import sys
import System
from System.IO import StringReader, FileStream, FileMode, MemoryStream, File, Path
from System.Collections.Generic import List

clr.AddReference('RevitAPI')
import Autodesk
from Autodesk.Revit.DB import *
import Autodesk.Revit.DB as DB

#import transactionManager and DocumentManager (RevitServices is specific to Dynamo)
clr.AddReference('RevitServices')
import RevitServices
from RevitServices.Persistence import DocumentManager
from RevitServices.Transactions import TransactionManager
doc = DocumentManager.Instance.CurrentDBDocument
uiapp = DocumentManager.Instance.CurrentUIApplication
uidoc = uiapp.ActiveUIDocument


clr.AddReference("LiveChartsCore")
clr.AddReference("LiveChartsCore.SkiaSharpView")
clr.AddReference("LiveChartsCore.SkiaSharpView.WPF")
clr.AddReference('SkiaSharp')
import LiveChartsCore
from LiveChartsCore import *
from LiveChartsCore.SkiaSharpView import *
from LiveChartsCore.SkiaSharpView import PieSeries
from LiveChartsCore.SkiaSharpView.WPF import *
from SkiaSharp import SKColors
from LiveChartsCore.SkiaSharpView.VisualElements import LabelVisual
from LiveChartsCore.SkiaSharpView.Painting import SolidColorPaint

clr.AddReference("System.Xml")
clr.AddReference("PresentationFramework")
clr.AddReference("System.Xml")
clr.AddReference("PresentationCore")
clr.AddReference("System.Windows")
import System.Windows.Controls 
from System.Windows.Controls import *
import System.Windows.Controls.Primitives 
from System.IO import StringReader
from System.Xml import XmlReader
from System.Windows import LogicalTreeHelper 
from System.Windows.Markup import XamlReader, XamlWriter
from System.Windows import Window, Application
from System.Windows.Media.Imaging import RenderTargetBitmap, PngBitmapEncoder, BitmapFrame 

import traceback
import time
 
# https://v0.lvcharts.com/App/examples/v1/Wpf/Pie%20or%20Doughnut
    
class MainWindow(Window):
        
    LAYOUT2 = '''
        <Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
         xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
         xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
         xmlns:lvc="clr-namespace:LiveChartsCore.SkiaSharpView.WPF;assembly=LiveChartsCore.SkiaSharpView.WPF"
            Title="LiveCharts"
            Width="404"
            Height="500">
            <Grid>
                <Grid.RowDefinitions>
                    <RowDefinition Height="*"/>
                    <RowDefinition Height="Auto"/>
                </Grid.RowDefinitions>
                
                <UserControl Grid.Row="0" mc:Ignorable="d" d:DesignHeight="300" d:DesignWidth="500">
                    <Grid>
                        <lvc:PieChart Name="Chart" LegendPosition="Right" />
                    </Grid>
                </UserControl>

                <!-- Add the Save Chart button at the bottom -->
                <Button Grid.Row="1" Name="SaveButton" Content="Push on a draft view" Height="30" Margin="10"/>
            </Grid>
        </Window>'''
    def __init__(self, title="MyTitle", keys=["A","B", "C"], values=[10,20,30], colors=[SKColors.Red, SKColors.Green, SKColors.Blue]):
        self.title = title
        self._keys = keys
        self._values = values
        self.colors = colors
        self.img_stream = None
        xr = XmlReader.Create(StringReader(MainWindow.LAYOUT2))
        self.winLoad = XamlReader.Load(xr) 
        # initialize a SeriesCollection and bind it to the PieChart
        self.seriesCollection = List[ISeries]()
        # populate the PieChart with data
        for key, value, colors in zip(keys, values, colors):
            pie_series = PieSeries[float]()
            pie_series.Values = [value]
            pie_series.Name = key
            pie_series.Fill = SolidColorPaint(colors) 
            self.seriesCollection.Add(pie_series)
        self.InitializeComponent()
        
    def InitializeComponent(self):
        #
        try:
            self.SaveButton = LogicalTreeHelper.FindLogicalNode(self.winLoad, "SaveButton")
            self.SaveButton.Click += self.save_button_clicked
            #
            self.Chart = LogicalTreeHelper.FindLogicalNode(self.winLoad, "Chart")
            self.Chart.Series = self.seriesCollection
            # set the title using LabelVisual
            lb_title = LabelVisual()
            lb_title.Text=self.title
            lb_title.TextSize=20
            lb_title.Paint=SolidColorPaint(SKColors.Black)
            self.Chart.Title = lb_title
        except Exception as ex:
            print(str(ex))
            
    def save_button_clicked(self, sender, args):
        """Event handler for the Save Chart button click"""
        try:
            self.save_chart_to_memory()
            self.winLoad.Close()
        except Exception as e:
            print(f"Error saving chart as image: {e}")
            
    def save_chart_to_memory(self):
        try:
            # use RenderTargetBitmap to capture the visual content of the chart
            dpi = 300
            size = self.Chart.RenderSize
            width = int(size.Width * dpi / 96)  
            height = int(size.Height * dpi / 96)  
            render_bitmap = RenderTargetBitmap(width, height, dpi, dpi, System.Windows.Media.PixelFormats.Pbgra32)
            # Render the chart's visual tree to the RenderTargetBitmap
            render_bitmap.Render(self.Chart)
            # Prepare to save the rendered bitmap to a file
            encoder = PngBitmapEncoder()
            frames = List[BitmapFrame](encoder.Frames)
            frames.Add(System.Windows.Media.Imaging.BitmapFrame.Create(render_bitmap))
            encoder.Frames = frames
            # save image to memoryStream
            self.img_stream = MemoryStream()
            encoder.Save(self.img_stream)
            # reset the memory stream position to the beginning
            self.img_stream.Seek(0, System.IO.SeekOrigin.Begin)
            print(f"Chart successfully saved to memory")
        except Exception as e:
            print(f"Error saving chart as image: {e}")
    
def create_draft_view(view_name):
    filter_Name = System.Predicate[System.Object](lambda x : x.ViewFamily == ViewFamily.Drafting )
    view_family_type = List[Element](FilteredElementCollector(doc).OfClass(ViewFamilyType).ToElements()).Find(filter_Name)
    #
    drafting_view = ViewDrafting.Create(doc, view_family_type.Id)
    try:
        drafting_view.Name = view_name
    except:
        drafting_view.Name = f"{view_name}_{int(time.time())}"
    return drafting_view
    
def create_image_on_draft_view(img_stream, view_name):
    TransactionManager.Instance.EnsureInTransaction(doc)
    #
    dft_view = create_draft_view(view_name)
    #
    temp_file_path = Path.GetTempFileName()
    temp_file_path_png = Path.ChangeExtension(temp_file_path, ".png")
    with File.OpenWrite(temp_file_path_png) as temp_file_stream:
        img_stream.CopyTo(temp_file_stream)
    #
    image_options = ImageTypeOptions(temp_file_path_png, False, ImageTypeSource.Import)
    placement_options = ImagePlacementOptions()
    placement_options.PlacementPoint = BoxPlacement.Center
    placement_options.Location = XYZ.Zero
    #
    image_type = ImageType.Create(doc, image_options)
    image_instance = ImageInstance.Create(doc, dft_view, image_type.Id, placement_options)
    #
    TransactionManager.Instance.TransactionTaskDone()
    TransactionManager.Instance.ForceCloseTransaction()
    uidoc.RequestViewChange(dft_view)
    return image_instance

my_window = MainWindow(IN[0], IN[1], IN[2], IN[3])
my_window.winLoad.ShowDialog()
if my_window.img_stream is not None:
    OUT = create_image_on_draft_view(my_window.img_stream, my_window.title)

But no colours are passing through

you need to use SkiaSharp.SKColor

import this
from SkiaSharp import SKColors, SKColor, SKColorF

then add this in your Initialization method

# convert dynamo color to hex color
hex_color = "#{:02X}{:02X}{:02X}".format(ds_color.Red, ds_color.Green, ds_color.Blue)
sk_color = SKColor.Parse(hex_color)
pie_series.Fill = SolidColorPaint(sk_color)

hmm, do I need to define what ds_color is? it gives me an error on that line. Where can I find the dynamo api docs if there is such a thing

Not much in the way of formal documentation, as the API is built for C# and most (all?) of the API calls are self documenting in your IDE. Doesn’t help for Python though.

The Dynamo Color class should be available after adding the following though:

clr.AddReference('DSCoreNodes')
import DSCore # enable design script in python

From there you can refer to the Dynamo color class via DSCore.Color and proceeding with the rest of that line.

still crashing, although I just realised that the color range in dynamo has a 4th parameter an Alpha so maybe that’s why it does not like it?

Color range or color?

Color range has a few different constructors so knowing which you’re using is a must. Color has an Alpha, Red, Green, and Blue (hence the ARGB name). If you’re not providing all four inputs then the last parameter (blue) is likely omitted, but I don’t believe color ranges support alpha channels so it might be erroring out there. Best to run a smaller snippet to get that bit working.

Yes, i am using Color Range which has an Alpha.

That color range doesn’t indicate an alpha input. Just colors, indices and values as usual. Can you post your full graph for review and a .rvt file? I’ll try and have a look tonight.

It’s just a small test graph at the moment, it’s not using any data from a Revit model.
Pie Chart Test.dyn (39.8 KB)

Need to know which Revit build you’re in too.

Revit 2025.