Flutter 状态管理框架Get

news2024/11/26 11:08:23

状态管理框架 Get的使用

目录

状态管理框架 Get的使用

GetMaterialApp

路由的注册

路由的跳转

middlewares的使用

组件使用 defaultDialog bottomSheet snackbar

状态刷新有很多种方式

ValueBuilder

Obx 基础使用

是时候引入GetxController, 也是Get里面的常用的

GetX

优化控制器的使用 put

优化控制器的使用find

优化控制器的使用 懒加载lazyput

GetView

接下来GetConnect就是网络请求的使用

GetConnect

GetConnectStatemixin

GetConnectDio

切换主题

 多语言切换

常使用的Getx自带的API


不得不说,GetX使你的代码量减少了许多, 一个obs. 一个obx. 搞定你的数据监听刷新, 给你不一样的简洁。

GetMaterialApp

 return GetMaterialApp(
      title: 'Flutter Demo',
      getPages: MyRoutes.routes, //注册路由
      // initialRoute: MyHomePage.route,  //该属性, 设置有有问题, 在自定义转场动画的时候, 跳转的时候右边会黑屏 
      unknownRoute: MyRoutes.notFoundPage, //默认404的路由地址
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
        useMaterial3: true,
      ),
      home: const MyHomePage(),
    );

// initialRoute: MyHomePage.route,//该属性,在自定义转场动画的时候, 跳转的时候右边会黑屏

路由的注册

class MyRoutes {
  static List<GetPage> routes = [
     //正常注册
    GetPage(name: MySettingPage.route, page: () => const MySettingPage()),
    GetPage(name: MyHomePage.route, page: () => const MyHomePage(), 
    children: [
      //children 这里可以注册子页面, 一级, 二级, 三级, 页面都可以
      GetPage(name: MyProductPage.route, page: () => const MyProductPage(), 
          children: [
          GetPage(name: MyProductDetailPage.route, page: () => const MyProductDetailPage()),
          GetPage(name: MySettingPage.route, page: () => const MySettingPage()),
          GetPage(name: MySettingPage.routeID, page: () => const MySettingPage()),
      ]), 
    ]),
  ];
}

路由的跳转

//使用路由String跳转, 这样写, 注册的时候 /MyProductPage 必须要在 MyHomePage 的children: [ 里面] 如上展示
Get.toNamed("/MyHomePage/MyProductPage");
Get.toNamed("/MyHomePage/MyProductPage/MySettingPage");
//直接跳转对应的页面
Get.to(const MyProductPage());
//使用路由跳转并传值
Get.toNamed("/MyHomePage/MyProductPage/MySettingPage?id==345");

//返回上一个页面, 并传值, 接收参数的 var result = await Get.toNamed("/MyHomePage/MyProductPage/MySettingPage?id==345");
Get.back(result: {"success": true}),

//特殊路由传值 需要定义路由: "/MySettingPage/:id";, 才能使用下方跳转传值
Get.toNamed("/MyHomePage/MyProductPage/MySettingPage/8910");

//跳转后, 消除上页面的堆栈
Get.off(const MyProductDetailPage());
Get.offNamed(MyProductDetailPage.route);

//跳转后, 消除所有的堆栈
Get.offAll(const MyProductDetailPage());
Get.offAllNamed(MyProductDetailPage.route);

middlewares的使用

一个类似拦截器的功能,可以传入多个,需要自定研究下优先级。

 GetPage(name: MyMinePage.route, page: () => const MyMinePage(), middlewares: [        MyRouteAuthMiddleware()]),
class MyRouteAuthMiddleware extends GetMiddleware {
  @override
  RouteSettings? redirect(String? route) {
    // 加入 AuthService 这里可以判断下用户是否登录,  如果true 
    return super.的方法
    //否则去都登陆页面
    Future.delayed(const Duration(seconds: 1), () => Get.snackbar("提示", "请先登录APP"));
    return const RouteSettings(name: MyLoginPage.route);
  }
}

组件使用 defaultDialog bottomSheet snackbar

Get.snackbar
Get.bottomSheet
Get.defaultDialog
自行调用, 没有难度

状态刷新有很多种方式

