Немодальная проблема pyRevit WPF

Итак, я только начинаю погружаться в WPF в pyRevit. Я попытался реализовать класс pyrevit.forms.WPFWindow следующим образом:

# -*- coding: UTF-8 -*-
"""
Third-Party software credits:
pyRevit: repository at https://github.com/eirannejad/pyRevit
"""

import System, clr, json, sys
clr.AddReference("System.Windows.Forms")
clr.AddReference('IronPython.Wpf')
import wpf
from Autodesk.Revit.DB import *
from pyrevit import revit, script, forms

class FactorySettings(forms.WPFWindow):

    def __init__(self):
        forms.WPFWindow.__init__(self, script.get_bundle_file('settings.xaml'))

    def something_click(self, A, B):
        plantings = FilteredElementCollector(revit.doc) \
            .WhereElementIsElementType() \
            .OfCategory(BuiltInCategory.OST_Planting)

ui = FactorySettings()
ui.show()

Это мой xaml:

<Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:sys="clr-namespace:System;assembly=mscorlib"
        xmlns:Collections="clr-namespace:System.Collections;assembly=mscorlib"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
        mc:Ignorable="d"
        ShowInTaskbar="False" ResizeMode="CanResize"
        WindowStartupLocation="CenterScreen"
        HorizontalContentAlignment="Center"
        Title="Set worksets" Width="300" SizeToContent="Height"
        >
    <StackPanel Margin="0,0,0,0" VerticalAlignment="Stretch" HorizontalAlignment="Stretch">
        <Button Content="Do something" Name="something" Click="something_click"/>
    </StackPanel>

</Window>

Это откроет немодальное окно из-за def show(self, modal=False): class-function. Проблема для меня в том, что, хотя моя функция щелчка вызывает только объект FilteredElementCollector, Revit аварийно завершает работу. Если я сделаю ui.show(modal=True), он будет работать, но тогда я ничего не смогу сделать в интерфейсе Revit UI. На самом деле я хотел сделать вот что:

def something_click(self, A, B):
    if self.PHfamSymbol != None:
         with forms.WarningBar(title='Place an instance of the placeholder object.'):
            revit.uidoc.PromptForFamilyInstancePlacement(self.PHfamSymbol)

Это не сработает, потому что основное внимание по-прежнему уделяется пользовательскому интерфейсу. Я пробовал это:

def something_click(self, A, B):
    if self.PHfamSymbol != None:
        self.Close()
         with forms.WarningBar(title='Place an instance of the placeholder object.'):
            try:
                revit.uidoc.PromptForFamilyInstancePlacement(self.PHfamSymbol)
            except:
                pass

Это работает, но мне нужно создать новый экземпляр пользовательского интерфейса после завершения. Возможно ли вообще иметь немодальный пользовательский интерфейс с pyRevit?


person Kyrre    schedule 14.05.2020    source источник


Ответы (1)


Для взаимодействия с Revit необходимо использовать ExternalEvent. в немодальных окнах. У вас много образцов в pyRevitMEP. Я создал здесь многоразовый класс. Я использую его во многих скриптах pyRevitMEP, например Element / а>. Вы также можете проверить мой старый блог [Revit API] Простая немодальная форма (внешний обработчик событий) в pyRevit пост, который ссылается на дополнительные ресурсы и имеет больше комментариев.

Образец

сценарий

"""
Copyright (c) 2017 Cyril Waechter
Python scripts for Autodesk Revit

This file is part of pypevitmep repository at https://github.com/CyrilWaechter/pypevitmep

pypevitmep is an extension for pyRevit. It contain free set of scripts for Autodesk Revit:
you can redistribute it and/or modify it under the terms of the GNU General Public License
version 3, as published by the Free Software Foundation.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

See this link for a copy of the GNU General Public License protecting this package.
https://github.com/CyrilWaechter/pypevitmep/blob/master/LICENSE
"""
from Autodesk.Revit.UI import IExternalEventHandler, ExternalEvent
from Autodesk.Revit.DB import Transaction
from Autodesk.Revit.Exceptions import InvalidOperationException
import rpw
from pyrevit.forms import WPFWindow
doc = rpw.revit.doc
uidoc = rpw.revit.uidoc

__doc__ = "A simple modeless form sample"
__title__ = "Modeless Form"
__author__ = "Cyril Waechter"
__persistentengine__ = True

# Simple function we want to run
def delete_elements():
    with rpw.db.Transaction("Delete selection"):
        for elid in uidoc.Selection.GetElementIds():
            doc.Delete(elid)

# Create a subclass of IExternalEventHandler
class SimpleEventHandler(IExternalEventHandler):
    """
    Simple IExternalEventHandler sample
    """

    # __init__ is used to make function from outside of the class to be executed by the handler. \
    # Instructions could be simply written under Execute method only
    def __init__(self, do_this):
        self.do_this = do_this

    # Execute method run in Revit API environment.
    def Execute(self, uiapp):
        try:
            self.do_this()
        except InvalidOperationException:
            # If you don't catch this exeption Revit may crash.
            print "InvalidOperationException catched"

    def GetName(self):
        return "simple function executed by an IExternalEventHandler in a Form"


# Now we need to make an instance of this handler. Moreover, it shows that the same class could be used to for
# different functions using different handler class instances
simple_event_handler = SimpleEventHandler(delete_elements)
# We now need to create the ExternalEvent
ext_event = ExternalEvent.Create(simple_event_handler)

# A simple WPF form used to call the ExternalEvent
class ModelessForm(WPFWindow):
    """
    Simple modeless form sample
    """

    def __init__(self, xaml_file_name):
        WPFWindow.__init__(self, xaml_file_name)
        self.simple_text.Text = "Hello World"
        self.Show()

    def delete_click(self, sender, e):
        # This Raise() method launch a signal to Revit to tell him you want to do something in the API context
        ext_event.Raise()

# Let's launch our beautiful and useful form !
modeless_form = ModelessForm("ModelessForm.xaml")

xaml файл

<Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="Delete things:" Height="150" Width="300" ShowInTaskbar="False" Topmost="True"
        WindowStartupLocation="CenterScreen" ScrollViewer.VerticalScrollBarVisibility="Disabled" HorizontalContentAlignment="Center">
    <StackPanel Margin="20" HorizontalAlignment="Stretch">
        <TextBlock x:Name="simple_text" Text="" Grid.Column="0" HorizontalAlignment="Center" FontWeight="Bold"/>
        <Button Content="Delete selected elements" Height="30" Margin="10,10" Click="delete_click"/>
    </StackPanel>
</Window>
person Cyril Waechter    schedule 15.05.2020