Doc == NonType Obect

Hi,

I’ve built a dockable pane, I’ve read lots of forum posts such as these:

I am still learning here, so please be gentle.

I want to return the doc (which loads after the pane), for things like ‘doc.GetWarnings()’

I see doc can be called in a number of ways, revit.doc, doc = HOST_APP.doc etc. I am clearly missing something, because I get 'nonType object has no attrribute ‘GetWarnings’ when I click my button.

Any assistance appreciated!

import System
import os.path as op
from pyrevit import HOST_APP, framework, coreutils, PyRevitException
from pyrevit import revit, DB, UI
from pyrevit import forms, script
from pyrevit.framework import wpf, ObservableCollection
from pyrevit import routes

import sys
import time
import os.path as op

class _WPFPanelProvider(UI.IDockablePaneProvider):
    def __init__(self, panel_type, default_visible=True):
        self._panel_type = panel_type
        self._default_visible = default_visible
        self.panel = self._panel_type()

    def SetupDockablePane(self, data):
        data.FrameworkElement = self.panel
        data.VisibleByDefault = self._default_visible

def register_dockable_panel(panel_type, default_visible=True):
    if not issubclass(panel_type, forms.WPFPanel):
        raise PyRevitException(
            "Dockable pane must be a subclass of forms.WPFPanel"
            )

    panel_uuid = coreutils.Guid.Parse(panel_type.panel_id)
    dockable_panel_id = UI.DockablePaneId(panel_uuid)
    panel_provider = _WPFPanelProvider(panel_type, default_visible)
    HOST_APP.uiapp.RegisterDockablePane(
        dockable_panel_id,
        panel_type.panel_title,
        panel_provider
        ) 
    return panel_provider.panel

class DockableExample(forms.WPFPanel):
    panel_title = "OBE Info Pane"
    panel_id = "1458b7cb-4dbe-4a8e-bad3-837e14b0a1ca"
    panel_source = "Z:\\Library\\BIM\\01 Revit\\05 Toolbar\\OBTool\\Oberlanders.extension\\OBE.tab\\Audit.Panel\\AuditStack.Stack\\Dockable.pushbutton\\dockable.xaml"

    def __init__(self):
        wpf.LoadComponent(self, self.panel_source)
        self.thread_id = framework.get_current_thread_id()
        self.selected_lb.Text = ""

    def button_click(self, sender, args):        
        try:
            from obe import model_audit_doc #if we call at the start there is no doc?
            doc = revit.doc 
            wrn = doc.GetWarnings() #test call fails
            data_string = "test"
            self.selected_lb.Text = data_string
        except Exception as e:
            print e.message


registered_panel = register_dockable_panel(DockableExample)

So I have tried to add an event handler, it’s really hard to debug when it loads on startup, the error window closes before I can even read it…


import System
import os.path as op
from pyrevit import HOST_APP, framework, coreutils, PyRevitException
from pyrevit import revit, DB, UI
from pyrevit import forms, script
from pyrevit.framework import wpf, ObservableCollection
from pyrevit import routes

import sys
import time
import os.path as op

data_string = ""
dockable_pane = None

class _WPFPanelProvider(UI.IDockablePaneProvider):
    def __init__(self, panel_type, default_visible=True):
        self._panel_type = panel_type
        self._default_visible = default_visible
        self.panel = self._panel_type()

    def SetupDockablePane(self, data):
        data.FrameworkElement = self.panel
        data.VisibleByDefault = self._default_visible

def register_dockable_panel(panel_type, default_visible=True):
    if not issubclass(panel_type, forms.WPFPanel):
        raise PyRevitException(
            "Dockable pane must be a subclass of forms.WPFPanel"
            )

    panel_uuid = coreutils.Guid.Parse(panel_type.panel_id)
    dockable_panel_id = UI.DockablePaneId(panel_uuid)
    panel_provider = _WPFPanelProvider(panel_type, default_visible)
    HOST_APP.uiapp.RegisterDockablePane(
        dockable_panel_id,
        panel_type.panel_title,
        panel_provider
        ) 
    return panel_provider.panel

class DockableExample(forms.WPFPanel, doc):
    panel_title = "OBE Info Pane"
    panel_id = "1458b7cb-4dbe-4a8e-bad3-837e14b0a1ca"
    panel_source = "Z:\\Library\\BIM\\01 Revit\\05 Toolbar\\OBTool\\Oberlanders.extension\\OBE.tab\\Audit.Panel\\AuditStack.Stack\\Dockable.pushbutton\\dockable.xaml"

    def __init__(self):
        wpf.LoadComponent(self, self.panel_source)
        self.thread_id = framework.get_current_thread_id()
        self.selected_lb.Text = data_string

    def button_refresh(self, sender, args):
      self.update_list()

    def update_list(self, data_string):        
        try:
            self.selected_lb.Text = data_string
        except Exception as e:
            print e.message

