《Windows API每日一练》7.3 计时器时钟

知道如何使用Windows计时器之后,可以看看一些有用的计时器应用程序了。时钟是计时器最明显的应用,我们来看两个例子:一个是数字时钟,另一个是模拟时钟。

本节必须掌握的知识点:

        第45练:7段数码管数字时钟

        第46练:模拟时钟

7.3.1 第45练:7段数码管数字时钟

/*------------------------------------------------------------------

045  WIN32 API 每日一练

     第45个例子DIGCLOCK.C:使用计时器---实现7段数码管数字时钟

     查表法

     OffsetWindowOrgEx函数

     GetLocaleInfo函数

(c) www.bcdaren.com, 2020

----------------------------------------------------------------*/

#include <windows.h>

#define ID_TIMER 1

LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ;

int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,

 PSTR szCmdLine, int iCmdShow)

{

     static TCHAR szAppName[] = TEXT("DigClock");

    (略)

     return msg.wParam;

 }

//显示数字

void DisplayDigit (HDC hdc, int iNumber)

{

      static BOOL fSevenSegment [10][7] = { //7段数码管表

      1, 1, 1, 0, 1, 1, 1, // 0

      0, 0, 1, 0, 0, 1, 0, // 1

      1, 0, 1, 1, 1, 0, 1, // 2

      1, 0, 1, 1, 0, 1, 1, // 3

      0, 1, 1, 1, 0, 1, 0, // 4

      1, 1, 0, 1, 0, 1, 1, // 5

      1, 1, 0, 1, 1, 1, 1, // 6

      1, 0, 1, 0, 0, 1, 0, // 7

      1, 1, 1, 1, 1, 1, 1, // 8

      1, 1, 1, 1, 0, 1, 1 } ; // 9

      static POINT ptSegment [7][6] = { //每根数码管六边形的顶点坐标

      7, 6, 11, 2, 31, 2, 35, 6, 31, 10, 11, 10,

      6, 7, 10, 11, 10, 31, 6, 35, 2, 31, 2, 11,

      36, 7, 40, 11, 40, 31, 36, 35, 32, 31, 32, 11,

      7 , 36, 11, 32, 31, 32, 35, 36, 31, 40, 11, 40,

      6 , 37, 10, 41, 10, 61, 6, 65, 2, 61, 2, 41,

      36, 37, 40, 41, 40, 61, 36, 65, 32, 61, 32, 41,

      7 , 66, 11, 62, 31, 62, 35, 66, 31, 70, 11, 70 } ;

     int iSeg ;

      for (iSeg = 0 ; iSeg < 7 ; iSeg++) //绘制数字

          if (fSevenSegment [iNumber][iSeg])

               Polygon (hdc, ptSegment [iSeg], 6) ; //绘制6边形

}

//显示2个数字,fSuppress---前导0标记值

void DisplayTwoDigits (HDC hdc, int iNumber, BOOL fSuppress)

{

     if (!fSuppress || (iNumber / 10 != 0))

          DisplayDigit(hdc, iNumber / 10);//不为0绘制第1位数

    OffsetWindowOrgEx(hdc, -42, 0, NULL);//原点坐标水平右移42单位宽

     DisplayDigit(hdc, iNumber % 10);   //绘制第2位数

     OffsetWindowOrgEx(hdc, -42, 0, NULL);//再次将原点坐标右移42个单位宽

}

void DisplayColon (HDC hdc) //显示冒号

{

     POINT ptColon[2][4] = { 2, 21, 6, 17, 10, 21, 6,

                         25, 2, 51, 6, 47, 10, 51, 6, 55 };

     //绘制多边形-冒号

     Polygon(hdc, ptColon[0], 4);

     Polygon(hdc, ptColon[1], 4);

     OffsetWindowOrgEx(hdc, -12, 0, NULL);//右移原点坐标--冒号12单位宽

}

//显示时间

void DisplayTime (HDC hdc, BOOL f24Hour, BOOL fSuppress)

{

     SYSTEMTIME st;

     GetLocalTime(&st);//获取当前时间

     if (f24Hour)//24小时显示格式

        DisplayTwoDigits(hdc, st.wHour, fSuppress);//

     else   //12小时显示格式

        DisplayTwoDigits(hdc, (st.wHour %= 12) ? st.wHour : 12, fSuppress);//

     DisplayColon(hdc);//冒号

     DisplayTwoDigits(hdc, st.wMinute, FALSE);//

     DisplayColon(hdc);//冒号

     DisplayTwoDigits(hdc, st.wSecond, FALSE);//

}

