Display pie charts in the Revit interface through UI pop ups

Hello everyone! I would like to know if there is a node in Dynamo that can act like UI.Messenger, a UI window pops up in the Revit interface to display information, except for the UI.Messenger displays text information, but this node can integrate the information into a pie chart.
The Pie chart node displays a pie chart in the Dynamo interface, but I need to display it in the Revit interface

Maybe Datashapes will do what you need?

1 Like

Thank you for your reply. But what I need is a node for outputting results, not an input node

all right…I found that Datashape can complete my task. Thank you for your reminder!


The pie chart created using Datashape is not very aesthetically pleasing. Can UI nodes achieve the same effect as above?

The python code in datashapes can be unwrapped and edited, if you want to dive into how they work you could use it as a platform to work from to get exactly what you want, just make sure you credit the original author in your code :slight_smile:

Others may know of some other methods to achive this as well :slight_smile:

I got it. thank you :slightly_smiling_face:

Hi,

2 examples with Python (LiveCharts and Matplotlib)

charts popup

  • WPF + LiveCharts (IronPython) (LiveCharts library is included in Dynamo 2.19+)
    be careful, there are some changes with dynamo 3 (the LiveCharts library has changed a little)
code IronPython wpf+LiveCharts

import clr	
import sys
import System
#import net library
from System.Collections.Generic import List

clr.AddReference('System')
try:
	clr.AddReference('System.Diagnostics')
except:pass
from System.Diagnostics import Process

net_clr_runtime_name =  str(System.Environment.Version)
targetFrameworkAttribute = System.Reflection.Assembly.GetExecutingAssembly().GetCustomAttributes(System.Runtime.Versioning.TargetFrameworkAttribute, False)[0].FrameworkName #.SingleOrDefault()
pocessName = Process.GetCurrentProcess().ProcessName

clr.AddReference("LiveCharts")
from LiveCharts import *
clr.AddReference("LiveCharts.Wpf")
from LiveCharts.Wpf import *
from LiveCharts.Defaults import *

try:
	clr.AddReference("IronPython.Wpf")
	clr.AddReference('System.Core')
	clr.AddReference('System.Xml')
	clr.AddReference('PresentationCore')
	clr.AddReference('PresentationFramework')
except IOError:
	raise
	
from System.IO import StringReader
from System.Windows.Markup import XamlReader, XamlWriter
from System.Windows import Window, Application
from System.Windows.Controls import *

try:
	import wpf
except ImportError:
	raise
	
