 So, I am going to introduce to you first thing is what are libraries and what are applications and what are the problems with applications and where envisage framework actually fits in how it works and get on with some examples of how actually envisage works. So, if you look at libraries we have looked at this at some rather crude level by looking at modules you can think of libraries in terms of say a concrete example like sending and receiving email you can libraries are basically designed to perform particular tasks and they are typically made so that a developer can use it. So, it typically defines what is called an API which is an application programmer interface which developers use. So, if you remember yesterday we looked at factorial. So, we had a head of file which said fact dot h which had a definition for the factorial function. So, we have a set of things like that like trades for example you have an API you have a certain way by which you can use a particular library. So, developers use this API these set of functions or classes and put things together in order to build either other libraries or write applications with it. Now this is an extremely good way to make to do code reuse you build libraries and people use your libraries. Good examples would be things like numpy, trades, scipy, various other things matplotlib is a library. On the other hand you have applications which do not require you to write code and they typically designed for the end user. So, if your mail user agent so when you check your email that interface you could think of that as an application. The interface itself is using libraries in order to do its job. So, typically you use end users could be both developers and non-developers use applications and typically these applications are standalone which means each application does its own thing and you cannot sort of mix and match with a library. I can write code which uses numpy which uses scipy which uses the web which uses something to plot and uses your own library whatever but with an application you do not have that kind of flexibility typically you only have the flexibility that the application writer has given you. So, the application writer may say change this configuration file or turn these options on right. So, it is only what the application developer has given you. So, for example, you cannot quite put two applications together and marry them into one big application it is not easy to do and if you do that you will end up with an application that looks like Frankenstein it does not look clean. For example, think of the iPython shell and then you want the sci-t editor and you want a music player and an email client all on the same thing. You cannot quite do it you can have five different applications that do it you cannot put them in one application and then ship that as yours. So, what is the problem? So, as I said libraries let you do this but applications cannot. So, how do you put a Python shell along with an editor and a matplot libendo? Is there a generic way to do it? The second thing is how can once you do do this supposing I do make something where I have put things together how can the user use it in their application? It is not enough if I do it and give you that and that is the end of it, but you take it from there and you build your thing into it. So, the question basically is how do you make a truly extensible application? This is where application frameworks step in. So, basically it is an approach to building extensible applications and the way this works is it uses what is called a framework. Framework is basically like a skeleton. A skeleton by itself cannot even hold itself up it does nothing. It just provides some basic support. Once you add all the muscle, the skin, all of the tendons and all of that then you actually have a structure. So, framework is like that you cannot do anything by itself, but it is integral to the entire body or your application in this example. So, what developers do in an application framework is they provide plugins that work with the framework and an application writer simply combines these plugins in order to create a rich application. So, think of it as the same library kind of model applied to an application. So, you provide reusable components called plugins that are used in order for application writers to put together. So, just to recap developer focuses on making a clean library. So, if I am writing say a plotting utility, I do not worry about who does what or anything. I say this is my API, this is how I plot and I do that and then there is a framework API. So, the framework has a way by which you create a plugin, a way by which you expose this API into the application. So, the guy who is going to write the plugin uses the framework of API, talks to the library underneath and exposes a plugin which can be inserted into your application. So, the plugin writer exposes the library or the objects associated with that library into the framework and the application writer then uses these plugins combines them and generates an application. And now the application writer could be you because it is just Python code, you could then take each of these plugins and say I do not like this plugin I want the other plugin instead, you change that and you have a new application which is different from what the original application writer gave you. So, it is an extensible way by which you can do two things, you can contribute your own code into it, you can change the existing behavior, you can remove plugins and add plugins. So, this is the general central idea of an application framework and this is how it basically looks. So, think of something like you have a matplotlib library, you have tbtk, we will discuss that later and let us say I have a WX Python shell, you have a code editor, something where you can edit code and I want to put this together in an application. So, someone a plugin writer who knows how to use matplotlib and also knows the framework API on the right side basically writes a plugin that connects your library to the application and exposes that in the application in a reusable manner and that is it. So, now the application writer just says hey I am going to use the MPL plugin, the tbtk plugin, the shell plugin and the editor plugin and then you have an application that does all of these four things. So, if now somebody chooses to say I am going to write a mail user client and he creates a plugin for it. Now, I can put this together, if somebody wants to write a music player and it is a plugin, I can just take that plugin, write that two lines or five lines of code and say I want this particular set of plugins, you are done, you end up with that particular plugin. Well, it is not as easy as I make it sound, there are certain issues that you have to be worried about. For example, things like you have to make sure that this plugin does not go and ruin another plugin. So, certain things you have to worry about, but as always you can see that this allows for a framework or for a basis on top of which you can build reusable applications. So, you can see that and there are obvious problems that you can always work around as a programmer, but this is the general idea. So, is that clear? So, once with this, now I am going to get into the details, how actually the envisage application framework uses this notion in order to build reusable applications with specific examples. So, the envisage application framework what I talked about just now is just the notion of application frameworks. There are lots of them. The envisage application framework was basically built by Martin Shilvers at Enthought and it is based on Java's Eclipse. So, it is actually a plugin based application framework and they have a huge developer community and it is a fairly very mature tool. So, basically what Martin did was he took this, took that and tried to expose many of the core concepts there into the Python world using envisage. So, Enthought.envisage is part of the Enthought tool suite and it is basically based on Eclipse. It is still under heavy development. There is envisage, the current version that I am going to speak about in this talk is envisage 2, but the latest bleeding edge version is envisage 3. I am not going to talk about it because it is still under development and certain things won't work and those things are things that I am interested in right now. So, the way it basically works is, there is a core for an application framework and the core basically does three things. It discovers plugins, so it has a mechanism by which you expose saying here is a plugin that you can use. It loads that plugin and then starts and stops the plugin. So, there are four steps involved. We will get into the details of each of these. Each plugin contributes some functionality to the application as we looked at before. So, the matplotlib thing will most probably contribute a nice plot window, a mechanism by which you can interact with the plot window and a mechanism by which you can enter commands and do things. The Python shell will give you a Python shell on the window where you can type things in and give you a way by which you can use it programmatically. So, some plugins that we will talk about here perhaps or you will look at are the GUI plugin. So, envisage is pretty neat that way. Your application doesn't have to be a UI, doesn't need to use a UI because the notion of framework scales beyond UI's. So, you can have a console application that's written with envisage. It doesn't matter. So, it turns out that the user interface itself is a separate plugin. So, you say I want the user interface plugin and then you write the user interface using that plugin. So, again, even the user interface is extensible, obviously. And that plugin is called the Workbench plugin and it uses something else called PyFace, which I'm not going to talk about, which is also part of the N-thought tool suite. And it's basically a thin layer on top of WX Python. Now, right now, all of these tools that we've talked about, both traits, TVTK, which I'm going to talk about and envisage are based on WX Python because it's a cross-platform UI toolkit. But the architecture is such that you could actually change the user interface, the toolkit that's used. And currently, there is serious effort in trying to port the entire stack to the QT toolkit. And if that's done, I believe 90% of the problem is solved and you could actually think of other UI toolkits. But the point is, this thing actually works on multiple platforms. I run it on a Mac, I run it on Linux, he uses it on Windows, so it is cross-platform. There's also a Python shell plugin, which lets you embed a shell in your application, a text editor plugin. I'm mentioning these simply because the examples I'm going to show have this. There's something for preferences. It's not, I wouldn't say it's great, but it's functional. And there's, all of MyAV2 is built as a plugin. MyAV2 is a 3D visualization system, and it's a plugin. So you can actually write an application which, say, does some computation and exposes the traits UI and then actually has the results that's using MyAV's visualization in your application, not as a separate application. And that's the key. So it's not like you write your application, generate your UI, have the user twiddle that, generate data, and then use a separate application to load that data. It's all on the same application. So resources for this are basically, there's a HTTP code nthought.com, and there's a sub-page there on envisage, which actually leads you to the development pages. And it's installable via what are called eggs. This is actually something that's ongoing. I don't know how far along it is, whether you've given any platform is going to work. But I believe in the next month or two, we hope, we'll have a setup where pretty much any common platform used today, you should be able to build or install all of this stuff using what are called eggs. I'm not going to go into details of that. So let's look at the internals of envisage and how it works. So as I said, your application consists of plugins. And the key is, how does the relationship of the application with the plugins? And how does the whole thing work? How does it fit together? So the envisage application is, there is an application object, I'm going to talk about that in a bit. But there's an envisage application, which is given a list of plugins, or plugin definitions. So each plugin has a definition, saying this is what the plugin is, this is what it does, this is what it uses, so on and so forth. We look at that, and the envisage application is given a list of this. These plugin definitions actually declare plugins and their contributions. So each plugin, as I said, contributes something to the application. So each of these does that. The application loads that, and then given each plugin, it figures out the ordering, depending on the dependency between these plugins, and starts them one by one. So each plugin has a start method, which is called, and sets itself up. And then the plugin is running. So then it starts up all of these plugins, and then you have your application running. And then when you shut it down, it closes down all of these plugins. So it's as, that's about it. Now the question is, how do you do communication between these plugins? So all of this is very nice. You have plugins, you have an applications of what, how do you actually connect these? And that's where the hard part is. So what plugins do is, they define what are called extension points. An extension point is something into which somebody else can contribute an extension to. So let's say I'm a UI plugin, and I have a contribution to my menus. So on an application, you have menus. So I want different plugin writers to be able to create different menus. So let's say I want to save to a text file. I want a plugin. I want my plugin to be able to do that. So you can request, or you can contribute an extension, the menu to the user interface plugin, UI plugin, Workbench plugin, saying I want this menu here. So this is a mechanism by which you communicate from one plugin to the other. And this is through what are called extension points. Okay? The other way, which is also extremely important is through what are called application objects. So supposing you have say an application, you have an application which generates some data, or you have some computation, something that's actually doing computing. You have a solver of some sorts, or you have a simulator of some sorts. Now you wrap that simulator, let's say you have a traits-based class, and that simulator has a view, which basically says this is how the user is going to interact with me. And now your whole application hinges around this simulation. Let's say you have a solver, you have something where you're doing flow past some body. So you have the simulation engine, and then you want to start that simulation engine. You want to create that object and be able to use it. So the way you do that is in the application you expose that simulation object into the application by using what's called an application object. So you basically say application object, here is a class, create it, here is the view I want you to see, and then use the view to do something else. Let's say you want to see a UI on the screen. We'll go over an example. But now the idea is this application object will now be given an ID, which is basically a string. Think of it as a URL. In EnvisageSpeak, they're called UILs, Universal Object Locators. And you can actually create an object and give it a Universal Object Locator. And you have access to this object. So you have a simulation object. So let's say you have a visualization thing and you have a simulation thing. So this has a URL, the other has a URL. And now once you've said both plugins are there, this guy can call that URL, get the object and script and use that object just like normal Python. So the way by which you do this is to use application object. So you expose objects from your plugin into the application using application objects. So let's go into a little more detail. I'll summarize this in the end again. So it takes a little getting used to. But I'll go into the details. So the application is basically an instance of nthought.envisage.api.application. So it's basically a class which has traits. All of this stuff uses traits. It's pervasive right through envisage. It's built on the notion of traits, on the notion of the fact that you can do all of this with traits. And when I said you're given a list of plugin definitions, it's basically a list of file names. So you say here's a file name which specifies this plugin definition, which actually declaratively writes out saying this is this plugin, it does this. We'll look at an example soon. Now the plugin definition itself imports what's called a plugin definition instance class and instantiate it and gives it all the details. And this basically specifies the plugin. I'll show you an example. Just keep this in mind that the application is there. It's given a list of file names which are plugin definitions. Plugin definition files contain plugin definitions. There's a problem with object-oriented programming. You have a plugin definition class and then you have an instance called plugin definition. So just play with me on that. So you basically have plugin definition files which declare plugins using the notion, using an instance of a plugin definition. Then the application gets all of these and starts it as I looked at before. The application manages two things. It manages an extension registry and a service registry. As I said, plugins contribute to each other using what are called extension points, right? Each plugin has an extension point. Think of it as a list of items. Somebody else says here is an extension item and sticks it in into this. So someone has to maintain this registry. Who's given me whose contributions? So the application maintains a global registry of extensions and everybody kind of contributes that. It's automatically done, but the application maintains that. The application also manages what's called a service registry. So let's say your simulation. I gave you an example of a simulation. The simulation object provides a service. And the service that it provides is to do the simulation. So you have a service registry where all of these objects are kind of contained. And you can get from the service registry various objects. So basically again, we'll go over it again. The application basically is given a bunch of plugin definitions. A plugin definition defines a plugin. The application figures out all of the plugins you have given it, starts them. And it maintains two things. One is an extension registry and a service registry. So what are plugin definitions? So plugin definitions basically, as I said, instantiate a plugin definition as you see here. And this file is actually defined here. So you may wanna take a look at it if you're poking around in the sources. Plugin definitions basically declaratively define a plugin which means they say this is the name of the plugin. It defines the particular plugin. It defines what are called extension points of this plugin. And it also defines all of the contributions that this plugin makes to other plugins. So again, have a plugin definition which basically defines a plugin. A plugin, the plugin definition describes what the plugin is. So is there a class that's gonna be instantiated that's gonna be running as a plugin? What are the extension points of that particular plugin? So if I'm a UI plugin, I'll have something which lets people contribute menu items to me. So that's basically an extension point. And I may want to contribute an application object to somebody, to the application. I may want to contribute a menu. I may even want to contribute a menu to myself. If I'm a plugin, that's a nice way to do things. So you have extensions that are contributed by this plugin to other plugins. So this is three parts. Plugin itself, extensions, extension points, and extensions you contribute. So more details on plugins. You don't always have to create a plugin. The examples I'm gonna show you today don't explicitly create a plugin, but you do create a plugin definition because you want to contribute. The mechanism by which your piece, your plugin contributes to other plugins is through the plugin definition file. But that doesn't mean you have a plugin that's listening for other people's extensions. So it's not a necessary thing that you actually create a plugin, but it is necessary that you create a plugin definition if you want to make contributions to other people's plugins. And plugin contributions are basically built in the following manner. Extension points. So extension points, as I said before, are places where other plugins contribute functionality. And application objects are well-known shared objects like that simulator or a visualization object that need to be published so that other people can use it in their plugins. Extension points themselves are simple classes. So it's again straightforward. So for example, if I want to count, there's a message of the day application which Martin Shelvers shows off. There's a base class called extension point. And all the messages saying is, there's an author who has a string and there's a text that the author has contributed which is another string. So this is an extension point. An extension which is contributed by somebody, a message, would be an instance of this class, okay? And all of these extensions are stored in the application extension register. Application objects themselves can be published. So we've done extensions now. So we basically have application, plugin definitions, declare plugin, contributions, extension points of a particular plugin, extensions made to other plugins. That's done. The other point is, I have that simulation object and I want to expose the simulation object into the application. How do I do that? So the way you do it is, if you have the application, you can publish an application object using this API or you can do it declaratively and we'll look at that in a short while. So in the plugin definition file itself, you can just create an application object and send it along. We'll look at that. But the key is, now we have application objects, right? So we have the simulation object, we may have a MayaV visualization plugin, an object. And these two application objects need to talk to each other. So let's say now I have a simulation, I'm going to write my code which is going to integrate the simulation along with the visualization, let's say. So I need to get access to the simulator and I need to get access to the visualizer. So the way you need it, now you need some way by which I can access it. So just like you remember URLs, in envisage you define what's called universal object locators, UOLs for short. These are used like URLs to basically specify objects. So you can support various types of protocols. These are all strings. So you can say name, which means it'll be going and asking in the naming service. Or if it's a service, you can give it something like this. So n-thot.myavv.script service. So you get a service, you say n-thot.myavv.script service. It'll give you that object back when you query the service registry. So you have an envisage application because of the lots of concepts here, I'm gonna just repeat it so you get some idea of how it works. A plugin basically contributes something to the application as you can see here. So you have the envisage application. The application manages an extension registry and a service registry. Plugins contribute extensions and these guys internally handle extension points. So a plugin will ask the application saying, is there any contribution from anybody to an extension point? Fetch all of those, generate the menus and things like that. So there's two way communication between the plugin and extension registry. The plugin will contribute extensions to other guys and fetch extensions from the extension registry in order to do its own work. The plugin will also contribute application objects like the simulator or the visualization. And now you have an application. And now you can go in and add your plugin which actually says which sits somewhere in between these two and makes two of the application objects talk to each other. So it's Python. So you can do pretty much anything you want there. So this is the model. So there are a few plugins that I'm gonna just run by. You have a core plugin which is the core plugin in all envisage applications. This plugin has the following extension points. So if you talk about a plugin at any point of time, you need to just describe two things about it. What are its extension points to which you can contribute? And what are the application objects the plugin contributes? So the extension points the core plugin has is something called a type manager which I'm not covering here because I have very little experience with the type manager. I've used most of the other things though. There's something called a runnable. Sometimes once all the plugins have started you want to do something. So when plugins are starting it's not a good idea to be doing something with the plugins. You wanna wait till everything has started now I want to do something. So runnables are extension points to which plugins can contribute runnables. So you create a class which has a run method and say here is a runnable. Please call this run method when all the plugins have started. And we said we'll do that for you. So this is the mechanism by which you do that and that's a runnable. It also has two application objects that it provides. One is a type manager. The other is the naming service. Naming service is like what are called white pages. Think of a telephone directory. White pages you actually know people by name. You don't know them by service that they offer. Service registry is like a yellow page where it's based on what service it provides. But right now that notion is not very sort of firmly defined and typically I tend to use the service registry because I'm typically exposing application objects. The other plugin which is very important for building UIs is the Workbench plugin. So the Workbench plugin actually provides the user interface. The entire user interface of applications using envisage are built using the Workbench. The extension points it contributes are typical user interface things like branding. So branding is, so what is an extension point? It's a class. It's a class that has traits. The message has an author which is a string. Text which is a string. So you can make it slightly more, you can make it far more complicated because it's just another class. And people instantiate these classes in order to expose, to create extensions to extension points. So branding is something like an about dialogue. So you click on about, it'll show you the message. Obviously if you're writing your application you want to create a branding thing. So you want to say it's my application not somebody else's application. A view where you want to add your views to the user interface. And views are nothing but traits views. Easiest way to expose a view is to make it a traits object. Have the traits view. Eric showed you examples of configured traits, edit traits which actually pop up a user interface. Each of these has a name. You can actually create a bunch of views. And you can say I want this view to be exposed on this application. You can do that. It also has something called perspective which basically represent a user configurable set of views. So you can say I want the editor here. I want the Python shell here. I want this editor over there. And it'll basically save out all that and let you specify that declaratively. So that's called a perspective. So it's another extension point that you can contribute to. The workbench itself, which basically manages the views and the perspectives. That's the workbench. The workbench manages views and the perspectives. And then of course actions. So you click on a toolbar item. You want something to be done. So there's an action contribution that you can make. Same way, menu bars. They invoke actions. So you say I want a menu here in file, this, which actually invokes this particular action. And you say that out. You just lay that out and it's done. It creates two application objects called the workbench and the workbench UI. So I'm not gonna go into the details of those. You seldom need them actually. Sometimes you do, but most often you don't. Here's an application which is Maya v2. And this is how it looks typically. So there are a bunch of menus standing out here. Each of these is an action. So when you invoke a menu, it's gonna actually call something, do some functionality. Open a file. It's a menu and that will have some detail about which function to call. It'll call the function for you and do the job. The whole thing put together is called a workbench window. What you see here is an editor. In this particular case, it's a scene editor. It lets you view a particular visualization. On the left, you have various views. The view on the right bottom here is a Python shell. And so the application here, for example, actually consists of a whole bunch of plugins. This guy, the editor you see here, the way in which it's constructed is such that the editor is one plugin. The left hand side, the two views you see here, these are contributed by the MyAvi plugin itself. The Python shell is contributed by a Python shell plugin. So I didn't write any code for the Python shell plugin. I just focused on writing MyAvi. I simply say one line in my code, we look at that next, which says, give me the Python shell and it sticks it in. There's also an editor plugin here, which you've not looked at, which is not showing up here. And there's also a logger plugin. If you look here closely, you'll see logger there, which is also another plugin. So basically I've created an application which constitutes several plugins. So now let's look at a concrete example. We've done all the theory. We'll look at a concrete example. So we look at a very trivial case. You wanna create an application that has a text editor and has a Python shell embedded in that. Now in this case, I'm not gonna create any contributions. I'm not gonna extend anything. I just want something which will say, give me this text editor plugin, give me the shell plugin. So I don't really have to create a plugin definition file. Okay, so we create, the way you do this, write this application is you create two files. You could have created one, but it's easier to do it in two so that you separate out the logic, okay? You create one file, which is nothing but a list of plugin definitions, plugins that you want. And that's called plugin underscore definition dot py. And it defines all the list of plugins that you want to load. And then you have run dot py. You could have called it your application dot py. I've just called it run dot py because that's how the standard examples go. And then you run this application as Python run dot py. Okay, so if you made it an executable, it just would run run dot py. And now you have a cross-platform application. So how does this plugin definition file look? So this is a complete plugin definition file. I'll show you how this runs. So the first line here is just gonna do join. So just for now ignore it. Join basically lets you join strings. Or if in this case, this particular join, when it's given a module, we'll figure out the location of that module and add the string to it, okay? So it's just a simple function. This list, so okay. If you notice the core, there's a capital plugin definitions. There's no sign convention here. You could have made it small plugin definitions. But it's conventionally, we just use a capital plugin definitions. And it's nothing but a list of strings. There's no magic involved here. I'm saying import the text editor as text editor simply in order to get their paths. I wanna know where this text editor is sitting. In message two, the way in which you find where a plugin is, is using Python's modules. So you have installed the text editor plugin. So you simply can import it. Once you import it, you can actually find where it is. Python lets you do that using what's called underscore underscore file underscore underscore. So given a module, maybe I'll show that. Say I'm gonna say import, what should I import? Import site. If you look here, there's a file. That's the file from which this module is loaded. So site dot underscore underscore file is the location of that file from which this Python module was loaded. So basically, I just do this in order to get where that module, where this contour, where this plugin is sitting. And say, please use the plugin definition that's available there. So these first three are core plugins. I want the user interface. So I want the core plugin to start. If you remember the core plugin, basically creates, what does it create? Oops, creates extension points for type manager, runnable and creates the naming service. Well, it's good to have the core plugin running. I want to create the text editor and the Python shell are gonna create menus. So you need actions. So you start up the action and the resource plugin may not be necessary. The UI itself is got by the workbench plugin. Remember we had the workbench plugin to which you're gonna contribute menus and things. And the workbench action for the menu actions. Then comes your critical code, which is the Python shell and the text editor. That's it. The next file is the run.py, which basically imports the application and imports the plugin definitions from your plugin definitions file. Is that clear? The previous one, we just created this plugin definitions and this guy is just gonna import that. And now I create a simple main function which starts up the UI. Now, as I said, pyface is something which abstracts out starting up the GUI. When you create user interfaces, you have what's called a main loop of execution for that, it processes event loops in that user interface. So just ignore this for now. Think of it as some kind of boilerplate. Just creating a user interface, making it, setting things up so that the user interface can be created. Now I'm just instantiating the application and as we said before, the application is past a list of plugin definitions. Here is that list, okay? And then I say application.start, GUI start event loop. And when I say start event loop, the user interface is gonna block. Because it's gonna start doing interacting with the user. So when you click application exit, the user interface shuts down and this function called returns. And then it says shut down the application. Okay, so this is typically the structure of any UI application that you're ever going to write. So there's not very different from that. So now how does this run? So here is the application with a Python shell. Oh my gosh. And I have a file menu where I can say open file. There's some bugs on the Mac and I can say open this file. That's a text editor. And this is my Python shell where I just did an import. You can see that. So I have file, open file. I can shut off the Python view if I don't want it. I can turn it on. I can move things around. So I can say, I can move it there. And now if I close the application, and run it again, it'll persist that state because the workbench remembers what editors have been opened and what the state of various things are. So that's it. So we've created an application which has an editor and a Python shell. And actually we've not exposed the niceties of those editors and so on. If I wanted to execute Python code on that editor, the editor and the interpreter are hooked so that if I press control R on the editor, it'll execute that code on the interpreter. So it's very neat. We'll do this sometime later. And that's the view you just saw. So I didn't, I didn't lie about that. And that's it. So you've just created a simple plugin definitions file, an application basically instantiating the application, passing the plugin definitions that you have created and it actually did all the work for you. Now let's look at something a little more complicated. So this is very trivial, not done anything. So now we look at actually adding a plugin definition file. We want to now create some object that I want to contribute into this application. So let's say we want to expose an object that we have created. So consider some dummy class that I have created. You can actually create some pretty sophisticated stuff which is subclass from hash trades. So this is a simple object. I import stuff from trades. The trade has trades class, a range and a string. And some things related to the view of a trades object. But basically if you look at this, it's very declarative. All you're saying is a name defaults to Tycoon. There's a range of his age, zero to 150. And his particular age is 40, the default. His address is something. And this guy over here just exposes this view to the user interface. So if you just create this simple object. So maybe I'll do that. So if I say import, okay, import simple object. That's the code. Just to show you it's not doing anything special. Simple object. There it is. Nothing more. So now if I mouse hover over this, it shows me a tooltip which says specify person's name. And that is also specified over here. The D-E-S-C is what it shows there. This is the age and I can change that. Now what I wanna do is this is kind of trivial. But I'm showing you the idea. Now you can actually build very sophisticated as you saw with Eric's demos. You can actually put a Chaco plot. We look at that. So you can make a complicated looking object. This object can in turn contain bunch of objects internally and have views for all of those. In fact, that's how my view is structured. Now I need to expose this application object into my application. So how do I do that? So now you have to create a plugin definition. In the previous case, we did not have to create our plugin definition. We just created plugin definitions that we use. We did not have to create our own. So here we create a simple plugin definition.py. We import this thing called plugin definition. It's a class. Then the core plugin definition defines the extension point called application object. So the notion of application object is given by the core plugin. That's very important to remember. So the core plugin is listening for everyone's contribution of an application object. It gets all of these, creates them. The way it does that is you create an application object which is an instance of core plugin definitions application object which simply says the class name I want you to instantiate is simple object dot simple object. What I did on the IPython shell, I'm asking, I'm writing that declaratively. The key now is this object I want to be published in the framework using a URL, UOL. The URL is service simple object dot simple object. The name is up to you, but you have to make sure that the name is unique. So if you call everything A, you're finished. You'll have to, you'll be overwriting that particular object. So you create unique names, just like in internet addresses. And now this object has a view, the tycoon, his age and his address. You want to expose that as well. So the way you do that is you look at the workbench plugin definitions extension points. And there's an extension point as we looked at earlier called view. The view basically says, this is the name of that view. This is its ID. So you can refer to it later and it can be persisted. That's the mechanism by which when you move that window around that view, it actually persists it. The URL of this particular object, why is this important? Because when you specify a view, it has no way of knowing which objects view you want. When you say this is the URL to use, it looks at the service registry and then grabs the object. Is that clear? All I've done here is I say create an application object and look at the view of this. The view is declared here. Use this view, place this UI on the left. That's the default placement. Now you change it, it's going to persist it as we saw. But the default placement when you start up the first time will be on the left. The only thing you need to do is to create a workbench which keeps all these views. Remember the workbench manages two things, views and perspectives. We've not created a perspective here, but we've created a view. Finally, you specify the plugin definition itself. So as I said, the plugin definition is what actually contributes all of these extension points, these extensions and the actual plugin to start to the application. The plugin definition goes like this. It has an ID, it has a name, a version, provider, URL. So this is just information. So if somebody wants a plugin and I want to say, where is this plugin? Who's the developer who made this lousy plugin or whatever, it's there. I'm not going to talk about these two. This is the requires. So this plugin requires the workbench plugin. In envisage three, I think this is completely eliminated because envisage three uses eggs entirely, but forget about that. In envisage two and all the examples today, you need to specify requires. What this basically says is, as we remember, the application starts plugins. Now each plugin has to be started in the right order. Because if I need another plugin and I'm contributing something to it, if that has not yet been started, I'm in trouble. So when you say requires, envisage looks at the requirements and make sure it starts everything in the right sequence. So all that's doing is this. Our plugin does not create any new extension points to which somebody else can contribute. But we are contributing two things. What are they? The workbench and the application object, which is that view. So I'll recap this. This plugin definition basically declaratively says, I want envisage to start up this application object that's defined here to take its view and stick it on the workbench. That's basically what you're saying here. Two more changes you need, because I'm building this on the old application. The ID of your new application you want to change because it's not the same as the old application. So I just changed that to simple app one from the original simple app. And in my plugin definitions.py, I change one line. I add this last line over here, which is the plugin definition that I created. Is that clear? So what have we done? We had a simple application that creates a text editor and a Python shell and shows that as an application. Now we've added, we want to add a view. So we first create an object that has this view and obviously in a real situation that object is going to do what you want to do. So it's something that's useful and meaningful, not like the one we have here. This object has a view that you want to stick into envisage. If it did not have a view, you would not need this workbench stuff. You would simply say extensions equals simple app object. Which is simply this application object. And all envisage will do is create that instance and stick it in the service registry. But now you don't want that. You want the view of that particular object. So the view basically lets you easily do that by saying get the service, show me its view on the left. And you're contributing that through the plugin definition. And now you simply say, I want the application to use my own plugin definition in addition to the other plugin definitions. That's it. So there are other applications and plugins that you can study. There's a Chaco plugin example. So would you like to see that? So there are three examples. One is Chaco plugin, which is nothing but the same simple object you saw here. Very similar to that. Instead of having a silly Tycoon address person kind of thing, it actually exposes a Chaco plot. Which you can actually configure and play with. The second one is a Maya V Explorer 3D example. I've not talked about Maya V at all, but Maya V is another plugin. And it has two modes. There's an application called Maya V2. And there's a plugin which it actually uses underneath. The plugin basically lets you do the stuff I'm going to show you now. The actual details of that plugin and the application we look at subsequently. Just to show you. Okay, so the Chaco application looks like this. Okay, is that visible? So we have on the top the same kind of stuff, plugin definition, envisage core, core plugin, application object. So what am I going to do here? I'm going to create an application, which I will show you here. It's bad code, but not had the chance to fix that. I just have a simple application called MyApp, which has X and Y as data. It has an equation which is going to plot. And there's a plot type view, which basically says if I want line plot or I want to scatter plot. And I have a view, which is basically a Chaco plot item, which in turn is imported from Chaco 2 and thought that Chaco 2. And I'm just saying here are the details. Show the labels, make it a horizontal orientation. This is the X label. This is the background color, foreground, so on and so forth. So that specifies the view. And all I do here is whenever the equation changes, so I initialize the data, X data to lin space minus 2 pi to plus 2 pi with 1000 points. Straight forward numpy code. And if you notice, this is just something that has traits, EQ is a string. So whenever EQ changes, what do I do? I simply say Y data, evaluate that string if possible. If you make a mistake, eval is a Python built in, which evaluates any code you give it. So now what I'm going to do is, I want to expose this application to envisage. So all I've done is very similar, create an application object, which specifies my application. This application that I just showed you. And show me the view of that, which I also declared there. The rest is the same old thing. I create a workbench and my app object. That's it. So I'll show you also the, these are the plugin definitions. Very similar, right? The core plugin, all of this stuff we've seen before. The only thing I've added is my application. Plugin definition. So now I say Python run.py, I hope it runs. There you go. Oops. Okay. So what you see here is, is that clear? So I just, the default plot is sine five X by X. So does anybody have suggestions? Something else to plot? So I'm going to plot sine five X into exp minus X. It's dynamic. And that's it. I didn't do anything about it. And I still have my Python shell here. So I can say, I could import something, do something else. If I want to change the scatter, it changes it in zoom. So there you have it. We have an application that's non-trivial. And that wasn't too hard to create. And if you remember, notice, I don't have an open file here, but if I go on an open file, let's see, where it is. So I have code here. If I wanted to run this, I could run it. So there's slightly more involved example. But again, the idea is the same. This is called a 3D explorer, function explorer. It uses the MyAvi plugin. So if you look at the plugin, oops. Okay. So there's no sanctity about choosing plugin definitions.py. In this case, I say it's explorer plugin definitions.py. Oh man. So again, it's very similar. Views. In this case, I've created a perspective. Okay. And that's about it. It's the same kind of structure that you see over here. Now this is just another class that has traits, bunch of stuff. And internally, it's gonna do something and call MyAvi here. Okay. So I'm not gonna get into the details of this. The example ships with MyAvi sources. You can play around with that. But what I will show you is the application. This is the same old thing. I've just merged the plugin definitions file into the run.py. What I've done is same old thing here. But I've just put this here. Instead of putting it in a separate file, I put it in the same file. Just to show you it's not, there's no sanctity about creating a separate plugin definitions.py. And this is no longer necessary. This code is old. But anyway, you get the idea. The same old application. Nothing much changes. This might take a bit longer to start off. So what this does is, just like Chaco did a 2D plot, this is gonna do a 3D plot. So it takes a block of, 3D block of array. I picked the wrong one. Same thing, very slight changes inside. Because there was a change in the way Enthod packaged its tools. This I hope runs. If you notice it's the same thing. Okay, fine. It did run. But if you go back to this, look at the directory structure it's the same. Explorer 3D, Explorer 3D app, and the plugin definition. So this is the UI I have. This is Maya V in action. It's a full 3D view. I can interact with the data. And then I can do stuff here. So I can say x plus x star z. I hit enter and it, this isn't very pretty looking. So here I've configured it, so if you hit return, it updates. It doesn't do it all the time. Okay, one second. Yes, I did something different. And it's actually updated an entire 3D numeric array block. And the beauty of this is, because of the way TBTK is built, is just using the NumPy stuff. So I just evaluate this, generate a block, pass it off to Maya V and it visualizes it. Of course, there's lots of fancy stuff you can do with Maya V. You can get a full screen view and whatnot. And you can change the size of your block. And the Maya V plugin is running. So which means I can go in here and configure an object which is built by Maya V. And there are a huge number of those objects. For example, if I want to visualize something, I can say I want to now slice through this data. I want to do a contour, let's say. Okay, so here you have a contour. You see that? So I'm going to configure it over here. It's a large data set. If you notice, as I change the slider, it changes the contours. So I have all of the functionality of Maya V in this little application that I built, which exposes a view where I can actually create some non-trivial thing. The other nice thing which I'd like to demo here is I have not turned on the Python view. Turn it on, I get my Python shell. And the Python shell is a really smart shell. So now I want to here, I want to script this. Let's say, oops, I can click this guy, drag it and drop it on my shell. I can say I is, so let's say I want to say what is the current contour level? Some obscene number, okay. So I make it half. So you can set multiple contours. Notice that I got an error message. So I have actually scripted a live object. I got an error message which says the contours straight of a contour instance must be a list of items. So the contours object actually lets me put multiple contours. So I just gave it a single value. So I put this in a list. It's changed it. And it's changed here too. Now this is the beauty of trades because I have a live object on which I'm just changing a value. My plot updates, my UI updates. And anything that's listening for contour change is gonna be updated. So I can build neat applications where I focus on saying, here is the thing I wanna do. Here are the events I'm gonna listen for. And that's it. I program just for those events and hook everything else up and everything just works. So here is an example of something completely non-trivial. We started with a simple application with a Python shell, a text editor. And then we built layer on top of that, your own view. Then I showed you how you can do a view of a 2D plot. And now we showed you how you can actually take a non-trivial plugin like MyAvi2, interface that, expose a view where you're doing something with the data, piping it through to another plugin where it's sitting and exposing an application object and doing a visualization. Now you can imagine somebody else says, hey, I wanna do both. I wanna do both, the Chaco plot and this. You can do it, no problem. So you create this, you create that, put this, put it together, put in a plugin definitions file, expose whatever application objects you want, have them communicate. And now you have UOLs. So if you wanna click on a button and say take this slice plotted with Chaco, you set up something so that you, when you click that, it calls the UOL, gets the application, asks it for its service, which is the MyAvi or whatever, gets the data, plots it, you set it. So here is a very powerful way by which you can build very neat applications. So in summary, what we looked at, we started with basics of applications, libraries, what the difference is, what is the problem. We looked at how application frameworks solved this problem. And then we looked at envisage, a framework, which lets us do this. And then I went over envisage details. It's kind of boring, but important. Then we looked at a simple example and went on to build a pretty non-trivial, but pretty looking example.