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

基于PyQt5的UI界面开发——图像与视频的加载与显示

介绍

这里我们的主要目标是实现一个基于PyQt5和OpenCV的图像浏览和视频播放应用。用户可以选择本地的图像或视频文件夹,进行图像自动播放和图像切换以及视频播放和调用摄像头等操作,并且支持图像保存功能。项目的核心设计包括文件路径选择、图像或视频的显示、自动播放、图像保存等功能。

UI的初步设计

整体的设计如图所示,主体的窗口名我命名为ImageLoadWindow,其他控件的标记我已经写在了上方图片中,大家可以和下面的代码进行对比。

视频的加载

文件路径的读取

当我们在开发视频处理或视频播放应用时,经常需要让用户选择视频文件以及指定保存路径。

SetPFilePath 方法用于选择视频文件。它使用 QFileDialog.getOpenFileName 弹出一个文件选择对话框,允许用户从文件系统中选择一个视频文件。如果用户选择了文件,该方法将更新界面上的文本和内部变量。

    def SetPFilePath(self):filename, _ = QFileDialog.getOpenFileName(self, "选择视频文件", '.', "Video Files (*.mp4 *.avi *.mkv)")if filename:self.PFilePathLiEd.setText(filename)self.PFilePath = filename

SetSFilePath 方法用于设置保存路径。它使用 QFileDialog.getExistingDirectory 弹出一个目录选择对话框,允许用户选择一个目录作为保存路径。如果用户选择了目录,该方法将更新界面上的文本和内部变量。

    def SetSFilePath(self):dirname = QFileDialog.getExistingDirectory(self, "选择保存目录", '.')if dirname:self.SFilePathLiEd.setText(dirname)self.SFilePath = dirname + '/'

这是一个很好用的框架,以后的ui设计可以直接对着我这里命名,这样在创建新的时候,可以实现代码的移植。

视频文件的读取和显示

这里,我们想让用户选择本地的视频文件或者通过摄像头实时获取视频流,并将视频帧显示在 PyQt5 的窗口中。

SetPFilePath,用于选择视频文件,并将文件路径显示在输入框中。此处的SetSFilePath,用于选择保存目录路径,但实际在下面的代码中并没有添加相关的逻辑。

当我们选择了视频文件,并点击运行按钮后,在左侧的OutputLab(QLable控件)显示了视频的播放。当不没有选择视频文件,就会根据LoadWayCBox(Combo Box控件)的索引去选择我们的摄像头,请注意此处是下拉框选项,索引是从0开始,符合摄像头的读取顺序,如果要用在其他项目时,要注意是否正确。

from PyQt5.QtWidgets import QApplication, QMainWindow, QFileDialog, QMessageBox
from PyQt5.QtGui import QPixmap
from PyQt5.QtCore import QTimer
import qimage2ndarray
import cv2
from load_image import Ui_ImageLoadWindowclass ImageLoadWindow(QMainWindow, Ui_ImageLoadWindow):def __init__(self):super().__init__()self.setupUi(self)self.PrepParameters()self.CallBackFunctions()self.timer = QTimer(self)self.timer.timeout.connect(self.update_frame)self.capture = None  # 用于存储视频捕获对象self.frame = None  # 用于存储当前帧def PrepParameters(self):# 选择文件路径self.PFilePath = ""# 保存文件路径self.SFilePath = ""self.PFilePathLiEd.setText(self.PFilePath)self.SFilePathLiEd.setText(self.SFilePath)def CallBackFunctions(self):self.PFilePathBt.clicked.connect(self.SetPFilePath)self.SFilePathBt.clicked.connect(self.SetSFilePath)self.RunBt.clicked.connect(self.start_video_playback)self.ExitBt.clicked.connect(self.close_application)def SetPFilePath(self):filename, _ = QFileDialog.getOpenFileName(self, "选择视频文件", '.', "Video Files (*.mp4 *.avi *.mkv)")if filename:self.PFilePathLiEd.setText(filename)self.PFilePath = filenamedef SetSFilePath(self):dirname = QFileDialog.getExistingDirectory(self, "选择保存目录", '.')if dirname:self.SFilePathLiEd.setText(dirname)self.SFilePath = dirname + '/'def start_video_playback(self):# 选择视频文件后,启动视频播放if self.PFilePath:self.capture = cv2.VideoCapture(self.PFilePath)else:load_way = self.LoadWayCBox.currentIndex()self.capture = cv2.VideoCapture(load_way)if self.capture.isOpened():self.timer.start(30)  # 每30毫秒更新一次帧else:print("无法打开视频文件或摄像头!")def update_frame(self):ret, self.frame = self.capture.read()if ret:rgb_image = cv2.cvtColor(self.frame, cv2.COLOR_BGR2RGB)# 使用 qimage2ndarray 将 NumPy 数组转换为 QImageqimg = qimage2ndarray.array2qimage(rgb_image)pixmap = QPixmap.fromImage(qimg)self.OutputLab.setPixmap(pixmap)else:self.timer.stop()  # 停止定时器,当视频播放完时def close_application(self):"""关闭应用程序"""reply = QMessageBox.question(self, '退出', '您确定要退出程序吗?', QMessageBox.Yes | QMessageBox.No, QMessageBox.No)if reply == QMessageBox.Yes:if self.capture:self.capture.release()QApplication.quit()if __name__ == "__main__":import sysapp = QApplication(sys.argv)window = ImageLoadWindow()window.show()sys.exit(app.exec_())

