python3GUI--图片浏览器By:PyQt5(附源码)

news2024/11/14 2:39:07

文章目录

  • 一.前言
  • 二.展示
    • 1.主界面
    • 2.添加图片
    • 3.多级目录
    • 4.查看文件信息
    • 5.调整UI布局
  • 三.源代码
    • 1.image_god_main_v.py
    • 2.image_god_GUI.py
  • 四.总结


一.前言

本次使用PyQt5开发一款图片浏览器,本篇主要练习QDockWidget、QTreeWidget,软件打包好放在了文末,可自取。

二.展示

1.主界面

在这里插入图片描述

2.添加图片

点击“添加目录”后,选择目录,软件会扫描文件夹及子文件夹下的所有图片文件,最后展示到左侧的目录树中。
在这里插入图片描述

3.多级目录

支持多级目录图片展示
在这里插入图片描述

4.查看文件信息

右击文件,点击查看文件信息
在这里插入图片描述

5.调整UI布局

通过鼠标拖动QDockWidget,选择悬停位置改变UI布局。
在这里插入图片描述

三.源代码

1.image_god_main_v.py

文件主逻辑调用

import os
import re
import sys
import traceback
from PyQt5.QtCore import Qt, QFileInfo, QSize
from PyQt5.QtGui import QPixmap, QIcon, QCursor
import qtawesome

from image_god_GUI import Ui_MainWindow as mainWindow
from webbrowser import open as open_url
from PyQt5.QtWidgets import QApplication, QMainWindow, QDockWidget, QTreeWidgetItem, QFileDialog, QAbstractItemView, \
    QMenu, QFileIconProvider, QMessageBox, QAction


