PyQt5桌面应用开发(16):定制化控件-QPainter绘图

news2024/9/20 23:39:59

本文目录

  • PyQt5桌面应用系列
  • 画画图,喝喝茶
  • QPainter和QPixmap
    • QPixmap
    • QPainter
    • 绘制事件
  • 一个魔改的QLabel
    • Canvas类
    • 主窗口
    • 主程序:
  • 总结

PyQt5桌面应用系列

  • PyQt5桌面应用开发(1):需求分析
  • PyQt5桌面应用开发(2):事件循环
  • PyQt5桌面应用开发(3):并行设计
  • PyQt5桌面应用开发(4):界面设计
  • PyQt5桌面应用开发(5):对话框
  • PyQt5桌面应用开发(6):文件对话框
  • PyQt5桌面应用开发(7):文本编辑+语法高亮与行号
  • PyQt5桌面应用开发(8):从QInputDialog转进到函数参数传递
  • PyQt5桌面应用开发(9):经典布局QMainWindow
  • PyQt5桌面应用开发(10):界面布局基本支持
  • PyQt5桌面应用开发(11):摸鱼也要讲基本法,两个字,16
  • PyQt5桌面应用开发(12):QFile与线程安全
  • PyQt5桌面应用开发(13):QGraphicsView框架
  • PyQt5桌面应用开发(14):数据库+ModelView+QCharts
  • PyQt5桌面应用开发(15):界面动画
  • PyQt5桌面应用开发(16):定制化控件-QPainter绘图

本文完整源代码@gitcode.net

画画图,喝喝茶

程序员的日子,还是挺悠闲。只要快捷键设置得好,摸鱼不要太简单。话说这天,本人文青病又犯了,就想着整点艺术。虽然艺术细胞不多……

先上艺术。

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

不过跟我儿子比,还是差点:

在这里插入图片描述
在这里插入图片描述

QPainter和QPixmap

前面都是胡扯,这里其是想写的是PyQt5中自定义控件的技术。对于PyQt5而言,所有的控件,都把自己作为一种bitmap来绘制,只有理解了这一点,才能说掌握了自定义控件。

QPixmap

计算机图像学里所有的图形都是像素点阵列,每个像素点都有自己的颜色,这种点阵列就是bitmap。bitmap也可以序列化成图像文件,不同的编码格式对应不同格式的文件,常见的有bmp、jpg、png等。

PyQt5中,所有的控件都是继承自QWidget,而QWidget继承自QPaintDevice,QPaintDevice是一个抽象类,它定义了一些绘制的接口。QPixmap和QImage都继承自QPaintDevice,所以QPixmap和QImage都可以作为绘制的目标。在Device之上,又有QPainter,QPainter是一个绘制的工具,它可以把图形绘制到QPaintDevice上。

控件的pixmap()函数都返回一个QPixmap对象,这个对象可以用来绘制控件的内容。

QPainter

QPainter是一个绘制工具,它可以把图形绘制到QPaintDevice上。QPainter的绘制函数有很多,这里只介绍几个常用的:

  • drawPoint(x, y):绘制一个点
  • drawLine(x1, y1, x2, y2):绘制一条直线
  • drawRect(x, y, width, height):绘制一个矩形
  • drawEllipse(x, y, width, height):绘制一个椭圆
  • drawArc(x, y, width, height, startAngle, spanAngle):绘制一个圆弧
  • drawPie(x, y, width, height, startAngle, spanAngle):绘制一个扇形
  • drawPolygon(points):绘制一个多边形
  • drawText(x, y, text):绘制一段文本
  • drawPixmap(x, y, pixmap):绘制一个图像
  • drawImage(x, y, image):绘制一个图像
  • fillRect(x, y, width, height, color):用color颜色填充一个矩形

此外,QPainter还提供了函数设置画图的笔触。

  • pen():获取当前的画笔
  • setPen(pen):设置画笔

这些函数,提供了多种不同的重载形式,具体查看文档就行。

绘制事件

QWidget的paintEvent()函数是绘制事件,当控件需要绘制的时候,就会调用这个函数。paintEvent()
函数的参数是QPaintEvent,它包含了绘制的区域,可以通过rect()函数获取。

paintEvent()函数的实现,一般是先创建一个QPainter对象,然后调用QPainter的绘制函数,最后销毁QPainter对象。