ValueBuilder

爱了, 爱了, 直接导入头文件, 只刷新该作用域的.

ValueBuilder<List<String>?>(
    initialValue: const ["A", "B", "C"],
    builder: (value, updateFn) {
      return Column(children: [
        Text("List -> $value"),
        ElevatedButton(
          onPressed: () {
            List<String> newList = [...value!]; // 使用扩展运算符来创建列表的浅拷贝
            newList.add("${newList.length}");
            // newList.removeAt(1); // 移除索引为1的元素
            newList.shuffle();//元素随机
            updateFn(newList); // 使用新列表更新状态
          },
          child: const Text('ValueBuilder -> add'),
        )
      ]);
    }),   

Obx 基础使用

比如在Widget, 声明一个属性, 使用的时候使用Obx(()=>{}) 包裹一下,然后在其他地方点击,修改count值就OK了, 因为增加了obs, 就成了, count.value++ 来修改值

//Int
RxInt count = 0.obs;
var count1 = 0.obs;
Obx(() => Column(
      children: [
         Text("int -> $count"),
         Text("int -> $count1"),
])

//刷新
updateCount() {
      count.value++;
      count1.value++;
 }
在监听list. map. 枚举 的时候要注意, 一定要从新赋值才会刷新

//刷新枚举
 void updateViewState(ViewState newValue) {  
    viewState.value = newValue; // 更新枚举值  
  }  
//刷新list
 updateList() {
      list[0] = "BMW";
      list1[0] = "BMW"; 
}
//刷新Map
updateMap() {
     map["Audi"] = "BMW";
     map1["Audi"] = "BMW";
}
//刷新List中的Model
updateModels() {
      models[0] = GetXModels(name: "BMW");
      models1[0] = GetXModels(name: "BMW");
    }
//刷新Model
updateModel() {
      model.value = GetXModels(name: "BMW");
      model1.value = GetXModels(name: "BMW");
}
使用的地方一地个要加上Obx()

是时候引入GetxController, 也是Get里面的常用的

GetX<MyGetxController>

可以包裹引用Controller值的地方, 进行局部刷新, 还有声明周期的回调 推荐 ☆☆☆

//要创建控制器Controller
class MyPutController extends GetxController {}
//在widget的build方法中
final MyGetxController getxController = MyGetxController();
//使用例子
 ListTile(
  title: const Text("update list"),
  subtitle: GetX<MyGetxController>(
    init: getxController,
    initState: (_) {},
    builder: (_) {
      return Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          Text('我要买${getxController.list.toString()}'),
          Text('我要买${getxController.list1.toString()}'),
        ],
      );
    },
  ),
  onTap: () {
    //list, 要修改list里面的元素, 才会去更新UI
    getxController.updateList();
  },
),

优化控制器的使用 put

//在Widget的build方法里面我们注册一个controller 推荐 ☆☆☆

final MyPutController countController = Get.put<MyPutController>(MyPutController());
在使用过程中使用Obx(())包裹即可, 
ListTile(
  title: const Text("update Count"),
  subtitle: Obx(() => Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          Text('count = ${countController.count.value}'),
          Text('count = ${countController.count1.value}'),
        ],
      )),
  onTap: () {
    countController.updateCount();
  },
),

优化控制器的使用find

//在put过MyProductController()的Widget的build方法里面即二级页面, 三级页面....子页面

final MyProductController productController = Get.find<MyProductController>();
在使用过程中使用Obx(())包裹即可, 也可以使用GetX<MyProductController>build 也可以
ListTile(
     title: Text("传值 Get.arguments; = ${details.toString()} parameters == ${parameters.toString()}"),
     subtitle: Obx(() => Text(productController.myProductList.length.toString())),
     onTap: () {
        productController.addProduct(MyProduct(name: 'iPhone 16', description: 'APPle 设备', price: 8199));
        productController.addCount();
     }),

优化控制器的使用 懒加载lazyput

这里就要引出Binding的使用了