class ImageGod(QMainWindow, mainWindow):
    pixRatio = 1

    def __init__(self):
        super(ImageGod, self).__init__()
        self.setupUi(self)
        self.ui_init()
        self.slot_init()

    def ui_init(self):
        def icon_init():
            self.action_addDir.setIcon(qtawesome.icon("mdi.folder-open", color="#3D59AB"))
            self.action_broom.setIcon(qtawesome.icon("ei.broom", color="#DDA0DD"))
            self.action_zoomIn.setIcon(qtawesome.icon("msc.zoom-in", color="brown"))
            self.action_zoomOut.setIcon(qtawesome.icon("msc.zoom-out", color="brown"))
            self.action_zoomFit.setIcon(qtawesome.icon("mdi.restore", color="green"))
            self.action_exit.setIcon(qtawesome.icon("mdi6.exit-to-app", color="gray"))

        self.treeWidget.setColumnCount(1)
        self.treeWidget.setColumnWidth(0, 30)
        self.treeWidget.setHeaderLabels(["目录文件"])
        self.treeWidget.setIconSize(QSize(25, 25))
        self.treeWidget.setSelectionMode(QAbstractItemView.ExtendedSelection)
        self.setCentralWidget(self.scrollArea)
        self.scrollArea.setWidgetResizable(True)
        self.action_broom.setEnabled(False)
        self.scrollArea.setAlignment(Qt.AlignCenter)
        self.label.setAlignment(Qt.AlignCenter)
        self.currPixmap = QPixmap()
        icon_init()
        self.change_action_ui(False)
        self.menu_init()

    def slot_init(self):
        self.action_addDir.triggered.connect(self.on_openDir_triggered)
        self.action_exit.triggered.connect(self.close)
        self.actionabout_author.triggered.connect(lambda: open_url("https://blog.csdn.net/a1397852386"))
        self.actionabout_qt.triggered.connect(lambda: QMessageBox.aboutQt(self, "关于QT"))
        self.action_zoomIn.triggered.connect(self.on_actionZoomIn_triggered)
        self.action_zoomOut.triggered.connect(self.on_actionZoomOut_triggered)
        self.action_zoomFit.triggered.connect(self.on_actionFid_triggered)
        self.action_broom.triggered.connect(self.clear_dirTree)
        self.actiontree.triggered.connect(lambda: self.dockWidget.setVisible(self.actiontree.isChecked()))
        # self.dockWidget.closeEvent=self.actiontree.setChecked(not self.dockWidget.isVisible())
        self.treeWidget.customContextMenuRequested.connect(self.onTreeWidgetCustomContextMenuRequested)
        self.treeWidget.itemClicked.connect(self.onTreeItemClicked)

    def menu_init(self):
        """
        treeWidget右击菜单初始化
        :return:
        """
        self.treeWidget_menu = QMenu(self.treeWidget)
        self.action_show_image = QAction(self)  # 查看图片
        self.action_show_infos = QAction(self)  # 查看文件信息
        self.action_open_image_folder = QAction(self)  # 打开所在文件夹
        self.action_show_image.setText("查看图片")
        self.action_show_infos.setText("查看文件信息")
        self.action_open_image_folder.setText("打开所在文件夹")
        self.treeWidget_menu.addActions(
            [self.action_show_image, self.action_show_infos, self.action_open_image_folder, ])

    def on_openDir_triggered(self):
        """
        打开有图片的目录
        :return:
        """

        def CreateTree(dirs, root, path):
            """
            创建目录树
            :param dirs:
            :param root:
            :param path:
            :return:
            """
            for i in dirs:
                path_new = path + '\\' + i
                if os.path.isdir(path_new):
                    fileInfo = QFileInfo(path_new)
                    child = QTreeWidgetItem(root)
                    dirs_new = os.listdir(path_new)
                    data = ""
                    if self.check_dir(path_new):  # 确定文件夹有图片文件后,再创建子节点
                        CreateTree(dirs_new, child, path_new)

                else:
                    fileInfo = QFileInfo(path_new)
                    if not self.check_file(fileInfo.suffix()): continue
                    child = QTreeWidgetItem(root)
                    data = fileInfo.absoluteFilePath()
                    # 添加其他队列
                    # child.setText(1, str(fileInfo.size()))
                    # child.setToolTip(1, str(fileInfo.size()))
                    # child.setText(2, fileInfo.suffix())
                    # child.setToolTip(2, fileInfo.suffix())
                child.setData(0, Qt.UserRole, data)
                fileIcon = QFileIconProvider()
                icon = QIcon(fileIcon.icon(fileInfo))
                child.setText(0, i)
                child.setToolTip(0, i)
                child.setIcon(0, QIcon(icon))
                child.setExpanded(False)

        path = QFileDialog.getExistingDirectory(self, "选取文件夹", "./")
        # self.treeWidget.setHeaderLabels(["目录文件", "大小", "类型", "创建时间", "上次修改时间"])
        if not path: return
        dirs = os.listdir(path)
        if self.check_dir(path, root=True):  # 确定文件夹有图片文件后,再创建子节点
            fileInfo = QFileInfo(path)
            fileIcon = QFileIconProvider()
            icon = QIcon(fileIcon.icon(fileInfo))
            fileName_root = QTreeWidgetItem(self.treeWidget)
            fileName_root.setText(0, fileInfo.fileName())
            fileName_root.setToolTip(0, fileInfo.fileName())
            fileName_root.setIcon(0, QIcon(icon))
            fileName_root.setData(0, Qt.UserRole, "")
            CreateTree(dirs, fileName_root, path)
            self.action_broom.setEnabled(True)
            QApplication.processEvents()
        else:
            QMessageBox.warning(self, "警告", "选择的目录不存在图片文件!")

    def clear_dirTree(self):
        """
        清空目录树
        :return:
        """
        self.treeWidget.clear()
        self.action_broom.setEnabled(False)
        self.label.setPixmap(QPixmap(""))
        self.change_action_ui(False)

    def onTreeItemClicked(self, item, column):
        """
        treeWidget节点被单击
        :return:
        """
        file = item.data(column, Qt.UserRole)
        self.show_image(file)

    def show_image(self, file):
        if file:
            self.pixRatio = 1  # 每次都重置为1
            self.statusbar.showMessage(file)  # 将文件名展示到状态栏
            self.currPixmap.load(file)
            self.on_zoomFitH_triggered()
            self.change_action_ui(True)

    def show_file_infos(self, file):
        """
        文件信息
        :param file:
        :return:
        """
        infos = ""
        try:
            file_obj = QFileInfo(file)
            file_name = file_obj.fileName()
            file_size = file_obj.size()
            file_suffix = file_obj.suffix()
            file_last = file_obj.lastModified().toString()
            file_create = file_obj.created().toString()
            file_ow_id = file_obj.ownerId()
            infos = "文件名:{}\n文件类型:{}\n文件大小:{}字节\n创建时间:{}\n上次修改时间:{}\nownerId:{}".format(file_name, file_suffix,
                                                                                        file_size,
                                                                                        file_create, file_last,
                                                                                        file_ow_id)
        except:
            traceback.print_exc()
        if infos:
            QMessageBox.information(self, "文件信息", infos)
        else:
            QMessageBox.warning(self, "警告", "文件未找到")

    def onTreeWidgetCustomContextMenuRequested(self, pos):
        """
        treeWidget被右击事件
        :return:
        """
        try:
            item = self.treeWidget.currentItem()
            index=self.treeWidget.indexFromItem(item,0).row()
            print("index===",index)
            column = self.treeWidget.currentColumn()
            item1 = self.treeWidget.itemAt(pos)
            file = item.data(column, Qt.UserRole)
            if item != None and item1 != None and file != "":
                try:
                    self.action_show_image.triggered.disconnect()
                    self.action_show_infos.triggered.disconnect()
                    self.action_open_image_folder.triggered.disconnect()
                except:
                    traceback.print_exc()
                print(item.text(column))
                print(file)
                self.action_show_image.triggered.connect(lambda: self.show_image(file))
                self.action_show_infos.triggered.connect(lambda: self.show_file_infos(file))
                self.action_open_image_folder.triggered.connect(lambda: os.startfile(os.path.dirname(file)))
                self.treeWidget_menu.exec_(QCursor.pos())
        except:
            traceback.print_exc()

    def on_zoomFitH_triggered(self):
        """
        自动适应高度
        :return:
        """
        height = self.scrollArea.height()
        readHeight = self.currPixmap.height()
        self.pixRatio = float(height) / readHeight
        pix = self.currPixmap.scaledToHeight(height - 30)
        self.label.setPixmap(pix)

    def on_zoomFitW_triggered(self):
        """
        自动适应宽度
        :return:
        """
        width = self.scrollArea.width()
        readHWidth = self.currPixmap.width()
        self.pixRatio = float(width) / readHWidth
        pix = self.currPixmap.scaledToWidth(width - 30)
        self.label.setPixmap(pix)

    def on_actionZoomIn_triggered(self):
        """
        放大图片
        :return:
        """
        self.pixRatio = self.pixRatio * 1.2
        width = self.pixRatio * self.currPixmap.width()
        height = self.pixRatio * self.currPixmap.height()
        pix = self.currPixmap.scaled(width, height)
        self.label.setPixmap(pix)

    def on_actionFid_triggered(self):
        """
        自适应大小
        :return:
        """
        self.pixRatio = 1
        self.label.setPixmap(self.currPixmap)

    def on_actionZoomOut_triggered(self):
        """
        放大图片
        :return:
        """
        pixRatio_tmp = self.pixRatio
        self.pixRatio = self.pixRatio * 0.8
        width = self.pixRatio * self.currPixmap.width()
        height = self.pixRatio * self.currPixmap.height()
        if width < self.scrollArea.width() and height < self.scrollArea.height():  # 避免缩小太小导致失真
            self.pixRatio = pixRatio_tmp
            return
        pix = self.currPixmap.scaled(width, height)
        self.label.setPixmap(pix)

    def closeEvent(self, event):
        reply = QMessageBox.question(self, '关闭', "确定要退出吗?", QMessageBox.Yes | QMessageBox.No, QMessageBox.Yes)
        if reply == QMessageBox.Yes:
            event.accept()
        else:
            event.ignore()

    def check_dir(self, dir, root=False):
        """
        查看文件夹是否有图片文件
        :param dir:
        :return:
        """
        for file in os.listdir(dir):
            file_abs = dir + '\\' + file
            if os.path.isdir(file_abs):
                return True
            if "." in file:
                if self.check_file(file.split(".")[-1]):
                    return True
        return False

    def check_file(self, suffix):
        """
        看文件是否为图片文件
        :param suffix:
        :return:
        """
        if suffix in ['jpg', 'png', 'jpeg', 'psd', 'bmp', 'webp']:
            return True
        else:
            return False

    def change_action_ui(self, flag):
        """
        改变按钮UI状态
        :param falg:
        :return:
        """
        self.action_zoomIn.setEnabled(flag)
        self.action_zoomOut.setEnabled(flag)
        self.action_zoomFit.setEnabled(flag)