def paintEvent(self, event):
    painter = QPainter(self)
    painter.drawPixmap(self.rect(), self.pixmap)
    painter.end()

QPainter提供了一对函数begin()和end()来管理绘制的生命周期,这两个函数是成对出现的,一般都是在begin()
函数中创建QPainter对象,然后在end()
函数中销毁QPainter对象。但是,QPainter的构造函数也可以直接传入一个QPaintDevice对象,这样就不需要调用begin()。但是end()
函数一般是需要自行调用的。

基本的内容就是这些,比较简单,接下来增加一点点细节。

一个魔改的QLabel

我们选择一个QLabel控件作为父类,定义一个Canvas类。在Canvas中,提供鼠标绘图的功能,所有的图形都是花在QLabel的pixmap()上。

希望提供的功能如下:

  • 鼠标绘制图形
  • 设置绘制的颜色
  • 设置线条粗细
  • 鼠标喷涂
  • 喷涂的过程增加一个框(框有不同的形状)
  • 保存绘制的图形
  • 清楚绘图区

界面的设计如下:

在这里插入图片描述

  • 下方工具栏:颜色选择,包括增加自定义颜色
  • 上方工具栏:颜色、保存之外其它所有交互功能

Canvas类

Canvas类继承自QLabel,它的构造函数中,设置了一些属性,包括:

  • setMouseTracking(False):设置鼠标跟踪,如果设置为True,那么鼠标移动事件就会被触发,否则只有鼠标按下和释放事件才会被触发。
  • last_x, last_y:记录鼠标的上一个位置,用于绘制直线。
  • pen_color:记录当前的画笔颜色。
  • is_spray:记录是否是喷涂模式。
  • gauss_size:喷涂的大小。
  • shape_type:记录当前的绘制形状。
  • is_rotate:形状是否旋转
  • is_gauss_sum_up:是否用高斯叠加函数形成一个特殊笔刷

类中间定义了一个enum,用于记录绘制的形状,包括:

  • NONE:无
  • RECT:矩形
  • CIRC:圆形
  • TRI:三角形
  • HEX:六角形

相应地还定义了一系列槽函数。重要的功能在重载的几个函数中实现。

  • def resizeEvent(self, a0: QResizeEvent) -> None:
  • def mouseReleaseEvent(self, ev: QMouseEvent) -> None:
  • def mousePressEvent(self, ev: QMouseEvent) -> None:
  • def mouseMoveEvent(self, e: QMouseEvent):

resizeEvent()
函数在控件大小改变的时候被调用,这里我们在这个函数中重新设置pixmap的大小,保证pixmap的大小和控件大小一致。mousePressEvent和mouseReleaseEvent处理鼠标按下和释放事件,mouseMoveEvent处理鼠标移动事件。

最后是一个功能函数把QPixamp保存为图片和内部函数绘制喷涂。