视频文件的处理 

如下所示,即为选择了视频文件后运行的情况。如果不选就会调用本地的摄像头。

 关于对视频处理的一块,你可以在下面注释的地方完成逻辑的实现。

    def update_frame(self):ret, self.frame = self.capture.read()if ret:# self.frame = ... 此处实现rgb_image = cv2.cvtColor(self.frame, cv2.COLOR_BGR2RGB)qimg = qimage2ndarray.array2qimage(rgb_image)pixmap = QPixmap.fromImage(qimg)self.OutputLab.setPixmap(pixmap)else:self.timer.stop()

图像的加载

文件路径的读取

SetPFilePath方法允许用户选择一个目录,目录中的所有文件都会被读取并存储在 self.pimage_list 中。为了仅获取图片文件,我过滤了所有非图片文件,确保 self.pimage_list 只包含有,比如.jpg、.png、.jpeg、.bmp 和 .gif 等图片格式。

    def SetPFilePath(self):dirname = QFileDialog.getExistingDirectory(self, "浏览", '.')if dirname:self.PFilePathLiEd.setText(dirname)self.PFilePath=dirname+'/'self.pimage_list = [f for f in os.listdir(self.PFilePath) iff.lower().endswith(('.png', '.jpg', '.jpeg', '.bmp', '.gif'))]

SetSFilePath方法选择一个保存目录。选择完成后,保存路径会被显示在 SFilePathLiEd 文本框中,并将 SFilePath 变量更新为所选目录的路径。

    def SetSFilePath(self):dirname = QFileDialog.getExistingDirectory(self, "浏览", '.')if dirname:self.SFilePathLiEd.setText(dirname)self.SFilePath=dirname+'/'

图像的切换

为了进一步丰富我们ui的功能,这里我们提供了两个方式去显示图像,第一种是通过控件显示图片,另外一种可以实现自动播放的功能。

下面为更新设计后的UI:

这里我们先要理清楚我们接下来要做什么,首先选择处理文件的路径,以及保存的路径,并点击运行按钮,显示第一张图像,如果没有选择保存路径,则只显示图像而不进行保存。

通过Last和Next键去控制图像的显示,并且第一张图像Last键被禁用,最后一张图像Next键被禁用。图像的显示和保存都通过opencv实现。