if __name__ == '__main__':
    app = QApplication(sys.argv)
    ui = ImageGod()
    ui.show()
    sys.exit(app.exec_())

2.image_god_GUI.py

UI文件,通过uic转化而来

# -*- coding: utf-8 -*-

# Form implementation generated from reading ui file 'image_god_GUI.ui'
#
# Created by: PyQt5 UI code generator 5.15.4
#
# WARNING: Any manual changes made to this file will be lost when pyuic5 is
# run again.  Do not edit this file unless you know what you are doing.


from PyQt5 import QtCore, QtGui, QtWidgets


class Ui_MainWindow(object):
    def setupUi(self, MainWindow):
        MainWindow.setObjectName("MainWindow")
        MainWindow.resize(1107, 622)
        self.centralwidget = QtWidgets.QWidget(MainWindow)
        self.centralwidget.setObjectName("centralwidget")
        self.horizontalLayout = QtWidgets.QHBoxLayout(self.centralwidget)
        self.horizontalLayout.setContentsMargins(0, 0, 0, 0)
        self.horizontalLayout.setSpacing(0)
        self.horizontalLayout.setObjectName("horizontalLayout")
        self.scrollArea = QtWidgets.QScrollArea(self.centralwidget)
        self.scrollArea.setWidgetResizable(True)
        self.scrollArea.setObjectName("scrollArea")
        self.scrollAreaWidgetContents = QtWidgets.QWidget()
        self.scrollAreaWidgetContents.setGeometry(QtCore.QRect(0, 0, 845, 553))
        self.scrollAreaWidgetContents.setObjectName("scrollAreaWidgetContents")
        self.verticalLayout_2 = QtWidgets.QVBoxLayout(self.scrollAreaWidgetContents)
        self.verticalLayout_2.setObjectName("verticalLayout_2")
        self.label = QtWidgets.QLabel(self.scrollAreaWidgetContents)
        self.label.setText("")
        self.label.setScaledContents(True)
        self.label.setObjectName("label")
        self.verticalLayout_2.addWidget(self.label)
        self.scrollArea.setWidget(self.scrollAreaWidgetContents)
        self.horizontalLayout.addWidget(self.scrollArea)
        MainWindow.setCentralWidget(self.centralwidget)
        self.menubar = QtWidgets.QMenuBar(MainWindow)
        self.menubar.setGeometry(QtCore.QRect(0, 0, 1107, 23))
        self.menubar.setObjectName("menubar")
        self.menu = QtWidgets.QMenu(self.menubar)
        self.menu.setObjectName("menu")
        self.menu_2 = QtWidgets.QMenu(self.menubar)
        self.menu_2.setObjectName("menu_2")
        self.menu_3 = QtWidgets.QMenu(self.menubar)
        self.menu_3.setObjectName("menu_3")
        MainWindow.setMenuBar(self.menubar)
        self.statusbar = QtWidgets.QStatusBar(MainWindow)
        self.statusbar.setObjectName("statusbar")
        MainWindow.setStatusBar(self.statusbar)
        self.dockWidget = QtWidgets.QDockWidget(MainWindow)
        self.dockWidget.setFeatures(QtWidgets.QDockWidget.DockWidgetFloatable|QtWidgets.QDockWidget.DockWidgetMovable)
        self.dockWidget.setObjectName("dockWidget")
        self.dockWidgetContents = QtWidgets.QWidget()
        self.dockWidgetContents.setObjectName("dockWidgetContents")
        self.verticalLayout = QtWidgets.QVBoxLayout(self.dockWidgetContents)
        self.verticalLayout.setContentsMargins(0, 0, 0, 0)
        self.verticalLayout.setObjectName("verticalLayout")
        self.treeWidget = QtWidgets.QTreeWidget(self.dockWidgetContents)
        self.treeWidget.setContextMenuPolicy(QtCore.Qt.CustomContextMenu)
        self.treeWidget.setObjectName("treeWidget")
        self.treeWidget.headerItem().setText(0, "1")
        self.verticalLayout.addWidget(self.treeWidget)
        self.dockWidget.setWidget(self.dockWidgetContents)
        MainWindow.addDockWidget(QtCore.Qt.DockWidgetArea(1), self.dockWidget)
        self.toolBar = QtWidgets.QToolBar(MainWindow)
        self.toolBar.setToolButtonStyle(QtCore.Qt.ToolButtonTextUnderIcon)
        self.toolBar.setObjectName("toolBar")
        MainWindow.addToolBar(QtCore.Qt.TopToolBarArea, self.toolBar)
        self.action_addDir = QtWidgets.QAction(MainWindow)
        self.action_addDir.setObjectName("action_addDir")
        self.action_broom = QtWidgets.QAction(MainWindow)
        self.action_broom.setObjectName("action_broom")
        self.action_zoomIn = QtWidgets.QAction(MainWindow)
        self.action_zoomIn.setObjectName("action_zoomIn")
        self.action_zoomOut = QtWidgets.QAction(MainWindow)
        self.action_zoomOut.setObjectName("action_zoomOut")
        self.action_zoomFit = QtWidgets.QAction(MainWindow)
        self.action_zoomFit.setObjectName("action_zoomFit")
        self.action_exit = QtWidgets.QAction(MainWindow)
        self.action_exit.setObjectName("action_exit")
        self.actiontree = QtWidgets.QAction(MainWindow)
        self.actiontree.setCheckable(True)
        self.actiontree.setChecked(True)
        self.actiontree.setObjectName("actiontree")
        self.actionabout_author = QtWidgets.QAction(MainWindow)
        self.actionabout_author.setObjectName("actionabout_author")
        self.actionabout_qt = QtWidgets.QAction(MainWindow)
        self.actionabout_qt.setObjectName("actionabout_qt")
        self.menu.addAction(self.action_addDir)
        self.menu.addAction(self.action_broom)
        self.menu.addAction(self.actiontree)
        self.menu.addSeparator()
        self.menu.addAction(self.action_exit)
        self.menu_2.addAction(self.action_zoomIn)
        self.menu_2.addAction(self.action_zoomOut)
        self.menu_2.addAction(self.action_zoomFit)
        self.menu_3.addAction(self.actionabout_author)
        self.menu_3.addAction(self.actionabout_qt)
        self.menubar.addAction(self.menu.menuAction())
        self.menubar.addAction(self.menu_2.menuAction())
        self.menubar.addAction(self.menu_3.menuAction())
        self.toolBar.addAction(self.action_addDir)
        self.toolBar.addAction(self.action_broom)
        self.toolBar.addSeparator()
        self.toolBar.addAction(self.action_zoomIn)
        self.toolBar.addAction(self.action_zoomOut)
        self.toolBar.addAction(self.action_zoomFit)
        self.toolBar.addSeparator()
        self.toolBar.addAction(self.action_exit)

        self.retranslateUi(MainWindow)
        QtCore.QMetaObject.connectSlotsByName(MainWindow)

    def retranslateUi(self, MainWindow):
        _translate = QtCore.QCoreApplication.translate
        MainWindow.setWindowTitle(_translate("MainWindow", "Image_god"))
        self.menu.setTitle(_translate("MainWindow", "目录"))
        self.menu_2.setTitle(_translate("MainWindow", "视图"))
        self.menu_3.setTitle(_translate("MainWindow", "关于"))
        self.dockWidget.setWindowTitle(_translate("MainWindow", "目录树"))
        self.toolBar.setWindowTitle(_translate("MainWindow", "toolBar"))
        self.action_addDir.setText(_translate("MainWindow", "添加目录"))
        self.action_addDir.setToolTip(_translate("MainWindow", "添加目录"))
        self.action_broom.setText(_translate("MainWindow", "清空目录树"))
        self.action_broom.setToolTip(_translate("MainWindow", "清空目录树"))
        self.action_zoomIn.setText(_translate("MainWindow", "放大"))
        self.action_zoomIn.setToolTip(_translate("MainWindow", "放大"))
        self.action_zoomOut.setText(_translate("MainWindow", "缩小"))
        self.action_zoomOut.setToolTip(_translate("MainWindow", "缩小"))
        self.action_zoomFit.setText(_translate("MainWindow", "原始大小"))
        self.action_zoomFit.setToolTip(_translate("MainWindow", "原始大小"))
        self.action_exit.setText(_translate("MainWindow", "退出"))
        self.action_exit.setToolTip(_translate("MainWindow", "退出"))
        self.actiontree.setText(_translate("MainWindow", "显示目录树"))
        self.actiontree.setToolTip(_translate("MainWindow", "显示目录树"))
        self.actionabout_author.setText(_translate("MainWindow", "关于作者"))
        self.actionabout_author.setToolTip(_translate("MainWindow", "关于作者"))
        self.actionabout_qt.setText(_translate("MainWindow", "关于QT"))
        self.actionabout_qt.setToolTip(_translate("MainWindow", "关于QT"))