//窗口过程

LRESULT CALLBACK WndProc ( HWND hwnd, UINT message, WPARAM wParam,LPARAM lParam)

{

     static BOOL f24Hour, fSuppress ;

     static HBRUSH hBrushRed ;

     static int cxClient, cyClient ;

     HDC hdc ;

     PAINTSTRUCT ps ;

     TCHAR szBuffer [2] ;

     switch (message)

     {

     case WM_CREATE:

           hBrushRed = CreateSolidBrush (RGB (255, 0, 0)) ; //创建红色画刷

           SetTimer (hwnd, ID_TIMER, 1000, NULL) ;//  设置计时器

          //继续往下执行

     case WM_SETTINGCHANGE: //检索系统消息

          //检索用户或进程的默认语言环境-时间格式

           GetLocaleInfo (LOCALE_USER_DEFAULT, LOCALE_ITIME, szBuffer, 2) ;

//时间格式:'1'为24小时格式,'0'为12小时格式

           f24Hour = (szBuffer[0] == '1') ;

          //LOCALE_ITLZERO是否保留小时数的前导0

           GetLocaleInfo (LOCALE_USER_DEFAULT, LOCALE_ITLZERO, szBuffer, 2) ;

           fSuppress = (szBuffer[0] == '0') ; //小时数-TRUE前导0

           InvalidateRect (hwnd, NULL, TRUE) ; //重绘窗口

           return 0 ;

     case WM_SIZE:

          cxClient = LOWORD(lParam);

          cyClient = HIWORD(lParam);

          return 0;

     case WM_TIMER:

          InvalidateRect(hwnd, NULL, TRUE);//重绘窗口工作区

          return 0;

      case WM_PAINT:

           hdc = BeginPaint(hwnd, &ps);

           SetMapMode(hdc, MM_ISOTROPIC);//指定设备上下文的映射模式-等比例缩放

          //MM_ISOTROPIC映射模式可以修改窗口或视口范围

           SetWindowExtEx(hdc, 276, 72, NULL);//窗口设备上下文的水平和垂直范围

//设置为视口设备上下文的水平和垂直范围

           SetViewportExtEx(hdc, cxClient, cyClient, NULL);

           SetWindowOrgEx(hdc, 138, 36, NULL);//指定窗口点映射到视区原点(0,0)

//指定设备点映射到窗口原点(0,0)

           SetViewportOrgEx(hdc, cxClient / 2, cyClient / 2, NULL);

           SelectObject(hdc, GetStockObject(NULL_PEN));//选入设备-空笔,无边框

           SelectObject(hdc, hBrushRed);//选入画刷

           DisplayTime(hdc, f24Hour, fSuppress);//显示时间

           EndPaint(hwnd, &ps);

           return 0;

      case WM_DESTROY:

           KillTimer(hwnd, ID_TIMER);//删除计时器

           DeleteObject(hBrushRed);//删除画刷

           PostQuitMessage(0);

           return 0;

     }

      return DefWindowProc (hwnd, message, wParam, lParam) ;

}

/***************************************************************************

GetLocaleInfo函数:检索有关标识符指定的语言环境的信息

int GetLocaleInfoA(

  LCID   Locale,    //要检索其信息的语言环境标识符。

  LCTYPE LCType,    //要检索的语言环境信息。

  LPSTR  lpLCData, //指向缓冲区指针,该函数在该缓冲区中检索请求的语言环境信息。

  int    cchData    //lpLCData指示的数据缓冲区的大小(以TCHAR值表示)。

);

***************************************************************************

OffsetWindowOrgEx函数:修改为使用指定的水平和垂直偏移的设备上下文窗口原点。

BOOL OffsetWindowOrgEx(

  HDC     hdc, //设备上下文

  int     x,   //水平偏移量,以逻辑单位表示。

  int     y,   //垂直偏移量,以逻辑单位表示。

  LPPOINT lppt //指向POINT结构的指针。先前窗口原点的逻辑坐标位于此结构中。如果lpPoint为NULL,则不返回先前的原点。

);

*/

       运行结果:

图7-3 7段数码管数字时钟

 

总结

实例DIGCLOCK.C的逻辑其实挺简单,之所以看起来稍微复杂是因为使用像素点绘制时间数字。

1.处理WM_CREATE消息是,创建红色画刷和一个计时器。

2.处理WM_SETTINGCHANGE消息,检索系统信息。