class Canvas(QLabel):
    class ShapeType:
        NONE = 0
        RECT = 1
        CIRC = 2
        TRI = 3
        HEX = 4

    def __init__(self, parent=None):
        super(Canvas, self).__init__(parent)

        self.last_x, self.last_y = None, None
        self.pen_color = QColor(Qt.black)

        self.setMouseTracking(False)

        self.is_spray = False

        self.gauss_size = 10
        self.gauss_density = 100
        self.shape_type = self.ShapeType.NONE

        self.is_rotate = False
        self.angle = None

        self.line_width = 4

        self.gauss_sum_up = False

    @pyqtSlot(bool)
    def set_gauss_sum_up(self, b: bool):
        self.gauss_sum_up = b

    @pyqtSlot(int)
    def set_line_width(self, w):
        self.line_width = w

    @pyqtSlot(int)
    def set_gauss_size(self, s):
        self.gauss_size = s

    @pyqtSlot(int)
    def set_gauss_density(self, d):
        self.gauss_density = d

    @pyqtSlot(bool)
    def set_rotate(self, b: bool):
        self.is_rotate = b

    @pyqtSlot(int)
    def set_shape_type(self, shape_type: ShapeType):
        self.shape_type = shape_type

    @pyqtSlot(QColor)
    def set_pen_color(self, c):
        self.pen_color = QColor(c)

    @pyqtSlot(bool)
    def toggle_spray(self, b: bool):
        self.is_spray = b

    @pyqtSlot()
    def clear(self) -> None:
        self.pixmap().fill(Qt.white)
        self.update()

    def resizeEvent(self, a0: QResizeEvent) -> None:
        pixmap = self.pixmap()
        if pixmap:
            self.setPixmap(pixmap.scaled(a0.size()))
        else:
            pixmap = QPixmap(a0.size())
            pixmap.fill(Qt.white)
            self.setPixmap(pixmap)

    def mouseReleaseEvent(self, ev: QMouseEvent) -> None:
        self.last_x, self.last_y = None, None

    def mousePressEvent(self, ev: QMouseEvent) -> None:
        self.last_x, self.last_y = ev.x(), ev.y()

        if self.is_spray:
            self._spray(ev.x(), ev.y(), QPainter(self.pixmap()))
            self.update()

    def mouseMoveEvent(self, e: QMouseEvent):
        painter = QPainter(self.pixmap())
        p = painter.pen()
        p.setColor(self.pen_color)
        p.setWidth(self.line_width)
        painter.setPen(p)

        if self.is_spray:
            self._spray(e.x(), e.y(), painter)
        else:
            painter.drawLine(self.last_x, self.last_y, e.x(), e.y())
        painter.end()
        self.update()

        self.last_x, self.last_y = e.x(), e.y()

    def save_png(self):
        fid, _ = QFileDialog.getSaveFileName(self, 'Save PNG', 'canvas.png', 'PNG (*.png)')
        if fid:
            self.pixmap().save(fid, 'PNG', 100)

    def _spray(self, x, y, painter):
        p = painter.pen()
        p.setWidth(1)
        p.setColor(self.pen_color)
        painter.setPen(p)

        painter.translate(x, y)
        # draw shape in local coordinates
        if self.is_rotate:
            self.angle = random.randrange(0, 360)
            painter.rotate(self.angle)

        if self.shape_type == self.ShapeType.RECT:
            angle = random.randrange(0, 360)
            painter.drawRect(QRectF(- self.gauss_size, - self.gauss_size, 2 * self.gauss_size, 2 * self.gauss_size))
        if self.shape_type == self.ShapeType.CIRC:
            painter.drawEllipse(QPointF(0, 0), self.gauss_size, self.gauss_size)
        if self.shape_type == self.ShapeType.TRI or self.shape_type == self.ShapeType.HEX:
            painter.drawPolygon(QPolygonF([
                QPointF(- math.sqrt(3) * self.gauss_size, self.gauss_size),
                QPointF(+ math.sqrt(3) * self.gauss_size, self.gauss_size),
                QPointF(0, - self.gauss_size * 2.0)
            ]))
        if self.shape_type == self.ShapeType.HEX:
            painter.drawPolygon(QPolygonF([
                QPointF(- math.sqrt(3) * self.gauss_size, - self.gauss_size),
                QPointF(+ math.sqrt(3) * self.gauss_size, - self.gauss_size),
                QPointF(0, self.gauss_size * 2.0)
            ]))
        if self.is_rotate:
            painter.rotate(-self.angle)

        xi, yi = 0, 0
        for i in range(self.gauss_density):
            if self.gauss_sum_up:
                xi += random.gauss(0, self.gauss_size / 3.0)
                yi += random.gauss(0, self.gauss_size / 3.0)
            else:
                xi, yi = random.gauss(0, self.gauss_size / 3.0), random.gauss(0, self.gauss_size / 3.0)

            painter.drawPoint(QPointF(xi, yi))

        # back to global coordinates
        painter.translate(-x, -y)

主窗口

主窗口要构造所有的界面元素,并提供保存按键组合的响应。