四.总结

本次练习了PyQt5相关组件使用,制作了一款图片浏览器,可以按照目录级别预览图片,支持放大、缩小、查看信息,实现功能比较基础,整体UI风格为PyQt5原生ui风格,未使用QSS做具体细节绘制。软件打包好放在了这里,解压后大小为37.5M。希望得到大家的赞,谢谢大家!
在这里插入图片描述

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

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

相关文章

AIGC新时代,注意政策走向,产业方向,拥抱可信AI。需要了解基本理论,基础模型,前沿进展,产品应用,以及小小的项目复现

AIGC&#xff08;AI-Generated Content&#xff0c;AI生成内容&#xff09;是指基于生成对抗网络&#xff08;GAN&#xff09;、大型预训练模型等人工智能技术的方法&#xff0c;通过对已有数据进行学习和模式识别&#xff0c;以适当的泛化能力生成相关内容的技术。类似的概念还…

免费、不用部署SD:AI二维码制作教程

大家好&#xff0c;我是可夫小子&#xff0c;《小白玩转ChatGPT》专栏作者&#xff0c;关注AIGC、读书和自媒体。 最近&#xff0c;风格化的AI二维码&#xff0c;应该没少见吧。生成的原理大家大概也知道&#xff0c;主要通过stable diffusion和一些插件来完成&#xff0c;但对…

