目录:


项目背景

可行性方面

需求分析

详细设计

测试

维护

项目效果图展示

完整代码


项目背景:

文字信息时代,传统的文字聊天方式已不能满足大众的需求,很多时候文字不能表达自己的想法,或者沟通技巧的欠缺,后就成为了尬聊。"一言不合就斗图",能用一张图说明的。

暴走表情广泛的遍布于网络,网民们大多用作斗图。一般常见于QQ、微信。斗图活动起始于QQ,群聊时大家发送搞趣图片以相互娱乐。后来发展到百度贴吧等各种论坛上,时常有人发帖组织斗图活动。

斗图发展到现在不仅仅用这些表情图,还会恶搞明星、电影人物。以图加文字的自由组合,结合网络语言,制作搞笑逗比的图片。

可行性方面:

  • 经济可行性——低成本
  • 操作可行性——简单
  • 技术可行性——借助其他工具

需求分析:

制作Gif动图,因此选择两种生成方式:1.图片生成;2.视频生成。

技术方面借助其他的工具实现:选择 ffmpeg 工具。

FFmpeg即是一块音视频编解码工具,同时也是一组音视频编解码开发套件,为开发者提供了丰富的音视频 处理调用接口。FFmpeg中的"FF"指的是"Fast Forward",mpeg则是动态图像专家组。 它提供了录制、转换 以及流化音视频的完整解决方案。 它包含了非常先进的音频/视频编解码库 libavcodec, 为了保证高可移植 性和编解码质量, libavcodec 里很多 codec 都是从头开发的。

FFmpeg项目由以下几部分组成:

  • 1. ffmpeg 视频文件转换命令行工具,也支持经过实时电视卡抓取和编码成视频文件。
  • 2. ffserver 基于 HTTP、RTSP 用于实时广播的多媒体服务器.也支持时间平移。
  • 3. ffplay 用 SDL 和 FFmpeg 库开发的一个简单的媒体播放器。
  • 4. libavcodec 一个包含了所有 FFmpeg 音视频编解码器的库.为了保证优性能和高可复用性,大多数编解 码器从头开发的。
  • 5. libavformat 一个包含了所有的普通音视格式的解析器和产生器的库

学习ffmpeg 工具我们可以知道:ffmpeg 是使用命令行的形式,给予 cmd 一定的命令,实现相应的操作。

因此我们的实验原理:在程序中通过 cmd 控制台调用 ffmpeg.exe 工具,并给该工具发送对应的命令,完成所需操作,发命令时,cmd窗口隐藏在后台。

总体设计:

本项目有两种生成gif动态图方式: 1. 使用图片生成 2. 使用短视频生成

实现原理:在程序中通过cmd控制台调用ffmpeg.exe工具,并给该工具发送对应的命令,完成所需操作,发命令时,cmd窗口隐藏在后台。

我们还需要有一定的操作界面:依靠 duilib 库。

DuiLib库是一款由杭州月牙儿网络技术有限公司开发,轻量级的C++界面开发库,遵循开源BSD协议,可以免费用于商业项目。Duilib界面库的优势在于:

  • 1. 基于GDI在窗口上自绘,无其他依赖,未使用特殊或危险的系统调用,能够很好的解决传统MFC界面的一系列问题
  • 2. 使用XML来描述界面风格,界面布局,将界面和逻辑分离,同时易于实现各种超炫的界面效果如换色,换肤,透明等
  • 3. 完全兼容ActiveX控件(如常见的IE控件和Flash),也可以和MFC等界面库配合使用
  • 4. 可广泛用于互联网客户端、工具软件客户端、管理系统客户端、多媒体客户端(如KTV、触摸屏)、车载电脑系统、gps系统和手机客户端软件等。

许多知名公司都采用Duilib作为界面库,比如:华为网盘、PPS、金山快盘、酷我音乐、爱奇艺视频、百度杀毒、百度卫士等一些列产品。