class MainWindow(QMainWindow):

    def __init__(self):
        super(MainWindow, self).__init__()
        self.toolbar_button_width = 30
        self.canvas = Canvas()
        self.setCentralWidget(self.canvas)

        self.palette_widget = QToolBar(self)
        self.palette_widget.setWindowTitle('Palette')

        self.current_color = QLabel()
        self.current_color.setFixedWidth(self.toolbar_button_width)
        self.current_color.setStyleSheet('background-color: #000000')
        self.palette_widget.addWidget(self.current_color)

        add_color = QPushButton("新")
        add_color.setFixedWidth(self.toolbar_button_width)
        add_color.clicked.connect(self.pick_color)

        self.palette_widget.addWidget(add_color)

        self.palette_widget.addSeparator()

        for color in COLORS:
            button = QPaletteButton(color, self.palette_widget, self.toolbar_button_width)
            self.palette_widget.addWidget(button)
            button.clicked.connect(partial(self.canvas.set_pen_color, QColor(color)))
            button.clicked.connect(partial(self.current_color.setStyleSheet, f'background-color: {color}'))

        self.addToolBar(Qt.BottomToolBarArea, self.palette_widget)
        self.palette_widget.setMovable(False)

        self.pen_style = QToolBar(self)
        self.pen_style.setWindowTitle('Pen Style')
        checkbox = QCheckBox('喷涂', self.pen_style)
        checkbox.clicked.connect(self.canvas.toggle_spray)

        sumup = QCheckBox('高斯叠加', self.pen_style)
        sumup.clicked.connect(self.canvas.set_gauss_sum_up)

        size_tunner = QSpinBox(self.pen_style)
        size_tunner.setMinimum(1)
        size_tunner.setMaximum(200)
        size_tunner.setValue(50)
        size_tunner.valueChanged.connect(self.canvas.set_gauss_size)
        size_tunner.setSingleStep(1)
        size_tunner.setFixedWidth(100)

        density_tunner = QSpinBox(self.pen_style)
        density_tunner.setMinimum(0)
        density_tunner.setMaximum(5000)
        density_tunner.setValue(500)
        density_tunner.valueChanged.connect(self.canvas.set_gauss_density)
        density_tunner.setSingleStep(10)
        density_tunner.setFixedWidth(100)

        clear_button = QPushButton('Clear', self.pen_style)
        clear_button.clicked.connect(self.canvas.clear)
        clear_button.setStyleSheet('background-color: #ff0000; color: #ffffff;')

        self.pen_style.addWidget(clear_button)
        self.pen_style.addSeparator()
        self.pen_style.addWidget(checkbox)
        self.pen_style.addWidget(sumup)
        self.pen_style.addSeparator()
        self.pen_style.addWidget(QLabel('喷涂大小:'))
        self.pen_style.addWidget(size_tunner)
        self.pen_style.addSeparator()
        self.pen_style.addWidget(QLabel('喷涂数目:'))
        self.pen_style.addWidget(density_tunner)

        self.outline = QComboBox(self.pen_style)
        self.outline.addItem("无")
        self.outline.addItem("方形")
        self.outline.addItem("圆形")
        self.outline.addItem("三角形")
        self.outline.addItem("六角形")

        self.outline.currentIndexChanged.connect(self.canvas.set_shape_type)

        self.pen_style.addSeparator()
        self.pen_style.addWidget(QLabel('边框:'))
        self.pen_style.addWidget(self.outline)

        is_rotate = QCheckBox('旋转', self.pen_style)
        is_rotate.clicked.connect(self.canvas.set_rotate)
        is_rotate.setChecked(False)
        self.pen_style.addWidget(is_rotate)

        self.pen_style.addSeparator()
        self.pen_style.addWidget(QLabel('线条宽度:'))
        self.pen_style.addSeparator()
        line_width = QSpinBox(self.pen_style)
        line_width.setMinimum(1)
        line_width.setMaximum(10)
        line_width.setValue(4)
        line_width.valueChanged.connect(self.canvas.set_line_width)
        line_width.setSingleStep(1)
        self.pen_style.addWidget(line_width)

        self.pen_style.setMovable(False)

        self.addToolBar(Qt.TopToolBarArea, self.pen_style)

    @pyqtSlot()
    def pick_color(self):
        color = QColorDialog.getColor()
        if color.isValid():
            button = QPaletteButton(color.name(), self.palette_widget, self.toolbar_button_width)
            self.palette_widget.addWidget(button)
            button.clicked.connect(partial(self.canvas.set_pen_color, color))
            button.clicked.connect(partial(self.current_color.setStyleSheet, f'background-color: {color.name()}'))
            button.clicked.emit()

            # resize the palette widget to fit all the buttons
            self.palette_widget.resize(self.palette_widget.sizeHint())

    def keyPressEvent(self, ev: QKeyEvent) -> None:
        if ev.matches(QKeySequence.Save):
            self.canvas.save_png()