//检索用户或进程的默认语言环境-时间格式

     GetLocaleInfo (LOCALE_USER_DEFAULT, LOCALE_ITIME, szBuffer, 2) ;

//时间格式:'1'为24小时格式,'0'为12小时格式

     f24Hour = (szBuffer[0] == '1') ;

     //LOCALE_ITLZERO是否保留小时数的前导0

    GetLocaleInfo (LOCALE_USER_DEFAULT, LOCALE_ITLZERO, szBuffer, 2) ;

fSuppress = (szBuffer[0] == '0') ; //小时数-TRUE前导0

最后调用InvalidateRect函数重绘窗口。

3.处理WM_SIZE消息获取客户区的宽和高。

4.处理WM_TIMER消息重绘窗口。

5.处理WM_PAINT消息绘制7段数码管时间。

首先调用SetMapMode函数将映射模式设置为MM_ISOTROPIC等比例拉伸模式。

然后分别设置窗口和视口的原点坐标和范围,将数字显示在客户区的中心位置。

最后选入空画笔和红色画刷,调用DisplayTime函数绘制无边框的红色时间数字。

DisplayTime函数的实现:

依据标记变量f24Hour判断当前系统的时间格式是24小时格式还是12小时格式,绘制两位数字“小时”。接着绘制冒号,然后再绘制两位数字时间“分”和冒号,最后绘制两位数字时间“秒”。单个数字和冒号直接采用像素矩阵的方法,分别由DisplayDigit函数和DisplayColon函数完成。

6.处理WM_DESTROY消息销毁计时器,删除红色画刷。

7.3.2 第46练:模拟时钟

/*------------------------------------------------------------------

046  WIN32 API 每日一练

     第46个例子CLOCK.C:使用计时器---模拟时钟

     RotatePoint函数-顺时针旋转

     SetIsotropic函数---设置视窗参数

     DrawClock 函数---画时钟

     DrawHands函数---画指针

(c) www.bcdaren.com, 2020

----------------------------------------------------------------*/

#include <windows.h>

#include <math.h>

#define ID_TIMER 1

#define TWOPI (2 * 3.14159)

LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ;

int WINAPI WinMain ( HINSTANCE hInstance, HINSTANCE hPrevInstance,

 PSTR szCmdLine, int iCmdShow)

{

     static TCHAR szAppName[] = TEXT("Clock");

    (略)

     return msg.wParam;

}

//设置视窗参数

void SetIsotropic (HDC hdc, int cxClient, int cyClient)

{

     SetMapMode(hdc, MM_ISOTROPIC);//映射模式-下,x,y轴等比例缩放

//指定的值设置窗口设备上下文的水平和垂直范围。

     SetWindowExtEx(hdc, 1000, 1000, NULL);

//使用指定的值设置为视口设备上下文的水平和垂直范围

     SetViewportExtEx(hdc, cxClient / 2, -cyClient / 2, NULL);

     SetViewportOrgEx(hdc, cxClient / 2, cyClient / 2, NULL);//设置视口原点

}

//顺时针旋转,公式:x = x* cos(α) + y* sin(α) ;y =y* cos(α)-x* sin(α)

void RotatePoint (POINT pt[], int iNum, int iAngle)

{

     int i;

     POINT ptTemp;

     for (i = 0; i < iNum; i++)

     {

          ptTemp.x = (int)(pt[i].x * cos(TWOPI * iAngle / 360) +

               pt[i].y * sin(TWOPI * iAngle / 360));

          ptTemp.y = (int)(pt[i].y * cos(TWOPI * iAngle / 360) -

               pt[i].x * sin(TWOPI * iAngle / 360));

          pt[i] = ptTemp;

     }

}

//画时钟的刻度

void DrawClock (HDC hdc)

{

     int iAngle;

     POINT pt[3];

     for (iAngle = 0; iAngle < 360; iAngle += 6)

     {

          pt[0].x = 0;

          pt[0].y = 900;

          //顺时针旋转

          RotatePoint(pt, 1, iAngle);

          pt[2].x = pt[2].y = iAngle % 5 ? 33 : 100;//圆的大小(直径)

          //Ellipse左上角的矩形坐标点

          pt[0].x -= pt[2].x / 2;

          pt[0].y -= pt[2].y / 2;

          //Ellipse右下角的矩形坐标点

          pt[1].x = pt[0].x + pt[2].x;

          pt[1].y = pt[0].y + pt[2].y;

          //选人黑色画刷

          SelectObject(hdc, GetStockObject(BLACK_BRUSH));

          //绘制椭圆

          Ellipse(hdc, pt[0].x, pt[0].y, pt[1].x, pt[1].y);

     }

}