class MyGetLazyPutBinding implements Bindings {
  @override
  void dependencies() {
    Get.lazyPut(() => MyGetLazyPutController());
  }
}
// MyGetLazyPutController 上面说的controller. 必须创建, 切继承GetxController
//注册路由的时候
GetPage(name: MyLazyPutPage.route, page: () => const MyLazyPutPage(),binding: MyGetLazyPutBinding()),
//在Widget页面的时候就,不用调用Getx.put的方法, 会自动Put
//直接去find找到该Controller
final MyGetLazyPutController getLazyPutController = Get.find<MyGetLazyPutController>();
//在使用过程中使用Obx(())包裹即可, 也可以使用GetX<MyProductController>build 也可以
//建议自己撸一遍代码, 会印象更加深刻

GetView

这个更加简单, 省略put, find 的步骤, 不过还是要一个Controller的

//创建Widget的时候,需要增加一个泛型
class MyGetViewPage extends GetView<MyGetLazyPutController> 
//上面的路由注册, 还是要注册一个Controller
GetPage(name: MyLazyPutPage.route, page: () => const MyLazyPutPage(),binding: MyGetLazyPutBinding()),
//在widget中, 就自带了controller的变量 ,可以翻看源码中
abstract class GetView<T> extends StatelessWidget {
  const GetView({Key? key}) : super(key: key);
  final String? tag = null;
  T get controller => GetInstance().find<T>(tag: tag)!;
  @override
  Widget build(BuildContext context);
}
//就会知道, 内部帮忙find_Controller

 

接下来GetConnect就是网络请求的使用

GetConnect

这里跟官网的不太一样, 官网我感觉有点麻烦, 创建那么多的文件, 其实就是请求层, 数据层, 页面布局划分就好了

//1.我们先继承GetConnect 设置下你的请求相关配置
//如: baseUrl headers 以及其他设置
class MyBaseHttp extends GetConnect {
  @override
  void onInit() {
    httpClient.baseUrl = "xxxxxx";
    // 请求拦截
    httpClient.addRequestModifier<void>((request) {
      Map<String, String> headerMap = {
        "os-type": GetPlatform.isIOS ? "ios" : "android",
        "timestamp": DateTime.now().microsecondsSinceEpoch.toString(),
      };
      request.headers.addAll(headerMap);
      return request;
    });
    // 响应拦截
    httpClient.addResponseModifier((request, response) {
      return response;
    });
  }
}
//2.然后继承 MyBaseHttp, 创建你的serviceController
class MyServiceController extends MyBaseHttp {
  //获取数据, 自定义数据
  Future<MyGetConnectModel> getContent() async {
    Response response = await get('/route/external_link.json'); //这里去请求的, 有post. put. 自己看下API
    if (response.statusCode == 200) {
      debugPrint(response.bodyString);
      return MyGetConnectModel(userId: 1, id: 2, title: 'title', body: 'body');
    } else {
      debugPrint(response.bodyString);
      return MyGetConnectModel(userId: 1, id: 2, title: 'title', body: 'body');
    }
  }
}
//3.创建页面的controller
class MyGetConnectController extends GetxController {
  // 获取实例
  final MyServiceController serviceController = Get.find<MyServiceController>();
  // model
  late MyGetConnectModel getConnectModel;
  //监听状态, 这里是一个枚举, 这里你也可以监听其他的,比如model的变化
  Rx<ViewState> viewState = ViewState.normal.obs;

  getData() async {
    updateViewState(ViewState.loading);
    getConnectModel = await serviceController.getContent();
    updateViewState(ViewState.normal);
  }

  void updateViewState(ViewState newValue) {  
    viewState.value = newValue; // 更新枚举值  
  }  
}
//4.注册
class MyGetContentBuinding extends Bindings {
  @override
  void dependencies() {
    Get.lazyPut(() => MyGetConnectService());
    Get.lazyPut(() => MyGetConnectController());
  }
}
//路由binding
GetPage(name: MyGetContentPage.route, page: () => const MyGetContentPage(),binding: MyGetContentBuinding()),
//5.创建Widget,布局
方式1: class MyGetContentPage extends GetView<MyGetConnectController> {}
方式2: class MyHomePage extends StatelessWidget{
final controller = Get.find<MyGetConnectController>();}
//数据刷新
GetX<MyGetConnectController>(initState: (state) {
  controller.getData();
}, init: controller,
builder: ((_) {
  debugPrint("controller.viewState.value: ${controller.viewState.value}")
  switch (controller.viewState.value) {
    case ViewState.loading:
      return const Center(
        child: CircularProgressIndicator(),
      );
    default:
     return _buildListView(controller.getConnectModel);
  }
})),
或者
Obx(()=>)
更新值的时候, 需要 controller哟