这里的代码乏善可陈,唯一好玩的是那个颜色选择界面。

COLORS = sorted(
    {'#000000', '#808080', '#800000', '#808000', '#008000', '#008080', '#000080', '#800080', '#808040', '#004040',
     '#004080', '#0000ff', '#004080', '#0080ff', '#0040ff', '#0080c0', '#00ffff', '#00ff00', '#00ff80', '#00ffbf',
     '#00ffff', '#00bfff', '#00bfbf', '#00b080', '#00bf80', '#00b040', '#00bf40', '#00bf00', '#00ff40', '#00ffbf',
     '#00ffff', '#80ff00', '#80ff80', '#80ffbf', '#80ffff', '#80bfff', '#80bfbf', '#80b080', '#80bf80', '#80b040',
     '#80bf40', '#80bf00', '#80ff40', '#80ffbf', '#80ffff', '#bf0000', '#bf0080', '#bf0080', '#bf8000', '#bf8040',
     '#bf8080', '#bf80c0', '#bf80ff', '#bf40ff', '#bf00ff', '#bf00bf', '#bf0080', '#bf0040', '#bf0000', '#bf4000',
     '#bf4040', '#bf4080', '#bf40c0', '#bf40ff', '#bf00ff', '#bf00bf', '#bf0080', '#bf0040', '#bf0000', '#bf4000',
     '#bf4040', '#bf4080', '#bf40c0', '#bf40ff', '#bf00ff', '#bf00bf', '#bf0080', '#bf0040', '#bf0000', '#bf4000',
     '#bf4040', '#bf4080', '#bf40c0', '#bf40ff', '#bf00ff', '#bf00bf', '#bf0080', '#bf0040', '#bf0000', '#bf4000',
     '#bf4040', '#bf4080', '#bf40c0', '#bf40ff', '#bf00ff', '#bf00bf', '#bf0080', '#bf0040', '#bf0000', '#bf4000',
     '#bf4040', '#bf4080', '#bf40c0', '#bf40ff', '#bf00ff', '#bf00bf', '#bf0080', '#bf0040', '#bf0000', '#bf4000',
     '#bf4040', '#bf4080', '#00ff00', '#00ff80', '#00ffbf', '#00ffff', '#00bfff', '#00bfbf', '#00b080', '#00bf80',
     '#00b040', '#00bf40', '#00bf00', '#00ff40', '#00ffbf', '#00ffff', '#80ff00', '#80ff80', '#80ffbf', '#80ffff',
     '#80bfff', '#80bfbf', '#80b080', '#80bf80', '#80b040', '#80bf40', '#80bf00', '#80ff40', '#80ffbf', '#80ffff',
     '#bf0000', '#bf0080', '#bf0080', '#bf8000', '#bf8040', '#bf8080', '#bf80c0', '#bf80ff', '#bf40ff', '#bf00ff',
     '#bf00bf', '#bf0080', '#ffff00', '#ffff80', '#ffffbf', '#ffffff', '#bfffff', '#bfbfbf', '#bf8080', '#bfbf80',
     '#bf4040', '#bfbf40', '#bf0000', '#bfbf00', '#bf00bf', '#bfbfbf', '#bf8080', '#bfbf80'})


class QPaletteButton(QPushButton):
    def __init__(self, color, parent=None, size=24):
        super(QPaletteButton, self).__init__(parent)
        self.setFixedSize(size, size)
        self.setStyleSheet(f'background-color: {color}')

这里就是随便写了一堆颜色,然后用一个按钮的子类来实现,这样就可以直接用按钮的clicked信号来实现颜色的选择了。

主程序:

if __name__ == '__main__':
    app = QApplication(sys.argv)
    window = MainWindow()
    window.setWindowTitle("Painter")
    window.resize(1400, 800)
    window.show()
    sys.exit(app.exec_())

总结

这里面需要注意的技术要素包括:

  1. 信号与槽的使用
  2. 事件处理,特别是鼠标事件,按下、释放、移动等
  3. 画图的基本原理,包括画笔、画刷、画布等
  4. 画图中,我们用了一个移动的技术,把画布移动到一个位置,以那个位置为0点画图,可以转动,然后移动回去。