class MyForm(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:LiveCharts.Wpf;assembly=LiveCharts.Wpf"
            Title="Wpf + LiveCharts"
            Width="404"
            Height="419">
            <Grid>
                <UserControl
                             mc:Ignorable="d" 
                             d:DesignHeight="300" d:DesignWidth="500">
                    <Grid>
                        <lvc:PieChart Name="Chart" LegendLocation="Bottom" Series="{Binding tableValues}" Hoverable="False" DataTooltip="{x:Null}">
                        <lvc:PieChart.ChartLegend>
                            <lvc:DefaultLegend BulletSize="20"></lvc:DefaultLegend>
                        </lvc:PieChart.ChartLegend>
                        </lvc:PieChart>  
                    </Grid>
                </UserControl>
            </Grid>
        </Window>"""
    def __init__(self, keys=["A","B", "C"], values=[10,20,30]):
        self.ui = wpf.LoadComponent(self, StringReader(MyForm.LAYOUT2))
        seriesCollection = SeriesCollection()
        #
        for i, j in zip(keys, values):
            values = ChartValues[System.Double]()
            values.Add(float(j))
            seriesCollection.Add(PieSeries(Title=i,Values=values,DataLabels=True))
        self.Chart.Series = seriesCollection

def appThread():
    my_window = MyForm(IN[0], IN[1])
    my_window.Show()
print(pocessName)

if pocessName ==  "Revit":
    thread = appThread()
else:
    thread = Application.Current.Dispatcher.Invoke(appThread)
  • Winform + matplotlib (CPython3)
code python3 Winfom + matplotlib
import clr	
import sys
import System
from System.Threading import Thread, ThreadStart, ApartmentState
#import net library
from System.Collections.Generic import List

clr.AddReference('System')
try:
	clr.AddReference('System.Diagnostics')
except:pass
from System.Diagnostics import Process

net_clr_runtime_name =  str(System.Environment.Version)
targetFrameworkAttribute = System.Reflection.Assembly.GetExecutingAssembly().GetCustomAttributes(System.Runtime.Versioning.TargetFrameworkAttribute, False)[0].FrameworkName #.SingleOrDefault()
pocessName = Process.GetCurrentProcess().ProcessName

clr.AddReference('System.Drawing')
clr.AddReference('System.Windows.Forms')
import System.Drawing
import System.Windows.Forms

from System.Drawing import *
from System.Windows.Forms import *
from System.Drawing.Imaging import *

clr.AddReference('Python.Included')
import Python.Included as pyInc
path_py3_lib = pyInc.Installer.EmbeddedPythonHome
sys.path.append(path_py3_lib + r'\Lib\site-packages')

import io
import numpy as np
import matplotlib.pyplot as plt
from PIL import Image
import base64


def decoThread(func):
    def wrapper():
        globals()['objForm'] = None
        if pocessName == "Revit":
            objForm = func()
        else:
            thread = Thread(ThreadStart(func))
            thread.SetApartmentState(ApartmentState.STA)
            thread.Start()
            thread.Join()
        return globals()['objForm']
    return wrapper

class PieForm(Form):
    def __init__(self, fig):
        image_from_plot = PieForm.plt2arr(fig)
        self.base64String = PieForm.ConvertToBitmap2(image_from_plot)
        self.InitializeComponent()
    
    def InitializeComponent(self):
        self._pictureBox1 = System.Windows.Forms.PictureBox()
        ((System.ComponentModel.ISupportInitialize)(self._pictureBox1)).BeginInit()
        self.SuspendLayout()
        # 
        # pictureBox1
        # 
        self._pictureBox1.Anchor = System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right
        pic = System.Convert.FromBase64String(self.base64String)
        self._pictureBox1.Image = System.Drawing.Image.FromStream(System.IO.MemoryStream(pic))
        self._pictureBox1.Location = System.Drawing.Point(2, 2)
        self._pictureBox1.Name = "pictureBox1"
        self._pictureBox1.Size = self._pictureBox1.Image.Size # System.Drawing.Size(414, 413)
        self._pictureBox1.TabIndex = 0
        self._pictureBox1.TabStop = False
        # 
        # Form40
        # 
        self.ClientSize = self._pictureBox1.Size
        self.Controls.Add(self._pictureBox1)
        self.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent
        self.Name = "PieForm"
        self.Text = "Winform + matplotlib"
        ((System.ComponentModel.ISupportInitialize)(self._pictureBox1)).EndInit()
        self.ResumeLayout(False)

    @staticmethod
    def plt2arr(fig):
        """
        need to draw if figure is not drawn yet
        """
        fig.canvas.draw()
        rgba_buf = fig.canvas.buffer_rgba()
        (w,h) = fig.canvas.get_width_height()
        rgba_arr = np.frombuffer(rgba_buf, dtype=np.uint8).reshape((h,w,4))
        return rgba_arr
        
    @staticmethod
    def ConvertToBitmap2(npImgArray):
        bitmap_ = None
        # remove alpha
        if npImgArray.ndim == 3 and npImgArray.shape[-1] == 4:
            npImgArray = npImgArray[:, :, :-1]
        # convert to PIL Image
        if npImgArray.ndim == 3:
            image = Image.fromarray(npImgArray, "RGB")
        else:
            image = Image.fromarray(npImgArray, "L")
        # convert to Python ByteArray
        s = io.BytesIO()
        plt.savefig(s, format='png', bbox_inches="tight")
        plt.close()
        s = base64.b64encode(s.getvalue()).decode("utf-8").replace("\n", "")
        return s
        
@decoThread
def main():
    total = sum(IN[1])
    fig, ax = plt.subplots()
    ax.pie(IN[1], labels=IN[0], autopct=lambda p: '{:.2f}'.format(p * total / 100), shadow=True)
    objForm = PieForm(fig)
    objForm.ShowDialog()

objForm = None
main()
3 Likes

thank you very much!!

1 Like