Tuesday, September 07, 2010
Google Custom Search

Go to...

Recent Forum Posts

Our Community

Membership Membership:
Latest New User Latest: lh@sysit.com
New Today New Today: 7
New Yesterday New Yesterday: 20
User Count Overall: 10691

People Online People Online:
Visitors Visitors: 10
Members Members: 1
Total Total: 11

Online Now Online Now:
01: pierreF

ClearCanvas Community Forums

Interactive Image Preview embedded in Worklist
Last Post 2009-05-05 12:30 PM by resnickj. 14 Replies.
Printer Friendly
Sort:
PrevPrev NextNext
You are not authorized to post a reply.
Author Messages
dreich
Basic Member
Basic Member
Posts:13

--
2009-04-17 10:28 AM  

 Hi.  Has anyone tried to create an interactive image preview on an Explorer?  I've been looking into placing an ImageViewerComponent into a SplitPane in an explorer window.  Way overkill, but I don't really want to tackle a whole new image viewer thingy yet.  I'd like to leverage some of the existing tools that already work so well (W/L and cine at least).

So far I've found that it kinda works.  I do see a small image viewer that I can load with images after creating it.  There is no tool bar which is nice.  Once the images are loaded, I can use the W/L and cine tools.  But I've run into some basic problems:

  • The image viewer expects its ApplicationHost to support a command history, but the SplitPaneHost does not so I get a NotSupportedException after using a tool.
  • I can only add images to the StudyTree in the image viewer, so changing the contents based on which study is selected in the browser is a little tricky (i.e. I think I'll have to tear down and recreate the whole component).
  • I don't want most of the image viewer tools yet.  Once the studies are fully loaded I want them, but I need to disable them in the preview context.

Any advice on how to approach the problem would be appreciated.  

Thanks

Dave



jasper.yeh
Senior Member
Senior Member
Posts:425
Avatar

--
2009-04-17 11:10 AM  
Hi Dave,

The SplitPaneComponent is really designed only to hold simple components (i.e. not the ImageViewerComponent). To solve your problem, you could copy SplitPaneComponent into another class in your plugin and modify it to support a CommandHistory (in SplitPaneHost, override the CommandHistory property and return a field initialized to new CommandHistory(1000)). Or, you could host your viewer component inside a custom host that supports the CommandHistory, which itself is owned by a component hosted inside the SplitPane - basically, you're putting another nested component into the mix - one that supports the history.

As for changing the contents of an existing image viewer, well, I'm not too sure how to accomplish that - although an alternate solution might be to create your own preview component and control. Presentation image objects have a DrawToBitmap(...) method on them which you can use to do the drawing yourself (a frequently asked question on these forums is how to write an image preview mechanism for the ImageServer using the ImageViewer library - and the same code snipper should work equally well in this case). Some other people on these forums might have additional thoughts on this.

To selectively choose which tools you wish to include, you can subclass the ImageViewerComponent, and override the ExportedActions property. The IActionSet has a .Select(predicate) function that you can use to select a subset of the tools - so you can have the property return base.ExportedActions.Select (predicate). One of the problems with a plugin based architecture is that you won't know what tools to be available, and you can't reference them by type, so what other people have done is to write their predicate function to check the ActionId of each action, and include everything on a white list of IDs, excluding everything else by default. The format of an action id is the fully-qualified name of the tool class, followed by a colon (:), followed by the action id as specified in the [*Action(...)] attributes decorating the tool class (e.g. W/L tool is ClearCanvas.ImageViewer.Tools.Standard.WindowLevelTool:activate). You can find a (partial) list of action ids in the solution's ImageViewer_dist.config file.

Hope this helps!


// Jasper
dreich
Basic Member
Basic Member
Posts:13

--
2009-04-17 11:22 AM  

Cool.  Thanks the for the quick reply.  I'll try the nested component approach to get around the command history requirements.  I think I'll also try to load the StudyTree in a custom LayoutManager since that is what reads the study tree and creates the image/display sets in the first place. 

I like the idea of overriding the ExportedActions as well.  I was initially looking at adding an ExtensionFilter to the ToolSet ctor so I could filter out unneeded extensions, but that was far deeper in the framework.   Might still be a useful addition to ToolSet, but I don't think I need it for this task.

I'll post back later to describe how (if) I managed to get a preview running.

Thanks

Dave



stewart
Senior Member
Senior Member
Posts:1425

--
2009-04-17 12:12 PM  
Hi Dave, if you have a look at the MPR code in the current trunk, it does a similar thing to what you have described; nothing is added to the StudyTree and the LayoutManager simply creates display sets and assigns them to image boxes. The MPR stuff is a little different because it creates the display sets from a Vtk volume, but the idea is there.

Hope this helps,
Stewart


dreich
Basic Member
Basic Member
Posts:13

--
2009-04-17 02:59 PM  

 Another slight problem.  I'd like to be able to reuse the StudyTree and even the logic for how the image viewer splits the tree into patients and series ImageSets and DisplaySets.  I have no problem with the sealed nature of the class (I don't see a need to subclass it) but I'd like to create one and use it to organize the sop's I'm loading in my plugin.  However, the ctor is internal to the ImageView assembly so I'm kinda stuck (only kinda because I can modify my local copy to test my nefarious plans for the StudyTree).

