当前位置: 首页 > news >正文

使用Windows创建一个MFC应用【带界面】

MFC使用教程【对初学者保姆型友好!】

目录

前提条件

1:创建MFC应用程序

2. 项目结构解读

引用

外部依赖项

头文件

源文件

资源文件

文件功能详解

项目的主要流程

步骤2:配置OpenCV

安装OpenCV

包含目录与库文件

步骤3:添加UI控件

添加控件

1. 创建MFC对话框应用程序

2. 设计UI界面

解决方法

3. 实现控件处理逻辑


前提条件

1. 安装Visual Studio 2019,并确保安装了MFC组件。

什么是MFC呢?
MFC(Microsoft Foundation Classes)是微软基础类库,以C++类的形式封装了Windows API,并且包含一个应用程序框架,以减少应用程序开发人员的工作量。它抽象了Windows API,提供了一个基于C++的面向对象的方式,使得程序员可以更容易地开发出图形用户界面(GUI)应用程序。

2. 安装OpenCV库,并配置Visual Studio以便在项目中使用OpenCV。

1:创建MFC应用程序

1. 打开Visual Studio 2019,创建一个新的项目。

2. 选择“MFC应用”并点击“下一步”。

可见,MFC有三个选项:MFC ActiveX控件、MFC应用程序、MFC DLL。

MFC ActiveX 控件:用来生成MFCActiveX控件程序。
MFC ActiveX控件程序
是一种基于微软基金类库(Microsoft Foundation Class Library,简称MFC)开发的ActiveX控件程序。
ActiveX控件是一种软件组件技术,允许开发者创建可重用的对象。
这些对象可以嵌入到不同的应用程序中,比如网页中的控件、桌面应用程序中的组件等。什么是MFC ActiveX控件程序?
MFC(Microsoft Foundation Class Library):是一组类库,用于简化Windows应用程序的开发。
它封装了Windows API,为开发者提供了一个面向对象的编程接口。
ActiveX控件:是一种COM(组件对象模型)组件,通常用于在不同行业应用中提供交互式功能。
ActiveX控件可以插入到网页、桌面应用程序或其他支持OLE(对象链接与嵌入)技术的环境中。什么时候需要用MFC ActiveX控件程序?
需要跨平台组件:如果你需要开发一个控件,它可以在不同的容器(如网页、桌面应用程序等)中使用,
那么ActiveX控件是一个合适的选择。通过使用MFC,开发者可以更方便地创建和管理这些控件。
已有MFC基础:如果你的团队已经熟悉MFC,并且有很多现有的MFC代码库,那么使用MFC来开发ActiveX控件可以节省时间和精力。
需要复杂的用户界面:MFC提供了大量的GUI控件和图形处理功能,这些功能可以用于创建复杂的、交互式的用户界面。
Windows平台开发:ActiveX控件主要针对Windows平台,如果你的应用程序或目标用户群体主要在Windows环境下,使用MFC ActiveX控件会是一个合理的选择。使用MFC ActiveX控件的步骤
创建项目:在Visual Studio中创建一个MFC ActiveX控件项目。这将为你提供一个基本的框架,包含必要的文件和设置。
设计控件:使用MFC类和资源编辑器来设计控件的外观和行为。你可以添加按钮、文本框、图像等元素,并定义它们的交互逻辑。
实现功能:编写控件的功能代码。你可以处理用户输入、与其他组件交互、进行数据处理等。
测试和调试:在开发过程中不断测试和调试控件,确保它在预期的环境中正常工作。
发布和部署:完成开发后,将ActiveX控件打包,并发布给用户。用户可以通过注册控件(使用regsvr32命令)在他们的系统中使用它。总结
MFC ActiveX控件程序在需要可重用的、跨平台的交互组件时非常有用,特别是在Windows环境下。
如果你熟悉MFC并且需要创建复杂的用户界面,MFC ActiveX控件程序是一个强大的工具。
MFC 应用程序:用来生成MFC应用程序。
MFC DLL:用来生成MFC动态链接库程序。

具体如何选择,请参见下下文:

MFC有三个选项:MFC ActiveX控件、MFC应用程序、MFC DLL,如何选择?icon-default.png?t=O83Ahttps://zhuanlan.zhihu.com/p/804853133

3. 配置项目名称和位置,然后点击“创建”。

4. 在MFC应用程序向导中,选择“基于对话框的应用程序”。

应用程序类型有三个选项——单个文档、多个文档、基于对话框。

单个文档:像Windows记事本、Windows画图、Windows写字板这样的程序,一个程序只有一个文档处于编辑状态。

多个文档:像Word,Excel这样可以在一个MDI窗口里面同时处理多个文档的类型。

基于对话框:像Windows扫雷、纸牌那样直接在对话框进行操作的程序。用不着文档。【因此,我们常常选这个!】

5. 点击“完成”以生成基本的MFC应用程序框架。

报错如下:

解决方案如下:

还有一个需要注意的地方就是:mfc项目名字中,不能带有特殊字符。比如下划线,横线等。不然,还是会报错,具体原因,有待考究。

成功后,就会进入到如下界面:

MFC应用程序项目结构。在Visual C++中,创建MFC应用程序时,IDE会生成一个基本的项目结构,其中包括一些头文件、源文件和资源文件。这些文件和目录有不同的功能和用途。

2. 项目结构解读

引用

为了更好的说明,截图来举例:

有同一个解决方案中,有多个项目。一个项目【ConsoleApplication2.1】要使用另一项目【Dll1】

引用:决定了项目的生成顺序。有三个用处:

1.  决定生成整个解决方案的项目顺序:如果不引用Dll1,点击生成整个解决方案时,项目 生成顺序会是ConsoleApplication2.1,Dll1【按照顺序从上到下】。结果是报错。因为ConsoleApplication2.1使用了Dll1,可Dll1还没有生成,所以ConsoleApplication2.1找不到Dll1的lib或dll文件。像这种情况很多公司常见,这时候往往会多生成几遍解决方案,错误会逐渐减少,直至最后成功生成所有项目。
2. 用于生成单个项目:如果我清理了解决方案,然后右键生成ConsoleApplication2.1,会报错,找不到Dll1。所以这时候应该在“引用”中添加Dll1。然后右键生成ConsoleApplication2.1,这时,会先生成Dll1,再生成ConsoleApplication2.1。
3. 用于导出:省略了__declspec(dllexport)。导出库中需要导出的所有前边可省略__declspec(dllexport)。