Canvas的resizeEvent实际上承担了两个功能:初始创建的时候,会调用resizeEvent,然后在resize的时候也会调用resizeEvent,所以我们需要在resizeEvent里面做一些初始化的工作,比如创建画布,然后在resize的时候,我们需要重新创建画布,然后把画布的内容复制到新的画布上面,然后删除旧的画布。

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

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

相关文章

深入了解vector

vector 1. vector的介绍及使用1.1 vector的介绍1.2 vector的使用1.2.1 vector的定义((constructor)构造函数声明)1.2.2 vector iterator 的使用1.2.3 vector Capacity1.2.4 vector Modifiers1.2.4 vector 迭代器失效问题 2. vector模拟实现 1. vector的介…

快速排序的三种方法

今日复习了一下快速排序的算法。 hoare法 快速排序由Hoare在1960年提出。它的基本思想是:通过排序将需要排序的数据分割成独立的两部分,左边的所有数据都比右边的小,然后再按此方法对这两部分数据分别进行快速排序递归,使其变成有…

时间序列预测 | 基于秃鹰算法优化BP神经网络(BES-BP)的时间序列预测,matlab代码

文章目录 效果一览文章概述部分源码参考资料效果一览 文章概述 基于秃鹰算法优化BP神经网络(BES-BP)的时间序列预测,matlab代码 评价指标包括:R2、MAE、MSE、RMSE等,代码质量极高,方便学习和替换数据。 部分源码 %% 清空环境变量 warning off % 关闭报警信息…

BurpSuite—-Spider模块(蜘蛛爬行)

本文主要介绍BurpSuite—-Spider模块(蜘蛛爬行)的相关内容 关于BurpSuite的安装可以看一下之前这篇文章: http://t.csdn.cn/0Qw2n 一、简介 Burp Spider 是一个映射 web 应用程序的工具。它使用多种智能技术对一个应用程序的内容和功能进行全面的清查。 Burp Spi…

基于Qt+FFmpeg的视频监控系统

github源码 需求分析 假设一个业务场景:每个员工工位旁有两个网络摄像头。老板需要一个员工监控软件,在上班时软件可以拉取RTSP视频流,也可以随时录制视频。这样老板就可以知道谁在摸鱼了 ◕‿◕ 为防有人上纲上线,在此特别声明…

【Redis】聊一下缓存双写一致性

缓存虽然可以提高查询数据的的性能,但是在缓存和数据 进行更新的时候 其实会出现数据不一致现象,而这个不一致其实可能会给业务来带一定影响。无论是Redis 分布式缓存还是其他的缓存机制都面临这样的问题。 数据不一致是如何发生? 数据一致…

【c语言】文件复制原理

创作不易&#xff0c;本篇文章如果帮助到了你&#xff0c;还请点赞 关注支持一下♡>&#x16966;<)!! 主页专栏有更多知识&#xff0c;如有疑问欢迎大家指正讨论&#xff0c;共同进步&#xff01; &#x1f525;c语言系列专栏&#xff1a;c语言之路重点知识整合 &#x…

pg事务:事务ID

事务ID pg中每个事务都会分配事务ID&#xff0c;事务ID分为虚拟事务ID和持久化事务ID&#xff08;transactionID&#xff09;。pg的事务ID非常重要&#xff0c;是理解事务、数据可见性、事务ID回卷等等的重要知识点。 虚拟事务ID 只读事务不会分配事务ID&#xff0c;事务ID是…

【我的C++入门之旅】(下)

前言 参考前章内容【我的C入门之旅】(上) 目录 前言1.引用常引用传值、传引用效率比较引用和指针的区别 2.auto关键字使用场景 3.范围for 语法糖4.inline函数5.指针空值nullptr 1.引用 取别名&#xff0c;一块空间有多个名字或者说是一个变量有多个名字 比如&#xff1a;李逵&…

【LeetCode: 44. 通配符匹配 | 暴力递归=>记忆化搜索=>动态规划 】

&#x1f680; 算法题 &#x1f680; &#x1f332; 算法刷题专栏 | 面试必备算法 | 面试高频算法 &#x1f340; &#x1f332; 越难的东西,越要努力坚持&#xff0c;因为它具有很高的价值&#xff0c;算法就是这样✨ &#x1f332; 作者简介&#xff1a;硕风和炜&#xff0c;…