Any chance the study tree could be made more available to plugins?  Seems like a reasonable data structure to organize Dicom sops and I would like to use it in more places.

I'm also writing different loading code that does not call ImageViewerComponent.LoadStudy.  Perhaps the loading of the StudyTree should be separated from the viewing of its contents?  

BTW, kudos on the framework.  Having to do minor tweaks versus writing the whole darn thing - priceless.

TTFN

Dave

 



dreich
Basic Member
Basic Member
Posts:13

--
2009-04-17 05:23 PM  

 I love the SplitComponentContainer - all the boring boiler plate code that contructs the UI is taken care of.  Just put the components together in the ViewModel and the UI builds itself.  Awesome!  But...

I'm trying the suggestion of wrapping the ImageViewerComponent in a host that fakes out the command history that is itself wrapped by the SplitPane framework.  It almost works.  The problem arises when the GUI code kicks in and tries to find the view associated with the component in the SplitPaneHost.   The way I created the wrapper was to create a FakeHost that implements both the IApplicationComponent and IApplicationComponentHost interfaces.  The implementation just delegates to real a component and host stored internally, except for the CommandHistory property and the SetHost() method.

All fine, except when the SplitComponentContainerControl calls pane1.ComponentHost.ComponentView.  The ComponentHost is a SplitPaneHost which subclasses the base ApplicationHost.  And that base class tries to find the view associated with the FakeHost component, because that is the type of component the SplitPaneHost is hosting.

Well, that does not work so well since there is no view associated with the FakeHost type.  So close!  The split pane stuff is really nice so I don't want to duplicate it.  I just need to override the SplitPaneHost a tiny little bit.  Sigh.

Tis a riddle, wrapped in an enigma, obscured by a conundrum...



jasper.yeh
Senior Member
Senior Member
Posts:425
Avatar

--
2009-04-17 06:18 PM  
Hi Dave,

You would have to create a FakeControl : UserControl to go with the FakeHost (which is also a component), and all the FakeControl does is drops the hosted viewer's gui element into itself. I think, though, that you may well be justified in duplicating the SplitComponentContainer since it (and its single pane with Ok-Cancel buttons cousin, the SimpleComponentContainer) was designed with dialogs and shelves in mind. I want to say that you won't have to duplicate the view, that you can just associate the component with the same view extension point as the original SplitComponentContainer, but a quick look at the code tells me it won't work (the view is tied to that specific component, and you can't subclass it because then it'll use the host that doesn't support a command history).

At any rate, there's probably some room here to improve the SplitComponentContainer in order to make it more flexible for other use cases - yours being one of them.

Hope this helps,


// Jasper
resnickj
Senior Member
Senior Member
Posts:891