外部依赖项

所谓外部依赖项,就是工程中显式包含的那些头文件本身所包含的头文件(非自己定义),主要包含一些外部库。

例如

虽然在main里包含了一个windows.h,但是windows.h本身就包含相当多的头文件。而这些头文件又包含更多的头文件。于是你的外部依赖项就变得很庞大。若想减少外部依赖项,那就尽量避免包含不必要的头文件。不过根据编译器的行为,可能编译时应该会过滤掉那些虽然包含进来,但完全没有用到的头文件,可以不必理会外部依赖项。

头文件

  • framework.h:定义了项目所使用的MFC应用程序框架相关的头文件。通常包括预编译头文件的引用。

  • MFCApplication1.h:这是主应用程序类的头文件,一般从CWinApp或其派生类继承。

  • MFCApplication1Dlg.h:这是对话框类的头文件,用于定义主对话框类及其成员函数和变量。对于以对话框为基础的应用程序,这是主要的用户界面。
  • pch.h:预编译头文件,用于提高编译速度。会包含项目中常用的头文件。PCH全称为:Pre-compiled Header,被预先编译过的头文件,对于比较大型的工程,往往编译时间会很久,通过使用PCH,把那些不经常发生改动的头文件都预先编译出来,就可以大大节省实际使用时候的编译时间。实际应用中,还经常把外部调用的API的头文件编译为PCH,比如调用STL、调用Windows的APIwindows.h等等。

使用PCH的缺点:会减弱文件间的关联性。举个例子,原本我有一个cpp,包含了#include <windows.h>,然后我把这些自己不会改的api的头文件放到PCH里,那么之后我再看这个代码,我就不知道这个cpp具体包含了哪些头文件了,我只知道它用到了PCH,但是不能一眼就看出来它包含了头文件windows.h。

  • Resource.h:定义了资源的ID,如对话框、控件、菜单、字符串等资源的标识符。
  • targetver.h:定义了目标版本的Windows平台,用于确保项目能正确地在指定的平台上进行编译。

源文件

  • MFCApplication1.cpp:主应用程序文件,包含CWinApp派生类的实现代码,如应用程序初始化和退出例程。
  • MFCApplication1Dlg.cpp:对话框类的实现文件,包含主要的用户界面逻辑和事件处理函数。
  • pch.cpp:用于生成预编译头文件的源文件。

资源文件

  • MFCApplication1.ico:应用程序图标文件。
  • MFCApplication1.rc:资源脚本文件,包含应用程序的资源定义,如图标、对话框、菜单等。这些资源在编译时被编译进应用程序的可执行文件中。
  • 其他的资源文件(如字符串表等):这些文件也会包含在资源文件中,有时会在目录中显示。

文件功能详解

  • 头文件 (.h)
  • 用于声明类、函数、变量等代码元素,并提供必要的信息给源文件 (.cpp)。

  • 源文件 (.cpp)
  • 包含程序的主要逻辑和实现代码,引用了头文件中声明的内容。

  • 资源文件 (.rc, .ico)
  • 这些文件提供了可视化资源(对话框、菜单、图标等)的定义和实现,通常使用资源编辑器进行管理。

项目的主要流程

  1. 框架初始化:MFCApplication1.cpp中的CWinApp派生类负责初始化应用程序框架。
  2. 用户界面:MFCApplication1Dlg.h和MFCApplication1Dlg.cpp中的对话框类负责展示和处理用户交互。
  3. 资源管理:各资源文件(如.rc文件和.h文件中的ID定义)提供UI元素和其他资源的定义和引用。

这个项目结构是MFC应用程序的标准形式,可以有效地组织和管理项目代码和资源,使开发者可以专注于应用功能的实现。

步骤2:配置OpenCV

安装OpenCV

Releases - OpenCV​opencv.org/releases/

2. 配置Visual Studio项目

右键点击项目,选择“属性”。
在“VC++目录”下,添加OpenCV的 包含目录库目录

包含目录

包含目录与库文件

说明:.h头文件是 编译时必须的,lib是 链接时需要的,dll是 运行时需要的。
不管我们编写的代码有多么简单,都必须经过【 编译 → 链接】 的过程才能生成可执行文件: 编译就是将我们编写的源代码 “翻译” 成计算机可以识别的二进制格式,它们以目标文件的形式存在; 链接就是一个 “打包” 的过程,它将所有的目标文件以及系统组件合成一个可执行文件。

include文件:包含头文件(.h 或.hpp),这些文件定义了函数、类、宏和变量的接口

lib文件:包含库文件(动态库.so 或静态库.a),这些文件是经过编译和链接的二进制文件,包含了实现代码

DLL文件:函数可执行代码。本质上来说,dll和exe的区别不大,都是可执行文件,只是没有直接执行的入口,要通过其他进程来调用而已。

包含目录:C:\Users\15135\Downloads\opencv490\opencv\build\include
库目录:C:\Users\15135\Downloads\opencv490\opencv\build\x64\vc16\lib

库目录

在“链接器”->“输入”下,添加OpenCV的库文件(如opencv_worldXXX.lib)。

步骤3:添加UI控件

1. 打开资源视图,找到对话框资源(通常是IDD_[项目名]_DIALOG)。

添加控件

使用拖放工具添加按钮、标签、目录选择控件等,类似于PyQt的界面。

1. 创建MFC对话框应用程序

在Visual Studio中,创建一个新的MFC应用程序并选择“对话框”作为应用程序类型。

2. 设计UI界面

说明,如果找不到ui界面,这样操作:

先点击rc文件

点击右侧的DIALOG,就会看到左侧是界面【一定要注意,不要随意更改ID后面的值,很容易滑动一下就改了,如果改了,要立刻改回来】。

注意:要对应【aboutbox就是这个关于说明!】

用资源编辑器设计对话框界面【主界面】,添加以下控件:

  • 在对话框上添加了以下控件:
    • 按钮 (Button):
      • “选择图像所在文件夹”:ID设为IDC_BUTTON_FOLDER
      • “图像处理”:ID设为IDC_BUTTON_PROCESS
    • 静态文本 (Static Text):
      • “已选择的文件夹”:ID设为IDC_STATIC_DIRECTORY
      • 日志显示:ID设为IDC_STATIC_LOG
    • 编辑框 (Edit Control):
      • 显示已选择的文件夹路径:ID设为IDC_EDIT_DIRECTORY

bug1