//画指针

void DrawHands (HDC hdc, SYSTEMTIME * pst, BOOL fChange)

{

     static POINT pt[3][5] = { 0, -150, 100, 0, 0, 600, -100, 0, 0, -150,//时针

                         0, -200, 50, 0, 0, 800, -50, 0, 0, -200,        //分针

                         0,0, 0, 0, 0, 0, 0, 0, 0, 800 };                //秒针

     int i, iAngle[3];

     POINT ptTemp[3][5];

     iAngle[0] = (pst->wHour * 30) % 360 + pst->wMinute / 2;//时针每小时30度。

     iAngle[1] = pst->wMinute * 6;//每分钟,分针走6度。

     iAngle[2] = pst->wSecond * 6;//每秒,秒针走6度。

     memcpy(ptTemp, pt, sizeof(pt));

     //判断是否只画秒针0-1-2或2

     for (i = fChange ? 0 : 2; i < 3; i++)

     {

          RotatePoint(ptTemp[i], 5, iAngle[i]);

          Polyline(hdc, ptTemp[i], 5);

     }

}

LRESULT CALLBACK WndProc ( HWND hwnd, UINT message, WPARAM wParam,LPARAM

lParam)

{

     static int cxClient, cyClient;

     static SYSTEMTIME stPrevious;

     BOOL fChange;

     HDC hdc;

     PAINTSTRUCT ps;

     SYSTEMTIME st;

     switch (message)

     {

     case WM_CREATE:

          //创建计时器,时间间隔1秒,不一定准确,与消息队列相关

          SetTimer(hwnd, ID_TIMER, 1000, NULL);

          GetLocalTime(&st);//获取本地时间

          stPrevious = st;

          return 0;

     case WM_SIZE:

          cxClient = LOWORD(lParam);

          cyClient = HIWORD(lParam);

          return 0;

     case WM_TIMER:

          GetLocalTime(&st);

          //小时和分钟是否改变

          fChange = st.wHour != stPrevious.wHour ||

               st.wMinute != stPrevious.wMinute;

          hdc = GetDC(hwnd);

          //设置窗口参数

          SetIsotropic(hdc, cxClient, cyClient);

          //选入白色画刷

          SelectObject(hdc, GetStockObject(WHITE_PEN));

          DrawHands(hdc, &stPrevious, fChange);//画指针--擦除指针

          //选入黑色画笔

          SelectObject(hdc, GetStockObject(BLACK_PEN));

          DrawHands(hdc, &st, TRUE);//画指针--重绘

          ReleaseDC(hwnd, hdc);

          stPrevious = st;

          return 0;

     case WM_PAINT:

          hdc = BeginPaint(hwnd, &ps);

          //设置窗口参数

          SetIsotropic(hdc, cxClient, cyClient);

          DrawClock(hdc);//画刻度圆点

          DrawHands(hdc, &stPrevious, TRUE);//画时针,分针,秒针

          EndPaint(hwnd, &ps);

          return 0;

     case WM_DESTROY:

          KillTimer(hwnd, ID_TIMER);

          PostQuitMessage(0);

          return 0;

     }

     return DefWindowProc(hwnd, message, wParam, lParam);

}

       运行结果:

图7-4 模拟时钟

 

总结

       ●实例CLOCK.C定义了4个功能函数。

        1.RotatePoint函数:顺时针旋转。公式:x = x* cos(α) + y* sin(α) ;y =y* cos(α)-x* sin(α)。

     2.SetIsotropic函数:设置视窗参数。映射模式设置为等比例缩放模式,设置窗口和视口范围,并将视口原点坐标设置到客户区中心位置。

     3.DrawClock 函数:画时钟。需要绘制12个直经为100像素的大圆和48个直经为33像素的小圆。每旋转6度绘制一个。

     4.DrawHands函数:画指针。根据像素矩阵绘制时针、分针和秒针。时针每小时走30度,每分钟,分针走6度,每秒,秒针走6度。此外还需要判断是否只需要绘制秒针:

     //判断是否只画秒针0-1-2或2

     for (i = fChange ? 0 : 2; i < 3; i++)

     {

          RotatePoint(ptTemp[i], 5, iAngle[i]);

          Polyline(hdc, ptTemp[i], 5);

     }

       ●剩下的是窗口过程相对简单多了。

       1.处理WM_CREATE消息,调用SetTimer函数创建一个计时器,并调用GetLocalTime函数获取本地时间。

       2.处理WM_SIZE消息,获取客户区的宽和高。

       3.处理WM_TIMER消息,先调用SetIsotropic设置窗口参数,然后选入白色画笔,擦除原有的时针、分针和秒针,接着再选入黑色画笔,重新绘制新的时针、分针和秒针。

       4.处理WM_PAINT消息,调用SetIsotropic设置窗口参数。调用DrawClock绘制时间刻度圆点。调用DrawHands函数绘制时针、分针和秒针。

       5.最后处理WM_DESTROY消息,销毁计时器。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/760596.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