--
2009-04-18 11:41 PM  
As I'm reading through this thread, it occurs to me that the framework seems to be getting in the way of doing something that ought to be relatively simple. I think we should just change the SplitComponentContainer (and similar classes) such that the CommandHistory property delegates up to the parent host. The current implementation which throws a NotSupportedException was not necessarily an intentional design choice - it just seemed like the safest thing to do at the time.


dreich
Basic Member
Basic Member
Posts:13

--
2009-04-20 09:10 AM  

The last suggestion of delgating to the parent host makes sense.  Is that a matter of adding an override to SplitPaneHost.CommandHistory to return _owner.Host.CommandHistory

..and in PagedComponentContainer.PageHost and whatever other intermediate hosts exist for the containers?

Looking over those classes I see a pretty common pattern.  I think a single generic host like:

class ContainedComponentHost : ApplicationComponentHost where T : ApplicationComponentContainer

{

//host overrides all look like this:

Title { return _owner.Host.Title; }

CommandHistory { return _owner.Host.CommandHistory; }

etc.

}

(actually, the maybe this does not even need to be generic - the owner is private)

I'd be willing to submit a patch with all the changes, docs and related unit tests.  I've found that the change does support my use case of embedding the ImageViewerComponent into a split pane.

Dave



resnickj
Senior Member
Senior Member
Posts:891

--
2009-04-20 04:06 PM  
Hi Dave,
Patches are always appreciated. My recommendation would be to simply override the CommandHistory property and have it delegate to the _owner.Host. I don't think it is worth trying to create a generic host class or anything like that at this time - it is easier just to override the CommandHistory property in the following classes:

PagedComponentContainer
SimpleComponentContainer
SplitComponentContainer
TabGroupComponentContainer
ChildComponentHost

I don't think the Title property should be changed at all. I think it makes sense to allow child components to share command history of the parent container, but I'm not sure the same logic applies to the Title of the parent container.

-j


dreich
Basic Member
Basic Member
Posts:13

--
2009-04-22 10:08 AM  

 Hi.  I've got a patch that solves my problems with the CommandHistory and I think also removes some boiler plate code from the various sub hosts in the containers.  I had already cut most of the code before jonathan replied, so there is a base class (non-generic) that handles the delegation.  

I kept the various sub hosts (like SplitPaneHost) in the various containers so that the ctor inferface would not have to change in those containers (i.e. the code still does new SplitPaneHost(this, _pane1);)  The value of keeping those specialized sub hosts may not be very high.  The alternative would have been to do this instead:

new SubHost(this, _pane1.Component);

I did notice and retain the SimpleComponentContainer.ContainedComponentHost's override that does allow setting the host title.  I was not able to test that though.

I modified ChildComponentHost to return the parent's command history, but I don't think that class is being used (at least not in ImageViewer.sln).  I'm not sure it even can work since the real desktop host is not attached to the AppCompContainers until after contruction.

I'd appreciate review feedback on the changes.  My choice of names is probably not ideal.

Thanks

Dave


Attachment: 002_001_ApplicationComponentContainerHosting.patch

dreich
Basic Member
Basic Member
Posts:13

--
2009-04-22 11:26 AM  

Missed one in PresetVoiLutOperationsComponentContainer.  This seemed to have some unneeded code in the ctor.  It was calling SetHost on the hosted component, but the base AppHost is doing the same.

I'm also not sure what the policy on the null checks are.  I think the base implementation should check, so I removed the checks since the SubHost base is doing the owner check.  I did notice that the AppHost base is not checking the component though.

Anyways, here's the patch.


Attachment: MoreAppCompContainerHosting.patch

resnickj
Senior Member
Senior Member
Posts:891

--
2009-04-24 08:19 AM  
Dave,
Thanks for the patches! Will have a look as soon as we can get around to it.


norman
Senior Member
Senior Member
Posts:793

--
2009-04-24 09:00 AM  
Hi Dave,

Thanks for the patch. Would you mind posting it in the Source Code Contributions forum as well, so all our contributions are in one place? Thanks!

N.


resnickj
Senior Member
Senior Member
Posts:891

--
2009-05-05 12:30 PM  


You are not authorized to post a reply.

Active Forums 4.1
Copyright 2010 ClearCanvas Inc.