GacUI:跨平台和渲染器
https://github.com/vczh/GacUIBlog
UI库跨平台的方法无非就是每个平台写一次。而如何把更多的共同点抽取出来,尽量的减少每个平台写一次的部分,是每一个跨平台的UI库的重点之一。GacUI的设计比较直接,所有平台相关的部分被集中到了几个接口里。每一次把GacUI一直到一个新的平台,就把所有的这些接口都重新实现一遍。目前GacUI能在Windows与macOS跑起来,以后还要逐渐支持UWP、命令行以及通过WASM让他跑到浏览器上去。简单地讲,所有的系统调用都从INativeController
开始,这是一个单例。通过观察这个接口提供的各个服务,它本身就会慢慢引导你实现各种GacUI需要的与系统交互的功能,包括创建窗口等等。
其中最值得提到的是渲染的部分。实际上我并不想抽象渲染器。因为每一个渲染器的运行过程实在是大相径庭,如果为了开发简便而过度抽象,势必会牺牲某些平台的性能。不过我还是打算在2.0里推出一个2D绘图API,这组API就只是用来给app画画的,跟绘制控件本身并没有关系。设计这套架构的一个挑战,是渲染器本身也可能是跨平台的。一个平台上面可以有多个渲染器(譬如Windows上你就可以用GDI和Direct2D),一个渲染器也可能跨多个平台(譬如OpenGL)。所以为系统调用开发的代码,以及为渲染器开发的代码,势必本身也是解耦的。你不能说明明都是OpenGL,但是为Windows和Linux你都要各写一遍使用OpenGL绘制控件的代码,那就显得太愚蠢了。
设计都得从需求谈起,要讲GacUI如何把系统调用和渲染器都从实现里面隔离开,就得谈一谈GacUI是如何渲染控件的。
几次设计GUI库的尝试
第一次设计GUI库
我第一次做通用的控件库还是大一的时候。以前受到VB6以及Delphi的影响,对UI库的理解无非都是,用户创建一大堆控件接成一棵树,窗口修改大笑的时候自己去想办法布局,IO操作来了就直接dispatch到控件里,需要绘图了就从Windows地Paint函数一直调用洗澡去,每个控件自己负责自己的绘制。后来我尝试直接从Win32 API开始写app,发现也是这样的,那个时候就没怎么多想。因此那年第一次做一个UI库的时候,我也就理所当然地这么设计了,秉承着为开发游戏写library的精神,我毅然决然地使用了OpenGL,最后在做高级文字渲染的时候被劝退了。大三的时候我又不死心,又做了一遍。但是我的设计水平并没有得到提高,仅仅因为技术水平的提高而实现了更复杂的UI,于是得到了下面的东西,然后项目流产。 上面的设计有一个显著的坏处,在把Win32 API发挥到极致的.net Windows Forms里面就体现了出来,控件和布局混为一谈。最典型的就是TableLayoutPanel
控件。这是一个控件,但是这个控件是没有交互功能的,他唯一的目标就是做layout。但是因为他被建模成了一个控件,就被赋予了渲染的义务,你依然可以修改它的边框和背景颜色什么的。第二个显著的坏处,就是既然每一个控件都要负责绘制自己,那不管你选择什么渲染技术,你都要把这套API需要的所有东西都带进控件里。于是在当年的这套控件库里面,每一个控件都会有一大堆OpenGL的概念放在private成员变量里。所有的代码被混淆了起来,越写越复杂。由于我大一的时候,算法数据结构设计模式什么都还没开始接触,最后便陷入了混乱,于是项目流产。当然就算是流产了,那也是像模像样的,只是再也无法重构下去了。
这个项目留下来的遗产,就是现在GacUI里面的WinNativeWindow.cpp。有些人看着这份文件可能会问,为什么抽象了操作系统的接口,还要在里面再封装一次Win32 API。其实就是因为这份代码并不是从头写的。
第二次设计GUI库
于是一个很自然的想法,就是要把绘图的部分从控件里拿掉。想拿掉就得彻底,所有OpenGL的东西都不能出现在控件里。但是控件又必须把自己的信息都传递过去,否则就没办法绘图了。而且窗口也是特殊的,因为所有的绘图设备都是窗口创建的,而控件也仍然需要拿到这些设备才能绘制。因此当初我就做了一个实验,把使用Win32 API创建窗口的部分抽象掉。UI库需要一个本地窗口的时候,拿到的就只是一个窗口的抽象接口。
然后开始创建控件,每一个控件的皮肤也被抽象成了一个接口。譬如说按钮的皮肤,基本上就是一个被动接受信息的接口,他会不断地接受从控件来的命令,譬如更改字体啊,更改文字啊,更改颜色,更改位置啊啊。其次按钮放到不同的容器里面的时候,皮肤接口也会接到通知,这个信息用来把控件告知的位置换算成以窗口的一个角作为原点的坐标系。但是皮肤自己要绘制,其实还是要知道UI库用的是OpenGL。于是窗口的抽象接口还是留了一个后门,会暴露自己的OpenGL相关的信息。因此这种抽象是不够彻底的,说到底只是一次隔离,控件不需要关心渲染器,但是皮肤不仅要关心渲染器还要关心控件。
GacUI现在的INativeController
的第一个版本就是从那个时候开始创建的。当时的代码长这样:
class INativeController : public Interface
{
public:
virtual INativeWindow* CreateNativeWindow() = 0;
virtual void DestroyNativeWindow(INativeWindow* window) = 0;
virtual INativeWindow* GetMainWindow() = 0;
virtual void Run(INativeWindow* window) = 0;
virtual bool InstallListener(INativeControllerListener* listener) = 0;
virtual bool UninstallListener(INativeControllerListener* listener) = 0;
virtual int GetScreenCount() = 0;
virtual INativeScreen* GetScreen(int index) = 0;
virtual INativeScreen* GetScreen(INativeWindow* window) = 0;
};
再看看今天的INativeController
class INativeController : public virtual IDescriptable, public Description
{
public:
virtual INativeCallbackService* CallbackService()=0;
virtual INativeResourceService* ResourceService()=0;
virtual INativeAsyncService* AsyncService()=0;
virtual INativeClipboardService* ClipboardService()=0;
virtual INativeImageService* ImageService()=0;
virtual INativeScreenService* ScreenService()=0;
virtual INativeWindowService* WindowService()=0;
virtual INativeInputService* InputService()=0;
virtual INativeDialogService* DialogService()=0;
virtual WString GetExecutablePath()=0;
};
功能多到塞不下了,只能切割成不同的service了。不过这也为2.0打了一点基础,像UWP、WASM和命令行这样的平台,GacUI基本上只能用单独的一个窗口来host里面的假窗口。UWP不同的窗口是基于不同的线程的,一下子想要支持这个觉得有点难先跳过。而命令行和WASM都没办法创建多窗口。因此可以预见到时候可能会有一些service的实现本身就可以跨平台,这样独立出来也好。
第三次设计GUI库
后来就有了上一篇博客提到的那个模板实验,但是那个时候渲染器已经换成了GDI。OpenGL在Windows上搞文字处理还是很蛋疼,上古时代唯一简便的方法就是用GDI先生成贴图然后送进显卡里当资源。那我干嘛不直接用GDI呢?反正Windows 7的GDI也有硬件加速了。我那个时候还做了另一个实验,用GDI实现GDI+的各种骚效果,也成功了。于是项目就换成了GDI。
当时的代码有些已经不见了,只剩下了cppblog上的几幅图。看着这些图还可以感受到时代的烙印。当年开发模板实验的时候用的还是Windows Vista,于是审美也被默默地影响了,画出来变成了这样: 那个时候还写了一个测试程序,使用GDI模拟了GDI+的可以使用各种半透明Brush来填充边框和内容的设定。仔细的话可以看得出来,里面穷举了各种Pen和Brush的组合,甚至连渲染文字的时候也有用。 不过当年自己设计的那套XML创建模板+简单数据绑定的系统还是过于粗糙了,于是等到真的拿来做GUI的时候,我还是选择了用代码创建。难看是难看了点,但是反正C++有各种技巧可以让他变得“好看”。又过了一段时间,这个项目也流产了,主要原因就是仅仅把渲染器的代码隔离了,而没有隔离渲染器的知识(也就是说我还得知道底下用的是GDI否则写不出皮肤的这件事)。
这个项目不仅最终启发了GacUI现在的设计,还留下了现在GacUI里面的WinGDI.h。这份对于GDI的封装是从另一个对Win32 API封装的UI库拿过来的,改吧改吧就用在了项目里面。后来由于这份代码实在是过于古老,与GacUI的.net式命名方式格格不入,还被我改了一遍,变成了今天的样子。
第四次设计GUI库
因此最后就迎来了GacUI。GacUI在这个基础上做了一个更优秀的设计,解决了上面说的这些问题。
以前在知乎上说过我UI做了十遍,其实是因为除了这条故事线以外,以前还为了各种项目开发和封装了各种不完全自绘的UI库,积累了丰富但是并没有什么卵用的经验。
GacUI的执行过程与渲染架构
在GacUI的架构里面,控件和布局被分开成了两个层次的概念。图元也被抽象出来了。譬如说我需要一个长方形,那我就创建一个长方形的对象,附着在一个布局的对象上。控件本身只是一个view,他接受用户的输入,提供一些功能,通过操作布局和附着在布局上的图元来显示结果。这样皮肤接口的实现就被解放了,从直接绘制,变成了拼一颗布局图元树,然后根据控件的要求来修改这棵树上面的属性。至于图元本身的渲染就被隔离开了。每一个图元在创建的时候会跟系统要一个渲染对象,然后把自己的属性输入进去,而渲染需要的坐标本身则是布局来计算的。
举个例子,一个窗口有一行字和确定取消两个按钮,长这个样子: 在布局图元树里,长这个样子: 而里面的结构是这样的(简化过)
+ + SolidBorder(blue)
+---+ (3x3)
+---+
| +---- + SolidLabel(Welcome to GacUI!)
+---+
| +---+ + SolidBorder(blue)
| +---+ + SolidBackground(light blue)
| +---- + SolidLabel(OK)
+---+
+---+ + SolidBorder(blue)
+---+ + SolidBackground(light blue)
+---- + SolidLabel(Cancel)
这里面, 、和 | 就是布局。每一种布局节点有自己的控制子节点位置的方法。一个布局节点可以附着一个图元,譬如SolidBorder 、SolidBackground 和SolidLabel 等。每一个图元代表一种图形。一个附着在布局节点上的图元,会填充整个布局节点代表的范围。SolidBorder 在一个 上,那么SolidBorder 画出来的边框就恰好是那个 所占的位置。在按钮里面,SolidBackground 要比SolidBorder 四周都紧一个像素,不然边框就被覆盖了。
一旦针对每个布局节点的设定完成了,那么以后在窗口被拖动的时候,两个按钮会永远对齐在右下角。而且窗口它自己就会“知道”,它的宽度和高度受到了那一行字以及按钮的限制,不能无限变小。当一个窗口里面的东西多起来之后,可以组合的布局就让app的实现变得非常简单。控件本身是不参与渲染的,所以这一切发生的时候,控件完全不需要知道。
而“控件本身只是一个view”的意思,就是指这个按钮接受用户的输入,然后通过皮肤控制布局图元树里面的一棵子树的这件事: 用户按下了一个鼠标,事情发生的顺序就是这样的:
用户按下了鼠标
INativeWindow的实现接收到了Windows发来的消息WM_LBUTTONDOWN,根据DPI设置变换了一下坐标系,广播了这个事件
GuiWindow负责处理事件的部件(GuiGraphicsHost)接收到了这个消息,根据坐标查找到了相关的布局对象,定点发送了这个消息
鼠标消息会从点中的布局对象开始一级一级往上传播
GuiButton由于监听了自己边框的鼠标消息,得知了这一事件的发生
对于GuiButton来说,鼠标按下了还没抬起来,状态发生了这个变化。于是这个状态由GuiButton重新发回给了ButtonTemplate
ButtonTemplate的职责就是修改布局图元树上的节点的属性,这个皮肤认为按钮被按下就应该是橘黄色的,于是修改了SolidBackground的颜色属性
布局图元树的某个一个节点的属性被改了,做了个标记,通知GacUI在适当的时候重新绘制窗口
在这个事件里,可能有其他的布局图元树的节点被修改了属性。于是在本次WM_LBUTTONDOWN结束之后,GacUI看一眼自己的标记,发现需要重绘了,于是开始启动重绘工作,并在必要的时候重新调整一下布局(举个例子,Cancel按钮的字改了,可能变得更长了,就要把OK挤到更远的地方)。
可以清晰地看出,GacUI的每一个部件的职责都是有限的,而且各个部件之间可以进行充分的沟通。
皮肤再也不需要管渲染是怎么完成的了,他要做的是控制布局图元树的一棵子树。这棵子树上所有的结构它都清楚地了解。自己接受来自控件的命令,每一个命令会有自己影响布局图元树的子树的方法。显而易见,不同的皮肤针对相同的命令会有不同的反应,譬如说修改的颜色可能不一样。
控件也不需要管皮肤长真么样子了。皮肤会告诉控件一些基本的信息,譬如说这棵子树的根节点是什么,有利于控件监听用户的输入。皮肤会把这些用户输入解读成语义上的命令,然后传达给皮肤。
控件也不需要管布局了。显然布局图元树才对布局负责。皮肤知道一个按钮应该长什么样子,所以它控制了属于按钮的这棵布局图元树的子树。而开发者知道app应该长什么样子,于是他会把控件放在他自己创建的窗口的布局图元树的节点上。于是控件就把自己皮肤的布局图元树接到了窗口的布局图元树上,于是整个app的界面变成了一棵巨大的布局图元树。Windows Forms的TableLayoutPanel 那样的设计成为了历史。
如果整个app的皮肤一套换掉了,那每一个按钮都会把新的按钮皮肤提供的布局图元树接到窗口的布局图元树上,而原来的那棵子树就光荣退役了。
当然了,窗口也有自己的皮肤,控件也可以嵌套,那都是后话了。至少我们很清晰地明白了,在GacUI里面,布局节点确定了每一个画上去的图元在整个窗口中所占的位置,而附着在一个布局节点上的图元对象则负责确定画上去的是什么东西。于是现在就进入了跨平台的话题:图元是如何最终控制GDI、Direct2D、OpenGL、CoreGraphics等等等等数不尽的渲染器,从而最终把界面绘制出来的呢?
平台抽象与渲染器抽象之间的解耦
在上面的讨论中,我们知道系统调用INativeController 的接口抽象掉了。这个接口是一个单例,熟悉设计模式的同学们都应该很清楚什么是单例。每一个平台都有一套INativeController 的实现。一个操作系统可以有很多个平台,典型的就是Windows,Win32、UWP、命令行,就是不同的东西。当我们创建一个GuiWindow 对象,这个对象就会执行GetCurrentController->WindowService()->CreateNativeWindow() ,向INativeController 要一个INativeWindow 。因为创建了一个INativeWindow ,系统里就出来一个窗口。要是没有窗口你还怎么往上面画东西呢?GuiWindow 拿到了INativeWindow 之后,就会把这个对象广播给以它为根节点的布局图元树上的每一个图元(IGuiGraphicsElement )。于是现在每一个图元都知道他自己在哪一个INativeWindow 上了。这是第一步。
一个平台可能有多个渲染器的实现,甚至这个渲染器本身并不做渲染。譬如在我的设想中,UWP平台上的GacUI实现,渲染就是把布局图元树同步到UWP自己的那套东西上面去,并不调用绘图API。INativeController 本身就会广播时间,不仅仅有全局鼠标钩子啊计时器这样的东西,每一次INativeWindow 被创建和释放的时候,INativeController 都会广播消息。需要知道这些消息的对象,可以实现一个INativeControllerListener ,然后注册进去。渲染器本身也是一个INativeControllerListener ,每次INativeWindow 被创建的时候,就会开始为这个窗口做一些初始化。
对于GDI来说,窗口初始化就是要创建一个HBITMAP,每次需要刷新了就往这个HBITMAP上画,最后一次性刷新到窗口上去。对于Direct2D来说,窗口的初始化就是要创建一个render target,做类似的事情。OpenGL则更有趣,因为不同的平台上都有OpenGL,渲染的方法是一样的,但是初始化的方法大相径庭。于是对于OpenGL渲染器,大部分代码都可以共享,而初始化的这部分就每个平台做一次。
于是INativeWindow 被创建好了之后,对应的渲染设备也被创建好了,这样就可以从一个INativeWindow 得到渲染设备。这是第二步。
一个GacUI初始化的时候就要确定使用的渲染器。这个渲染器程序启动的时候,会为每一种图元绑定好一个图元渲染器的工厂示例。GacUI目前的实现是用图元的名字绑定的,其实不是太好,以后要改。不过尽管如此,实践表明在这种问题上那字符串做index对性能的影响微乎其微。每一个图元被(在第一不中)通知自己所依附的INativeWindow 之后,就会去要这个图元渲染器的工厂示例。工厂示例创建了一个图元渲染器,这个图元渲染器当然知道他自己负责的是什么图元,也知道他自己需要在哪个INativeWindow 上画,自然就知道应该去获得什么渲染设备了。这是第三步。
最后一步就容易了,每当GacUI觉得自己需要刷新了,就会挨个通知这些图元,这些图元就会通知自己的图元渲染器,图元渲染器就是干具体工作的地方了。
于是在这个架构里面,图元根本不需要自己跑在什么渲染器上,开发者只需要在初始化的时候说好要用什么渲染器,那么图元渲染器就会被连接到每一个图元上。图元渲染器的实现当然是每一个渲染器都会提供一份的,所以图元渲染器本身具有“现在开发者想用哪个渲染器”这样的知识,那他当然也就知道向谁要某个INativeWindow 对应的渲染设备了。至此控件与平台、图元与渲染器之间的解耦就完成了。
Win32平台下Direct2D渲染器的启动过程
我们可以看Direct2D作为例子。GacUI的开发者想要使用Direct2D渲染器在Win32平台上启动GacUI,那么典型的代码就是这样子:
int CALLBACK WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int CmdShow)
{
return SetupWindowsDirect2DRenderer();
}
void GuiMain()
{
MyWindow window;
GetApplication()->Run(&window);
}
SetupWindowsDirect2DRenderer 函数当然知道自己既Windows也Direct2D,于是他就调用WinMainDirect2D 函数。这个函数主要的功能就是创建Win32版本的INativeController 实现注册到单例里面,把Direct2DWindowsNativeControllerListener 注册到INativeController 里面,最后启动SetWindowsDirect2DObjectProvider 函数以及RendererMainDirect2D 函数。从此每当一个INativeWindow 被创建的时候,Direct2DWindowsNativeControllerListener 就会得到通知,并为每一个窗口创建一个ID2D1HwndRenderTarget 。从INativeWindow 到ID2D1HwndRenderTarget 的知识,就可以通过调用GetWindowsDirect2DObjectProvider 来获得。
RendererMainDirect2D 函数当然知道自己是Direct2D。在这个函数里面,它为每一个图元都注册了一个Direct2D相关的图元渲染器工厂示例。在每一个图元被创建之后,工厂示例就会创建相应的图元渲染器。这个渲染器理所当然的就知道GetWindowsDirect2DObjectProvider 这件事。在做完了这些事情之后,GuiApplicationMain 函数就被调用了。
GuiApplicationMain 已经是平台无关的函数了,它负责统筹GacUI的各项资源,初始化好后调用GuiMain 。
GuiMain 就是用户实现的函数了。
在这个例子里面,GuiMain 创建了MyWindow ,它是GuiWindow 的子类。于是在初始化函数里面,GetCurrentController()->WindowService()->CreateNativeWindow 就被调用,于是:
WinMainDirect2D 注册进去的Windows版本的INativeController 就开始发力了,创建出了一个HWND 。
WinMainDirect2D 注册进去的Direct2DWindowsNativeControllerListener 就会被通知,创建出了一个ID2D1HwndRenderTarget 。
RendererMainDirect2D 注册进去的每一个图元渲染器工厂示例也开始发力了。一个简单的窗口上可能会有几十个图元,于是几十个图元渲染器就被挂在了布局图元树上。
然后GetApplication()->Run(&window) 被执行,窗口第一次被渲染,这几十个图元渲染器纷纷调用GetWindowsDirect2DObjectProvider 之后,通过它提供的函数获取到了相应的ID2D1HwndRenderTarget 对象,开始画图。
一个GacUI窗口就显示在了用户的面前。
尾声
UI对于系统的功能需求是稳定的,GacUI在把GDI和Direct2D渲染器写好之后,INativeController 这部分基本就没怎么修改了。在2014年,GacUI被darkfall同学首先移植到了macOS上,然后就去上班没有更新了。他的反馈是移植很容易,虽然INativeController 接口很浓厚的Windows的味道,但是总的来说并没有对macOS的工作造成什么障碍。在这之后的6年里,GacUI发生了巨大的变化。到了2020年,roodkcab同学把已经没法编译的iGac repo 做了一下更新,追赶到了GacUI的最新版本,发现着6年里面GacUI对系统层面的修改微乎其微,几乎所有的进度都是跨平台的。
这一定程度上表明了,GacUI的系统解耦工作是相当成功的。GacUI在设计上把对操作系统的需求隔离并最小化,于是每次一直到一个新的平台上,都不需要花多少努力就可以完成。而且INativeController 对GacUI剩余的部分是一无所知,所以移植者只需要对目标平台有足够的了解,不需要深入了解GacUI具体是怎么运转的,也不妨碍实现INativeController 的的工作。当然这个解耦也不完美,譬如GacUI实现菜单的功能需要挂一个全局的鼠标钩子,才能在用户点击桌面或者其他程序的时候把菜单关掉。这一点对Linux平台的移植造成了一些困难,使得xGac repo 依赖了让一些程序员不开心的东西(逃。以后可能考虑把菜单的实现修改掉,尽可能脱离对全局鼠标钩子的依赖。
你可能感兴趣的:(GacUI,前世今生,c++,ui)
SpringBoot中websocket拦截器获取cookie中的token信息
小的~~
spring boot websocket java
@Getter@Slf4j@ComponentpublicclassWebSocketSecurityTokenInterceptorimplementsHandshakeInterceptor{privateTokenAcquireHandlertokenAcquireHandler;privateTokenAnalysisHandlertokenAnalysisHandler;{tokenAc
leetcode盛水最多的容器c++深度剖析【双指针篇】
重铸自身根基
优选算法 leetcode c++ 算法
盛水最多的容器前言对于程序员来说,必不可少的就是一定的算法能力,可以跟着小编一起学习题目的做题思路,培养算法能力,这里的双指针抽象为一种数组下标,更像是一种做题思路文章目录盛水最多的容器前言题目解读算法思路编写代码总结题目解读查看原题请点击&height){intleft=0,right=height.size()-1,ret=0;while(leftheight[right])right--;e
Windows图形界面(GUI)-QT-C/C++ - QT 文本编辑控件详解
0xCC说逆向
windows qt c++ 开发语言 WIN32 c语言 java
公开视频->链接点击跳转公开课程博客首页->链接点击跳转博客主页目录概述1.QLineEdit1.1特点1.2属性1.3常用方法1.4拓展应用2.QTextEdit2.1特点2.2属性2.3常用方法2.4拓展应用3.QPlainTextEdit3.1特点3.2属性3.3常用方法概述在QT中,文本编辑控件是用户界面设计中不可或缺的一部分。QT提供了多种文本编辑控件,包括QLineEdit、QText
红 - 黑树和 B+树?
百态老人
笔记
红黑树是一种自平衡二叉查找树,由RudolfBayer发明,在1978年被LeoJ.Guibas和RobertSedgewick改称为“红黑树”。它的特点包括每个节点非红即黑;根节点是黑色;每个叶子节点都是黑色的空节点;如果一个节点是红色的,那么它的两个子节点都是黑色;从任一节点到其每个叶子的所有路径都包含相同数目的黑色节点。在Java集合框架中,很多部分如HashMap、TreeMap、Tree
Docker 与持续集成 / 持续部署(CI/CD)的集成(二)
计算机毕设定制辅导-无忧学长
# Docker docker ci/cd 容器
五、代码示例与解释(一)Dockerfile示例以下是一个简单的基于PythonFlask应用的Dockerfile示例:#使用Python3.10-slim作为基础镜像FROMpython:3.10-slim#设置工作目录WORKDIR/app#复制项目文件到容器内的工作目录COPY./app#安装项目依赖RUNpipinstall-rrequirements.txt#暴露应用运行的端口EXPO
cocos2d-x 3.x lua的api与C++的关系
鱼儿-1226
cocos lua cocos2d-lua
cocos2d-x3.xlua的api与C++的关系1、Lua中如何使用Cocos2d-x引擎中的类?在C++中是这样调用的://Sprite*sprite=Sprite::create();sprite->setPosition(Vec2(100,100));this->addChild(sprite);//而在Lua中是这样调用的://localsprite=cc.Sprite:create(
数据结构:利用邻接矩阵构造图及图的输出c++
Belieber53
c++ 数据结构 算法
输入:请输入顶点数及弧数请按照(顶点,顶点,权值)格式输入各边依附的顶点及权值输出:图的结构如下,用邻接矩阵输出#include#include#include#defineINFINITYINT_MAX//最大值#defineMAX_VERTEX_NUM20//最大顶点个数#defineFALSE0#defineTRUE1#defineOK1#defineERROR-2#defineOVERFL
Lua 5.1 参考手册
weixin_30822451
Lua5.1参考手册byRobertoIerusalimschy,LuizHenriquedeFigueiredo,WaldemarCeles云风译www.codingnow.comCopyright©2006Lua.org,PUC-Rio.Allrightsreserved.1-介绍Lua是一个扩展式程序设计语言,它被设计成支持通用的过程式编程,并有相关数据描述的设施。Lua也能对面向对象编程,
【蓝桥杯C/C++】彻底理解双指针算法
不会喷火的小火龙
# 蓝桥杯 算法与数据结构 算法 数据结构 c++
目录学习目标什么是双指针?双指针的分类核心思想模板写法经典例题移除元素双指针法分析题意具体代码最长连续不重复子序列输入格式输出格式数据范围输入样例:输出样例:核心思路数组元素的目标和输入格式输出格式数据范围输入样例:输出样例:核心思路总结一下学习目标了解双指针算法是什么以及分类理解双指针算法的原理会用代码编写双指针算法在实际题目中灵活运用双指针在数组的开章中我们提到了这个算法,如果没有看的话可以学
C++自研游戏引擎-碰撞检测组件-八叉树AABB检测算法实现
千年奇葩
三维引擎 c++ 人工智能 算法 八叉树
八叉树碰撞检测是一种在三维空间中高效处理物体碰撞检测的算法,其原理可以类比为一个管理三维空间物体的智能系统。这个示例包含两个部分:八叉树部分用于宏观检测,AABB用于微观检测。AABB可以更换为均值或节点检测来提高检测精度。八叉树的构建确定根节点范围首先要为整个碰撞检测系统确定一个初始范围,这就像是为所有参与碰撞检测的物体划定一个“活动区域”。这个范围是一个能够完全容纳所有待检测物体的三维立方体空
elementuiPlus日期范围选择el-date-picker动态禁用时间选择
瑶琴AI前端
vue.js javascript 前端 elementui
记录项目中的一个小需求:使用elementuiPlus日期选择组件时,需要动态禁用可选择的日期,禁止选中今天之后的日期,且选中的日期范围不饿能超过30天。饿了么组件的plus版本去掉了v2版本的配置项picker,改用@calendar-change事件来代替,使用disabled-date属性和@calendar-change事件就可以完成动态禁用日期的行为。代码如下:constcurDay=r
linux es后台启动命令,小白学ES 02-Linux中部署Elasticsearch单机服务
热带汽水
linux es后台启动命令
目录@此部署过程以Elasticsearch-5.6.10版本为例,后续的学习和演示也用此版本.1前提:安装JDK学习使用ES的前提是成功安装JDK——很基础的一项步骤,这里省略.此处学习演示所用的JDK版本为:[root@host-10-0-20-50~]#java-versionjavaversion"1.8.0_151"Java(TM)SERuntimeEnvironment(build1.
java web 项目中封装的下拉列表小组件:实现下拉列表使用者前后端0行代码 ...
weixin_34383618
java 前端 后端 ViewUI
导读:主要从4个方面来阐述,1:背景;2:思路;3:代码实现;4:使用一:封装背景像easyui之类的纯前端组件,也有下拉列表组件,但是使用的时候,每个下拉列表,要配一个URL,以及设置URL反回来的值和select的text,和value的对应关系,这有2个问题:一使用者必须知道URL,二,如果页面有10个下拉表表,要请求后台10次,肯定影响性能,而我想要的是使用者只要申明用哪个数据字典就行了,
dialog element 删掉标题_ElementUI 销毁Dialog数据(简单粗暴)
鸿宇太子哥
dialog element 删掉标题
在使用element开发通过之中使用Dialog弹窗创建数据或者数据回显在经常不过了。而且数据创建和数据编辑正常都是使用同一组件。出现的问题:title="提示弹窗":visible.sync="dialogVisible"width="30%"destroy-on-close>使用dialog提供的属性destroy-on-close也并不能实现实时的dialog销毁,进行创建和编辑数据正常的切
【element】谷歌浏览器 el-dialog 概率出现背景全黑,透明度不生效
丶思想
vue vue.js javascript 前端
项目场景:谷歌浏览器问题描述谷歌浏览器el-dialog概率出现背景全黑,透明度不生效classv-modalopacity:0.5不生效解决方案:采用opacity:1background:0.5透明度的黑色代码如下:importVuefrom'vue'importElementfrom'element-ui'import'../element-variables.scss'/***来自Kare
Part 3 第八章 风格指南与规则(Style Guides and Rules)
odoo中国
软件工程 软件工程
概要第八章探讨了Google如何通过风格指南和规则来管理其庞大的代码库,并确保代码的可维护性和一致性。以下是本章的核心内容:1.为什么需要规则?规则的目标:规则旨在鼓励“好的”行为并阻止“坏的”行为。这些定义因组织而异,取决于组织的目标和价值观。规则与指导的区别:规则是强制性的法律,而指导则是建议和最佳实践,通常允许一定的灵活性。Google的风格指南:Google的风格指南不仅是代码格式化的集合
element-ui修改dialog样式全局、局部修改问题 el-dialog__title/el-dialog__header/el-dialog__body/el-dialog__footer
春云资源
element ui css html css3
//没有scoped全局css全部文件生效但是其他地方用dialog样式也会生效//有scoped为局部css--scoped只在本文件生效在这里设置el-dialog__header不生效//因此通过el-dialog的class进行全局css中局部定义只在当前文件生效点击打开Dialogdialog内容取消确定exportdefault{data(){return{centerDialogVi
OpenWebUI,RAG+外部知识库+AI写文的开源应用
m0_74824780
人工智能 开源
引言自从去年AI火起来之后,很多人便热衷于寻找适合自用的AI开源项目,把各家大模型API接入到自己的AI程序里,便可以通过AI辅助完成一系列日常任务,比如内容翻译/润色/总结/撰写、格式转换、数据分类、代码分析、角色扮演等等。一般情况下,大模型依靠自身训练数据便能够完成的任务质量偏高,像翻译总结、格式转换之类,市面上所有的AI程序基本都能够满足这一点需求;但是需要结合外部资料/超长上文信息/实时信
PHP 使用 Redis 实现分布式锁
半桶水专家
php Redis php redis 分布式
要在PHP中使用Redis实现分布式锁,可以使用类似的逻辑:通过SETNXPX命令获取锁,并通过唯一标识符(UUID)确保释放锁的正确性。以下是基于PHP的实现。PHP使用Redis实现分布式锁1.安装Redis扩展在PHP中使用Redis,你需要安装phpredis扩展。可以通过以下命令安装:peclinstallredis安装完成后,确保在php.ini中启用了Redis扩展:extensio
共享内存的数据结构 ——循环队列+信息量 ——互斥锁、多进程的消费者模型 源码模型 测试代码 C++
sevenysq
数据结构 c++ centos linux
前言:简单来说,共享内存不能自动扩展,申请多少就是多少,而且只能用C++内置的数据类型。也不能用STL容器,例如vector会自动扩展,容易造成内存泄漏,越界等问题。移动语义也不能用。要想实现多进程的生产/消费者模型只能采用循环队列。循环队列类值得一提的是这里面头尾指针的移动算法:(指针+1)取最大长度的余数。其他都很简单。#include#include#include#include#incl
DeepSeek大模型本地化部署与实践指南
星辰@Sea
人工智能 人工智能 DeepSeek AI nlp
前言在数据隐私要求严苛或网络环境受限的场景中,本地化部署大模型成为企业AI落地的关键需求。本文将手把手教你如何实现DeepSeek大模型的本地化部署,并提供完整的实践案例代码。部署准备硬件要求配置项推荐规格最低要求GPUNVIDIAA10080Gx4RTX309024GCPUIntelXeonSilver4314i7-12700K内存512GBDDR464GBDDR4存储2TBNVMeSSD512
【Py/Java/C++/C/JS/Go六种语言OD独家2024E卷真题】20天拿下华为OD笔试之【前缀和】2024E-分割数组的最大差值【欧弟算法】全网注释最详细分类最全的华为OD真题题解
闭着眼睛学算法
最新华为OD真题 # 前缀和 java c++ c语言 华为od javascript 算法 python
可上欧弟OJ系统练习华子OD、大厂真题绿色聊天软件戳oj1441了解算法冲刺训练(备注【CSDN】否则不通过)文章目录相关推荐阅读题目描述与示例题目描述输入描述输出描述示例输入输出说明解题思路代码pythonjavaC++CNodejavaScriptGo时空复杂度华为OD算法/大厂面试高频题算法练习冲刺训练相关推荐阅读【华为OD机考】2024E+D卷最全真题【完全原创题解|详细考点分类|不断更新
【Py/Java/C++/C/JS/Go六种语言OD独家2024E卷真题】20天拿下华为OD笔试之【排序】2024E-热点网站统计【欧弟算法】全网注释最详细分类最全的华为OD真题题解
闭着眼睛学算法
最新华为OD真题 # 模拟 java c++ c语言 华为od golang 算法 leetcode
可上欧弟OJ系统练习华子OD、大厂真题绿色聊天软件戳oj1441了解算法冲刺训练(备注【CSDN】否则不通过)文章目录相关推荐阅读题目描述与示例题目描述输入描述输出描述示例一输入输出示例二输入输出解题思路代码pythonjavacppCNodejavaScriptGo时空复杂度华为OD算法/大厂面试高频题算法练习冲刺训练相关推荐阅读【华为OD机考】2024E+D卷最全真题【完全原创题解|详细考点分
【Py/Java/C++/C/JS/Go六种语言OD独家2024E卷真题】20天拿下华为OD笔试之【前缀和】2024E-环中最长子串2【欧弟算法】全网注释最详细分类最全的华为OD真题题解
闭着眼睛学算法
最新华为OD真题 # 前缀和 # 哈希表 算法 java c++ leetcode javascript c语言 华为od
可上欧弟OJ系统练习华子OD、大厂真题绿色聊天软件戳oj1441了解算法冲刺训练(备注【CSDN】否则不通过)文章目录相关推荐阅读题目描述与示例题目描述输入描述输出描述示例输入输出说明解题思路考虑非环字符串通过前缀确定任意连续字串的情况只考奇偶性而非具体数量三个差值均为偶数的情况将奇偶性状态压缩为数字状态压缩为后的前缀和数组的构建根据前缀和数组找到最长子字符串考虑环形字符串原字符串自身拼接储存下标
【Py/Java/C++/C/JS/Go六种语言OD独家2024E卷真题】20天拿下华为OD笔试之【BFS】2024E-狼羊过河【欧弟算法】全网注释最详细分类最全的华为OD真题题解
闭着眼睛学算法
最新华为OD真题 # BFS # 模拟 算法 java c++ 华为od c语言 javascript leetcode
可上欧弟OJ系统练习华子OD、大厂真题绿色聊天软件戳oj1441了解算法冲刺训练(备注【CSDN】否则不通过)文章目录相关推荐阅读题目描述与示例题目描述输入描述输出描述补充说明示例输入输出说明解题思路转化为搜索状态树最小层数问题节点的设计以及更新重复状态的排除代入BFS代码框架代码pythonJavaC++CNodeJavaScriptGo时空复杂度华为OD算法/大厂面试高频题算法练习冲刺训练相关
【Py/Java/C++/C/JS/Go六种语言OD独家2024E卷真题】20天拿下华为OD笔试之【模拟】2024E-找终点【欧弟算法】全网注释最详细分类最全的华为OD真题题解
闭着眼睛学算法
最新华为OD真题 # 模拟 java c++ c语言 leetcode golang 华为od
可上欧弟OJ系统练习华子OD、大厂真题绿色聊天软件戳oj1441了解算法冲刺训练(备注【CSDN】否则不通过)文章目录相关推荐阅读题目描述与示例题目描述输入描述输出描述示例一输入输出说明示例二输入输出解题思路代码pythonjavaC++CNodejavaScriptGo时空复杂度华为OD算法/大厂面试高频题算法练习冲刺训练相关推荐阅读【华为OD机考】2024E+D卷最全真题【完全原创题解|详细考
【Py/Java/C++/C/JS/Go六种语言OD独家2024E卷真题】20天拿下华为OD笔试之【DFS/BFS】2024E-战场索敌【欧弟算法】全网注释最详细分类最全的华为OD真题题解
闭着眼睛学算法
最新华为OD真题 # BFS # DFS 算法 java c++ c语言 leetcode 华为od javascript
可上欧弟OJ系统练习华子OD、大厂真题绿色聊天软件戳oj1441了解算法冲刺训练(备注【CSDN】否则不通过)文章目录相关推荐阅读题目描述与示例题目描述输入描述输出描述示例输入输出解题思路代码解法一:BFSpythonjavacppCNodejavaScriptGo解法二:DFSpythonjavacppCNodejavaScriptGo时空复杂度华为OD算法/大厂面试高频题算法练习冲刺训练相关推
【Py/Java/C++/C/JS/Go六种语言OD独家2024E卷真题】20天拿下华为OD笔试之【贪心】2024E-用户调度问题【欧弟算法】全网注释最详细分类最全的华为OD真题题解
闭着眼睛学算法
最新华为OD真题 # 贪心 java c++ c语言 leetcode 华为od javascript python
可上欧弟OJ系统练习华子OD、大厂真题绿色聊天软件戳oj1441了解算法冲刺训练(备注【CSDN】否则不通过)文章目录相关推荐阅读题目描述与示例题目描述输入描述输出描述示例输入输出说明解题思路代码pythonjavacppCNodejavaScriptGo时空复杂度华为OD算法/大厂面试高频题算法练习冲刺训练相关推荐阅读【华为OD机考】2024E+D卷最全真题【完全原创题解|详细考点分类|不断更新
【Py/Java/C++/C/JS/Go六种语言OD独家2024E卷真题】20天拿下华为OD笔试之【贪心】2024E-静态代码扫描服务【欧弟算法】全网注释最详细分类最全的华为OD真题题解
闭着眼睛学算法
最新华为OD真题 # 贪心 java c++ c语言 华为od python 算法 javascript
可上欧弟OJ系统练习华子OD、大厂真题绿色聊天软件戳oj1441了解算法冲刺训练(备注【CSDN】否则不通过)文章目录相关推荐阅读题目描述与示例题目描述输入描述输出描述示例一输入输出说明示例二输入输出解题思路题意理解贪心策略代码pythonjavacppCNodejavaScriptgo时空复杂度华为OD算法/大厂面试高频题算法练习冲刺训练相关推荐阅读【华为OD机考】2024E+D卷最全真题【完全
【Py/Java/C++/C/JS/Go六种语言OD独家2024E卷真题】20天拿下华为OD笔试之【二分查找】2024E-部门人力分配【欧弟算法】全网注释最详细分类最全的华为OD真题题解
闭着眼睛学算法
最新华为OD真题 # 二分查找 # 贪心 java c++ 华为od leetcode 算法 python
可上欧弟OJ系统练习华子OD、大厂真题绿色聊天软件戳oj1441了解算法冲刺训练(备注【CSDN】否则不通过)文章目录相关推荐阅读题目描述与示例题目描述输入描述输出描述示例输入输出说明解题思路代码pythonjavacppCNodejavaScriptgo时空复杂度本题易错点左指针初始化问题华为OD算法/大厂面试高频题算法练习冲刺训练相关推荐阅读【华为OD机考】2024E+D卷最全真题【完全原创题
怎么样才能成为专业的程序员?
cocos2d-x小菜
编程 PHP
如何要想成为一名专业的程序员?仅仅会写代码是不够的。从团队合作去解决问题到版本控制,你还得具备其他关键技能的工具包。当我们询问相关的专业开发人员,那些必备的关键技能都是什么的时候,下面是我们了解到的情况。
关于如何学习代码,各种声音很多,然后很多人就被误导为成为专业开发人员懂得一门编程语言就够了?!呵呵,就像其他工作一样,光会一个技能那是远远不够的。如果你想要成为
java web开发 高并发处理
BreakingBad
java Web 并发 开发 处理 高
java处理高并发高负载类网站中数据库的设计方法(java教程,java处理大量数据,java高负载数据) 一:高并发高负载类网站关注点之数据库 没错,首先是数据库,这是大多数应用所面临的首个SPOF。尤其是Web2.0的应用,数据库的响应是首先要解决的。 一般来说MySQL是最常用的,可能最初是一个mysql主机,当数据增加到100万以上,那么,MySQL的效能急剧下降。常用的优化措施是M-S(
mysql批量更新
ekian
mysql
mysql更新优化:
一版的更新的话都是采用update set的方式,但是如果需要批量更新的话,只能for循环的执行更新。或者采用executeBatch的方式,执行更新。无论哪种方式,性能都不见得多好。
三千多条的更新,需要3分多钟。
查询了批量更新的优化,有说replace into的方式,即:
replace into tableName(id,status) values
微软BI(3)
18289753290
微软BI SSIS
1)
Q:该列违反了完整性约束错误;已获得 OLE DB 记录。源:“Microsoft SQL Server Native Client 11.0” Hresult: 0x80004005 说明:“不能将值 NULL 插入列 'FZCHID',表 'JRB_EnterpriseCredit.dbo.QYFZCH';列不允许有 Null 值。INSERT 失败。”。
A:一般这类问题的存在是
Java中的List
g21121
java
List是一个有序的 collection(也称为序列)。此接口的用户可以对列表中每个元素的插入位置进行精确地控制。用户可以根据元素的整数索引(在列表中的位置)访问元素,并搜索列表中的元素。
与 set 不同,列表通常允许重复
读书笔记
永夜-极光
读书笔记
1. K是一家加工厂,需要采购原材料,有A,B,C,D 4家供应商,其中A给出的价格最低,性价比最高,那么假如你是这家企业的采购经理,你会如何决策?
传统决策: A:100%订单 B,C,D:0%
&nbs
centos 安装 Codeblocks
随便小屋
codeblocks
1.安装gcc,需要c和c++两部分,默认安装下,CentOS不安装编译器的,在终端输入以下命令即可yum install gccyum install gcc-c++
2.安装gtk2-devel,因为默认已经安装了正式产品需要的支持库,但是没有安装开发所需要的文档.yum install gtk2*
3. 安装wxGTK
yum search w
23种设计模式的形象比喻
aijuans
设计模式
1、ABSTRACT FACTORY—追MM少不了请吃饭了,麦当劳的鸡翅和肯德基的鸡翅都是MM爱吃的东西,虽然口味有所不同,但不管你带MM去麦当劳或肯德基,只管向服务员说“来四个鸡翅”就行了。麦当劳和肯德基就是生产鸡翅的Factory 工厂模式:客户类和工厂类分开。消费者任何时候需要某种产品,只需向工厂请求即可。消费者无须修改就可以接纳新产品。缺点是当产品修改时,工厂类也要做相应的修改。如:
开发管理 CheckLists
aoyouzi
开发管理 CheckLists
开发管理 CheckLists(23) -使项目组度过完整的生命周期
开发管理 CheckLists(22) -组织项目资源
开发管理 CheckLists(21) -控制项目的范围开发管理 CheckLists(20) -项目利益相关者责任开发管理 CheckLists(19) -选择合适的团队成员开发管理 CheckLists(18) -敏捷开发 Scrum Master 工作开发管理 C
js实现切换
百合不是茶
JavaScript 栏目切换
js主要功能之一就是实现页面的特效,窗体的切换可以减少页面的大小,被门户网站大量应用思路:
1,先将要显示的设置为display:bisible 否则设为none
2,设置栏目的id ,js获取栏目的id,如果id为Null就设置为显示
3,判断js获取的id名字;再设置是否显示
代码实现:
html代码:
<di
周鸿祎在360新员工入职培训上的讲话
bijian1013
感悟 项目管理 人生 职场
这篇文章也是最近偶尔看到的,考虑到原博客发布者可能将其删除等原因,也更方便个人查找,特将原文拷贝再发布的。“学东西是为自己的,不要整天以混的姿态来跟公司博弈,就算是混,我觉得你要是能在混的时间里,收获一些别的有利于人生发展的东西,也是不错的,看你怎么把握了”,看了之后,对这句话记忆犹新。 &
前端Web开发的页面效果
Bill_chen
html Web Microsoft
1.IE6下png图片的透明显示:
<img src="图片地址" border="0" style="Filter.Alpha(Opacity)=数值(100),style=数值(3)"/>
或在<head></head>间加一段JS代码让透明png图片正常显示。
2.<li>标
【JVM五】老年代垃圾回收:并发标记清理GC(CMS GC)
bit1129
垃圾回收
CMS概述
并发标记清理垃圾回收(Concurrent Mark and Sweep GC)算法的主要目标是在GC过程中,减少暂停用户线程的次数以及在不得不暂停用户线程的请夸功能,尽可能短的暂停用户线程的时间。这对于交互式应用,比如web应用来说,是非常重要的。
CMS垃圾回收针对新生代和老年代采用不同的策略。相比同吞吐量垃圾回收,它要复杂的多。吞吐量垃圾回收在执
Struts2技术总结
白糖_
struts2
必备jar文件
早在struts2.0.*的时候,struts2的必备jar包需要如下几个:
commons-logging-*.jar Apache旗下commons项目的log日志包
freemarker-*.jar  
Jquery easyui layout应用注意事项
bozch
jquery 浏览器 easyui layout
在jquery easyui中提供了easyui-layout布局,他的布局比较局限,类似java中GUI的border布局。下面对其使用注意事项作简要介绍:
如果在现有的工程中前台界面均应用了jquery easyui,那么在布局的时候最好应用jquery eaysui的layout布局,否则在表单页面(编辑、查看、添加等等)在不同的浏览器会出
java-拷贝特殊链表:有一个特殊的链表,其中每个节点不但有指向下一个节点的指针pNext,还有一个指向链表中任意节点的指针pRand,如何拷贝这个特殊链表?
bylijinnan
java
public class CopySpecialLinkedList {
/**
* 题目:有一个特殊的链表,其中每个节点不但有指向下一个节点的指针pNext,还有一个指向链表中任意节点的指针pRand,如何拷贝这个特殊链表?
拷贝pNext指针非常容易,所以题目的难点是如何拷贝pRand指针。
假设原来链表为A1 -> A2 ->... -> An,新拷贝
color
Chen.H
JavaScript html css
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <HTML> <HEAD>&nbs
[信息与战争]移动通讯与网络
comsci
网络
两个坚持:手机的电池必须可以取下来
光纤不能够入户,只能够到楼宇
建议大家找这本书看看:<&
oracle flashback query(闪回查询)
daizj
oracle flashback query flashback table
在Oracle 10g中,Flash back家族分为以下成员:
Flashback Database
Flashback Drop
Flashback Table
Flashback Query(分Flashback Query,Flashback Version Query,Flashback Transaction Query)
下面介绍一下Flashback Drop 和Flas
zeus持久层DAO单元测试
deng520159
单元测试
zeus代码测试正紧张进行中,但由于工作比较忙,但速度比较慢.现在已经完成读写分离单元测试了,现在把几种情况单元测试的例子发出来,希望有人能进出意见,让它走下去.
本文是zeus的dao单元测试:
1.单元测试直接上代码
package com.dengliang.zeus.webdemo.test;
import org.junit.Test;
import o
C语言学习三printf函数和scanf函数学习
dcj3sjt126com
c printf scanf language
printf函数
/*
2013年3月10日20:42:32
地点:北京潘家园
功能:
目的:
测试%x %X %#x %#X的用法
*/
# include <stdio.h>
int main(void)
{
printf("哈哈!\n"); // \n表示换行
int i = 10;
printf
那你为什么小时候不好好读书?
dcj3sjt126com
life
dady, 我今天捡到了十块钱, 不过我还给那个人了
good girl! 那个人有没有和你讲thank you啊
没有啦....他拉我的耳朵我才把钱还给他的, 他哪里会和我讲thank you
爸爸, 如果地上有一张5块一张10块你拿哪一张呢....
当然是拿十块的咯...
爸爸你很笨的, 你不会两张都拿
爸爸为什么上个月那个人来跟你讨钱, 你告诉他没
iptables开放端口
Fanyucai
linux iptables 端口
1,找到配置文件
vi /etc/sysconfig/iptables
2,添加端口开放,增加一行,开放18081端口
-A INPUT -m state --state NEW -m tcp -p tcp --dport 18081 -j ACCEPT
3,保存
ESC
:wq!
4,重启服务
service iptables
Ehcache(05)——缓存的查询
234390216
排序 ehcache 统计 query
缓存的查询
目录
1. 使Cache可查询
1.1 基于Xml配置
1.2 基于代码的配置
2 指定可搜索的属性
2.1 可查询属性类型
2.2 &
通过hashset找到数组中重复的元素
jackyrong
hashset
如何在hashset中快速找到重复的元素呢?方法很多,下面是其中一个办法:
int[] array = {1,1,2,3,4,5,6,7,8,8};
Set<Integer> set = new HashSet<Integer>();
for(int i = 0
使用ajax和window.history.pushState无刷新改变页面内容和地址栏URL
lanrikey
history
后退时关闭当前页面
<script type="text/javascript">
jQuery(document).ready(function ($) {
if (window.history && window.history.pushState) {
应用程序的通信成本
netkiller.github.com
虚拟机 应用服务器 陈景峰 netkiller neo
应用程序的通信成本
什么是通信
一个程序中两个以上功能相互传递信号或数据叫做通信。
什么是成本
这是是指时间成本与空间成本。 时间就是传递数据所花费的时间。空间是指传递过程耗费容量大小。
都有哪些通信方式
全局变量
线程间通信
共享内存
共享文件
管道
Socket
硬件(串口,USB) 等等
全局变量
全局变量是成本最低通信方法,通过设置
一维数组与二维数组的声明与定义
恋洁e生
二维数组 一维数组 定义 声明 初始化
/** * */ package test20111005; /** * @author FlyingFire * @date:2011-11-18 上午04:33:36 * @author :代码整理 * @introduce :一维数组与二维数组的初始化 *summary: */ public c
Spring Mybatis独立事务配置
toknowme
mybatis
在项目中有很多地方会使用到独立事务,下面以获取主键为例
(1)修改配置文件spring-mybatis.xml <!-- 开启事务支持 --> <tx:annotation-driven transaction-manager="transactionManager" /> &n
更新Anadroid SDK Tooks之后,Eclipse提示No update were found
xp9802
eclipse
使用Android SDK Manager 更新了Anadroid SDK Tooks 之后,
打开eclipse提示 This Android SDK requires Android Developer Toolkit version 23.0.0 or above, 点击Check for Updates
检测一会后提示 No update were found
| | |