KV260视觉AI套件--PYNQ-DPU

目录 1. 简介 2. DPU 原理介绍 2.1 基本原理 2.2 增强型用法 3. DPU 开发流程 3.1 添加 DPU IP 3.2 在 BD 中调用 3.3 配置 DPU 参数 3.4 DPU 与 Zynq MPSoC互联 3.5 分配地址 3.6 生成 Bitstream 3.7 生成 BOOT.BIN 4. 总结 1. 简介 在《Vitis AI 环境搭建 &…

three.js - MeshPhongMaterial材质(实现玻璃水晶球效果)

1、概念 phong网格材质&#xff1a;Mesh - Phong - Material 一种用于具有镜面高光的光泽表面的材质。 它可以模拟&#xff0c;具有镜面高光的光泽表面&#xff0c;提供镜面反射效果。 MeshPhongMaterial&#xff1a; MeshPhongMaterial是一种基于Phong光照模型的材质&#…

Geeker-Admin:现代化的开源后台管理框架

Geeker-Admin&#xff1a;优雅管理&#xff0c;高效开发&#xff0c;尽在Geeker-Admin- 精选真开源&#xff0c;释放新价值。 概览 Geeker-Admin是一个基于Vue 3.4、TypeScript、Vite 5、Pinia和Element-Plus构建的开源后台管理框架。它为开发者提供了一套现代化、响应式的管理…

如何在Ubuntu20上离线安装joern(包括sbt和scala)

在Ubuntu 20上离线安装Joern&#xff0c;由于Joern通常需要通过互联网从其官方源或GitHub等地方下载&#xff0c;但在离线环境中&#xff0c;我们需要通过一些额外的步骤来准备和安装。&#xff08;本人水平有限&#xff0c;希望得到大家的指正&#xff09; 我们首先要做的就是…

【机器学习】Python sorted 函数

目录&#xff1a; 什么是sorted()函数列表降序排序应用到字符串自定义排序规则实际应用 Python中的内置函数——sorted()。 1. 什么是sorted()函数 在Python中&#xff0c;sorted()是一个内置函数&#xff0c;用于对任何可迭代对象&#xff08;如列表、元组、字符串等&…

jenkins 发布服务到linux服务器

1.环境准备 1.1 需要一台已经部署了jenkins的服务器&#xff0c;上面已经集成好了&#xff0c;jdk、maven、nodejs、git等基础的服务。 1.2 需要安装插件 pusblish over ssh 1.3 准备一台额外的linux服务器&#xff0c;安装好jdk 2.流程描述 2.1 配置jenkins&#xff0c;包括p…

每日一题——Python实现PAT乙级1090 危险品装箱(举一反三+思想解读+逐步优化)4千字好文

一个认为一切根源都是“自己不够强”的INTJ 个人主页&#xff1a;用哲学编程-CSDN博客专栏&#xff1a;每日一题——举一反三Python编程学习Python内置函数 Python-3.12.0文档解读 题目链接&#xff1a;https://pintia.cn/problem-sets/994805260223102976/exam/problems/typ…

LoadBalance 负载均衡