from PyQt5.QtWidgets import QApplication, QMainWindow, QFileDialog, QMessageBox
from PyQt5.QtGui import QPixmap
import qimage2ndarray
import cv2
import os
from load_image import Ui_ImageLoadWindowclass ImageLoadWindow2(QMainWindow, Ui_ImageLoadWindow):def __init__(self):super().__init__()self.setupUi(self)self.PrepParameters()self.CallBackFunctions()def PrepParameters(self):self.PFilePath = r''  # 初始化路径为空self.SFilePath = r''  # 初始化保存路径为空self.PFilePathLiEd.setText(self.PFilePath)self.SFilePathLiEd.setText(self.SFilePath)self.pimage_list = []  # 存储图像文件列表self.current_image_idx = -1  # 用于追踪当前显示的图像索引self.current_image = None  # 用于存储当前显示的图像数据def CallBackFunctions(self):self.PFilePathBt.clicked.connect(self.SetPFilePath)self.SFilePathBt.clicked.connect(self.SetSFilePath)self.RunBt.clicked.connect(self.start_image_show)  # 运行按钮,开始显示图像self.LastBt.clicked.connect(self.show_last_image)  # 上一张按钮self.NextBt.clicked.connect(self.show_next_image)  # 下一张按钮self.ExitBt.clicked.connect(self.close_application)  # 退出按钮def SetPFilePath(self):dirname = QFileDialog.getExistingDirectory(self, "浏览", '.')if dirname:self.PFilePathLiEd.setText(dirname)self.PFilePath = dirname + '/'self.pimage_list = [f for f in os.listdir(self.PFilePath)if f.lower().endswith(('.png', '.jpg', '.jpeg', '.bmp', '.gif'))]self.current_image_idx = -1  # 重置图像索引def SetSFilePath(self):dirname = QFileDialog.getExistingDirectory(self, "浏览", '.')if dirname:self.SFilePathLiEd.setText(dirname)self.SFilePath = dirname + '/'def start_image_show(self):if not self.pimage_list:QMessageBox.warning(self, '警告', '没有找到图像文件!', QMessageBox.Ok)return# 如果保存路径为空,弹出警告窗口询问是否继续if not self.SFilePath:reply = QMessageBox.question(self, '保存路径未选择', '保存路径未选择,是否继续显示图像?',QMessageBox.Yes | QMessageBox.No, QMessageBox.No)if reply == QMessageBox.No:return  # 如果用户选择 No,返回不继续self.current_image_idx = 0  # 从第一张图像开始显示self.show_image(self.current_image_idx)# 启用和禁用按钮self.update_navigation_buttons()def show_image(self, idx):if 0 <= idx < len(self.pimage_list):# 获取图像文件路径img_path = os.path.join(self.PFilePath, self.pimage_list[idx])image = cv2.imread(img_path)if image is not None:# 转换为RGB模式rgb_image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)self.current_image = imageqimg = qimage2ndarray.array2qimage(rgb_image)pixmap = QPixmap.fromImage(qimg)self.OutputLab.setPixmap(pixmap)# 如果有保存路径,才进行自动保存if self.SFilePath:self.auto_save_image(self.pimage_list[idx])else:print(f"无法读取图像: {img_path}")def auto_save_image(self, image_filename):if self.current_image is None:returnsave_path = os.path.join(self.SFilePath, image_filename)os.makedirs(self.SFilePath, exist_ok=True)cv2.imwrite(save_path, self.current_image)print(f"图像已自动保存到: {save_path}")def show_last_image(self):if self.current_image_idx > 0:self.current_image_idx -= 1self.show_image(self.current_image_idx)self.update_navigation_buttons()def show_next_image(self):if self.current_image_idx < len(self.pimage_list) - 1:self.current_image_idx += 1self.show_image(self.current_image_idx)self.update_navigation_buttons()def update_navigation_buttons(self):if self.current_image_idx == 0:self.LastBt.setEnabled(False)  # 禁用上一张按钮else:self.LastBt.setEnabled(True)  # 启用上一张按钮if self.current_image_idx == len(self.pimage_list) - 1:self.NextBt.setEnabled(False)  # 禁用下一张按钮else:self.NextBt.setEnabled(True)  # 启用下一张按钮def close_application(self):"""关闭应用程序"""reply = QMessageBox.question(self, '退出', '您确定要退出程序吗?', QMessageBox.Yes | QMessageBox.No, QMessageBox.No)if reply == QMessageBox.Yes:QApplication.quit()if __name__ == "__main__":import sysapp = QApplication(sys.argv)window = ImageLoadWindow2()window.show()sys.exit(app.exec_())

逻辑流程概述:

  1. 用户选择图像文件夹和保存路径(可选)。
  2. 点击运行按钮开始显示第一张图像。
  3. 如果保存路径未选择,程序会弹出提示,询问用户是否继续显示图像。
  4. 用户可以通过上一张(LastBt)和下一张(NextBt)按钮切换图像。
  5. 当图像显示时,若保存路径已选择,程序会自动保存当前显示的图像。
  6. 如果用户关闭程序,会弹出确认退出的对话框。

实现自动播放功能

下面就是完整的设计了,由于考虑到自动播放,所以我在这里添加了暂停恢复键,用于播放过程中的暂停。

好的,这里我们还是需要理清楚我们需要做什么。首先在选择好图像文件夹路径和保存路径后,如果勾选了自动播放就会禁用掉Last和Next键,点击运行会弹出窗口是否进行循环播放,如果选择进行循环播放,则图像会显示在OutputLab上,如果选择了否也会进行自动播放,只是在播放完成之后,可以切换到Last和Next键进行图片的切换。