GetConnectStatemixin

//跟上面基本一致, 这里说下不同点
//创建页面控制器的时候需要增加监听的类型数据
class MyGetConnectStateMixinController extends GetxController with StateMixin<List<MyGetConnectModel>> { }
//List<MyGetConnectModel>就是我要监听的数据类型
//创建页面方式
class MyGetConnectStateMixinPage extends GetView<MyGetConnectStateMixinController> {}
也可以使用find的方式找到该 MyGetConnectStateMixinController
然后通过controller.obx 进行监听处理, 有多种状态,
  final bool isLoading;
  final bool isError;
  final bool isSuccess;
  final bool isEmpty;
  final bool isLoadingMore;
  final String? errorMessage;
// 是在页面的MyGetConnectStateMixinController 控制器里面进行定义的.相关代码
 @override
  void onInit() {
    fetchList(); //获取数据
    super.onInit();
  }

  // 拉取数据列表
  Future<void> fetchList() async {
    // 获取数据, 也可以处理好处理传过来,
    final Response response = await connectStateMixinService.getContent(); //通过find找到该控制器 connectStateMixinService, 是继承上面的MyBaseHttp的请求类控制器
    // 判断请求是否有错误
    if (response.hasError) {
      // 改变数据,传入错误状态,在ui中会处理这些错误
     //返回状态的定义
      // change(null, status: RxStatus.error(response.statusText));
      // change(null, status: RxStatus.empty());
    } else {
      // 成功,自定义数据,改变状态为成功
      List<MyGetConnectModel> dataList = [];
      for (var i = 0; i < 10; i++) {
        MyGetConnectModel getConnectModel = MyGetConnectModel(userId: 12312, id: 231231, title: "title", body: "body");
        dataList.add(getConnectModel);
      }
      change(dataList, status: RxStatus.success());
    }

//页面布局Widget, 根据不同的状态, 可以写不同的结果
controller.obx(
  (state) {
    return ListView.separated(
      itemCount: state!.length,
      itemBuilder: (context, index) {
        final MyGetConnectModel getConnectModel = state[index];
        return ListTile(
          onTap: () {},
          title: Text(getConnectModel.title),
          trailing: Text("\$${getConnectModel.body}"),
        );
      },
      separatorBuilder: (BuildContext context, int index) {
        return const Divider();
      },
    );
  },
  onError: (error) {
    return Center(
      child: Text(error.toString()),
    );
  },
  onLoading: const SizedBox(
    width: double.infinity,
    height: double.infinity,
    child: Column(
      mainAxisAlignment: MainAxisAlignment.center,
      crossAxisAlignment: CrossAxisAlignment.center,
      children: [
        CircularProgressIndicator(),
        SizedBox(height: 10),
        Text(
          "疯狂加载中...",
          style: TextStyle(color: Colors.blue, fontSize: 16),
        ),
      ],
    ),
  ),
  onEmpty: const Center(
    child: Text("没有数据"),
  ),
));

GetConnectDio

需要自行导入Dio的框架
把上面的 GetConnect 部分中的请求部分, 更换为Dio请求, 然后使用Rx定义,监听的变量。

//在Widget中, 通过Obx, 或者GetX<MyGetConnectController> 去刷新数据即可
class MyGetConnectDioPage extends GetView<MyGetConnectDioController> 

切换主题

return Scaffold(
      appBar: AppBar(
        title: const Text("切换主题"),
      ),
      body: Center(
          child: ElevatedButton(
        onPressed: () {
          Get.changeTheme(Get.isDarkMode ? ThemeData.light() : ThemeData.dark());
        },
        child: Text("是否黑色主题 -> ${Get.isDarkMode}"),
      )),
    );

 多语言切换

