不得不承认World Wind的代码真的很庞大,没有太多帮助文档的前提下,一头钻进代码里肯定令你头疼的,甚至研究代码间关联仿佛是在走迷宫。我最近一直想弄明白如何在MenuBar中加载那些插件的,WorldWind学习系列四中研究的只是特殊的三个功能加载的,那三个没有继承Plugin类,不算是插件功能加载。所以WorldWind学习系列四加载的三个是特殊情况,不是一般的插件加载。今天下午终于柳暗花明,如果你真正关注World Wind分析,那么就好好看看下面的插件加载过程全解析。
我们先看看Plugin类的继承图,看看到底都有些什么插件,然后在分析一般性的插件加载全过程。
{
try
{
Settings = (WorldWindSettings) SettingsBase.Load(Settings, SettingsBase.LocationType.User);
if(!File.Exists(Settings.FileName))
{
Settings.PluginsLoadedOnStartup.Add("ShapeFileInfoTool");
//Settings.PluginsLoadedOnStartup.Add("OverviewFormLoader");
//Settings.PluginsLoadedOnStartup.Add("Atmosphere");
Settings.PluginsLoadedOnStartup.Add("SkyGradient");
Settings.PluginsLoadedOnStartup.Add("BmngLoader");
//Settings.PluginsLoadedOnStartup.Add("Compass");
//Settings.PluginsLoadedOnStartup.Add("ExternalLayerManagerLoader");
Settings.PluginsLoadedOnStartup.Add("MeasureTool");
//Settings.PluginsLoadedOnStartup.Add("MovieRecorder");
Settings.PluginsLoadedOnStartup.Add("NRLWeatherLoader");
Settings.PluginsLoadedOnStartup.Add("ShapeFileLoader");
Settings.PluginsLoadedOnStartup.Add("Stars3D");
Settings.PluginsLoadedOnStartup.Add("GlobalClouds");
Settings.PluginsLoadedOnStartup.Add("PlaceFinderLoader");
Settings.PluginsLoadedOnStartup.Add("LightController");
Settings.PluginsLoadedOnStartup.Add("Earthquake_2.0.2.1");
Settings.PluginsLoadedOnStartup.Add("Historical_Earthquake_2.0.2.2");
Settings.PluginsLoadedOnStartup.Add("KMLImporter");
//Settings.PluginsLoadedOnStartup.Add("doublezoom");
//Settings.PluginsLoadedOnStartup.Add("PlanetaryRings");
Settings.PluginsLoadedOnStartup.Add("TimeController");
//Settings.PluginsLoadedOnStartup.Add("WavingFlags");
Settings.PluginsLoadedOnStartup.Add("ScaleBarLegend");
Settings.PluginsLoadedOnStartup.Add("Compass3D");
Settings.PluginsLoadedOnStartup.Add("AnaglyphStereo");
Settings.PluginsLoadedOnStartup.Add("GlobeIcon");
}
// decrypt encoded user credentials
DataProtector dp = new DataProtector(DataProtector.Store.USE_USER_STORE);
if(Settings.ProxyUsername.Length > 0) Settings.ProxyUsername = dp.TransparentDecrypt(Settings.ProxyUsername);
if(Settings.ProxyPassword.Length > 0) Settings.ProxyPassword = dp.TransparentDecrypt(Settings.ProxyPassword);
}
catch(Exception caught)
{
Log.Write(caught);
}
}
2.Main()中后面调用MainApplication()方法,该MainApplication()调用OpenStartupWorld(),用来初始化启动World对象。OpenStartupWorld()方法首先确定加载的是Earth/Moon等,然后开始加载一个星球。
/// Loads a new planet
/// </summary>
private void OpenWorld(string worldXmlFile)
{
if(this.worldWindow.CurrentWorld != null)
{
try
{
this.worldWindow.ResetToolbar();
}
catch
{}
try
{
foreach(PluginInfo p in this.compiler.Plugins)
{
try
{
if(p.Plugin.IsLoaded)
p.Plugin.Unload();
}
catch
{}
}
}
catch
{}
try
{
this.worldWindow.CurrentWorld.Dispose();
}
catch
{}
}
if(this.gotoDialog != null)
{
this.gotoDialog.Dispose();
this.gotoDialog = null;
}
if(this.rapidFireModisManager != null)
{
this.rapidFireModisManager.Dispose();
this.rapidFireModisManager = null;
}
if(this.animatedEarthMananger != null)
{
this.animatedEarthMananger.Dispose();
this.animatedEarthMananger = null;
}
if(this.wmsBrowser != null)
{
this.wmsBrowser.Dispose();
this.wmsBrowser = null;
}
worldWindow.CurrentWorld = WorldWind.ConfigurationLoader.Load(worldXmlFile, worldWindow.Cache);
this.splashScreen.SetText("Initializing menus...");
// InitializePluginCompiler()是我们要关注的初始化插件编辑器PluginCompiler(真正管理加载插件的类)
InitializePluginCompiler();
foreach(RenderableObject worldRootObject in this.worldWindow.CurrentWorld.RenderableObjects.ChildObjects)
{
this.AddLayerMenuButtons(this.worldWindow, worldRootObject);
}
//加载Earth专有的MenuButton( AnimatedEarthManager和RapidFireModisManager)的配置,
this.AddInternalPluginMenuButtons();
this.menuItemModisHotSpots.Enabled = worldWindow.CurrentWorld.IsEarth;
this.menuItemAnimatedEarth.Enabled = worldWindow.CurrentWorld.IsEarth;
}
3.我们看看InitializePluginCompiler()如何加载插件的?
/// Compile and run plug-in "scripts"
/// </summary>
private void InitializePluginCompiler()
{
Log.Write(Log.Levels.Debug, "CONF", "initializing plugin compiler...");
this.splashScreen.SetText("Initializing plugins...");
string pluginRoot = Path.Combine(DirectoryPath, "Plugins");
compiler = new PluginCompiler(this, pluginRoot);
//#if DEBUG
// Search for plugins in worldwind.exe (plugin development/debugging aid)
compiler.FindPlugins(Assembly.GetExecutingAssembly());
//#endif
//从启动文件夹下Plugins文件夹,加载插件
compiler.FindPlugins();
compiler.LoadStartupPlugins();
}
4.加载启动插件函数的代码
/// </summary>
public void LoadStartupPlugins()
{
foreach(PluginInfo pi in m_plugins)
{
if(pi.IsLoadedAtStartup)
{
try
{
// Compile
Log.Write(Log.Levels.Debug, LogCategory, "loading "+pi.Name+" ...");
worldWind.SplashScreen.SetText("Initializing plugin " + pi.Name);
Load(pi);
}
catch(Exception caught)
{
// Plugin failed to load
string message = "Plugin " + pi.Name + " failed: " + caught.Message;
Log.Write(Log.Levels.Error, LogCategory, message);
Log.Write(caught);
// Disable automatic load of this plugin on startup
pi.IsLoadedAtStartup = false;
worldWind.SplashScreen.SetError(message);
}
}
}
}
5.现在看两个插件类重载的Load()方法的实例。例如:Compass3D.cs和TimeController.cs。
Compass3D.cs的Load()方法
{
m_menuItem = new System.Windows.Forms.MenuItem("Compass");
m_menuItem.Click += new EventHandler(m_menuItem_Click);
m_menuItem.Checked = World.Settings.ShowCompass;
ParentApplication.ToolsMenu.MenuItems.Add(m_menuItem);
m_form = new FormWidget("Compass");
m_form.ClientSize = new System.Drawing.Size(200, 200);
m_form.Location = new System.Drawing.Point(0, 400);
m_form.BackgroundColor = World.Settings.WidgetBackgroundColor;
m_form.AutoHideHeader = true;
m_form.VerticalScrollbarEnabled = false;
m_form.HorizontalScrollbarEnabled = false;
m_form.BorderEnabled = false;
//注册两个窗体事件
m_form.OnResizeEvent += new FormWidget.ResizeHandler(m_form_OnResizeEvent);
m_form.OnVisibleChanged += new VisibleChangedHandler(m_form_OnVisibleChanged);
//实例化Compass3DWidget
m_compass = new Compass3DWidget();
m_compass.Location = new System.Drawing.Point(5, 0);
m_compass.Font = new System.Drawing.Font("Ariel", 10.0f, System.Drawing.FontStyle.Bold);
m_compass.ParentWidget = m_form;
m_form_OnResizeEvent(m_form, m_form.WidgetSize);
m_form.ChildWidgets.Add(m_compass);
m_form.Visible = World.Settings.ShowCompass;
//将要绘制的Widget加载到DrawArgs.NewRootWidget.ChildWidgets,为了在WorldWindow.cs的Render()方法里渲染
DrawArgs.NewRootWidget.ChildWidgets.Add(m_form);
//Compass 3D工具菜单按钮
m_toolbarItem = new WorldWind.NewWidgets.WidgetMenuButton(
"Compass 3D",
basePath + "\\Data\\Icons\\Interface\\compass2.png",
m_form);
//向MenuBar中添加Compass 3D菜单按钮
ParentApplication.WorldWindow.MenuBar.AddToolsMenuButton(m_toolbarItem);
base.Load();
}
TimeController.cs的Load方法,如下:
public override void Load()
{
try
{
m_window = new WorldWind.NewWidgets.FormWidget("Time Control");
m_window.Name = "Time Control";
m_window.ClientSize = new System.Drawing.Size(300, 183);
// Bug in FormWidget required anchor to be set before Location and parent widget
m_window.Anchor = WorldWind.NewWidgets.WidgetEnums.AnchorStyles.Left | WorldWind.NewWidgets.WidgetEnums.AnchorStyles.Bottom;
m_window.ParentWidget = DrawArgs.NewRootWidget;
m_window.Location = new System.Drawing.Point(0, DrawArgs.NewRootWidget.ClientSize.Height - 183);
m_window.Text = "Double Click to Re-Open";
m_window.BorderEnabled = false;
m_window.AutoHideHeader = true;
m_window.BackgroundColor = System.Drawing.Color.FromArgb(0, 0, 0, 0);
m_window.HeaderEnabled = true;
m_window.Visible = false;
time = new WorldWind.NewWidgets.PictureBox();
time.Name = "Time";
time.ImageUri = basePath + "\\Data\\Icons\\Time\\time off.png";
time.ClientLocation = new System.Drawing.Point(12, 59);
time.ClientSize = new System.Drawing.Size(42, 42);
time.Visible = true;
time.ParentWidget = m_window;
time.OnMouseEnterEvent += new EventHandler(time_OnMouseEnterEvent);
time.OnMouseLeaveEvent += new EventHandler(time_OnMouseLeaveEvent);
time.OnMouseUpEvent += new MouseEventHandler(time_OnMouseUpEvent);
time.CountHeight = false;
time.CountWidth = true;
m_window.ChildWidgets.Add(time);
play = new WorldWind.NewWidgets.PictureBox();
play.Name = "Play";
if (TimeKeeper.Enabled)
{
play.ImageUri = basePath + "\\Data\\Icons\\Time\\play on.png";
}
else
{
play.ImageUri = basePath + "\\Data\\Icons\\Time\\play off.png";
}
play.ClientLocation = new System.Drawing.Point(50, 1);
play.ClientSize = new System.Drawing.Size(82, 82);
play.Visible = true;
play.ParentWidget = m_window;
play.OnMouseEnterEvent += new EventHandler(play_OnMouseEnterEvent);
play.OnMouseLeaveEvent += new EventHandler(play_OnMouseLeaveEvent);
play.OnMouseUpEvent += new System.Windows.Forms.MouseEventHandler(play_OnMouseUpEvent);
play.CountHeight = true;
play.CountWidth = true;
m_window.ChildWidgets.Add(play);
close = new WorldWind.NewWidgets.PictureBox();
close.Name = "Close";
close.ImageUri = basePath + "\\Data\\Icons\\Time\\close off.png";
close.ClientLocation = new System.Drawing.Point(29, 3);
close.ClientSize = new System.Drawing.Size(22, 22);
close.Visible = true;
close.ParentWidget = m_window;
close.OnMouseEnterEvent += new EventHandler(close_OnMouseEnterEvent);
close.OnMouseLeaveEvent += new EventHandler(close_OnMouseLeaveEvent);
close.OnMouseUpEvent += new MouseEventHandler(close_OnMouseUpEvent);
close.CountHeight = false;
close.CountWidth = false;
m_window.ChildWidgets.Add(close);
rewind = new WorldWind.NewWidgets.PictureBox();
rewind.Name = "Rewind";
rewind.ImageUri = basePath + "\\Data\\Icons\\Time\\repeat off.png";
rewind.ClientLocation = new System.Drawing.Point(16, 26);
rewind.ClientSize = new System.Drawing.Size(32, 32);
rewind.Visible = true;
rewind.ParentWidget = m_window;
rewind.OnMouseEnterEvent += new EventHandler(rewind_OnMouseEnterEvent);
rewind.OnMouseLeaveEvent += new EventHandler(rewind_OnMouseLeaveEvent);
rewind.OnMouseUpEvent += new MouseEventHandler(rewind_OnMouseUpEvent);
rewind.CountHeight = false;
rewind.CountWidth = false;
m_window.ChildWidgets.Add(rewind);
pause = new WorldWind.NewWidgets.PictureBox();
pause.Name = "Pause";
if (TimeKeeper.Enabled)
{
pause.ImageUri = basePath + "\\Data\\Icons\\Time\\pause off.png";
}
else
{
pause.ImageUri = basePath + "\\Data\\Icons\\Time\\pause on.png";
}
pause.ClientLocation = new System.Drawing.Point(35, 88);
pause.ClientSize = new System.Drawing.Size(64, 64);
pause.Visible = true;
pause.ParentWidget = m_window;
pause.OnMouseEnterEvent += new EventHandler(pause_OnMouseEnterEvent);
pause.OnMouseLeaveEvent += new EventHandler(pause_OnMouseLeaveEvent);
pause.OnMouseUpEvent += new System.Windows.Forms.MouseEventHandler(pause_OnMouseUpEvent);
pause.CountHeight = true;
pause.CountWidth = false;
m_window.ChildWidgets.Add(pause);
slow = new WorldWind.NewWidgets.PictureBox();
slow.Name = "Slow";
slow.ImageUri = basePath + "\\Data\\Icons\\Time\\slow off.png";
slow.ClientLocation = new System.Drawing.Point(97, 88);
slow.ClientSize = new System.Drawing.Size(64, 64);
slow.Visible = true;
slow.ParentWidget = m_window;
slow.OnMouseEnterEvent += new EventHandler(slow_OnMouseEnterEvent);
slow.OnMouseLeaveEvent += new EventHandler(slow_OnMouseLeaveEvent);
slow.OnMouseUpEvent += new System.Windows.Forms.MouseEventHandler(slow_OnMouseUpEvent);
slow.CountHeight = false;
slow.CountWidth = false;
m_window.ChildWidgets.Add(slow);
fast = new WorldWind.NewWidgets.PictureBox();
fast.Name = "Fast";
fast.ImageUri = basePath + "\\Data\\Icons\\Time\\fast off.png";
fast.ClientLocation = new System.Drawing.Point(158, 88);
fast.ClientSize = new System.Drawing.Size(64, 64);
fast.Visible = true;
fast.ParentWidget = m_window;
fast.OnMouseEnterEvent += new EventHandler(fast_OnMouseEnterEvent);
fast.OnMouseLeaveEvent += new EventHandler(fast_OnMouseLeaveEvent);
fast.OnMouseUpEvent += new System.Windows.Forms.MouseEventHandler(fast_OnMouseUpEvent);
fast.CountHeight = false;
fast.CountWidth = false;
m_window.ChildWidgets.Add(fast);
timeLabel = new WorldWind.NewWidgets.TextLabel();
timeLabel.Name = "Current Time";
timeLabel.ClientLocation = new System.Drawing.Point(134, 65);
timeLabel.ClientSize = new System.Drawing.Size(140, 60);
timeLabel.Visible = true;
timeLabel.ParentWidget = m_window;
timeLabel.Text = GetCurrentTimeString();
TimeKeeper.Elapsed += new System.Timers.ElapsedEventHandler(TimeKeeper_Elapsed);
timeLabel.CountHeight = false;
timeLabel.CountWidth = true;
timeLabel.WordBreak = true;
m_window.ChildWidgets.Add(timeLabel);
DrawArgs.NewRootWidget.ChildWidgets.Add(m_window);
m_toolbarItem = new WorldWind.NewWidgets.WidgetMenuButton(
"Time Controller",
basePath + "\\Data\\Icons\\Time\\time off.png",
m_window);
//向MenuBar中添加菜单按钮
ParentApplication.WorldWindow.MenuBar.AddToolsMenuButton(m_toolbarItem);
}
catch (Exception ex)
{
Log.Write(ex);
}
base.Load();
}
其他插件的重载后的Load()方法也是类似的,不再赘述。希望能帮研究WW的朋友理清插件加载过程的思路,少走弯路。
其他部分:
WorldWind学习系列四:功能分析——Show Planet Axis、Show Position 、Show Cross Hairs功能
WorldWind学习系列三:简单功能分析——主窗体的键盘监听处理及拷贝和粘贴位置坐标功能
WorldWind学习系列三:功能分析——截屏功能和“关于”窗体分析