25 janv. 2025

[Dynamo+=Python] Un exemple de barre de Progression (WPF)






Une variante d'une barre de progression en WPF dans Dynamo

#DynamoBIM #Python #ProgressBar #WPF #AutodeskExpertElite #AutodeskCommunity 

if this article is not in your language, use the Google Translate widget (bottom of page for Mobile version)


Cet exemple fait suite à un précédent article sur le même sujet, mais qui était réalisé avec WinForms

Cette fois-ci, nous utiliserons WPF avec l'aide du dispatcher pour la régénération de l'interface utilisateur.

Le dispatcher est un mécanisme de WPF qui permet d’exécuter du code sur le thread qui possède l’objet de l’interface utilisateur.
Ainsi, il garantit que toutes les modifications apportées à l'interface utilisateur (par exemple, mettre à jour le texte d'un label ou la valeur d'une barre de progression) sont effectuées sur le thread UI, même si la logique ou les calculs proviennent d'un autre thread.


Comme dans la version Winforms, l'initialisation et la progression de la barre se font toujours à l'aide d'un context manager.

with ProgressBarUtils.MyProgressBroadcaster(len(lstPoints), "Test PythonNet3 Engine") as pgb:
    for p in lstPoints:
        time.sleep(0.02)
        pgb.next(f"Success Process with item : {p.ToString()}")

.


.

Le code est compatible avec les derniers moteurs Python3 (CPython3, IronPython3 et PythonNet3)

À partir de Revit 2025 (.Net8+) je recommande fortement d'abandonner définitivement IronPython2 


 

import sys
import clr
import System
from System import Array

clr.AddReference("System.Xml")
clr.AddReference("PresentationFramework")
clr.AddReference("System.Xml")
clr.AddReference("PresentationCore")
clr.AddReference("System.Windows")
clr.AddReference("WindowsBase")
import System.Windows.Controls 
from System.Windows.Controls import *
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

import traceback

class ProgressBarUtils():
    def __init__(self):
        pass
        
    def __name__(self):
        return 'ProgressBarUtils'
        
        
    class ProgressBarDialog(Window):
        LAYOUT = '''
        <Window 
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        Title="Progression"
        Height="200" Width="400"
        MinHeight="200" MinWidth="400"
        ResizeMode="CanResizeWithGrip"
        x:Name="MainWindow">
        <Grid>
            <ProgressBar
                Name="pbar"
                Minimum="0" Maximum="100"
                Grid.Column="0" Grid.Row="0"
                HorizontalAlignment="Stretch" VerticalAlignment="Top"
                Margin="20,40,20,10"
                Height="30" />
            <TextBlock
                Name="progressText"
                TextAlignment="Center"
                Text="--"
                Height="20"
                VerticalAlignment="Top" HorizontalAlignment="Stretch"
                Grid.Row="0" Grid.Column="0"
                Margin="5,45,5,5" />
            <TextBlock
                Name="infotext"
                TextAlignment="Left"
                Text="--"
                Height="40"
                VerticalAlignment="Bottom" HorizontalAlignment="Stretch"
                Grid.Row="0" Grid.Column="0"
                Margin="20,20,20,25" />
        </Grid>
    </Window>'''
        
        def __init__(self, max, title = ""):
            super().__init__()
            xr = XmlReader.Create(StringReader(ProgressBarUtils.ProgressBarDialog.LAYOUT))
            self.winLoad = XamlReader.Load(xr) 
            self.progressText = LogicalTreeHelper.FindLogicalNode(self.winLoad, "progressText")
            self.pbar = LogicalTreeHelper.FindLogicalNode(self.winLoad, "pbar")
            self.infoText = LogicalTreeHelper.FindLogicalNode(self.winLoad, "infotext")
            self.new_value = 0
            self.new_textInfo = "--"
            self.infoText.Text = self.new_textInfo
            self.pbar.Maximum = max
            self.winLoad.Title = title
            self.method_info_dispatcher  = next(
                                                (m for m in self.winLoad.Dispatcher.GetType().GetMethods()\
                                                if "Void Invoke(System.Action, System.Windows.Threading.DispatcherPriority)" == m.ToString()),
                                                None)
            self._dispatch_updater()
            
    
        def _dispatch_updater(self):
            try:
                # ask WPF dispatcher for gui update
                if self.method_info_dispatcher is not None:
                    args = Array[System.Object]([System.Action(self._update_pbar), System.Windows.Threading.DispatcherPriority.Background])
                    self.method_info_dispatcher.Invoke(self.winLoad.Dispatcher, args)
            except Exception as ex:
                print(traceback.format_exc())
    
        def _update_pbar(self):
            try:
                self.pbar.Value = self.new_value
                self.progressText.Text = "Items Processing: {}/{}".format(int(self.pbar.Value), int(self.pbar.Maximum))
                self.infoText.Text = self.new_textInfo
                if self.pbar.Value == self.pbar.Maximum:
                    self.winLoad.Close()
            except Exception as ex:
                print(traceback.format_exc())
        
        def update_progress(self, value):
            try:
                self.new_value = value
                self._dispatch_updater()
                self.winLoad.Activate()
            except Exception as ex:
                print(traceback.format_exc())
            
    class MyProgressBroadcaster():
        """
        main Class to Start UI and build a custom Event with a  ContextManager
        """
        def __init__(self, numberLines, title = "My_progressBar"):
            self.numberLines = numberLines
            self.title = title
            self.ui = None
            
        def next(self, infoPgb="-"):
            if self.ui is not None:
                try:
                    self.ui.new_textInfo = infoPgb
                    self.ui.update_progress(self.ui.new_value + 1)
                except Exception as ex:
                    print(traceback.format_exc())
            
        def __enter__(self):        
            try:
                self.ui = ProgressBarUtils.ProgressBarDialog(self.numberLines, self.title)
                self.ui.winLoad.Show()
                return self
            except Exception as ex:
                print(traceback.format_exc())
                return None
            
        def __exit__(self, exc_type, exc_value, traceback):
            try:
                print(exc_type)
                print(exc_value)
                if traceback:
                    exc_tbnext = traceback
                    lst_lines_error = []
                    for i in range(2):
                        if exc_tbnext is not None:
                            lst_lines_error.append(str(exc_tbnext.tb_lineno))
                            exc_tbnext = exc_tbnext.tb_next
                        else:
                            break
                    #print(lst_lines_error)
                    error = "Error : {} at lines : line : {}".format( exc_value, " -> line : ".join(lst_lines_error))
                    print("{}\n{}".format(exc_type.__name__, error))
            except Exception as ex:
                print(traceback.format_exc())

OUT = ProgressBarUtils


Notes :

  • Vous pouvez utiliser cette Classe en tant que module ou instance de Classe 
  • Si vous voulez utiliser cette Classe dans DynamoSandBox , il faudra utiliser un thread STA

Le Projet (Winforms & WPF) avec des exemples de fichiers Dynamo se trouve ici

https://github.com/Cyril-Pop/Dynamo_PythonProgressBar


0 commentaires:

Enregistrer un commentaire