class MyTranslationService extends Translations {
  static Locale? get locale => Get.deviceLocale;
  static const fallbackLocale = Locale('en', 'US');

  @override
  Map<String, Map<String, String>> get keys => {
        'en_US': enUS,  
        'zh_Hans': zhHans,
        'zh_HK': zhHK,
      };
}
//enUS  zhHans zhHK 要去创建文件 如: 
const Map<String, String> zhHans = {
  'title': '这是标题',
  'login': '登录用户 @name,邮箱账号 @email',
};

GetMaterialApp下:
  locale: MyTranslationService.locale,
  fallbackLocale: MyTranslationService.fallbackLocale,
  translations: MyTranslationService(),
// 使用
Text(
  "title -> ${'title'.tr}"),
Text(
  "login -> ${'login'.trParams({'name': 'xxx', 'email': 'xxx@gmail.com'})}"),

//使用tr. trParams 来处理多语言
//设置多语音
var locale = const Locale('zh', 'HK');
Get.updateLocale(locale);

常使用的Getx自带的API

Get.arguments

//给出以前的路由名称
Get.previousRoute

// 给出要访问的原始路由,例如,rawRoute.isFirst()
Get.rawRoute

// 允许从GetObserver访问Rounting API。
Get.routing

// 检查 snackbar 是否打开
Get.isSnackbarOpen

// 检查 dialog 是否打开
Get.isDialogOpen

// 检查 bottomsheet 是否打开
Get.isBottomSheetOpen

// 删除一个路由。
Get.removeRoute()

//反复返回,直到表达式返回真。
Get.until()

// 转到下一条路由,并删除所有之前的路由,直到表达式返回true。
Get.offUntil()

// 转到下一个命名的路由,并删除所有之前的路由,直到表达式返回true。
Get.offNamedUntil()

//检查应用程序在哪个平台上运行。
GetPlatform.isAndroid
GetPlatform.isIOS
GetPlatform.isMacOS
GetPlatform.isWindows
GetPlatform.isLinux
GetPlatform.isFuchsia

//检查设备类型
GetPlatform.isMobile
GetPlatform.isDesktop
//所有平台都是独立支持web的!
//你可以知道你是否在浏览器内运行。
//在Windows、iOS、OSX、Android等系统上。
GetPlatform.isWeb


// 相当于.MediaQuery.of(context).size.height,
//但不可改变。
Get.height
Get.width

// 提供当前上下文。
Get.context

// 在你的代码中的任何地方,在前台提供 snackbar/dialog/bottomsheet 的上下文。
Get.contextOverlay

// 注意:以下方法是对上下文的扩展。
// 因为在你的UI的任何地方都可以访问上下文,你可以在UI代码的任何地方使用它。

// 如果你需要一个可改变的高度/宽度(如桌面或浏览器窗口可以缩放),你将需要使用上下文。
context.width
context.height

// 让您可以定义一半的页面、三分之一的页面等。
// 对响应式应用很有用。
// 参数: dividedBy (double) 可选 - 默认值:1
// 参数: reducedBy (double) 可选 - 默认值:0。
context.heightTransformer()
context.widthTransformer()

/// 类似于 MediaQuery.of(context).size。
context.mediaQuerySize()

/// 类似于 MediaQuery.of(context).padding。
context.mediaQueryPadding()

/// 类似于 MediaQuery.of(context).viewPadding。
context.mediaQueryViewPadding()

/// 类似于 MediaQuery.of(context).viewInsets。
context.mediaQueryViewInsets()

/// 类似于 MediaQuery.of(context).orientation;
context.orientation()

///检查设备是否处于横向模式
context.isLandscape()

///检查设备是否处于纵向模式。
context.isPortrait()

///类似于MediaQuery.of(context).devicePixelRatio。
context.devicePixelRatio()

///类似于MediaQuery.of(context).textScaleFactor。
context.textScaleFactor()

///查询设备最短边。
context.mediaQueryShortestSide()

///如果宽度大于800,则为真。
context.showNavbar()

///如果最短边小于600p,则为真。
context.isPhone()

///如果最短边大于600p,则为真。
context.isSmallTablet()

///如果最短边大于720p,则为真。
context.isLargeTablet()