registered_panel = register_dockable_panel(DockableExample)

def docopened_eventhandler(sender, args):
    forms.alert("Doc Opened")
    doc = HOST_APP.doc
    data_string = 'test_opened'
    registered_panel.update_list(data_string)

HOST_APP.app.DocumentOpened += framework.EventHandler[DB.Events.DocumentOpenedEventArgs](docopened_eventhandler)


I experimented a bit on my side.
This is definitely not a topic I fully grasp.
I started from the sample in the dev extension of pyRevit

The key seems to be to handle what is related to the Document in the EventHandler definition
I used the ViewActivated one, getting its arguments ‘args you can grab the Document and build from there.

python code in my 'startup.py' script
import System
import os.path as op
from pyrevit import HOST_APP, framework, coreutils, PyRevitException
from pyrevit import revit, DB, UI
from pyrevit import forms, script
from pyrevit.framework import wpf, ObservableCollection

sample_panel_id = "1458b7cb-4dbe-4a8e-bad3-837e14b0a1ca"

warnings_list = []


class _WPFPanelProvider(UI.IDockablePaneProvider):
    def __init__(self, panel_type, default_visible=True):
        self._panel_type = panel_type
        self._default_visible = default_visible
        self.panel = self._panel_type()

    def SetupDockablePane(self, data):
        data.FrameworkElement = self.panel
        data.VisibleByDefault = self._default_visible

def register_dockable_panel(panel_type, default_visible=True):
    if not issubclass(panel_type, forms.WPFPanel):
        raise PyRevitException(
            "Dockable pane must be a subclass of forms.WPFPanel"
            )

    panel_uuid = coreutils.Guid.Parse(panel_type.panel_id)
    dockable_panel_id = UI.DockablePaneId(panel_uuid)
    panel_provider = _WPFPanelProvider(panel_type, default_visible)
    HOST_APP.uiapp.RegisterDockablePane(
        dockable_panel_id,
        panel_type.panel_title,
        panel_provider
        )
    return panel_provider.panel

class DockableExample(forms.WPFPanel):
    panel_source = op.join(op.dirname(__file__), "DockablePaneSample.xaml")
    panel_title = "Dockable Pane Sample"
    panel_id = sample_panel_id
    def __init__(self):
        wpf.LoadComponent(self, self.panel_source)
        self.thread_id = framework.get_current_thread_id()
        self.warnings_lb.ItemsSource = []


    def update_list(self):
        try:
            template_list = [forms.TemplateListItem(w) for w in warnings_list]
            self.warnings_lb.ItemsSource = ObservableCollection[forms.TemplateListItem](template_list)
        except Exception as e:
            print e.message


registered_panel = register_dockable_panel(DockableExample)

def viewactivated_eventhandler(sender, args):
    try: dockable_pane = UI.DockablePane(UI.DockablePaneId(System.Guid(sample_panel_id)))
    except: return

    global warnings_list

    if HOST_APP.uidoc and dockable_pane.IsShown():
        try:
            warns = args.Document.GetWarnings()
            warns = [i.GetDescriptionText() for i in warns]
            print warns
            # you could print here to check if it's working
            if warns:
                warnings_list = warns
                registered_panel.update_list()
        except Exception as e:
            print e.message

HOST_APP.uiapp.ViewActivated += \
    framework.EventHandler[UI.Events.ViewActivatedEventArgs](viewactivated_eventhandler)
The xaml side 'DockablePaneSample.xaml'
<Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
      Background="White">
    <Page.Resources>
        <ControlTemplate x:Key="ItemTemplate">
            <TextBlock Text="{Binding name}"
                       ToolTip="{Binding name}"
                       VerticalAlignment="Center"
                       Margin="10,0,0,0">
            </TextBlock>
        </ControlTemplate>

        <DataTemplate x:Key="ItemContainerTemplate">
            <Grid>
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="Auto"/>
                </Grid.ColumnDefinitions>
                <Control Grid.Column="0"
                         VerticalAlignment="Center" VerticalContentAlignment="Center"
                         Template="{DynamicResource ItemTemplate}">
                </Control>
            </Grid>
        </DataTemplate>

        <ItemsPanelTemplate x:Key="ItemsPanelTemplate">
            <StackPanel />
        </ItemsPanelTemplate>
    </Page.Resources>
    <DockPanel>
        <TextBlock DockPanel.Dock="Top" 
                   Text="Revit Warnings" 
                   HorizontalAlignment="Stretch" 
                   Background="#FFEEEEEE"/>
        <ListView x:Name="warnings_lb" 
                  HorizontalAlignment="Stretch" 
                  ItemTemplate ="{DynamicResource ItemContainerTemplate}"
                  ItemsPanel="{DynamicResource ItemsPanelTemplate}">
        </ListView>
    </DockPanel>
</Page>
2 Likes

Thank you so much! I will continue to tinker with it :slight_smile: