GEF入门实例_总结_04_Eclipse插件启动流程分析
一、前言
本文承接上一节:GEF入门实例_总结_03_显示菜单和工具栏
注意到app目录下的6个类文件。
这6个文件对RCP应用程序而言非常重要,可能我们现在对这几个文件的理解还是云里雾里,这一节我们将通过这几个文件来了解Eclipse插件的启动过程。
二、Eclipse工作台层次结构
1.Eclipse工作台示例
(1)运行Eclipse之后,出现下图所示界面,其中顶层窗口就是Eclipse的工作台窗口。
(2)每个工作台窗口又包括菜单栏、工具栏、状态栏 和 多个工作台页面
(3)每个工作台页面又包含多个视图、编辑器
顶层窗口就是Eclipse的工作台窗口
三、6个类文件的作用
这六个类文件的作用如下:
序号 | 类名 | 作用 |
1 | Application | RCP应用程序的入口 |
2 | ApplicationWorkbenchAdvisor | 负责应用程序生命周期管理 |
3 | ApplicationWorkbenchWindowAdvisor | 负责窗口生命周期管理。控制窗口界面的UI元素 |
4 | ApplicationActionBarAdvisor | 负责定义创建窗口的行为。 负责创建菜单栏、工具栏、状态行 |
5 | Perspective | 默认透视图,负责界面布局的安排 |
6 | Activator | 控制插件的生命周期 |
1.Application
package gef.tutorial.step.app;import org.eclipse.equinox.app.IApplication; import org.eclipse.equinox.app.IApplicationContext; import org.eclipse.swt.widgets.Display; import org.eclipse.ui.IWorkbench; import org.eclipse.ui.PlatformUI;/*** This class controls all aspects of the application's execution*/ public class Application implements IApplication {@Overridepublic Object start(IApplicationContext context) throws Exception {Display display = PlatformUI.createDisplay();try {//========工作台打开之前,可做登录操作//即将打开工作台int returnCode = PlatformUI.createAndRunWorkbench(display, new ApplicationWorkbenchAdvisor());if (returnCode == PlatformUI.RETURN_RESTART)return IApplication.EXIT_RESTART;elsereturn IApplication.EXIT_OK;} finally {display.dispose();}}@Overridepublic void stop() {if (!PlatformUI.isWorkbenchRunning())return;final IWorkbench workbench = PlatformUI.getWorkbench();final Display display = workbench.getDisplay();display.syncExec(new Runnable() {public void run() {if (!display.isDisposed())workbench.close();}});} }
View Code
(1)Application类是RCP程序的入口,它实现了IApplication接口,在RCP程序启动时会执行该接口的start方法。
(2)事件循环:工作台启动之后,会处于持续打开状态。这时,应用程序开始处理用户的鼠标单击、移动、按键等各种事件,一直到用户关闭程序退出,这就是所谓的事件循环。
2.ApplicationWorkbenchAdvisor
package gef.tutorial.step.app;import org.eclipse.ui.application.IWorkbenchConfigurer; import org.eclipse.ui.application.IWorkbenchWindowConfigurer; import org.eclipse.ui.application.WorkbenchAdvisor; import org.eclipse.ui.application.WorkbenchWindowAdvisor;public class ApplicationWorkbenchAdvisor extends WorkbenchAdvisor {private static final String PERSPECTIVE_ID = "gef.tutorial.step.perspective"; //$NON-NLS-1$ @Overridepublic WorkbenchWindowAdvisor createWorkbenchWindowAdvisor(IWorkbenchWindowConfigurer configurer) {return new ApplicationWorkbenchWindowAdvisor(configurer);}/**1.在工作台开始运行之前执行初始化操作。* 在打开任何窗口之前,在工作台初始化期间调用此方法。* */@Overridepublic void initialize(IWorkbenchConfigurer configurer) {super.initialize(configurer);//1.1 设置应用程序每次退出时保存当前窗口状态configurer.setSaveAndRestore(true);}/*** 2.指定工作台的初始透视图*/@Overridepublic String getInitialWindowPerspectiveId() {return PERSPECTIVE_ID;} }
View Code
(1)负责应用程序生命周期管理。
(2)可以在该类中实现程序启动或关闭时的某种处理
(3)主要方法
序号 | 方法名 | 生命周期 | 用法 |
1 | initialize | 最先调用。在工作台开始运行之前执行一些初始化操作。 | 可用来处理初始化配置操作 |
2 | preStartup | initialize之后、第一个窗口打开之前调用 | 可以用来处理临时或者可选处理操作 |
3 | postStartup | 第一个窗口打开之后但启动事件循环之前调用 | 可以用来进行那些需要自动处理的动作 |
4 | preShutdown | 事件循环结束之后但工作台关闭之前调用 | 可以用来进行保存数据、关闭数据库服务器等操作 |
5 | postShutdown | 工作台关闭之后调用 | 可以用来进行保存应用程序 |
3.ApplicationWorkbenchWindowAdvisor
package gef.tutorial.step.app;import org.eclipse.swt.graphics.Point; import org.eclipse.ui.application.ActionBarAdvisor; import org.eclipse.ui.application.IActionBarConfigurer; import org.eclipse.ui.application.IWorkbenchWindowConfigurer; import org.eclipse.ui.application.WorkbenchWindowAdvisor;public class ApplicationWorkbenchWindowAdvisor extends WorkbenchWindowAdvisor {public ApplicationWorkbenchWindowAdvisor(IWorkbenchWindowConfigurer configurer) {super(configurer);}/*** 1.创建一个新的action bar advisor* 指定由ApplicationActionBarAdvisor来配置窗口的操作条。*/@Overridepublic ActionBarAdvisor createActionBarAdvisor(IActionBarConfigurer configurer) {return new ApplicationActionBarAdvisor(configurer);}/*** 2.在窗口打开之前执行*/@Overridepublic void preWindowOpen() {IWorkbenchWindowConfigurer configurer = getWindowConfigurer();//1.设置窗口初始化大小configurer.setInitialSize(new Point(700, 550));//2.显示工具栏configurer.setShowCoolBar(true);//3.不显示状态栏configurer.setShowStatusLine(false);//4.设置窗口标题configurer.setTitle("GEF入门实例"); //$NON-NLS-1$ } }
View Code
(1)负责应用程序窗口生命周期管理。
(2)该类扩展自WorkbenchWindowAdvisor。每个应用程序都需要一个WorkbenchWindowAdvisor 来控制窗口界面的UI元素。
开发人员可以控制窗口创建时(或其他生命周期时)的大小、标题、位置等。
(3)主要方法
序号 | 方法名 | 生命周期 | 用法 |
1 | preWindowOpen | 窗口控件创建之前调用 | 可用于设置窗口的初始大小、状态栏、工具栏等的可视性 |
2 | postWindowRestore | 当窗口根据上一次的保存状态恢复创建之后调用 | 可用于调整窗口的恢复状态 |
3 | postWindowCreate | 窗口创建之后调用 | 可用于调整窗口 |
4 | postWindowOpen | 窗口已经打开之后调用 | 可用于注册窗口监听,例如在此方法中实现系统托盘 |
4.ApplicationActionBarAdvisor
package gef.tutorial.step.app;import org.eclipse.jface.action.ICoolBarManager; import org.eclipse.jface.action.IMenuManager; import org.eclipse.jface.action.MenuManager; import org.eclipse.jface.action.Separator; import org.eclipse.jface.action.ToolBarManager; import org.eclipse.swt.SWT; import org.eclipse.ui.IWorkbenchWindow; import org.eclipse.ui.actions.ActionFactory; import org.eclipse.ui.actions.ActionFactory.IWorkbenchAction; import org.eclipse.ui.application.ActionBarAdvisor; import org.eclipse.ui.application.IActionBarConfigurer;import gef.tutorial.step.action.DiagramAction;public class ApplicationActionBarAdvisor extends ActionBarAdvisor {private IWorkbenchAction exitAction;private IWorkbenchAction aboutAction;private DiagramAction diagramAction;public ApplicationActionBarAdvisor(IActionBarConfigurer configurer) {super(configurer);}/*** 1.注册菜单或者工具栏的Action。Action只有注册后才能添加到菜单中。* */@Overrideprotected void makeActions(IWorkbenchWindow window) {//退出exitAction = ActionFactory.QUIT.create(window);register(exitAction);//关于aboutAction = ActionFactory.ABOUT.create(window);register(aboutAction);//绘图diagramAction = new DiagramAction(window);register(diagramAction);}/*** 2.填充菜单栏。用Action来填充菜单* * (1) 菜单管理器负责管理菜单项、设置菜单行为、创建级联菜单或者对菜单项进行分组。* (2) MenuManager构造函数:菜单项文本、菜单项ID* (3) new Separator() 为一条分割线*/@Overrideprotected void fillMenuBar(IMenuManager menuBar) {//(1)一级菜单 FileMenuManager fileMenuManager= new MenuManager("File", "fileMenuManager");//加入绘图动作,是叶子节点菜单,也是二级菜单。点击之后将执行Action的run方法 fileMenuManager.add(diagramAction);//加入分隔符fileMenuManager.add(new Separator());//加入退出动作 fileMenuManager.add(exitAction);//(2)一级菜单 HelpMenuManager helpMenuManager = new MenuManager("Help", "helpMenuManager");helpMenuManager.add(aboutAction);//加入二级菜单 helpMenuManager.add(fileMenuManager);//(3)将菜单加入菜单栏 menuBar.add(fileMenuManager);menuBar.add(helpMenuManager);}/*** 3.填充工具栏。用Action来填充工具栏* (1) 工具栏默认是不显示。在 ApplicationWorkbenchWindowAdvisor 类中的* preWindowOpen方法中有一句 configurer.setShowCoolBar(false);将false改为true即可显示工具栏* (2) 工具栏管理器负责工具栏的分类管理* (3) SWT.FLAT将工具栏设置成平滑方式,SWT.SHADOW_OUT用于在工具栏和菜单栏之间加一条分割线* */@Overrideprotected void fillCoolBar(ICoolBarManager coolBar) {//1.生成工具栏ToolBarManager toolBarManager = new ToolBarManager(SWT.FLAT | SWT.SHADOW_OUT);//2.将工具放入工具栏 toolBarManager.add(diagramAction);//3.将工具栏放入 coolBar.add(toolBarManager);}}
View Code
(1)负责定义创建窗口的行为。
(2)该类扩展自ActionBarAdvisor,用于创建菜单栏、工具栏、状态行
(3)主要方法
序号 | 方法名 | 用法 |
1 | makeActions | 注册菜单或者工具栏的工作 |
2 | fillMenuBar | 添加菜单栏 |
3 | fillCoolBar | 添加工具栏 |
4 | fillStatusLine | 添加状态栏 |
5.Perspective
package gef.tutorial.step.app;import org.eclipse.ui.IFolderLayout; import org.eclipse.ui.IPageLayout; import org.eclipse.ui.IPerspectiveFactory;public class Perspective implements IPerspectiveFactory {public void createInitialLayout(IPageLayout layout) {//一、基本配置//1.1 获取布局的编辑器final String editorArea = layout.getEditorArea();//1.2 设置显示编辑区layout.setEditorAreaVisible(true);//二、添加各种视图//2.1 添加属性视图IFolderLayout bottomFolder = layout.createFolder("bottom",IPageLayout.BOTTOM, 0.75f, editorArea);bottomFolder.addView(IPageLayout.ID_PROP_SHEET);//2.2 添加大纲视图IFolderLayout rightBottomFolder = layout.createFolder("right",IPageLayout.RIGHT, 0.75f, editorArea);rightBottomFolder.addView(IPageLayout.ID_OUTLINE);} }
View Code
(1)是程序默认的透视图
(2)负责初始页面布局并显示
(3)主要方法
方法名 | 用法 |
createInitialLayout |
创建页面的初始布局。此方法的实现人员可以向透视图添加其他视图。 |
6.Activator
package gef.tutorial.step.app;import org.eclipse.jface.resource.ImageDescriptor; import org.eclipse.ui.plugin.AbstractUIPlugin; import org.osgi.framework.BundleContext;/*** The activator class controls the plug-in life cycle*/ public class Activator extends AbstractUIPlugin {// The plug-in IDpublic static final String PLUGIN_ID = "gef.tutorial.step"; //$NON-NLS-1$// The shared instanceprivate static Activator plugin;/*** The constructor*/public Activator() {}/** (non-Javadoc)* @see org.eclipse.ui.plugin.AbstractUIPlugin#start(org.osgi.framework.BundleContext)*/public void start(BundleContext context) throws Exception {super.start(context);plugin = this;}/** (non-Javadoc)* @see org.eclipse.ui.plugin.AbstractUIPlugin#stop(org.osgi.framework.BundleContext)*/public void stop(BundleContext context) throws Exception {plugin = null;super.stop(context);}/*** Returns the shared instance** @return the shared instance*/public static Activator getDefault() {return plugin;}/*** Returns an image descriptor for the image file at the given* plug-in relative path** @param path the path* @return the image descriptor*/public static ImageDescriptor getImageDescriptor(String path) {return imageDescriptorFromPlugin(PLUGIN_ID, path);} }
View Code
(1)是插件类,控制插件的生命周期
(2)见第二节: GEF入门实例_总结_02_新建初始RCP空项目 ,如下图,
若2处勾选 "This plug-in will make contributions to the UI",表示插件对UI进行添加,则此类将扩展自AbstractUIPlugin。
否则,该类扩展自 AbstractUIPlugin 的父类 Plugin。
(3)主要方法
序号 | 方法名 | 作用 |
1 | start | 插件启动时的处理 |
2 | stop | 插件停止时的处理 |
3 | getDefault | 获取默认的插件类实例。单例模式。 |
4 | getImageDescriptor | 根据插件相对路径返回图片描述符,可用于获取图片 |
四、Eclipse插件启动流程图
有问题的地方:第9步的实际的具体流程我暂时还不清楚。
Eclipse启动流程图如下,都是自己总结的,所以可能有不当之处,希望读者能解惑并将其完善。
温馨提示:可将下图拖入浏览器中查看原图
五、Eclipse插件启动流程分析
以下流程与上述流程图步骤对应,通过进行断点调试,可发现启动流程如下:
0.run
首先是运行插件,可参见: GEF入门实例_总结_02_新建初始RCP空项目 的 “三、启动项目” 部分。
(1)其中注意上图:run an application ,这里主要是设置程序的入口,这里我们选择的是 “gef.tutorial.step.application”,含义为(插件ID).(扩展点 runtime.applications 的ID)。
(2)打开plugin.xml,在overview页,可以看到插件id为gef.tutorial.step
(3)在 Extensions 页,可以看到扩展点“org.eclipse.core.runtime.applications”的 ID 为 application。
(4)至此,我们知道插件启动时,执行这个扩展点,那为啥会执行到Application类中去呢?如下图:
1.插件启动
Application.start
如上所述,因为Run Configuration 中 run an application 配置的入口程序为 Application 类,因此程序启动时,会执行到 Application 的 start 方法中来。
int returnCode = PlatformUI.createAndRunWorkbench(display, new ApplicationWorkbenchAdvisor());
然后由此处代码创建一个 ApplicationWorkbenchAdvisor ,并进行到第二步
2.获取工作台窗口初始透视标识
ApplicationWorkbenchAdvisor.getInitialWindowPerspectiveId
/*** 2.指定工作台的初始透视图*/@Overridepublic String getInitialWindowPerspectiveId() {return PERSPECTIVE_ID;}
3.创建workbench window advisor
ApplicationWorkbenchAdvisor.createWorkbenchWindowAdvisor
@Overridepublic WorkbenchWindowAdvisor createWorkbenchWindowAdvisor(IWorkbenchWindowConfigurer configurer) {return new ApplicationWorkbenchWindowAdvisor(configurer);}
在这一步去创建了 ApplicationWorkbenchWindowAdvisor ,通过这个Advisor 可以配置工作台窗口。
4.窗口打开前的处理
ApplicationWorkbenchWindowAdvisor.preWindowOpen
/*** 2.在窗口打开之前执行*/@Overridepublic void preWindowOpen() {IWorkbenchWindowConfigurer configurer = getWindowConfigurer();//1.设置窗口初始化大小configurer.setInitialSize(new Point(700, 550));//2.显示工具栏configurer.setShowCoolBar(true);//3.不显示状态栏configurer.setShowStatusLine(false);//4.设置窗口标题configurer.setTitle("GEF入门实例"); //$NON-NLS-1$}
View Code
在窗口打开前,设置了窗口大小、标题、工具栏与状态栏的可视性
5.创建 ActionBarAdvisor
ApplicationWorkbenchWindowAdvisor.createActionBarAdvisor
/*** 1.创建一个新的action bar advisor* 指定由ApplicationActionBarAdvisor来配置窗口的操作条。*/@Overridepublic ActionBarAdvisor createActionBarAdvisor(IActionBarConfigurer configurer) {return new ApplicationActionBarAdvisor(configurer);}
6.注册菜单或者工具栏的Action
ApplicationActionBarAdvisor.makeActions
/*** 1.注册菜单或者工具栏的Action。Action只有注册后才能添加到菜单中。* */@Overrideprotected void makeActions(IWorkbenchWindow window) {//退出exitAction = ActionFactory.QUIT.create(window);register(exitAction);//关于aboutAction = ActionFactory.ABOUT.create(window);register(aboutAction);//绘图diagramAction = new DiagramAction(window);register(diagramAction);}
View Code
7.填充菜单栏
ApplicationActionBarAdvisor.fillMenuBar
/*** 2.填充菜单栏。用Action来填充菜单* * (1) 菜单管理器负责管理菜单项、设置菜单行为、创建级联菜单或者对菜单项进行分组。* (2) MenuManager构造函数:菜单项文本、菜单项ID* (3) new Separator() 为一条分割线*/@Overrideprotected void fillMenuBar(IMenuManager menuBar) {//(1)一级菜单 FileMenuManager fileMenuManager= new MenuManager("File", "fileMenuManager");//加入绘图动作,是叶子节点菜单,也是二级菜单。点击之后将执行Action的run方法 fileMenuManager.add(diagramAction);//加入分隔符fileMenuManager.add(new Separator());//加入退出动作 fileMenuManager.add(exitAction);//(2)一级菜单 HelpMenuManager helpMenuManager = new MenuManager("Help", "helpMenuManager");helpMenuManager.add(aboutAction);//加入二级菜单 helpMenuManager.add(fileMenuManager);//(3)将菜单加入菜单栏 menuBar.add(fileMenuManager);menuBar.add(helpMenuManager);}
View Code
8.填充工具栏
ApplicationActionBarAdvisor.fillCoolBar
/*** 3.填充工具栏。用Action来填充工具栏* (1) 工具栏默认是不显示。在 ApplicationWorkbenchWindowAdvisor 类中的* preWindowOpen方法中有一句 configurer.setShowCoolBar(false);将false改为true即可显示工具栏* (2) 工具栏管理器负责工具栏的分类管理* (3) SWT.FLAT将工具栏设置成平滑方式,SWT.SHADOW_OUT用于在工具栏和菜单栏之间加一条分割线* */@Overrideprotected void fillCoolBar(ICoolBarManager coolBar) {//1.生成工具栏ToolBarManager toolBarManager = new ToolBarManager(SWT.FLAT | SWT.SHADOW_OUT);//2.将工具放入工具栏 toolBarManager.add(diagramAction);//3.将工具栏放入 coolBar.add(toolBarManager);}
View Code
9.getInitialWindowPerspectiveId(有疑问)
ApplicationWorkbenchAdvisor.getInitialWindowPerspectiveId
/*** 2.指定工作台的初始透视图*/@Overridepublic String getInitialWindowPerspectiveId() {return PERSPECTIVE_ID;}
View Code
断点调试时是又走了一遍此方法,
至于为啥走了两遍,怎么走到第10步的,这个还不清楚。
希望读者能为我解惑,不胜感激。
10.创建默认透视图的初始布局
Perspective.createInitialLayout
package gef.tutorial.step.app;import org.eclipse.ui.IFolderLayout; import org.eclipse.ui.IPageLayout; import org.eclipse.ui.IPerspectiveFactory;public class Perspective implements IPerspectiveFactory {public void createInitialLayout(IPageLayout layout) {//一、基本配置//1.1 获取布局的编辑器final String editorArea = layout.getEditorArea();//1.2 设置显示编辑区layout.setEditorAreaVisible(true);//二、添加各种视图//2.1 添加属性视图IFolderLayout bottomFolder = layout.createFolder("bottom",IPageLayout.BOTTOM, 0.75f, editorArea);bottomFolder.addView(IPageLayout.ID_PROP_SHEET);//2.2 添加大纲视图IFolderLayout rightBottomFolder = layout.createFolder("right",IPageLayout.RIGHT, 0.75f, editorArea);rightBottomFolder.addView(IPageLayout.ID_OUTLINE);} }
View Code
在这一步创建工作台默认透视图的初始布局,可在此方法中添加其他视图。
这样窗口就创建好了
11.窗口创建之后
ApplicationWorkbenchWindowAdvisor.postWindowCreate
在窗口创建完毕之后,会执行此方法。可用此方法来调整窗口
六、参考资料
1.《Eclipse RCP 应用系统开发方法与实践》
2.《Eclipse插件开发学习笔记》
3.本系列总结配套PDF教程《GEF开发简单实例.pdf》
GEF入门实例_总结_04_Eclipse插件启动流程分析相关推荐
- 《HFSS电磁仿真设计从入门到精通》一第2章 入门实例——T形波导的内场分析和优化设计...
本节书摘来自异步社区<HFSS电磁仿真设计从入门到精通>一书中的第2章,作者 易迪拓培训 , 李明洋 , 刘敏,更多章节内容可以访问云栖社区"异步社区"公众号查看 第2 ...
- u-boot启动流程分析(1)_平台相关部分
转自:http://www.wowotech.net/u-boot/boot_flow_1.html 1. 前言 本文将结合u-boot的"board->machine->arc ...
- 解析并符号 读取dll_Spring IOC容器之XmlBeanFactory启动流程分析和源码解析
一. 前言 Spring容器主要分为两类BeanFactory和ApplicationContext,后者是基于前者的功能扩展,也就是一个基础容器和一个高级容器的区别.本篇就以BeanFactory基 ...
- MyBatis启动流程分析
目录 MyBatis简单介绍 启动流程分析 简单总结 附录 MyBatis内置别名转换 参考 MyBatis简单介绍 MyBatis是一个持久层框架,使用简单,学习成本较低.可以执行自己手写的SQL语 ...
- NameNode之启动流程分析
NameNode启动流程分析 public staticvoid main(Stringargv[]) throws Exception { if (DFSUtil.parseHelpArgument ...
- springboot中获得app_Spring Boot 应用程序启动流程分析
SpringBoot 有两个关键元素: @SpringBootApplication SpringApplication 以及 run() 方法 SpringApplication 这个类应该算是 S ...
- SpringBoot(十二)启动流程分析之创建应用上下文AnnotationConfigServletWebServerApplicationContext
SpringBoot版本:2.1.1 ==>启动流程分析汇总 接上篇博客Spring Boot 2.1.1(十一)启动流程分析之设置系统属性spring.beaninfo.ignore ...
- Android系统开机到Launcher启动流程分析
本文基于Android10.0的源码. 由于google团队在对framework层代码进行大量重构,所以代码变动还是挺大的. 常见基础问题: SystemServer系统服务进程是如何创建的?Lau ...
- Hyperledger Fabric笔记3--BYFN启动流程分析
Hyperledger Fabric笔记3--BYFN启动流程分析 BYFN--构建你的第一个网络,该方案提供了一个示例Hyperledger Fabric网络,该网络由两个组织组成,每个组织都维护两 ...
最新文章
- 按键的c语言代码表,各种按键模式的扫描
- Mysql:AVG()函数如何去除0值做平均值
- apache 工作模式prefork进程模式和worker线程模式参式详解和推荐设置
- Java web小项目_个人主页(2)—— 边缘加速原理与实现
- 如何设置省略号对其序号 html,html – 包含省略号和垂直对齐中间的框中的多行...
- 【MyBatis】Mybatis使用SqlSessionFactory加载xml文件
- 浅谈axios.interceptors拦截器
- 软件构架 课堂练习一
- 黑客X档案PDF完整版(06年1月-12年12月)
- 龙芯3A3000和龙芯3B3000芯片处理器参数介绍
- PS如何调整图片像素大小
- 08-【go】go语言中的*和的使用方法
- Android打包动态配置签名
- 【读书笔记】商业自传-阿里巴巴/淘宝/阿里云/支付宝,亚洲电子商务教父:马云传_2019.10.25
- 网络原理4 数据链路层
- MIT牛人解说数学体系
- Dao层service层controller层mannager层和biz层详解
- zzuli 20级第六次周赛 2733 问题 K: 键盘霸主hrs
- Excel表格中如何快速跨表复制粘贴,将表1数据一键复制到表2、表3、表4
- 【C语言】输出1900至2000年(包含1900年和2000年)间的所有闰年
热门文章
- 第 24 章 状态模式
- 登录时判断用户还是管理员_高低温试验仪器出现内漏时如何判断是高压还是低压内漏?...
- j - cyk追楠神系列一_「清单」小米烧水杯太萌了,喜茶桂花冻太香了,优衣库新系列太美了!...
- 数据结构与算法python语言描述答案_《数据结构与算法Python语言描述》习题第二章第一题(python版)...
- 万年历c语言编程怎么做,用C语言如何编写“万年历”
- linux as5 启动mysql_RedHat AS5 PHP添加JSON模块
- ps画画模糊笔刷_如何用笔刷做出大神级效果?1000多款PS插画笔刷,简直就是你想要的神器...
- 卷积滤波 英文_图像处理必备英文词汇
- typescript vuex_将已有的Vue项目升级支持TypeScript
- 浪潮服务器u盘安装系统未找到任何驱动器,u盘重装win10时找不到任何驱动器