///如果当前设备是平板电脑,则为真
context.isTablet()

///根据页面大小返回一个值<T>。
///可以给值为:
///watch:如果最短边小于300
///mobile:如果最短边小于600
///tablet:如果最短边(shortestSide)小于1200
///desktop:如果宽度大于1200
context.responsiveValue<T>()

状态管理框架 Provider 和 Get 的Likes, 各有千秋

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

DevOps实践:在GitLab CI/CD中集成静态分析Helix QAC的工作原理与优势

基于云的GitLab CI/CD平台使开发团队能够简化其CI/CD流程&#xff0c;并加速软件开发生命周期&#xff08;SDLC&#xff09;。 将严格的、基于合规性的静态分析&#xff08;如Helix QAC所提供&#xff09;作为新阶段添加到现有的GitLab CI/CD流程中&#xff0c;将进一步增强SD…

华为云购买弹性云服务器(教程)

配置弹性云服务器 基础配置 实例 操作系统

1.CentOS安装

CentOS安装 新建虚拟机 选择安装方式 指定镜像方式 选择操作系统类型 设置虚拟机名称和位置 指定磁盘大小 点击“自定义硬件” 指定内存大小 指定镜像位置 点击“开启此虚拟机” 选择“Install CentOS 7”并回车 选择语言 选择安装“GNOME桌面”环境 配置安装位置 配置网络和…

排序05 排序模型的特征

特征介绍&#xff1a; id embedding&#xff0c;通常用32或64维向量 特征处理 线上服务的系统架构 统计数据是有时效性的&#xff0c;不能缓存在服务器本地。画像可以&#xff0c;保证读取快就好。 tf serving 这里 tensorflow会给笔记打分&#xff0c;分数返回给排序服务器&…

本地docker部署中间件和应用

Docker Desktop搭建 安装完成之后使用docker下载镜像&#xff0c;报以下错误&#xff1a; 解决办法&#xff1a; Docker Engine配置能访问的镜像地址&#xff1a; {"builder": {"gc": {"defaultKeepStorage": "20GB","enabled…

搭建 mongodb 副本集,很详细

搭建 mongodb 副本集&#xff0c;很详细 一、前言二、创建用户1、创建 root 用户2、创建测试用户3、修改用户密码 三、修改配置文件&#xff08;主节点&#xff09;1、开启登录认证2、加上副本集3、最终配置文件 四、副本节点1、创建副本节点目录2、编辑配置文件3、启动副本节点…

【python实操】python小程序之参数化以及Assert(断言)

引言 python小程序之参数化以及Assert&#xff08;断言&#xff09; 文章目录 引言一、参数化2.1 题目2.2 代码2.3 代码解释 二、Assert&#xff08;断言&#xff09;2.1 概念2.1.1 Assert语句的基本语法&#xff1a;2.1.2 基本断言2.1.3 断言函数参数2.1.4 断言前后状态一致 2…

网页HTML编写练习:华语榜中榜

网页效果 HTML代码 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta http-equiv"X-UA-Compatible" content"IEedge"><meta name"viewport" content"widthdevice…

NLP--一起学习Word Vector【实践】

纸上得来终觉浅&#xff0c;绝知此事要躬行。 《冬夜读书示子聿》 值此1024的程序员节&#xff0c;我们一起学习 Word Vector。 本章一起学习文本向量化&#xff0c;掌握文本向量的相关概念&#xff0c;了解各个文本向量&#xff0c;实现文本向量的算法 我开启了一个NLP共学坊…

arm ubuntu22.04 安装es7.16.2