初学者,写代码写着写着,MFC右边工具箱不见了。

解决方法

判断工程的程序是否运行,如果运行那就关闭,如果没运行那就先运行再让他关闭,F5调试,Shift+F5关闭调试,一般运行程序关闭,工具箱自然浮现。或者多点几次界面上的按钮,就有了。

3. 实现控件处理逻辑

要在MFC应用程序中实现控件处理逻辑,需要在对话框类(头文件MFCApplicationImageDealDlg.h中定义的类)中,添加控件变量消息处理函数

初始代码【项目创建一开始,vs就帮我们创建了一些代码】如下:

// MFCApplicationImageDealDlg.h: 头文件
//#pragma once// CMFCApplicationImageDealDlg 对话框
class CMFCApplicationImageDealDlg : public CDialogEx
{
// 构造
public:CMFCApplicationImageDealDlg(CWnd* pParent = nullptr);	// 标准构造函数// 对话框数据
#ifdef AFX_DESIGN_TIMEenum { IDD = IDD_MFCAPPLICATIONIMAGEDEAL_DIALOG };
#endifprotected:virtual void DoDataExchange(CDataExchange* pDX);	// DDX/DDV 支持// 实现
protected:HICON m_hIcon;// 生成的消息映射函数virtual BOOL OnInitDialog();afx_msg void OnSysCommand(UINT nID, LPARAM lParam);afx_msg void OnPaint();afx_msg HCURSOR OnQueryDragIcon();DECLARE_MESSAGE_MAP()
};

修改后的代码:

// MFCApplicationImageDealDlg.h: 头文件
//#pragma once// CMFCApplicationImageDealDlg 对话框
class CMFCApplicationImageDealDlg : public CDialogEx
{// 构造
public:CMFCApplicationImageDealDlg(CWnd* pParent = nullptr);	// 标准构造函数// 对话框数据
#ifdef AFX_DESIGN_TIMEenum { IDD = IDD_MFCAPPLICATIONIMAGEDEAL_DIALOG };
#endifprotected:virtual void DoDataExchange(CDataExchange* pDX);	// DDX/DDV 支持// 实现
protected:HICON m_hIcon;// 生成的消息映射函数virtual BOOL OnInitDialog();afx_msg void OnSysCommand(UINT nID, LPARAM lParam);afx_msg void OnPaint();afx_msg HCURSOR OnQueryDragIcon();DECLARE_MESSAGE_MAP()private:// 控件变量CButton m_btnSelectFolder;CButton m_btnProcess;CStatic m_staticDirectory;CEdit m_editDirectory;// 选定文件夹路径CString m_selectedFolderPath;void UpdateLog(const CString& message);public:afx_msg void OnBnClickedButtonFolder();afx_msg void OnBnClickedButtonProcess();
};

接下来,需要在相应的实现文件MFCApplicationImageDealDlg.cpp中实现这些新增的函数和逻辑,以及将相应的功能,添加到对话框的事件处理函数中。

原始代码如下:

// MFCApplicationImageDealDlg.cpp: 实现文件
//#include "pch.h"
#include "framework.h"
#include "MFCApplicationImageDeal.h"
#include "MFCApplicationImageDealDlg.h"
#include "afxdialogex.h"#ifdef _DEBUG
#define new DEBUG_NEW
#endif// 用于应用程序“关于”菜单项的 CAboutDlg 对话框class CAboutDlg : public CDialogEx
{
public:CAboutDlg();// 对话框数据
#ifdef AFX_DESIGN_TIMEenum { IDD = IDD_ABOUTBOX };
#endifprotected:virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV 支持// 实现
protected:DECLARE_MESSAGE_MAP()
};CAboutDlg::CAboutDlg() : CDialogEx(IDD_ABOUTBOX)
{
}void CAboutDlg::DoDataExchange(CDataExchange* pDX)
{CDialogEx::DoDataExchange(pDX);
}BEGIN_MESSAGE_MAP(CAboutDlg, CDialogEx)
END_MESSAGE_MAP()// CMFCApplicationImageDealDlg 对话框CMFCApplicationImageDealDlg::CMFCApplicationImageDealDlg(CWnd* pParent /*=nullptr*/): CDialogEx(IDD_MFCAPPLICATIONIMAGEDEAL_DIALOG, pParent)
{m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
}void CMFCApplicationImageDealDlg::DoDataExchange(CDataExchange* pDX)
{CDialogEx::DoDataExchange(pDX);
}BEGIN_MESSAGE_MAP(CMFCApplicationImageDealDlg, CDialogEx)ON_WM_SYSCOMMAND()ON_WM_PAINT()ON_WM_QUERYDRAGICON()
END_MESSAGE_MAP()// CMFCApplicationImageDealDlg 消息处理程序BOOL CMFCApplicationImageDealDlg::OnInitDialog()
{CDialogEx::OnInitDialog();// 将“关于...”菜单项添加到系统菜单中。// IDM_ABOUTBOX 必须在系统命令范围内。ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);ASSERT(IDM_ABOUTBOX < 0xF000);CMenu* pSysMenu = GetSystemMenu(FALSE);if (pSysMenu != nullptr){BOOL bNameValid;CString strAboutMenu;bNameValid = strAboutMenu.LoadString(IDS_ABOUTBOX);ASSERT(bNameValid);if (!strAboutMenu.IsEmpty()){pSysMenu->AppendMenu(MF_SEPARATOR);pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);}}// 设置此对话框的图标。  当应用程序主窗口不是对话框时,框架将自动//  执行此操作SetIcon(m_hIcon, TRUE);			// 设置大图标SetIcon(m_hIcon, FALSE);		// 设置小图标// TODO: 在此添加额外的初始化代码return TRUE;  // 除非将焦点设置到控件,否则返回 TRUE
}void CMFCApplicationImageDealDlg::OnSysCommand(UINT nID, LPARAM lParam)
{if ((nID & 0xFFF0) == IDM_ABOUTBOX){CAboutDlg dlgAbout;dlgAbout.DoModal();}else{CDialogEx::OnSysCommand(nID, lParam);}
}// 如果向对话框添加最小化按钮,则需要下面的代码
//  来绘制该图标。  对于使用文档/视图模型的 MFC 应用程序,
//  这将由框架自动完成。void CMFCApplicationImageDealDlg::OnPaint()
{if (IsIconic()){CPaintDC dc(this); // 用于绘制的设备上下文SendMessage(WM_ICONERASEBKGND, reinterpret_cast<WPARAM>(dc.GetSafeHdc()), 0);// 使图标在工作区矩形中居中int cxIcon = GetSystemMetrics(SM_CXICON);int cyIcon = GetSystemMetrics(SM_CYICON);CRect rect;GetClientRect(&rect);int x = (rect.Width() - cxIcon + 1) / 2;int y = (rect.Height() - cyIcon + 1) / 2;// 绘制图标dc.DrawIcon(x, y, m_hIcon);}else{CDialogEx::OnPaint();}
}//当用户拖动最小化窗口时系统调用此函数取得光标
//显示。
HCURSOR CMFCApplicationImageDealDlg::OnQueryDragIcon()
{return static_cast<HCURSOR>(m_hIcon);
}

修改后的代码如下:

// MFCApplicationImageDealDlg.cpp: 实现文件
//#include "pch.h"
#include "framework.h"
#include "MFCApplicationImageDeal.h"
#include "MFCApplicationImageDealDlg.h"
#include "afxdialogex.h"
#include "atlimage.h" // 用于处理图像
#include <fstream>#ifdef _DEBUG
#define new DEBUG_NEW
#endif// 用于应用程序“关于”菜单项的 CAboutDlg 对话框class CAboutDlg : public CDialogEx
{
public:CAboutDlg();// 对话框数据
#ifdef AFX_DESIGN_TIMEenum { IDD = IDD_ABOUTBOX };
#endifprotected:virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV 支持// 实现
protected:DECLARE_MESSAGE_MAP()
};CAboutDlg::CAboutDlg() : CDialogEx(IDD_ABOUTBOX)
{
}void CAboutDlg::DoDataExchange(CDataExchange* pDX)
{CDialogEx::DoDataExchange(pDX);
}BEGIN_MESSAGE_MAP(CAboutDlg, CDialogEx)
END_MESSAGE_MAP()// CMFCApplicationImageDealDlg 对话框CMFCApplicationImageDealDlg::CMFCApplicationImageDealDlg(CWnd* pParent /*=nullptr*/): CDialogEx(IDD_MFCAPPLICATIONIMAGEDEAL_DIALOG, pParent)
{m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
}void CMFCApplicationImageDealDlg::DoDataExchange(CDataExchange* pDX)
{CDialogEx::DoDataExchange(pDX);DDX_Control(pDX, IDC_BUTTON_FOLDER, m_btnSelectFolder);DDX_Control(pDX, IDC_BUTTON_PROCESS, m_btnProcess);DDX_Control(pDX, IDC_STATIC_DIRECTORY, m_staticDirectory);DDX_Control(pDX, IDC_EDIT_DIRECTORY, m_editDirectory);
}BEGIN_MESSAGE_MAP(CMFCApplicationImageDealDlg, CDialogEx)ON_WM_SYSCOMMAND()ON_WM_PAINT()ON_WM_QUERYDRAGICON()ON_BN_CLICKED(IDC_BUTTON_FOLDER, &CMFCApplicationImageDealDlg::OnBnClickedButtonFolder)ON_BN_CLICKED(IDC_BUTTON_PROCESS, &CMFCApplicationImageDealDlg::OnBnClickedButtonProcess)
END_MESSAGE_MAP()// CMFCApplicationImageDealDlg 消息处理程序BOOL CMFCApplicationImageDealDlg::OnInitDialog()
{CDialogEx::OnInitDialog();// 将“关于...”菜单项添加到系统菜单中。// IDM_ABOUTBOX 必须在系统命令范围内。ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);ASSERT(IDM_ABOUTBOX < 0xF000);CMenu* pSysMenu = GetSystemMenu(FALSE);if (pSysMenu != nullptr){BOOL bNameValid;CString strAboutMenu;bNameValid = strAboutMenu.LoadString(IDS_ABOUTBOX);ASSERT(bNameValid);if (!strAboutMenu.IsEmpty()){pSysMenu->AppendMenu(MF_SEPARATOR);pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);}}// 设置此对话框的图标。  当应用程序主窗口不是对话框时,框架将自动//  执行此操作SetIcon(m_hIcon, TRUE);			// 设置大图标SetIcon(m_hIcon, FALSE);		// 设置小图标// TODO: 在此添加额外的初始化代码return TRUE;  // 除非将焦点设置到控件,否则返回 TRUE
}void CMFCApplicationImageDealDlg::OnSysCommand(UINT nID, LPARAM lParam)
{if ((nID & 0xFFF0) == IDM_ABOUTBOX){CAboutDlg dlgAbout;dlgAbout.DoModal();}else{CDialogEx::OnSysCommand(nID, lParam);}
}// 如果向对话框添加最小化按钮,则需要下面的代码
//  来绘制该图标。  对于使用文档/视图模型的 MFC 应用程序,
//  这将由框架自动完成。void CMFCApplicationImageDealDlg::OnPaint()
{if (IsIconic()){CPaintDC dc(this); // 用于绘制的设备上下文SendMessage(WM_ICONERASEBKGND, reinterpret_cast<WPARAM>(dc.GetSafeHdc()), 0);// 使图标在工作区矩形中居中int cxIcon = GetSystemMetrics(SM_CXICON);int cyIcon = GetSystemMetrics(SM_CYICON);CRect rect;GetClientRect(&rect);int x = (rect.Width() - cxIcon + 1) / 2;int y = (rect.Height() - cyIcon + 1) / 2;// 绘制图标dc.DrawIcon(x, y, m_hIcon);}else{CDialogEx::OnPaint();}
}//当用户拖动最小化窗口时系统调用此函数取得光标
//显示。
HCURSOR CMFCApplicationImageDealDlg::OnQueryDragIcon()
{return static_cast<HCURSOR>(m_hIcon);
}void CMFCApplicationImageDealDlg::OnBnClickedButtonFolder()
{CFolderPickerDialog folderDlg;if (folderDlg.DoModal() == IDOK){m_selectedFolderPath = folderDlg.GetPathName();m_editDirectory.SetWindowText(m_selectedFolderPath);UpdateLog(_T("已选择的文件夹: ") + m_selectedFolderPath);}
}void CMFCApplicationImageDealDlg::OnBnClickedButtonProcess()
{UpdateLog(_T("正在处理文件夹: ") + m_selectedFolderPath);// 创建保存处理后图像的文件夹路径CString resultFolderPath = m_selectedFolderPath + _T("\\result");if (!CreateDirectory(resultFolderPath, NULL) && GetLastError() != ERROR_ALREADY_EXISTS)//如果文件夹创建成功或已存在【文件夹创建失败是因为已经存在】,程序继续执行后续操作;//否则【文件夹创建失败,且不是因为已经存在的原因】,程序会显示错误信息并停止处理。{AfxMessageBox(_T("无法创建处理后的图像文件夹!"), MB_OK | MB_ICONERROR);return;}// 图像处理逻辑CFileFind finder; // 创建一个 CFileFind 对象,用于查找文件// CFileFind 是 MFC 提供的类,用于在目录中查找文件。// 设置要查找的文件路径和模式CString strWildcard(m_selectedFolderPath); // 初始化查找路径为用户选择的文件夹路径strWildcard += _T("\\*.jpg"); // 在路径后面添加通配符 "*.jpg" 以查找所有 JPG 图像文件// 开始查找文件BOOL bWorking = finder.FindFile(strWildcard); // 使用通配符查找文件,返回查找的初始状态UpdateLog(_T("查找文件的初始状态: ") + CString(bWorking ? _T("TRUE") : _T("FALSE"))); // 记录初始查找状态while (bWorking){bWorking = finder.FindNextFile(); //在调用 finder.FindFile 后,finder.FindNextFile()仍然遵循最初设定的查找模式;//(例如* .jpg),并会继续查找该目录下的下一个符合模式的文件。UpdateLog(_T("bWorking 状态: ") + CString(bWorking ? _T("TRUE") : _T("FALSE")));// 打印第一次调用 FindFile 找到的文件路径if (bWorking) {CString scenondFilePath = finder.GetFilePath();UpdateLog(_T("第2次找到的文件路径: ") + scenondFilePath);}if (finder.IsDots() || finder.IsDirectory()){UpdateLog(_T("忽略文件夹或点文件: ") + finder.GetFilePath());continue;}CString strFilePath = finder.GetFilePath();UpdateLog(_T("正在处理文件: ") + strFilePath);CImage image;HRESULT hr = image.Load(strFilePath);if (FAILED(hr)){UpdateLog(_T("加载图像失败: ") + strFilePath);continue;}UpdateLog(_T("成功加载图像: ") + strFilePath);// 示例处理:转换为灰度图像for (int y = 0; y < image.GetHeight(); y++){for (int x = 0; x < image.GetWidth(); x++){COLORREF color = image.GetPixel(x, y);BYTE gray = BYTE(0.299 * GetRValue(color) + 0.587 * GetGValue(color) + 0.114 * GetBValue(color));image.SetPixel(x, y, RGB(gray, gray, gray));}}UpdateLog(_T("成功处理图像像素: ") + strFilePath);// 构建新的文件路径CString fileName = finder.GetFileName();CString newFilePath = resultFolderPath + _T("\\") + fileName;hr = image.Save(newFilePath);if (FAILED(hr)){UpdateLog(_T("保存图像失败: ") + newFilePath);}else{UpdateLog(_T("处理并保存了图像: ") + newFilePath);}}AfxMessageBox(_T("恭喜,图像处理完成!"), MB_OK | MB_ICONINFORMATION);
}void CMFCApplicationImageDealDlg::UpdateLog(const CString& message)
{// 将日志信息追加到log.txt文件中std::ofstream logFile;logFile.open("log.txt", std::ios_base::app); // 以追加模式打开文件if (logFile.is_open()){CT2CA pszConvertedAnsiString(message);std::string strStd(pszConvertedAnsiString);logFile << strStd << std::endl;logFile.close();}else{// 如果无法打开日志文件,可以在这里处理错误AfxMessageBox(_T("无法打开日志文件进行写入。"), MB_OK | MB_ICONERROR);}
}

此时,运行项目就会出现如下界面:

为了将图像处理过程独立出来,作为一个单独的函数,方便复用和代码维护。而且图像处理逻辑,可能会在其他地方用到,因此,我们将其放在一个类中【例如一个“工具类”,或者专门处理图像的类】。

因此,我们需要创建一个新的.cpp文件【例如MyImageProcessing.cpp】,并在相应的头文件MyImageProcessing.h中声明这个函数,这样做的目的是:可以保持代码的清晰和模块化。

增加代码:

//MyImageProcessing.h
#pragma once
#include <atlimage.h>  // 使用 CImage 类class MyImageProcessing
{
public:static HRESULT ConvertToGrayscale(CImage& image);
};

增加代码:

// MyImageProcessing.cpp
#include "MyImageProcessing.h"HRESULT ImageProcessing::ConvertToGrayscale(CImage& image)
{for (int y = 0; y < image.GetHeight(); y++){for (int x = 0; x < image.GetWidth(); x++){COLORREF color = image.GetPixel(x, y);BYTE gray = BYTE(0.299 * GetRValue(color) + 0.587 * GetGValue(color) + 0.114 * GetBValue(color));image.SetPixel(x, y, RGB(gray, gray, gray));}}return S_OK;
}

然后在OnBnClickedButtonProcess函数中调用这个新函数:

// MFCApplicationImageDealDlg.cpp: 实现文件
//#include "pch.h"
#include "framework.h"
#include "MFCApplicationImageDeal.h"
#include "MFCApplicationImageDealDlg.h"
#include "afxdialogex.h"
#include "atlimage.h" // 用于处理图像
#include <fstream>
#include "MyImageProcessing.h"#ifdef _DEBUG
#define new DEBUG_NEW
#endif// 用于应用程序“关于”菜单项的 CAboutDlg 对话框class CAboutDlg : public CDialogEx
{
public:CAboutDlg();// 对话框数据
#ifdef AFX_DESIGN_TIMEenum { IDD = IDD_ABOUTBOX };
#endifprotected:virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV 支持// 实现
protected:DECLARE_MESSAGE_MAP()
};CAboutDlg::CAboutDlg() : CDialogEx(IDD_ABOUTBOX)
{
}void CAboutDlg::DoDataExchange(CDataExchange* pDX)
{CDialogEx::DoDataExchange(pDX);
}BEGIN_MESSAGE_MAP(CAboutDlg, CDialogEx)
END_MESSAGE_MAP()// CMFCApplicationImageDealDlg 对话框CMFCApplicationImageDealDlg::CMFCApplicationImageDealDlg(CWnd* pParent /*=nullptr*/): CDialogEx(IDD_MFCAPPLICATIONIMAGEDEAL_DIALOG, pParent)
{m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
}void CMFCApplicationImageDealDlg::DoDataExchange(CDataExchange* pDX)
{CDialogEx::DoDataExchange(pDX);DDX_Control(pDX, IDC_BUTTON_FOLDER, m_btnSelectFolder);DDX_Control(pDX, IDC_BUTTON_PROCESS, m_btnProcess);DDX_Control(pDX, IDC_STATIC_DIRECTORY, m_staticDirectory);DDX_Control(pDX, IDC_EDIT_DIRECTORY, m_editDirectory);
}BEGIN_MESSAGE_MAP(CMFCApplicationImageDealDlg, CDialogEx)ON_WM_SYSCOMMAND()ON_WM_PAINT()ON_WM_QUERYDRAGICON()ON_BN_CLICKED(IDC_BUTTON_FOLDER, &CMFCApplicationImageDealDlg::OnBnClickedButtonFolder)ON_BN_CLICKED(IDC_BUTTON_PROCESS, &CMFCApplicationImageDealDlg::OnBnClickedButtonProcess)
END_MESSAGE_MAP()// CMFCApplicationImageDealDlg 消息处理程序BOOL CMFCApplicationImageDealDlg::OnInitDialog()
{CDialogEx::OnInitDialog();// 将“关于...”菜单项添加到系统菜单中。// IDM_ABOUTBOX 必须在系统命令范围内。ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);ASSERT(IDM_ABOUTBOX < 0xF000);CMenu* pSysMenu = GetSystemMenu(FALSE);if (pSysMenu != nullptr){BOOL bNameValid;CString strAboutMenu;bNameValid = strAboutMenu.LoadString(IDS_ABOUTBOX);ASSERT(bNameValid);if (!strAboutMenu.IsEmpty()){pSysMenu->AppendMenu(MF_SEPARATOR);pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);}}// 设置此对话框的图标。  当应用程序主窗口不是对话框时,框架将自动//  执行此操作SetIcon(m_hIcon, TRUE);			// 设置大图标SetIcon(m_hIcon, FALSE);		// 设置小图标// TODO: 在此添加额外的初始化代码return TRUE;  // 除非将焦点设置到控件,否则返回 TRUE
}void CMFCApplicationImageDealDlg::OnSysCommand(UINT nID, LPARAM lParam)
{if ((nID & 0xFFF0) == IDM_ABOUTBOX){CAboutDlg dlgAbout;dlgAbout.DoModal();}else{CDialogEx::OnSysCommand(nID, lParam);}
}// 如果向对话框添加最小化按钮,则需要下面的代码
//  来绘制该图标。  对于使用文档/视图模型的 MFC 应用程序,
//  这将由框架自动完成。void CMFCApplicationImageDealDlg::OnPaint()
{if (IsIconic()){CPaintDC dc(this); // 用于绘制的设备上下文SendMessage(WM_ICONERASEBKGND, reinterpret_cast<WPARAM>(dc.GetSafeHdc()), 0);// 使图标在工作区矩形中居中int cxIcon = GetSystemMetrics(SM_CXICON);int cyIcon = GetSystemMetrics(SM_CYICON);CRect rect;GetClientRect(&rect);int x = (rect.Width() - cxIcon + 1) / 2;int y = (rect.Height() - cyIcon + 1) / 2;// 绘制图标dc.DrawIcon(x, y, m_hIcon);}else{CDialogEx::OnPaint();}
}//当用户拖动最小化窗口时系统调用此函数取得光标
//显示。
HCURSOR CMFCApplicationImageDealDlg::OnQueryDragIcon()
{return static_cast<HCURSOR>(m_hIcon);
}void CMFCApplicationImageDealDlg::OnBnClickedButtonFolder()
{CFolderPickerDialog folderDlg;if (folderDlg.DoModal() == IDOK){m_selectedFolderPath = folderDlg.GetPathName();m_editDirectory.SetWindowText(m_selectedFolderPath);UpdateLog(_T("已选择的文件夹: ") + m_selectedFolderPath);}
}void CMFCApplicationImageDealDlg::OnBnClickedButtonProcess()
{UpdateLog(_T("正在处理文件夹: ") + m_selectedFolderPath);// 创建保存处理后图像的文件夹路径CString resultFolderPath = m_selectedFolderPath + _T("\\result");if (!CreateDirectory(resultFolderPath, NULL) && GetLastError() != ERROR_ALREADY_EXISTS)//如果文件夹创建成功或已存在【文件夹创建失败是因为已经存在】,程序继续执行后续操作;//否则【文件夹创建失败,且不是因为已经存在的原因】,程序会显示错误信息并停止处理。{AfxMessageBox(_T("无法创建处理后的图像文件夹!"), MB_OK | MB_ICONERROR);return;}// 图像处理逻辑CFileFind finder; // 创建一个 CFileFind 对象,用于查找文件// CFileFind 是 MFC 提供的类,用于在目录中查找文件。// 设置要查找的文件路径和模式CString strWildcard(m_selectedFolderPath); // 初始化查找路径为用户选择的文件夹路径strWildcard += _T("\\*.jpg"); // 在路径后面添加通配符 "*.jpg" 以查找所有 JPG 图像文件// 开始查找文件BOOL bWorking = finder.FindFile(strWildcard); // 使用通配符查找文件,返回查找的初始状态UpdateLog(_T("查找文件的初始状态: ") + CString(bWorking ? _T("TRUE") : _T("FALSE"))); // 记录初始查找状态while (bWorking){bWorking = finder.FindNextFile(); //在调用 finder.FindFile 后,finder.FindNextFile()仍然遵循最初设定的查找模式;//(例如* .jpg),并会继续查找该目录下的下一个符合模式的文件。UpdateLog(_T("bWorking 状态: ") + CString(bWorking ? _T("TRUE") : _T("FALSE")));// 打印第一次调用 FindFile 找到的文件路径if (bWorking) {CString scenondFilePath = finder.GetFilePath();UpdateLog(_T("第2次找到的文件路径: ") + scenondFilePath);}if (finder.IsDots() || finder.IsDirectory()){UpdateLog(_T("忽略文件夹或点文件: ") + finder.GetFilePath());continue;}CString strFilePath = finder.GetFilePath();UpdateLog(_T("正在处理文件: ") + strFilePath);CImage image;HRESULT hr = image.Load(strFilePath);if (FAILED(hr)){UpdateLog(_T("加载图像失败: ") + strFilePath);continue;}// 示例处理:转换为灰度图像hr = MyImageProcessing::ConvertToGrayscale(image); // 调用独立的图像处理函数if (FAILED(hr)){UpdateLog(_T("处理图像失败: ") + strFilePath);continue;}// 构建新的文件路径CString fileName = finder.GetFileName();CString newFilePath = resultFolderPath + _T("\\") + fileName;hr = image.Save(newFilePath);if (FAILED(hr)){UpdateLog(_T("保存图像失败: ") + newFilePath);}else{UpdateLog(_T("处理并保存了图像: ") + newFilePath);}}AfxMessageBox(_T("恭喜,图像处理完成!"), MB_OK | MB_ICONINFORMATION);
}void CMFCApplicationImageDealDlg::UpdateLog(const CString& message)
{// 将日志信息追加到log.txt文件中std::ofstream logFile;logFile.open("log.txt", std::ios_base::app); // 以追加模式打开文件if (logFile.is_open()){CT2CA pszConvertedAnsiString(message);std::string strStd(pszConvertedAnsiString);logFile << strStd << std::endl;logFile.close();}else{// 如果无法打开日志文件,可以在这里处理错误AfxMessageBox(_T("无法打开日志文件进行写入。"), MB_OK | MB_ICONERROR);}
}

相关文章:

使用Windows创建一个MFC应用【带界面】

MFC使用教程【对初学者保姆型友好&#xff01;】 目录 前提条件 1&#xff1a;创建MFC应用程序 2. 项目结构解读 引用 外部依赖项 头文件 源文件 资源文件 文件功能详解 项目的主要流程 步骤2&#xff1a;配置OpenCV 安装OpenCV 包含目录与库文件 步骤3&#xff1…...

springboot整合lombok

只需要引入lombok依赖 <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.16.18</version></dependency> 然后application.yml配置文件中加上 logging: level: …...

使用Arcgis批量自动出图

操作方法如下&#xff1a; 1 2 3 4 5 6 7 设置好选项&#xff0c;开始打印。 8 生成pdf。 第一步&#xff1a;shp放到数据库中&#xff0c;标注转注记&#xff0c;然后编辑注记&#xff0c;符号样式设置好。准备出图&#xff1a;&#xff08;转注记时候尽量压盖监测等选最…...

Web Worker加载外部文件实践

概述 在Web Worker 多线程编程一文中介绍了Web Worker的编程思想,碰巧最近工作中某个工程需要加载外部文件,最大的文件大小达到30MB,Web Worker无疑是不错的选择。 编程实现 不用 Web Worker 加载外部文件使用原生的fetch方法读取文件,其核心代码如下: function loadland…...

2024年中国工业大模型行业发展研究报告|附43页PDF文件下载

工业大模型伴随着大模型技术的发展&#xff0c;逐渐渗透至工业&#xff0c;处于萌芽阶段。 就大模型的本质而言&#xff0c;是由一系列参数化的数学函数组成的计算系统&#xff0c;且是一个概率模型&#xff0c;其工作机制是基于概率和统计推动进行的&#xff0c;而非真正的理解…...

99. UE5 GAS RPG 被动技能实现

在这一篇&#xff0c;我们在之前打下的基础下&#xff0c;实现一下被动技能。 被动技能需要我们在技能栏上面选择升级解锁技能后&#xff0c;将其设置到技能栏&#xff0c;我们先增加被动技能使用的标签。 FGameplayTag Abilities_Passive_HaloOfProtection; //被动技能-守护光…...

U盘装系统,使用U盘启动,提示需要装驱动

今天装win10系统&#xff0c;用的是U盘启动&#xff0c;但安装过程中出现了找不到驱动程序无法完成安装的问题&#xff0c;逛了许多的论坛&#xff0c;换过两三个iso文件都不顶用&#xff0c;使用了许多种方式&#xff0c;都安装失败&#xff0c;最后在某个论坛看到一种安装方式…...

gaussdb 主备 8 数据库安全学习

1 用户及权限 1.1 默认权限机制-未开启三权分立 1.1.1 数据库系统管理员具有与对象所有者相同的权限。也就是说对象创建后&#xff0c;默认只有对象所有者或者系统管理员可以查询、修改和销毁对象&#xff0c;以及通过GRANT将对象的权限授予其他用户。 1.1.2 GaussDB支持以下的…...

React 基础阶段学习计划

React 基础阶段学习计划 目标 能够创建和使用React组件。理解并使用State和Props。掌握事件处理和表单处理。 学习内容 环境搭建 安装Node.js和npm 访问 Node.js官网 下载并安装最新版本的Node.js。打开终端或命令行工具&#xff0c;输入 node -v 和 npm -v 检查是否安装…...

FFmpeg的简单使用【Windows】--- 指定视频的时长

目录 功能描述 效果展示 代码实现 前端代码 后端代码 routers 》users.js routers 》 index.js app.js 功能描述 此案例是在上一个案例【FFmpeg的简单使用【Windows】--- 视频混剪添加背景音乐-CSDN博客】的基础上的进一步完善&#xff0c;可以先去看上一个案例然后再…...

请求参数中字符串的+变成了空格

前端请求 后端接收到的结果 在URL中&#xff0c;某些字符&#xff08;包括空格、、&、? 等&#xff09;需要被编码。具体而言&#xff0c;在URL中&#xff0c;空格通常被编码为 或 %20。因此&#xff0c;如果你在请求参数中使用 &#xff0c;它会被解释为一个空格。 如果…...

前端开发攻略---使用AJAX监控网络请求进度

1、XHR实现 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8" /><meta name"viewport" content"widthdevice-width, initial-scale1.0" /><title>Document</title></head&…...

[已解决]DockerTarBuilder永久解决镜像docker拉取异常问题

前阵子发现阿里云的docker加速镜像失效了&#xff08;甚至连nginx都拉取不了&#xff09;&#xff0c;重新换了并且加多了网络上比较常用的dokcer加速源&#xff0c;可以解决一部分问题&#xff0c;但仍然有一些镜像的某个版本或一些比较冷的镜像就是拉取不了&#xff0c;原因未…...

机器学习实战27-基于双向长短期记忆网络 BiLSTM 的黄金价格模型研究

大家好&#xff0c;我是微学AI&#xff0c;今天给大家介绍一下机器学习实战27-基于双向长短期记忆网络 BiLSTM 的黄金价格模型研究。本文针对黄金价格预测问题&#xff0c;展开基于改造后的长短期记忆网络BiLSTM的黄金价格模型研究。文章首先介绍了项目背景&#xff0c;随后详细…...

阿拉伯应用市场的特点

阿拉伯应用程序市场是一个充满活力和快速增长的行业&#xff0c;由年轻、精通技术的人口、高智能手机渗透率和整个地区数字化程度的提高所塑造。作为世界上最大的消费群体之一&#xff0c;阿拉伯语受众为希望扩大影响力的开发商和品牌提供了巨大的潜力。 文化相关性在应用程序…...

音频响度归一化 - python 实现

在处理音频样本时&#xff0c;往往我们的音频样本由于录制设备&#xff0c;环境&#xff0c;人发音的音量大小的不同影响&#xff0c;会造成音频响度不统一&#xff0c;分布在一个不同的响度值域上。为了让语音模型更好的学习音频特征&#xff0c;就有必要对音频的响度进行归一…...

嵌入式硬件设计详解

嵌入式硬件设计详解 嵌入式硬件设计是一个复杂而精细的过程&#xff0c;它涉及将微控制器&#xff08;MCU&#xff09;、微处理器&#xff08;MPU&#xff09;或数字信号处理器&#xff08;DSP&#xff09;等核心芯片与其他外围电子元件&#xff08;如传感器、执行器、存储器、…...

Linux防火墙与SElinux

文章目录 一、防火墙介绍二、iptables和firewalld的区别操作方式&#xff1a;配置层面&#xff1a;性能和管理&#xff1a; 三、iptables与firewalld的优缺点iptablesfirewalld 四、iptables的工作流程五、firewalld的工作流程六、iptables安装与使用6.1、关闭firewalld服务6.2…...

【MySQL】基本查询(上):创建、读取

1.Create(创建) 语法&#xff1a; INSERT [INTO] table_name [(column [, column] ...)] VALUES (value_list) [, (value_list)] ...value_list: value, [, value] ... 接下来我们用这个下表作为例子&#xff1a; -- 创建一张学生表 CREATE TABLE students ( id INT UNSIGN…...

在线刷题系统测试报告

一、项目背景 1. 本项目是一个在线刷题系统&#xff0c;灵感来源于力扣和牛客等刷题平台&#xff0c;旨在锻炼自己的代码能力和剖析系统整体结构与各模块之间关系的能力。系统支持用户注册与登录&#xff0c;查看题目列表与题目详情&#xff0c;在线提交代码并提供反馈。 2. 该…...

即时通讯增加Redis渠道

情况说明 在本地和服务器分别启动im服务&#xff0c;当本地发送消息时&#xff0c;会发现服务器上并没有收到消息 初版im只支持单机版&#xff0c;不支持分布式的情况。此次针对该情况对项目进行优化,文档中贴出的代码非完整代码&#xff0c;可自行查看参考资料[2] 代码结构调…...

C++list

list简介 list是我们的链表&#xff0c;而且是带头双向循环链表&#xff0c;如下图 我们都知道&#xff0c;链表是由一个一个的节点组成的&#xff0c;它的成员由下面几个部分组成 通过对前面string,vector的学习&#xff0c;其实再来看我们的链表及其成员函数&#xff0c;是…...

设计模式 - 结构型

结构型 适配器模式&#xff0c;代理模式&#xff0c;桥接模式&#xff0c;装饰器模式&#xff0c;外观模式&#xff0c;组合模式&#xff0c;享元模式&#xff0c; 单一职责避免子类爆炸Bridge 模式对象的实现Decorator 模式对对象的职责&#xff0c;不生成子类接口隔离Adapt…...

STM32编码器接口

一、概述 1、Encoder Interface 编码器接口概念 编码器接口可接收增量&#xff08;正交&#xff09;编码器的信号&#xff0c;根据编码器旋转产生的正交信号脉冲&#xff0c;自动控制CNT自增或自减&#xff0c;从而指示编码器的位置、旋转方向和旋转速度每个高级定时器和通用…...

2024客户世界年度大会开幕,码号卫士赋能数字运营服务新升级

10月15日&#xff0c;2024年客户世界年度的大会在通州北投希尔顿酒店开幕。作为行业内的一个重要活动&#xff0c;本次大会以“数字运营支撑服务产业新升级”为主题&#xff0c;吸引了众多行业专家和企业代表。 据悉&#xff0c;本次大会以“数字运营支撑服务产业新升级”为主题…...

AcWing 802. 区间和(离散化算法,python)

本篇博客详细讲解一下离散化知识点&#xff0c;通过讲解和详细列题带大家掌握离散化。 题目&#xff1a; 原题链接&#xff1a;https://www.acwing.com/problem/content/description/804/ 假定有一个无限长的数轴&#xff0c;数轴上每个坐标上的数都是 0。 现在&#xff0c;…...

【网页设计】CSS 盒子模型

目标 能够准确阐述盒子模型的 4 个组成部分能够利用边框复合写法给元素添加边框能够计算盒子的实际大小能够利用盒子模型布局模块案例能够给盒子设置圆角边框能够给盒子添加阴影能够给文字添加阴影 1. 盒子模型 页面布局要学习三大核心, 盒子模型, 浮动 和 定位. 学习好盒子模…...

如何通过构建对应的api服务器使Vue连接到数据库

一、安装数据库驱动 在后端安装 MySQL 数据库驱动&#xff0c;比如在 Node.js 环境中可以使用 mysql2 包来连接 MySQL 数据库。在项目目录下运行以下命令安装&#xff1a; npm install mysql2或者使用 yarn&#xff1a; yarn add mysql2二、创建数据库连接模块 创建一个专门…...

新手给视频加字幕的方法有哪些?4种加字幕方法推荐!

在视频制作中&#xff0c;字幕不仅是传递信息的重要手段&#xff0c;还能增强视频的观感和专业性。对于新手来说&#xff0c;如何给视频添加字幕可能是一个挑战。本文将介绍字幕的类型、推荐添加字幕的工具&#xff0c;以及详细添加字幕方法&#xff0c;帮助新手轻松掌握视频字…...

Oracle实际需要用到但常常被忽略的函数

1、Oracle中nvl()与nvl2()函数 函数nvl(expression1,expression2)根据参数1是否为null返回参数1或参数2的值&#xff1b; 函数nvl2(expression1,expression2,expression3)根据参数1是否为null返回参数2或参数3的值 【函数格式】&#xff1a;nvl(expression1,expression2) 若…...