在自动播放过程中,如果点击了暂停键,文字变为恢复,再点击文字变为暂停,在点击了暂停后,再点击运行可以重新进行选择。

from PyQt5.QtWidgets import QApplication, QMainWindow, QFileDialog, QMessageBox
from PyQt5.QtGui import QPixmap
from PyQt5.QtCore import QTimer
import qimage2ndarray
import cv2
import os
from load_image import Ui_ImageLoadWindowclass ImageLoadWindow2(QMainWindow, Ui_ImageLoadWindow):def __init__(self):super().__init__()self.setupUi(self)self.PrepParameters()self.CallBackFunctions()def PrepParameters(self):self.PFilePath = r''  # 初始化路径为空self.SFilePath = r''  # 初始化保存路径为空self.PFilePathLiEd.setText(self.PFilePath)self.SFilePathLiEd.setText(self.SFilePath)self.pimage_list = []  # 存储图像文件列表self.current_image_idx = -1  # 用于追踪当前显示的图像索引self.current_image = None  # 用于存储当前显示的图像数据self.is_autoplay = False  # 是否启用自动播放标志self.is_loop = False  # 是否循环播放标志self.is_paused = False  # 是否暂停自动播放标志def CallBackFunctions(self):self.PFilePathBt.clicked.connect(self.SetPFilePath)self.SFilePathBt.clicked.connect(self.SetSFilePath)self.RunBt.clicked.connect(self.start_image_show)  # 运行按钮,开始显示图像self.LastBt.clicked.connect(self.show_last_image)  # 上一张按钮self.NextBt.clicked.connect(self.show_next_image)  # 下一张按钮self.ExitBt.clicked.connect(self.close_application)  # 退出按钮self.AutoplaycheckBox.stateChanged.connect(self.toggle_autoplay)  # 连接自动播放勾选框self.StopRecoverBt.clicked.connect(self.pause_or_resume_autoplay)  # 连接暂停/恢复按钮def SetPFilePath(self):dirname = QFileDialog.getExistingDirectory(self, "浏览", '.')if dirname:self.PFilePathLiEd.setText(dirname)self.PFilePath = dirname + '/'self.pimage_list = [f for f in os.listdir(self.PFilePath)if f.lower().endswith(('.png', '.jpg', '.jpeg', '.bmp', '.gif'))]self.current_image_idx = -1  # 重置图像索引def SetSFilePath(self):dirname = QFileDialog.getExistingDirectory(self, "浏览", '.')if dirname:self.SFilePathLiEd.setText(dirname)self.SFilePath = dirname + '/'def toggle_autoplay(self, state):"""根据勾选状态设置是否启用自动播放"""self.is_autoplay = state == 2  # 2表示勾选状态self.StopRecoverBt.setEnabled(self.is_autoplay)  # 只有启用自动播放时才启用暂停/恢复按钮def start_image_show(self):if not self.pimage_list:QMessageBox.warning(self, '警告', '没有找到图像文件!', QMessageBox.Ok)return# 如果保存路径为空,弹出警告窗口询问是否继续if not self.SFilePath:reply = QMessageBox.question(self, '保存路径未选择', '保存路径未选择,是否继续显示图像?',QMessageBox.Yes | QMessageBox.No, QMessageBox.No)if reply == QMessageBox.No:return  # 如果用户选择 No,返回不继续self.current_image_idx = 0  # 从第一张图像开始显示self.show_image(self.current_image_idx)if self.is_autoplay:# 如果是自动播放模式,弹出对话框确认是否循环播放reply = QMessageBox.question(self, '循环播放', '是否循环播放图像?',QMessageBox.Yes | QMessageBox.No, QMessageBox.No)if reply == QMessageBox.Yes:self.is_loop = True  # 启用循环播放else:self.is_loop = False  # 不循环播放# 启动自动播放self.start_autoplay()else:# 启用和禁用按钮self.update_navigation_buttons()# 如果是自动播放并且已经暂停了,点击运行时,确保按钮文本恢复为'暂停'if self.is_paused:self.StopRecoverBt.setText('暂停')  # 恢复为暂停按钮文本def start_autoplay(self):"""启动自动播放模式"""if self.is_paused:# 如果当前是暂停状态,直接恢复定时器self.is_paused = Falseself.StopRecoverBt.setText('暂停')  # 修改按钮文本为 '暂停'self.LastBt.setEnabled(False)  # 禁用上一张按钮self.NextBt.setEnabled(False)  # 禁用下一张按钮# 使用QTimer定时器进行自动播放if not hasattr(self, 'autoplay_timer'):  # 如果定时器不存在,则创建self.autoplay_timer = QTimer(self)self.autoplay_timer.timeout.connect(self.next_image_in_autoplay)self.autoplay_timer.start(1000)  # 每1秒切换一张图像def next_image_in_autoplay(self):"""自动播放下一张图像"""if self.is_paused:return  # 如果已暂停,不进行任何操作if self.current_image_idx < len(self.pimage_list) - 1:self.current_image_idx += 1self.show_image(self.current_image_idx)else:if self.is_loop:self.current_image_idx = 0  # 如果是循环播放,回到第一张self.show_image(self.current_image_idx)else:self.stop_autoplay()  # 自动播放完成后停止并恢复按钮def stop_autoplay(self):"""停止自动播放"""if hasattr(self, 'autoplay_timer'):self.autoplay_timer.stop()self.update_navigation_buttons()  # 恢复按钮状态def pause_or_resume_autoplay(self):"""暂停或恢复自动播放"""if self.is_paused:self.is_paused = Falseself.StopRecoverBt.setText('暂停')  # 修改按钮文本为 '暂停'self.start_autoplay()  # 恢复播放else:self.is_paused = Trueself.StopRecoverBt.setText('恢复')  # 修改按钮文本为 '恢复'self.autoplay_timer.stop()  # 暂停定时器def show_image(self, idx):if 0 <= idx < len(self.pimage_list):# 获取图像文件路径img_path = os.path.join(self.PFilePath, self.pimage_list[idx])image = cv2.imread(img_path)if image is not None:# 转换为RGB模式rgb_image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)self.current_image = imageqimg = qimage2ndarray.array2qimage(rgb_image)pixmap = QPixmap.fromImage(qimg)self.OutputLab.setPixmap(pixmap)# 如果有保存路径,才进行自动保存if self.SFilePath:self.auto_save_image(self.pimage_list[idx])else:print(f"无法读取图像: {img_path}")def auto_save_image(self, image_filename):if self.current_image is None:returnsave_path = os.path.join(self.SFilePath, image_filename)os.makedirs(self.SFilePath, exist_ok=True)cv2.imwrite(save_path, self.current_image)print(f"图像已自动保存到: {save_path}")def show_last_image(self):if self.current_image_idx > 0:self.current_image_idx -= 1self.show_image(self.current_image_idx)self.update_navigation_buttons()def show_next_image(self):if self.current_image_idx < len(self.pimage_list) - 1:self.current_image_idx += 1self.show_image(self.current_image_idx)self.update_navigation_buttons()def update_navigation_buttons(self):"""更新上一张和下一张按钮的状态"""if self.current_image_idx == 0:self.LastBt.setEnabled(False)  # 禁用上一张按钮else:self.LastBt.setEnabled(True)  # 启用上一张按钮if self.current_image_idx == len(self.pimage_list) - 1:self.NextBt.setEnabled(False)  # 禁用下一张按钮else:self.NextBt.setEnabled(True)  # 启用下一张按钮def close_application(self):"""关闭应用程序"""reply = QMessageBox.question(self, '退出', '您确定要退出程序吗?', QMessageBox.Yes | QMessageBox.No, QMessageBox.No)if reply == QMessageBox.Yes:QApplication.quit()if __name__ == "__main__":import sysapp = QApplication(sys.argv)window = ImageLoadWindow2()window.show()sys.exit(app.exec_())