【Linux】HTTPS协议

目录 &#x1f37a;前言&#x1f37b;HTTPS协议原理&#x1f380;1、概念&#x1f381;2、加密和解密&#x1f382;3、常见加密方式&#x1f341;3.1、对称加密&#x1f342;3.2、非对称加密 &#x1f383;4、数据摘要和数据指纹&#x1f364;5、HTTPS工作原理&#x1f338;5.1…

学生速看!免费领取一台阿里云服务器全流程

阿里云学生服务器优惠活动&#xff1a;高效计划&#xff0c;可以免费领取一台阿里云服务器&#xff0c;如果你是一名高校学生&#xff0c;想搭建一个linux学习环境、git代码托管服务器&#xff0c;或者创建个人博客网站记录自己的学习成长历程&#xff0c;拥有一台云服务器是很…

零基础速成simulink代码生成——简单滤波器实现2

simulink setting 找到model settings solver求解器配置 Code Generation 代码生成配置 生成代码报告 添加stateflow注释 可以将变量保存在定义的文件(选) 实践 简单一阶滤波器

鼠标键盘实验

文章目录 USB参考资料USB设备STM32F407USB 硬件连接软件移植官方HIDSTM32F4USB通信库 USB参考资料 ①《STM32F4xx中文参考手册》-第30章 全速USB on-the-go(OTG_FS) ②光盘&#xff1a;STM32参考资料:STM32 USB 学习资料-CD00289278.pdf(UM1021) ③光盘&#xff1a;STM32参考资…