【Linux】线程详解之线程概念

前言 在我们的教材中&#xff0c;对线程给出以下的概念&#xff1a; 是进程内部的一个执行分支&#xff0c;在进程的内部运行&#xff0c;属于进程的一部分&#xff0c;比进程更加轻量化。 可能有的人看完之后都是懵的&#xff0c;什么叫在进程的内部运行&#xff0c;什么又是…

【正点原子STM32连载】 第十二章 SYSTEM文件夹介绍 摘自【正点原子】STM32F103 战舰开发指南V1.2

1&#xff09;实验平台&#xff1a;正点原子stm32f103战舰开发板V4 2&#xff09;平台购买地址&#xff1a;https://detail.tmall.com/item.htm?id609294757420 3&#xff09;全套实验源码手册视频下载地址&#xff1a; http://www.openedv.com/thread-340252-1-1.html 第十二…

文件夹路径保存不同,什么批量修改名称

在日常工作中不知道大家有没有遇到过&#xff0c;需要批量修改文件夹名称&#xff0c;并且文件夹保存路径不同呢&#xff0c;像这种情况到底不能批量修改呢。我也问了很多身边的朋友&#xff0c;他们有的说&#xff0c;他一般都修改保存路径是同一个&#xff0c;还很少遇到像我…

【C++ STL】 趣学stackqueuepriority_queue【对话情景版】

文章目录 &#x1f4cd;前言C STL 之 stack&queue基础知识及其模拟实现&#x1f4cd;容器适配器&#x1f388;什么是适配器&#xff1f;&#x1f388;STL标准库中stack和queue的底层结构&#x1f388;deque的简单介绍(了解)&#x1f4cc;deque的原理介绍&#x1f4cc;deque…

强化学习:基本概念

以 grid-world 为例&#xff0c;进行强化学习基础概念的介绍。如图&#xff0c;机械人处于一个网格世界中&#xff0c;不同的网格代表不同的功能&#xff0c;白色代表机械人可以自由的进入&#xff0c;黄色代表陷阱&#xff08;机械人一旦进入就会被强制返回起点&#xff09;&a…

《Reinforcement Learning: An Introduction》第1章笔记

文章目录 1.1 强化学习1.2 强化学习的例子1.3 强化学习的要素1.4 局限和范围1.5 拓展例子&#xff1a;井字游戏1.6 总结1.7 强化学习的早期历史参考资料 1.1 强化学习 强化学习是学习做什么—如何将情景映射到动作—以便最大化数字奖励信号。学习者不会被告知该采取什么动作&a…

MySQL基础(三十九)MySQL常用命令

1 MySQL常用命令 1.1 mysql 该mysql不是指mysql服务&#xff0c;而是指mysql的客户端工具。 语法 &#xff1a; mysql [options] [database]1.1. 连接选项 #参数 &#xff1a; -u, --username 指定用户名 -p, --password[name] 指定密码 -h, --hostname 指定服务器IP或域名…

计算机组成原理实验报告二-认识汇编语言

实验资料&#xff1a; https://wwpv.lanzoue.com/b05drqjef 密码:d19t 使用txt文档编写下面C源码&#xff0c;文档命名为【学号_hello.c】并使用Mingw工具&#xff08;是 Minimalist GNU for Windows的缩写&#xff09;的bin文件夹下gcc.exe带选项编译&#xff08;&#xff09…

JUC之线程池的标准创建方式

文章目录 JUC之线程池的标准创建方式核心和最大线程数量空闲时长(keepAliveTime)线程工厂(ThreadFactory)任务阻塞队列线程池的拒绝策略线程池的任务调度流程 JUC之线程池的标准创建方式 ​ 因为使用Executors快捷创建线程池在使用时会有严重的潜在问题&#xff0c;因此在实战…

数据结构——队列的实现(细就完事了)

1.队列 1.1队列的概念和结构 今天我们要是实现的队列是完全相反的&#xff0c;队列是数据先进先出。而在栈中我们使用的顺序表(数组)来实现的。而队列却用单链表来实现是更加合适的。 队列&#xff1a;只允许在一端进行插入数据操作&#xff0c;在另一端进行数据操作的特殊线…