什么是负载均衡 负载均衡(Load Balance&#xff0c;简称 LB),是⾼并发,⾼可⽤系统必不可少的关键组件. 当服务流量增⼤时,通常会采⽤增加机器的⽅式进⾏扩容,负载均衡就是⽤来在多个机器或者其他资源中,按照⼀定的规则合理分配负载. 负载均衡的⼀些实现 服务多机部署时,开发⼈…

微积分-导数3(微分法则)

常见函数的导数 常量函数的导数 d d x ( c ) 0 \frac{d}{dx}(c) 0 dxd​(c)0 常量函数的图像是一条水平线 y c y c yc&#xff0c;它的斜率为0&#xff0c;所以我们必须有 f ′ ( x ) 0 f(x) 0 f′(x)0。从导数的定义来看&#xff0c;证明也很简单&#xff1a; f ′ …

44 - 50题高级字符串函数 / 正则表达式 / 子句 - 高频 SQL 50 题基础版

目录 1. 相关知识点2.例子2.44 - 修复表中的名字2.45 - 患某种疾病的患者2.46 - 删除重复的电子邮箱2.47 - 第二高的薪水2.48 - 按日期分组销售产品2.49 - 列出指定时间段内所有的下单产品2.50 - 查找拥有有效邮箱的用户 1. 相关知识点 相关函数 函数含义concat()字符串拼接upp…

MT6989(天玑9300)芯片性能参数_MTK联发科5G处理器

MT6989是联发科Dimensity旗舰系列的成员&#xff0c;旨在为旗舰5G智能手机供应商提供最先进的技术和性能。MT6989也是联发科目前最具创新和强大的5G智能手机芯片&#xff0c;具有领先的功耗效率&#xff0c;无与伦比的计算架构&#xff0c;有史以来最快和最稳定的5G调制解调器&…

MySQL之主从同步、分库分表

1、主从同步的原理 MySQL主从复制的核心是二进制日志 二进制日志&#xff08;binlog&#xff09;记录了所有DDL语句和DML语句&#xff0c;但不包括数据查询&#xff08;select、show&#xff09;语句。 1.1、复制分三步 master主库在事务提交时&#xff0c;会把数据变更记录…

九浅一深Jemalloc5.3.0 -- ②浅*size class

目前市面上有不少分析Jemalloc老版本的博文&#xff0c;但5.3.0却少之又少。而且5.3.0的架构与之前的版本也有较大不同&#xff0c;本着“与时俱进”、“由浅入深”的宗旨&#xff0c;我将逐步分析Jemalloc5.3.0的实现。 另外&#xff0c;单讲实现代码是极其枯燥的&#xff0c;…

mmap()函数和munmap()函数的例子

代码&#xff1a; #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <sys/mman.h> #include <string.h> #include <stdio.h> #include <unistd.h>#define FILELENGTH 80 int main(void) {int fd-1;char …

Objective-C使用块枚举的细节

对元素类型的要求 在 Objective-C 中&#xff0c;NSArray 只能存储对象类型&#xff0c;而不能直接存储基本类型&#xff08;例如 int&#xff09;。但是&#xff0c;可以将基本类型封装在 NSNumber 等对象中&#xff0c;然后将这些对象存储在 NSArray 中。这样&#xff0c;en…

爬虫中如何创建Beautiful Soup 类的对象

在使用 lxml 库解析网页数据时&#xff0c;每次都需要编写和测试 XPath 的路径表达式&#xff0c;显得非常 烦琐。为了解决这个问题&#xff0c; Python 还提供了 Beautiful Soup 库提取 HTML 文档或 XML 文档的 节点。 Beautiful Soup 使用起来很便捷&#xff0c;…

Web后端开发概述环境搭建项目创建servlet生命周期

Web开发概述 web开发指的就是网页向后再让发送请求,与后端程序进行交互 web后端(javaEE)程序需要运行在服务器中 这样前端才可以对其进行进行访问 什么是服务器? 解释1: 服务器就是一款软件,可以向其发送请求,服务器会做出一个响应.可以在服务器中部署文件&#xff0c;让…

使用世界变换的逆转置矩阵对法线进行变换

法向量变换细节记录 最近在做法向量变换的时候&#xff0c;踩了两个坑&#xff0c;记录一下相关的知识点 法向量做变换&#xff0c;最后一位是补0 我们知道&#xff0c;顶点在做变换的时候最后一位是 1.0&#xff0c;法线最后一位是补0.0 vec3 normCurrent (getMatrixWorld() …

【NodeJs】入门

目录 一、前导 二、 url模块 三、path模块 四、buffer模块 五、fs模块 六、stream流模块 七、os模块 八、crypto模块 九、util模块 十、http模块 nodejs官网 Node.js — 在任何地方运行 JavaScript nmp是Node.js包管理器&#xff0c;用来安装各种库、框架和工具&…

基于STM32的八位数码管显示和闹钟计时【Proteus仿真】

某鱼&#xff1a;两栖电子 一、系统功能 采用矩阵键盘&#xff0c;按下对应的数字再按下确认按键&#xff0c;数码管会显示自己输入的数字&#xff0c;如果按错可以使用删除按钮进行删除。点击计时按钮可以显示当前的时间。 二、使用器件 DS1302实时时钟芯片&#xff0c;8位数…