图像文件的处理

可以在注释处进行图像处理。

    def show_image(self, idx):if 0 <= idx < len(self.pimage_list):# 获取图像文件路径img_path = os.path.join(self.PFilePath, self.pimage_list[idx])image = cv2.imread(img_path)# image = ... 图像处理if image is not None:# 转换为RGB模式rgb_image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)self.current_image = imageqimg = qimage2ndarray.array2qimage(rgb_image)pixmap = QPixmap.fromImage(qimg)self.OutputLab.setPixmap(pixmap)# 如果有保存路径,才进行自动保存if self.SFilePath:self.auto_save_image(self.pimage_list[idx])else:print(f"无法读取图像: {img_path}")

其他

这里记录一下使用plainTextEdit显示信息。由于内容有限,所以不另外再写一篇博客了。

名字就叫plainTextEdit,这里我就不修改了。

这里只需要做两个修改就好了。

在初始化的时候PrepParameters,添加一个

self.plainTextEdit.clear()  # 清除文本框内容

你也可以和按钮结合在一起。

还有一个是将内容显示在plainTextEdit上面:

 运行效果如下所示:

总结

本篇展示了如何使用PyQt5和OpenCV实现一个图像浏览与视频播放应用,涵盖了文件路径选择、图像/视频显示、自动播放或切换、暂停与恢复、图像保存等多种功能。本篇的代码具有较高的灵活性,你只需要按照相同的命名方式就能够实现代码的移植。希望通过本篇,为其他大家提供有用的参考和实现思路。