1、更新软件包 sudo apt update && sudo apt upgrade -y 2、安装jdk11 sudo apt install openjdk-11-jdk -y 安装查看版本 java -version 输出应该是这样的 openjdk version "11.0.11" 2021-04-20 OpenJDK Runtime Environment (build 11.0.119-Ub…

【主机漏洞扫描常见修复方案】:Tomcat安全(机房对外Web服务扫描)

文章目录 引言I SSL/TLS Not ImplementedTomcat 服务器 SSL 证书安装部署(JKS 格式)Tomcat 服务器 SSL 证书安装部署(PFX 格式)HTTP 自动跳转 HTTPS 的安全配置(可选)修复SSL证书版本低II 主机漏洞扫描常见修复方案Apache JServ protocol serviceSlow HTTP DEnial of Ser…

Ubuntu 上安装 Redmine 5.1 指南

文章目录 官网安装文档&#xff1a;命令步骤相关介绍GemRubyRailsBundler 安装 Redmine更新系统包列表和软件包&#xff1a;安装必要的依赖&#xff1a;安装 Ruby&#xff1a;安装 bundler下载 Redmine 源代码&#xff1a;安装 MySQL配置 Redmine 的数据库配置文件&#xff1a;…

54万字WORD电力数字化转型智慧电力一体化监管云平台整体解决方案

▲关注智慧方案文库&#xff0c;学习9000多份最新解决方案&#xff0c;其中 PPT、WORD超过7000多份 &#xff0c;覆盖智慧城市多数领域的深度知识社区&#xff0c;稳定更新4年&#xff0c;日积月累&#xff0c;更懂行业需求。 1459页54万字WORD丨电力行业数字化转型 智慧电力…

【ArcGIS Pro实操第4期】绘制三维地图

【ArcGIS Pro实操第4期】绘制三维地图 ArcGIS Pro绘制三维地图-以DEM高程为例参考 如何使用ArcGIS Pro将栅格数据用三维的形式进行表达&#xff1f;在ArcGIS里可以使用ArcScene来实现&#xff0c;ArcGIS Pro实现原理跟ArcScene一致。由于Esri未来将不再对ArcGIS更新&#xff0c…

53页 PPT煤炭行业数字化转型规划方案

▲关注智慧方案文库&#xff0c;学习9000多份最新解决方案&#xff0c;其中 PPT、WORD超过7000多份 &#xff0c;覆盖智慧城市多数领域的深度知识社区&#xff0c;稳定更新4年&#xff0c;日积月累&#xff0c;更懂行业需求。 53页 PPT煤炭行业数字化转型规划方案 通过对煤企高…

手机玩使命召唤21:黑色行动6?GameViewer远程玩使命召唤教程

使命召唤21&#xff1a;黑色行动 6这个第一人称射击游戏&#xff0c;将于10月25号上线&#xff01;如果你是使命召唤的老玩家&#xff0c;是不是也在期待这部新作&#xff1f;其实这个游戏不仅可以用电脑玩&#xff0c;还可以用手机玩&#xff0c;使用网易GameViewer远程就能让…

【Qt6聊天室项目】 主界面功能实现

1. 获取当前用户的个人信息 1.1 前后端逻辑分析&#xff08;主界面功能&#xff09; 主界面上所有的前后端交互逻辑相同&#xff0c;分析到加载会话列表后其余功能仅实现。 核心逻辑总结 异步请求-响应模型 客户端发起请求&#xff0c;向服务器发送包含会话ID的请求服务端处…

python画图|曲线动态输出

【1】引言 前序教程中的曲线动态输出&#xff0c;其实是把曲线按照左右移动的形式输出&#xff08;波的传递形式&#xff09;。 python画图|曲线动态输出基础教程_python 动态曲线-CSDN博客 但有些时候我们更期待的是曲线不移动&#xff0c;随着自变量的增加而输出因变量&am…

信号与系统学习:傅里叶级数

一、基本概念 1. 什么是傅里叶级数&#xff1f; 傅里叶级数是一种数学工具&#xff0c;可以将一个周期函数分解为一系列正弦和余弦函数&#xff08;即三角函数&#xff09;的和。这些正弦和余弦函数的频率是原函数的整数倍。 2. 为什么要使用傅里叶级数&#xff1f; 信号分…

【STM32+HAL】OV2640捕获图像显示

一、准备工作 有关CUBEMX的初始化配置&#xff0c;参见我的另一篇blog&#xff1a;【STM32HAL】CUBEMX初始化配置 二、所用工具 1、芯片&#xff1a; STM32F407ZGT6 2、IDE&#xff1a; MDK-Keil软件 3、库文件&#xff1a;STM32F4xxHAL库 三、实现功能 通过OV2640捕获图像…