注意:Duilib仅仅是基于Win32的一套UI库。因此要了解 Win32 程序相关知识。


Win32 的相关知识:

一个Win32应用程序可以分为程序代码和UI资源两大部分,两部分终是以rc整合成一个完整的exe可执行程序。所谓UI资源,指的是功能菜单、对话框外貌、程序图标、光标形状等东西。

代码示例,展示Win32 界面:

 #include <Windows.h>
#include <tchar.h>//消息回调函数
LRESULT CALLBACK WinProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) {    switch (message) {    case WM_CLOSE:        if (IDOK == MessageBox(hWnd, _T("你确定退出?"), _T("退出"), MB_OKCANCEL))  {            DestroyWindow(hWnd);  return 0; }    case WM_DESTROY:  PostQuitMessage(0); return 0;   default:   return DefWindowProc(hWnd, message, wParam, lParam);    }
}int APIENTRY _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow) {//Step1:注册一个窗口类    HWND hwnd; //窗口的句柄    WNDCLASSEX wc; //窗口类结构    wc.cbSize = sizeof(WNDCLASSEX);    wc.style = CS_VREDRAW | CS_HREDRAW;    wc.lpszMenuName = 0;   wc.lpszClassName = _T("Win32");   wc.lpfnWndProc = WinProc; //消息回调函数 wc.hInstance = hInstance;  wc.hIcon = NULL; wc.hCursor = NULL;   wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);   wc.cbWndExtra = 0;  wc.cbClsExtra = 0;   wc.hIconSm = NULL;   RegisterClassEx(&wc); //注册窗口   //Step2:创建一个窗口  hwnd = CreateWindow(        _T("Win32"), //窗口的类名,也就是上面我们自定义的窗口类的名字    _T("我的第一个Win32程序"), //窗口的标题    WS_OVERLAPPEDWINDOW, //窗口style      500, //窗口位置x坐标       300, //窗口位置y坐标      800, //窗口宽度       600, //窗口高度      NULL, //父窗口句柄     NULL, //菜单句柄      hInstance, //实例句柄     NULL //创建数据      );    if (!hwnd) {       return FALSE;}       ShowWindow(hwnd, SW_SHOW); //显示窗口   UpdateWindow(hwnd); //刷新//Step3:消息循环   MSG msg;    while (GetMessage(&msg, NULL, 0, 0)){ TranslateMessage(&msg);DispatchMessage(&msg);}return 0;
}

注意:Win32程序的入口点是WinMain,WinMain的四个参数由操作系统负责传递,main是控制台程序的入 口点。

从上文可以看出,一个简单的Win32程序包括以下步骤:

  • 1. 注册窗口类:RegisterClass 窗口创建前,对窗口的属性进行一些设置,主要是窗口的外貌以及行为,比如:窗口的边框、颜色、位 置、标题、颜色、大小等就是窗口的外貌,窗口接受到消息后如何响应,就是为该窗口绑定窗口处理函数,窗口在创建前,必须使用RegisterClass函数告诉系统。
  • 2. 创建窗口:CreateWindow 按照设置的窗口大小、窗口风格、窗口标题、窗口位置等将窗口创建成功。注意:CreateWindow
  • 3. 显示窗口和更新窗口将窗口在界面中展示出来。
  • 4. 消息循环不断从应用程序消息队列中获取消息,交给注册窗口时设定的消息响应函数进行处理。

 Win32的消息循环:

什么是消息?

Windows程序的运行是依靠外部的事件来驱动。换句话说,程序不断等待,等待任何可能的输入,然后做出 判断,再做适当的处理。上述的“输入”是由操作系统捕获到后,以消息的形式发送给应用程序。消息,其实 就是系统内设的一种数据结构:

typedef struct MSG {     HWND hwnd;//hwnd 是窗口的句柄,这个参数将决定由哪个窗口过程函数对消息进行处理      UINT message;      //message是一个消息常量,用来表示消息的类型      WPARAM wParam;     //32 位的附加信息,具体表示什么内容,要视消息的类型而定      LPARAM lParam;     //32 位的附加信息,具体表示什么内容,要视消息的类型而定      DWORD time;       //time 是消息发送的时间      POINT pt;         //消息发送时鼠标所在的位置
}

从上面的消息定义可以看出,消息类型其实就是一个UINT类型的变量。系统定义消息值的范围是:0x00000x03ff,用户自定义消息值的范围是:0x0400-0x07ff,为了便于使用,系统定义了一个宏WM_USER来表示 用户自定义消息的起始值,#define WM_USER 0x0400。

消息的分类

消息类型可以分为两大类:系统定义消息和用户自定义消息。

系统定义消息分为:窗口消息、命令消息、控件通知消息。

  • 1. 窗口消息 与窗口的内部运作有关的消息,如创建窗口,绘制窗口,销毁窗口等  可以是一般的窗口,也可以是 MainFrame,Dialog,控件等。 如:WM_CREATE, WM_PAINT, WM_MOUSEMOVE, WM_CTLCOLOR, WM_HSCROLL等。
  • 2. 命令消息 当用户从菜单选中一个命令项目、按下一个快捷键、点击工具栏上的一个按钮或者点击控件都将发送 WM_COMMAND命令消息。通过消息结构中的wParam和lParam成员就能清楚得知道消息的来源。
  • LOWORD(wParam):代表菜单ID、或控件ID,或快捷键ID;
  • HIWORD(wParam):表示通知码,当消息是 从菜单发出时,则这个值为0,当消息是从快捷键发出时,这个值为1,当消息是从控件发出时,这个值 为通知码,比如按钮的通知码:BN_CLICKED, BN_DBLCLK等。

消息队列:

应用程序获取到各种消息,由硬件产生的消息(如:鼠标移动或键盘按下),放在系统消息队列中,windows 系统或其他windows程序传送过来的消息,放在应用程序的消息队列中,然后由应用程序不断的将消息取走 进行响应。其实就是,程序中有一个获取消息的循环代码,会不断的从操作系统中获取消息。

消息是否被放进消息队列,可将消息分为:

  • 队列消息:一般,程序都是从消息队列中获取消息。消息会先保存在消息队列中,消息循环会从此队列中取出消息并分发到各窗口处理 ,如:WM_PAINT,WM_TIMER,WM_CREATE,WM_QUIT,以及鼠标,键盘消息等。其中,WM_PAINT,WM_TIMER只有在队列中没有其他消息的时候才会被处理,WM_PAINT消息 还会被合并以提高效率。其他所有消息以先进先出(FIFO)的方式被处理。 系统中维护着一个全局的系统消息队列,还会为每一个UI线程维护一个UI线程消息队列。当系统消息队列中存在消息时,系统会根据消息所属的UI线程,分发到应用程序对应的UI线程消息队列中去。
  • 非队列消息:但是还有一部分消息会绕过消息队列,直接发送到窗口过程进行处理 。如WM_ACTIVATE, WM_SETFOCUS, WM_SETCURSOR,WM_WINDOWPOSCHANGED。

详细设计:

1.相关环境的配置。VS2013导入 duilib 库。

参考网上专业博客教程:https://www.cnblogs.com/Alberl/p/3342030.html

2.学习duilib库的使用。

学习专业博客:https://www.xuebuyuan.com/1656742.html

3.各个部分的代码实现:

  • 功能设置部分:
class CDuiFrameWnd : public WindowImplBase
{
public:virtual LPCTSTR    GetWindowClassName() const   { return _T("DUIMainFrame"); }virtual CDuiString GetSkinFile()                { return _T("duilib.xml"); }virtual CDuiString GetSkinFolder()              { return _T(""); }//MessageBox(NULL, _T("路径"), _T("测试"), IDOK);virtual void Notify(TNotifyUI& msg){CDuiString sCtrlName = msg.pSender->GetName();if (msg.sType == _T("click")) {if (sCtrlName == _T("closebtn")) {Close();return;}else if (sCtrlName == _T("minbtn")) {SendMessage(WM_SYSCOMMAND, SC_MINIMIZE, 0);return;}else if (sCtrlName == _T("maxbtn")) {SendMessage(WM_SYSCOMMAND, SC_MAXIMIZE, 0);return;}else if (sCtrlName == _T("restorebtn")) {SendMessage(WM_SYSCOMMAND, SC_RESTORE, 0);return;}else if (sCtrlName == _T("Look")) {LoadFile();return;}else if (sCtrlName == _T("CreatGif")) {CComboBoxUI* pCombox = (CComboBoxUI*)m_PaintManager.FindControl(_T("Combox_Select"));int i = pCombox->GetCurSel();if (i==0) {      //选择方式生成方式:视频 or 图片GenerateGifWithPic();}else {GenerateGifWithView();}return;}else if (sCtrlName == _T("cut")) {Cut();return;}return;}}
  • 加载文件
    //加载文件void LoadFile() {TCHAR  szPeFileExt[1024] = TEXT("*.*");  //打开任意类型的文件TCHAR szPathName[80*MAX_PATH];               //文件路径大小OPENFILENAME ofn = { sizeof(OPENFILENAME) };//ofn.hwndOwner = hWnd;// 打开OR保存文件对话框的父窗口ofn.lpstrFilter = szPeFileExt;lstrcpy(szPathName, TEXT(""));ofn.lpstrFile = szPathName;ofn.nMaxFile = sizeof(szPathName);//存放用户选择文件的 路径及文件名 缓冲区ofn.lpstrTitle = TEXT("选择文件");//选择文件对话框标题ofn.Flags = OFN_EXPLORER | OFN_FILEMUSTEXIST | OFN_ALLOWMULTISELECT;//如果需要选择多个文件 则必须带有  OFN_ALLOWMULTISELECT标志BOOL bOk = GetOpenFileName(&ofn);if (bOk) {CEditUI* pPathEdit = (CEditUI*)m_PaintManager.FindControl(_T("Edit"));pPathEdit->SetText(szPathName);}}
  • 给cmd发送命令
void GenerateGifWithPic() {//CDuiString strCMD(_T(" ffmpeg -r 1 -i D:\项目\项目1\ProjectZhou\Debug\\ffmpeg\\ffmpeg\\Pictrue\\%d.jpg D:\项目\项目1\ProjectZhou\Debug\\ffmpeg\\ffmpeg\\out.gif"));//构造命令CDuiString strFFmpegPath = CPaintManagerUI::GetInstancePath() + _T("\\ffmpeg\\ffmpeg\\ffmpeg ");CDuiString strPictruePath = CPaintManagerUI::GetInstancePath() + _T("\\ffmpeg\\ffmpeg\\Pictrue\\%d.jpg ");CDuiString strOutPath = CPaintManagerUI::GetInstancePath() + _T("\\ffmpeg\\ffmpeg\\out.gif");CDuiString strCMD = (_T("/c "));strCMD += strFFmpegPath;strCMD += _T("-r 1 -i ");strCMD += strPictruePath + strOutPath;SendCmd(strCMD);MessageBox(m_hWnd, _T("图片方式生成Gif成功!"), _T("GIFF"), IDOK);}
  • 视频截取部分
void Cut() {CDuiString strStartTime = ((CEditUI *)m_PaintManager.FindControl(_T("BeginTime")))->GetText();if (!IsVaildTime(strStartTime)) {MessageBox(m_hWnd, _T("输入起始时间有误!"), _T("GIFF"), IDOK);return;}CDuiString strFinishTime = ((CEditUI *)m_PaintManager.FindControl(_T("EndTime")))->GetText();if (!IsVaildTime(strFinishTime)) {MessageBox(m_hWnd, _T("输入结束时间有误!"), _T("GIFF"), IDOK);return;}//构造截取视频的命令CDuiString strFFmpegPath = CPaintManagerUI::GetInstancePath() + _T("\\ffmpeg\\ffmpeg\\ffmpeg ");CDuiString strOutPath = CPaintManagerUI::GetInstancePath() + _T("\\ffmpeg\\ffmpeg\\output.mp4");CDuiString strCMD = (_T("/c "));strCMD += strFFmpegPath;strCMD += _T(" -ss ");strCMD += strStartTime;strCMD += _T(" -to ");strCMD += strFinishTime;strCMD += _T(" -i ");CDuiString strViewPath = ((CEditUI *)m_PaintManager.FindControl(_T("Edit")))->GetText();strCMD += strViewPath;strCMD += _T(" -vcodec copy -acodec copy ");strCMD += strOutPath;SendCmd(strCMD);MessageBox(m_hWnd, _T("视频截取成功!"), _T("GIFF"), IDOK);}
  • 发送cmd 命令是一样的,封装起来。
void SendCmd(const CDuiString& strCMD) {//1. 初始化结构体SHELLEXECUTEINFO strSEInfo;memset(&strSEInfo, 0, sizeof(SHELLEXECUTEINFO));strSEInfo.cbSize = sizeof(SHELLEXECUTEINFO);strSEInfo.fMask = SEE_MASK_NOCLOSEPROCESS;strSEInfo.lpFile = _T("C:\\Windows\\System32\\cmd.exe");构造命令//CDuiString strPictruePath = CPaintManagerUI::GetInstancePath() + _T("\\ffmpeg\\ffmpeg\\Pictrue\\%d.jpg ");//CDuiString strOutPath = CPaintManagerUI::GetInstancePath() + _T("\\ffmpeg\\ffmpeg\\out.gif");//CDuiString strCMD = (_T("/c "));//strCMD += strFFmpegPath;//strCMD += _T("-r 1 -i ");//strCMD += strPictruePath + strOutPath;strSEInfo.lpParameters = strCMD;strSEInfo.nShow = SW_HIDE; //隐藏cmd'窗口//2. 发送cmd命令ShellExecuteEx(&strSEInfo);WaitForSingleObject(strSEInfo.hProcess, INFINITE);//D:\项目\项目1\ProjectZhou\Debug\\ffmpeg\\ffmpeg ffmpeg -r 1 -i D:\项目\项目1\ProjectZhou\Debug\\ffmpeg\\ffmpeg\\Pictrue\\%d.jpg D:\项目\项目1\ProjectZhou\Debug\\ffmpeg\\ffmpeg\\out.gif}
  • 时间判断:
bool IsVaildTime(const CDuiString& strTime) {if (strTime.GetLength() != 8) {return false;}for (int i = 0; i < 8;i++) {if (':'==strTime[i]) {continue;}else if (isdigit(strTime[i])) {continue;}else {return false;}}return true;}
  • 视频生成
//视频生成void GenerateGifWithView() {  //构造命令CDuiString strFFmpegPath = CPaintManagerUI::GetInstancePath() + _T("\\ffmpeg\\ffmpeg\\ffmpeg ");CDuiString strViewPath = CPaintManagerUI::GetInstancePath() + _T("\\ffmpeg\\ffmpeg\\output.mp4 ");CDuiString strOutPath = CPaintManagerUI::GetInstancePath() + _T("\\ffmpeg\\ffmpeg\\outView.gif");CDuiString strCMD = (_T("/c "));strCMD += strFFmpegPath;strCMD += _T("-r 50 -i ");strCMD += strViewPath + strOutPath;SendCmd(strCMD);MessageBox(m_hWnd, _T("视频方式生成Gif成功!"), _T("GIFF"), IDOK);}
  • ASS生成:
    //生成ASSvoid CreatASS() {//构造命令CDuiString strFFmpegPath = CPaintManagerUI::GetInstancePath() + _T("\\ffmpeg\\ffmpeg\\ffmpeg ");CDuiString strViewPath = CPaintManagerUI::GetInstancePath() + _T("\\ffmpeg\\ffmpeg\\output.mp4 ");CDuiString strOutPath = CPaintManagerUI::GetInstancePath() + _T("\\ffmpeg\\ffmpeg\\output.ass ");CDuiString strCMD = (_T("/c "));strCMD += strFFmpegPath;strCMD += _T("-i ");strCMD += strViewPath;strCMD += _T("-an -vn -scodec copy ");strCMD += strOutPath;SendCmd(strCMD);MessageBox(m_hWnd, _T("视频方式生成Gif成功!"), _T("GIFF"), IDOK);}
  • 主函数部分:
int APIENTRY _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow)
{CPaintManagerUI::SetInstance(hInstance);CDuiFrameWnd duiFrame;duiFrame.Create(NULL, _T("DUIWnd"), UI_WNDSTYLE_FRAME, WS_EX_WINDOWEDGE);duiFrame.CenterWindow();duiFrame.ShowModal();return 0;
}
  • 依靠duilib中的工具进行界面设计:

Duilib的界面布局器:使用Duilib自带的界面布局器打开XML文件,进行自己编辑。

注:这个界面只是自己单独实现的,字幕功能还没有实现,完善,是为需求分析中所实现的功能接口,还并未实现,在不断的完善。

测试:

测试方式:单元测试,且测试方法采用白盒测试

文件加载测试:按钮中实现选择文件,确认是否能够选择。

cmd 命令发送测试:打开 cmd 命令行,在程序中查看输出的命令,将输出命令输入命令行,验证命令输出组装是否成功。

视频剪切测试:和 cmd 命令测试类似,打开 cmd 命令行,在程序中查看输出的命令,将输出命令输入命令行,验证命令输出组装是否成功。注意时间的判断,输入错误时间验证是否有效时间。

视频生成测试:打开 cmd 命令行,在程序中查看输出的命令,将输出命令输入命令行,验证命令输出组装是否成功。

总体测试:完整操作,验证是否和功能对应。


维护:

主要完善性维护。功能的完善,在字幕。

其中有适应性维护,测试中图片的规格过于大,显示上会有一点儿的延迟。


项目效果图展示:

主界面:


图片方式生成:

生成位置:工程中ffmpeg位置的默认位置—— 那个就是最终图片生成的Gif图。


视频方式生成效果展示图:

这里字幕实现忽略,后续完善,直接生成Gif:

完整代码:

https://github.com/Zzzhouxiaochen/Gif

项目:私“图”定制——利用ffmpeg制作Gif相关推荐

  1. 利用FFmpeg制作视频序列

    前言 本文介绍了利用FFmpeg进行视频测试序列制作的方法.所谓测试序列就是将若干个不同场景的视频片段,按照不同的分辨率.码率.编码方法等进行处理,然后拼接成一条长视频,供测试者打分.一般包括以下几个 ...

  2. python绘制渐变图_Python利用imshow制作自定义渐变填充柱状图(colorbar)

    目的 在各种各样的理论计算中,常常需要绘制各种填充图,绘制完后需要加渐变填充的colorbar.可是有些软件如VMD,colorbar渲染后颜色分布有些失真,不能较准确的表达各颜色对应的数值.用ps中 ...

  3. 项目:“表情包”制作---利用ffmpeg制作Gif动态图

    文章目录 项目背景 可行性分析 需求分析 总体设计 详细设计 项目测试 项目结果 项目源码 项目背景 随着互联网传播技术的普及和网络社交文化的繁荣,传统的文字传播在日常表达上不具有完整的信息,同样一句 ...

  4. Mac OS中利用ffmpeg为视频添加字幕

    Mac系统下,利用ffmpeg加字幕 字幕类型 硬字幕,类似视频水印,作为视频的一部分内嵌了. 软字幕,封装字幕,也是内嵌到视频里,不过只是作为渲染,而且需要播放器支持才行. 外挂字幕,就是外部字幕文 ...

  5. 如何用项目甘特图,做好项目汇报

    先引用百度百科的词条,再来回顾一下,什么是甘特图: 甘特图(Gantt chart)又称为横道图.条状图(Bar chart).其通过条状图来显示项目.进度和其他时间相关的系统进展的内在关系随着时间进 ...

  6. 用FFmpeg制作WebP动图

    去年写过一篇文章,是教大家用FFmpeg制作GIF动画的.今天在讨论到项目中碰到的一个.apng动画素材引起的程序崩溃时,有位同学建议:我们为啥不用WebP来代替.apng?是啊,why not? 网 ...

  7. 为利用 QT 制作的项目设置图标

    为利用 QT 制作的项目设置图标 在 .pro 文件末尾添加语句 RC_ICONS = logo.ico logo.ico 即为图标文件的名称

  8. CSS Sprites精灵图(雪碧图)的使用以及利用工具制作

    CSS Sprites的原理(图片整合技术)----精灵图(雪碧图) 原理: 将导航背景图片.按钮背景图片等有规则的合并成一张背景图,将多张图片合为一张整图,然后用background-positio ...

  9. 利用Python制作动图演示坐标变换理论

    利用Python制作动图演示坐标变换理论 永磁同步电机是一个非线性.强耦合的物理系统,因而不便直接进行控制.后有研究人员创造性的提出了坐标变换理论(后逐渐成为矢量控制的一个部分),让永磁同步电机得以转 ...

最新文章

  1. 激光雷达Lidar与毫米波雷达Radar:自动驾驶的利弊
  2. java 导出文件,导出多个文件方案~
  3. c语言实现stack的算法(附完整源码)
  4. Xshell5 评估过期,需要采购,不能使用
  5. Git 12 岁了,送给你 12 个 Git 使用技巧
  6. java response 状态码_response(向客户端写入数据、对相应进行设置(状态码、响应头))...
  7. 16g电脑内存有什么好处_电脑内存8g和16g差别大吗
  8. 12月中国区块链经理人指数:环比出现大幅下跌 企业融资不理想
  9. 2018.11.02 洛谷P2661 信息传递(拓扑排序+搜索)
  10. 网络切片技术缺点_5G中网络切片研究的现状与挑战
  11. Windows7 64位下SDK Manager.exe无法运行问题解决方法
  12. 篮球计时计分器c语言程序,篮球赛计时计分器程序源代码.doc
  13. 量子点电视,下一代显示技术的必争之地
  14. java当中jxl合并行、列
  15. 《可以量化的经济学》凯恩斯主义与…
  16. 损失函数MSE和MAE的区别以及如何选择
  17. 软考论文-成本管理(1)
  18. Omnigraffle Pro 注册码/许可证
  19. wireshark 802.11 WLAN无线报文分析常用技巧总结
  20. #完全背包#CH 5202 自然数拆分Lunatic版

热门文章

  1. 北京翰鑫信息科技有限公司怎么样
  2. JS实现邮箱提示补全效果
  3. Java - JavaFx之fxml文件的编写及基本使用方式
  4. 设计模式(四)注册模式 解决:解决全局共享和交换对象
  5. bat中rar压缩命令
  6. Miktex 安装遇到过的问题
  7. 跳石板-2017网易校招
  8. 时空-问题集锦(转载)
  9. 嵌入式电路中的BUCK VS LDO【转】
  10. Python3.5抓取代理IP并验证有效性