另外,需要注意的一点是,在循环播放的时候图像会重复的保存,关于这一部分的相关逻辑,我不想再修改,也不怎么碍事。最后,我这里不提供原来设计ui文件,因为,我写的已经很清楚了,大家能够自己简单的设计,没必要再找我要。

相关文章:

基于PyQt5的UI界面开发——图像与视频的加载与显示

介绍 这里我们的主要目标是实现一个基于PyQt5和OpenCV的图像浏览和视频播放应用。用户可以选择本地的图像或视频文件夹&#xff0c;进行图像自动播放和图像切换以及视频播放和调用摄像头等操作&#xff0c;并且支持图像保存功能。项目的核心设计包括文件路径选择、图像或视频的…...

[python SQLAlchemy数据库操作入门]-16.CTE:简化你的复杂查询

哈喽,大家好,我是木头左! 在SQL的世界里,Common Table Expressions(公共表表达式),简称CTE,是一种强大的工具,它允许用户在单个查询中定义临时的结果集。这些结果集可以在整个查询中被多次引用,就像它们是数据库中的物理表一样。CTE不仅提供了一种组织和简化复杂查询…...

多分类的损失函数

在多分类任务中,常用的损失函数能够衡量模型输出的类别分布与目标类别之间的差异,帮助模型学习更准确的分类能力。以下是多分类任务中常用的损失函数: 1. 交叉熵损失(Cross-Entropy Loss) 公式: CrossEntropyLoss = − 1 N ∑ i =...

在WSL的系统中配置免密和GitHub传输数据(SSH)

在 WSL&#xff08;Windows Subsystem for Linux&#xff09;系统中配置免密与 GitHub 传输数据&#xff0c;主要包括设置 SSH 密钥对、将公钥添加到 GitHub 账户以及确保可以通过 WSL 正常使用这些密钥。以下是详细的步骤&#xff1a; 1. 检查现有 SSH 密钥 首先&#xff0c…...

Python中元组(tuple)内置的数据类型

在Python中&#xff0c;元组&#xff08;tuple&#xff09;是一种内置的数据类型&#xff0c;用于存储不可变的有序元素集合。元组在很多方面与列表&#xff08;list&#xff09;相似&#xff0c;但它们之间存在一些关键的区别。以下是关于Python元组的详细解释&#xff1a; 定…...

chrome缓存机制以及验证缓存机制

一、Chrome 缓存机制 浏览器缓存机制旨在提高网页加载速度、减少服务器负载和节约带宽。Chrome 的缓存主要包括以下几种类型&#xff1a; 1. 强缓存 (Strong Cache) 无需向服务器发送请求即可使用缓存的资源。由 HTTP 响应头控制&#xff0c;包括&#xff1a; Expires&…...

医药进出口交易|基于SSM+vue的医药进出口交易系统的设计与实现(源码+数据库+文档)

医药进出口交易系统 目录 基于SSM&#xff0b;vue的医药进出口交易系统的设计与实现 一、前言 二、系统设计 三、系统功能设计 5.1系统登录 5.2管理员功能模块 5.3仓储部门功能模块 5.4业务部门功能模块 5.5供应部门功能模块 5.6财务部功能模块 5.7客户功能模块 …...

爱快 IK-Q6000 WiFi6无线路由器 简单开箱评测和拆解

爱快&#xff08;iKuai&#xff09; IK-Q6000 WiFi6无线路由器 简单开箱评测和拆解 因为用的爱快软路由&#xff0c;所以就想着将AP全换成爱快的&#xff0c;方便管理&#xff0c;目前买了多款爱快的无线路由器当AP&#xff08;IK-Q6000、IK-W35、IK-Q3600&#xff09;&#x…...