Android 14 新特性:语法性别 Grammatical Gender

背景 如同汉语里的他、她、它&#xff0c;英语里的 He、She、it&#xff0c;很多语言都存在依据性别、对象不同而造成的语法差异&#xff0c;甚至不仅限于名词&#xff0c;还涉及到形容词、动词等&#xff0c;复杂得多。 而这部分语言所涉及到的人群多达 30 亿之众&#xff0…

【树形DP+可重集排列】至至子的公司排队

好屌的题 F-至至子的公司排队_牛客小白月赛55 (nowcoder.com) 题意&#xff1a; 思路&#xff1a; 其实题目问的就是&#xff0c;森林的拓扑序有几种 那么我们先去考虑一棵树的拓扑序有几种 这个可以用树形DP来解决 设dp[u]为&#xff0c;以u为根的子树的拓扑序的种类数&…

【Java】项目中大批量数据查询导致OOM

文章目录 背景内存溢出的具体原因错误模拟问题复现解决办法流式查询和分页查询的使用场景查询数据的建议 背景 项目中有时候一次性将大批量数据都查出来到内存中导致内存占用过多很可能会导致内存溢出 内存溢出的具体原因 在JVM内存结构中分为以下几个模块 程序计数器虚拟机…

SSL协议,一文带你了解