时间敏感网络中全面分析与调度的模型驱动方法

论文&#xff1a;A Model-Driven Approach for the Comprehensive Analysis and Scheduling in Time-Sensitive Networks》 背景与动机 TSN 的发展与应用领域&#xff1a;自 2012 年起&#xff0c;IEEE 802.1 TSN 任务组致力于开发通信标准&#xff0c;增强 IEEE 802 网络&…...

统计颜色Count Color(POJ2777)题解

有一个长度为L厘米板&#xff0c;L是一个正整数&#xff0c;所以我们可以把它均匀地划分成L个部分&#xff0c;分别从左到右编号为1&#xff0c;2……L&#xff0c;每一个部分长度都为1厘米。现在我们必须给每个部分涂色&#xff0c;一个部分一种颜色&#xff0c;要求完成以下两…...

MySQL数据的增删改查(一)

目录 新增&#xff08;create&#xff09; 插入单条记录 插入多条记录 查询&#xff08;retrieve&#xff09; 查询所有列 查询特定列 查询字段为表达式 别名 去重 排序 按单列排序 按多列排序 使用表达式或别名排序 排序NULL值 条件查询 比较运算符 逻辑运算…...

国产文本编辑器EverEdit - 如何给小众语言开发大纲分析脚本

1 开发参考&#xff1a;小众语言如何开发大纲分析脚本 1.1 应用场景 在使用IDE进行代码开发时&#xff0c;代码中的变量、结构体、函数等&#xff0c;在大纲视图中都会显示出来&#xff0c;用户可以快速的了解当前文档的结构&#xff0c;以及快速跳转到函数、变量的声明位置。…...

【数据结构】线性数据结构——数组

1. 定义 数组是一种线性数据结构&#xff0c;由一组相同类型的元素组成&#xff0c;这些元素使用连续的内存空间存储。数组通过索引&#xff08;下标&#xff09;访问&#xff0c;每个元素的索引是固定的&#xff0c;从零开始递增。 2. 特点 顺序存储&#xff1a; 元素在内存…...

QT---------GUI程序设计基础

代码UI化设计&#xff08;QT&#xff09; 实例功能概述 假设我们要创建一个简单的计算器应用程序。该应用程序具有以下功能&#xff1a; 包含数字按钮&#xff08;0-9&#xff09;、操作符按钮&#xff08;、-、*、/&#xff09;、等于按钮&#xff08;&#xff09;和清除按…...

2、Bert论文笔记

Bert论文 1、解决的问题2、预训练微调2.1预训练微调概念2.2深度双向2.3基于特征和微调&#xff08;预训练下游策略&#xff09; 3、模型架构4、输入/输出1.输入&#xff1a;2.输出&#xff1a;3.Learned Embeddings(学习嵌入)1. **Token Embedding**2. **Position Embedding**3…...

Linux之ARM(MX6U)裸机篇----7.蜂鸣器实验

一&#xff0c;蜂鸣器模块 封装步骤&#xff1a; ①初始化SNVS_TAMPER这IO复用为GPIO ②设置SNVS_TAMPPER这个IO的电气属性 ③初始化GPIO ④控制GPIO输出高低电平 bsp_beep.c: #include "bsp_beep.h" #include "cc.h"/* BEEP初始化 */ void beep_init…...

Zabbix 监控平台 添加监控目标主机

Zabbix监控平台是一个企业级开源解决方案&#xff0c;用于分布式系统监视和网络监视。它由Zabbix Server和可选组件Zabbix Agent组成&#xff0c;通过C/S模式&#xff08;客户端-服务器模型&#xff09;采集数据&#xff0c;并通过B/S模式&#xff08;浏览器-服务器模型&#x…...

SpringCloud整合skywalking实现链路追踪和日志采集

1.部署skywalking https://blog.csdn.net/qq_40942490/article/details/144701194 2.添加依赖 <!-- 日志采集 --><dependency><groupId>org.apache.skywalking</groupId><artifactId>apm-toolkit-logback-1.x</artifactId><version&g…...

html文件通过script标签引入外部js文件,但没正确加载的原因

移动端H5应用&#xff0c;html文件通过script标签引入外部js文件&#xff0c;但没正确加载&#xff0c;在移动设备上难以排查。通过PC浏览器打开&#xff0c;发现js被阻止了&#xff1a;blocked:mixed-content。 原因在于&#xff1a; “blocked:mixed - content” 是浏览器的…...

OpenHarmony开发板环境搭建

程序员Feri一名12年的程序员,做过开发带过团队创过业,擅长Java相关开发、鸿蒙开发、人工智能等,专注于程序员搞钱那点儿事,希望在搞钱的路上有你相伴&#xff01;君志所向,一往无前&#xff01; 0.OpenHarmony 0.1 OpenHarmony OpenHarmony是一款面向全场景、全连接、全智能的…...

变量 varablie 声明- Rust 变量 let mut 声明与 C/C++ 变量声明对比分析

一、变量声明设计&#xff1a;let 与 mut 的哲学解析 Rust 采用 let 声明变量并通过 mut 显式标记可变性&#xff0c;这种设计体现了语言的核心哲学。以下是深度解析&#xff1a; 1.1 设计理念剖析 安全优先原则&#xff1a;默认不可变强制开发者明确声明意图 let x 5; …...

C++:std::is_convertible

C++标志库中提供is_convertible,可以测试一种类型是否可以转换为另一只类型: template <class From, class To> struct is_convertible; 使用举例: #include <iostream> #include <string>using namespace std;struct A { }; struct B : A { };int main…...

日语学习-日语知识点小记-构建基础-JLPT-N4阶段(33):にする

日语学习-日语知识点小记-构建基础-JLPT-N4阶段(33):にする 1、前言(1)情况说明(2)工程师的信仰2、知识点(1) にする1,接续:名词+にする2,接续:疑问词+にする3,(A)は(B)にする。(2)復習:(1)复习句子(2)ために & ように(3)そう(4)にする3、…...

React Native在HarmonyOS 5.0阅读类应用开发中的实践

一、技术选型背景 随着HarmonyOS 5.0对Web兼容层的增强&#xff0c;React Native作为跨平台框架可通过重新编译ArkTS组件实现85%以上的代码复用率。阅读类应用具有UI复杂度低、数据流清晰的特点。 二、核心实现方案 1. 环境配置 &#xff08;1&#xff09;使用React Native…...

HBuilderX安装(uni-app和小程序开发)

下载HBuilderX 访问官方网站&#xff1a;https://www.dcloud.io/hbuilderx.html 根据您的操作系统选择合适版本&#xff1a; Windows版&#xff08;推荐下载标准版&#xff09; Windows系统安装步骤 运行安装程序&#xff1a; 双击下载的.exe安装文件 如果出现安全提示&…...

C++中string流知识详解和示例

一、概览与类体系 C 提供三种基于内存字符串的流&#xff0c;定义在 <sstream> 中&#xff1a; std::istringstream&#xff1a;输入流&#xff0c;从已有字符串中读取并解析。std::ostringstream&#xff1a;输出流&#xff0c;向内部缓冲区写入内容&#xff0c;最终取…...

GitHub 趋势日报 (2025年06月08日)

&#x1f4ca; 由 TrendForge 系统生成 | &#x1f310; https://trendforge.devlive.org/ &#x1f310; 本日报中的项目描述已自动翻译为中文 &#x1f4c8; 今日获星趋势图 今日获星趋势图 884 cognee 566 dify 414 HumanSystemOptimization 414 omni-tools 321 note-gen …...

【JavaSE】绘图与事件入门学习笔记

-Java绘图坐标体系 坐标体系-介绍 坐标原点位于左上角&#xff0c;以像素为单位。 在Java坐标系中,第一个是x坐标,表示当前位置为水平方向&#xff0c;距离坐标原点x个像素;第二个是y坐标&#xff0c;表示当前位置为垂直方向&#xff0c;距离坐标原点y个像素。 坐标体系-像素 …...

uniapp中使用aixos 报错

问题&#xff1a; 在uniapp中使用aixos&#xff0c;运行后报如下错误&#xff1a; AxiosError: There is no suitable adapter to dispatch the request since : - adapter xhr is not supported by the environment - adapter http is not available in the build 解决方案&…...

大数据学习(132)-HIve数据分析

​​​​&#x1f34b;&#x1f34b;大数据学习&#x1f34b;&#x1f34b; &#x1f525;系列专栏&#xff1a; &#x1f451;哲学语录: 用力所能及&#xff0c;改变世界。 &#x1f496;如果觉得博主的文章还不错的话&#xff0c;请点赞&#x1f44d;收藏⭐️留言&#x1f4…...