SSL简介 SSL&#xff08;Secure Sockets Layer&#xff09;是一种安全协议&#xff0c;用于保护互联网上的数据传输安全。SSL协议最初由网景公司开发&#xff0c;现在已经被TLS&#xff08;Transport Layer Security&#xff09;协议所取代。SSL协议和TLS协议都是为了保护数据传…

一文带你弄懂【时间复杂度】

文章目录 算法时间复杂度时间复杂度计算常见的时间复杂度时间复杂度的差异 总结 算法 算法&#xff08;Algorithm&#xff09;是求解一个问题需要遵循的&#xff0c;被清楚指定的简单指令的集合。 一个算法的评价主要从时间复杂度和空间复杂度来考虑。而时间复杂度是一个函数…

Netty核心技术四--Netty概述

1. 原生NIO存在的问题 NIO 的类库和 API 繁杂&#xff0c;使用麻烦&#xff1a;需要熟练掌握Selector、ServerSocketChannel、SocketChannel、ByteBuffer 等。需要具备其他的额外技能&#xff1a;要熟悉 Java 多线程编程&#xff0c;因为NIO编程涉及到Reactor模式&#xff0c;…

从tomcat说起全面理解Java web开发原理

从tomcat说起全面理解Java web开发原理 简介&#xff1a;Java开发分为Java ME&#xff0c;Java SE&#xff0c;Java EE。回顾过去这些的开发工作基本上都是围绕着Java EE的&#xff0c;在开发经历中分别经历了Java EE开发框架从jsp servlet一路经历了ssh&#xff0c; ss…

存储笔记8 ipsan

Module Objectives IP SAN的组件 IP SAN的好处 描述SAN中的IP融合及其影响 描述的基本架构 –iSCSI –FCIP –FCoE 讨论IP SAN技术的市场驱动因素 列出IP SAN技术 列出iSCSI的组件和连接选项 描述iSCSI体系结构和拓扑结构 解释iSNS操作 描述FCIP的体系结构 IP SAN互联…

Springboot整合第三方登录

文章目录 Springboot整合第三方登录为什么采用第三方登录整合第三方登录创建应用导入依赖创建controller类 Springboot整合第三方登录 为什么采用第三方登录 ​ 采用第三方登录可以避免重新注册账号的繁琐&#xff0c;也不需要再为密码和昵称发愁&#xff0c;而第三方登录有一…

Linux命令——top相关之Load Average平均负载

Linux 平均负载 Load Average 详解_系统1f分钟负载_欧晨eli的博客-CSDN博客 一、什么是Load Average&#xff1f; 系统负载&#xff08;System Load&#xff09;是系统CPU繁忙程度的度量&#xff0c;即有多少进程在等待被CPU调度&#xff08;进程等待队列的长度&#xff09;。…

2-JVM运行流程

JVM 是 Java 运行的基础&#xff0c;也是实现一次编译到处执行的关键&#xff0c;那么 JVM 是如何执行的呢&#xff1f; 程序在执行之前先要把java源代码&#xff08;.java&#xff09;转换成字节码文件&#xff08;.class&#xff09;。JVM 首先需要通过一定的方式类加载器&a…

如何做好前端性能优化

前端推荐官网: http://luckycola.com.cn/ 前言: 前端性能优化一直是一个前端开发人员必须关注的经典话题,虽然现在随着技术的不断发展,网页容器(浏览器、webview)性能也越来越强大,但是网站应用的功能也不断丰富,体积不可避免的增加,当网络环境等因素不好时,仍然会存在白屏时…

【后端面经】MySQL主键、唯一索引、联合索引的区别和作用

【后端面经】MySQL主键、唯一索引、联合索引的区别和作用 0. 简介1. 主键2. 唯一索引3. 联合索引4. 索引对数据库操作的影响5. 其他索引5.1 普通索引5.2 全文索引5.3 前缀索引 6. 总结7. 参考资料 0. 简介 索引是一类特殊的文件&#xff0c;用来存储检索信息&#xff0c;使数据…

数值天气预报期末复习

数值天气预报期末复习 文章目录 数值天气预报期末复习&#xff08;零&#xff09;重点需要掌握知识点&#xff08;一&#xff09;什么是数值天气预报&#xff08;二&#xff09;数值模式的分类&#xff08;三&#xff09;各坐标系下的大气运动方程组3.1 局地直角坐标系3.2 球坐…