PyQt5桌面应用开发(16):定制化控件-QPainter绘图
本文目录
- 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 = 0RECT = 1CIRC = 2TRI = 3HEX = 4def __init__(self, parent=None):super(Canvas, self).__init__(parent)self.last_x, self.last_y = None, Noneself.pen_color = QColor(Qt.black)self.setMouseTracking(False)self.is_spray = Falseself.gauss_size = 10self.gauss_density = 100self.shape_type = self.ShapeType.NONEself.is_rotate = Falseself.angle = Noneself.line_width = 4self.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, Nonedef 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 coordinatesif 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, 0for 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 coordinatespainter.translate(-x, -y)
主窗口
主窗口要构造所有的界面元素,并提供保存按键组合的响应。
class MainWindow(QMainWindow):def __init__(self):super(MainWindow, self).__init__()self.toolbar_button_width = 30self.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 buttonsself.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_())
总结
这里面需要注意的技术要素包括:
- 信号与槽的使用
- 事件处理,特别是鼠标事件,按下、释放、移动等
- 画图的基本原理,包括画笔、画刷、画布等
- 画图中,我们用了一个移动的技术,把画布移动到一个位置,以那个位置为0点画图,可以转动,然后移动回去。
Canvas的resizeEvent实际上承担了两个功能:初始创建的时候,会调用resizeEvent,然后在resize的时候也会调用resizeEvent,所以我们需要在resizeEvent里面做一些初始化的工作,比如创建画布,然后在resize的时候,我们需要重新创建画布,然后把画布的内容复制到新的画布上面,然后删除旧的画布。
相关文章:
PyQt5桌面应用开发(16):定制化控件-QPainter绘图
本文目录 PyQt5桌面应用系列画画图,喝喝茶QPainter和QPixmapQPixmapQPainter绘制事件 一个魔改的QLabelCanvas类主窗口主程序: 总结 PyQt5桌面应用系列 PyQt5桌面应用开发(1):需求分析 PyQt5桌面应用开发(2…...
spring5源码篇(9)——mybatis-spring整合原理
spring-framework 版本:v5.3.19 spring和mybatis的整合无非主要就是以下几个方面: 1、SqlSessionFactory怎么注入? 2、Mapper代理怎么注入? 3、为什么要接管mybatis事务? 文章目录 一、SqlSessionFactory怎么注入SqlSe…...
为什么需要防雷接地,防雷接地的作用是什么
为什么需要电气接地? 您是否曾经在工作条件下使用任何电器时接触过电击?几乎每个人的答案都是肯定的,有时这些电击是轻微的,但有时会对电气和电子设备造成损坏,并可能危及生命。为防止对人的生命和电器造成任何损害&a…...
如何应用金字塔模型提高结构化表达能力
看一下结构化表达的定义: 结构化表达:是基于结构化思维,理清事物整理与部分之间关系、换位思考后,进行简洁、清晰和有信服力的表达,是一种让受众听得明白、记得清楚、产生认同的精益沟通方式。 结构化表达的基本原则是…...
2023年系统分析师考前几页纸
企业战略规划是用机会和威胁评价现在和未来的环境,用优势和劣势评价企业现状,进而选择和确定企业的总体和长远目标,制定和抉择实现目标的行动方案。信息系统战略规划关注的是如何通过该信息系统来支撑业务流程的运作,进而实现企业的关键业务目标,其重点在于对信息系统远景…...
openwrt-安装NGINX
openwrt-安装NGINX 介绍 OpenWrt 是一个用于嵌入式设备的开源操作系统。它基于 Linux 内核,并且主要被设计用于路由器和网络设备。 OpenWrt 的主要特点包括: 完全可定制:OpenWrt 提供了一个完全可写的文件系统,用户可以自定义设…...
Linux安装MongoDB数据库并内网穿透在外远程访问
文章目录 前言1.配置Mongodb源2.安装MongoDB数据库3.局域网连接测试4.安装cpolar内网穿透5.配置公网访问地址6.公网远程连接7.固定连接公网地址8.使用固定公网地址连接 转发自CSDN cpolarlisa的文章:Linux服务器安装部署MongoDB数据库 - 无公网IP远程连接「内网穿透…...
flutter系列之:使用AnimationController来控制动画效果
文章目录 简介构建一个要动画的widget让图像动起来总结 简介 之前我们提到了flutter提供了比较简单好用的AnimatedContainer和SlideTransition来进行一些简单的动画效果,但是要完全实现自定义的复杂的动画效果,还是要使用AnimationController。 今天我…...
golang 函数调用栈笔记
一个被函数在栈上的情况:(栈从高地址向低地址延伸) 返回地址(函数执行结束后,会跳转到这个地址执行) BP(函数的栈基)局部变量返回值(指的是函数返回值,eg&am…...
云端一体助力体验升级和业务创新
随着音视频和AI技术的发展,在满足用户基础体验和需求情况下,更极致的用户体验和更丰富的互动玩法,成为各个平台打造核心竞争力的关键。LiveVideoStackCon 2022 北京站邀请到火山引擎视频云华南区业务负责人——张培垒,基于节跳动音…...
【Linux Network】高级IO
目录 前言 五种IO模型 阻塞IO 非阻塞IO 信号驱动IO IO多路转接 异步IO 小结 同步通信 vs 异步通信 阻塞 vs 非阻塞 其他高级IO 非阻塞IO fcntl函数 代码测试 高级IO🌷 前言 IO:所谓的I便是 input,所谓的O便是 output,简单点来说&a…...
Python语言基本控制结构
Python语言基本控制结构包括:条件语句:if、elif、else 循环语句:for、while 跳转语句:break、continue、return 下面是它们的基本用法: 条件语句 if condition1: statement1 elif condition2: statement2 else: stat…...
旅游网站版面设计方案
门户网站的页面设计:采用天蓝色为主色调、以红色、绿色为基调,突出网站的青春活波性。 网站风格尽量简单统一、容易让大多数上网者接受,个性化的网站,再者,网站有主体信息结构及布 word . . . . 局,它是总体…...
sudo unable to open read-only file system”的原因
此错误是由多种原因引起的,包括: 文件系统不一致。文件系统配置错误(/etc/fstab 文件中的错误条目)。由于各种原因(包括突然断电或电缆损坏)导致系统意外或突然关闭。在某些情况下,Windows 的双…...
Dynamics 365 DevOps CI/CD之WebResource
对于D365自身的发布,简单点来说就是Solution的发布,复杂一些会涉及周边集成接口等一系列的发布。如果是单纯的Solution的发布的Azure DevOps商店里有很多工具,比如Power DevOps Tools,这个我之前也有博文转载过相关文章࿰…...
Linux常用指令及基础配置
Linux常用指令及基础配置 Linux系统操作指令文件管理指令系统设置相关指令addr2line的运用 vim相关vim配置操作Vim功能操作分屏擦操作删除操作换行符转换操作:文件比较合并操作折叠指令 Linux系统操作指令 文件管理指令 find ./ -iname filename find ./ -name “…...
Linux 服务器上Nvidia相关指令
1、GPU驱动的内存常驻模式 1)操作命令: 确保你具有root或sudo权限,以执行下面的命令。打开终端或命令行界面。运行以下命令来设置GPU驱动的内存常驻模式: nvidia-smi -pm 1这会将GPU驱动程序设置为内存常驻模式。 4. 验证设置是否成功。运…...
ChatGPT的工作原理是什么?
ChatGPT是美国OpenAI研发的聊天机器人程序,2022年11月30日发布。ChatGPT是人工智能技术驱动的自然语言处理工具,它能够通过理解和学习人类的语言来进行对话。 ChatGPT的原理 ChatGPT是一种基于人工智能技术的自然语言生成模型,它能够从大量…...
C++进阶——红黑树
C进阶——红黑树 概念 红黑树,是一种二叉搜索树,但在每个结点上增加一个存储位表示结点的颜色,可以是Red或Black。 通过 对任何一条从根到叶子的路径上各个结点着色方式的限制,红黑树确保没有一条路径会比其他路径长出俩 倍&…...
什么是NTFS for Mac?2023新版本如何下载
在NTFS for Mac中包含了多种功能操作,促进软件更好地使用,可以进行全局设置,也可以针对某一各挂载的磁盘进行针对性设置。 本集小编主要向大家介绍它包含的一些基本功能,看看这款mac读写工具能够实现那些功能,全面了解…...
变量 varablie 声明- Rust 变量 let mut 声明与 C/C++ 变量声明对比分析
一、变量声明设计:let 与 mut 的哲学解析 Rust 采用 let 声明变量并通过 mut 显式标记可变性,这种设计体现了语言的核心哲学。以下是深度解析: 1.1 设计理念剖析 安全优先原则:默认不可变强制开发者明确声明意图 let x 5; …...
手游刚开服就被攻击怎么办?如何防御DDoS?
开服初期是手游最脆弱的阶段,极易成为DDoS攻击的目标。一旦遭遇攻击,可能导致服务器瘫痪、玩家流失,甚至造成巨大经济损失。本文为开发者提供一套简洁有效的应急与防御方案,帮助快速应对并构建长期防护体系。 一、遭遇攻击的紧急应…...
AtCoder 第409场初级竞赛 A~E题解
A Conflict 【题目链接】 原题链接:A - Conflict 【考点】 枚举 【题目大意】 找到是否有两人都想要的物品。 【解析】 遍历两端字符串,只有在同时为 o 时输出 Yes 并结束程序,否则输出 No。 【难度】 GESP三级 【代码参考】 #i…...
在四层代理中还原真实客户端ngx_stream_realip_module
一、模块原理与价值 PROXY Protocol 回溯 第三方负载均衡(如 HAProxy、AWS NLB、阿里 SLB)发起上游连接时,将真实客户端 IP/Port 写入 PROXY Protocol v1/v2 头。Stream 层接收到头部后,ngx_stream_realip_module 从中提取原始信息…...
智能分布式爬虫的数据处理流水线优化:基于深度强化学习的数据质量控制
在数字化浪潮席卷全球的今天,数据已成为企业和研究机构的核心资产。智能分布式爬虫作为高效的数据采集工具,在大规模数据获取中发挥着关键作用。然而,传统的数据处理流水线在面对复杂多变的网络环境和海量异构数据时,常出现数据质…...
Maven 概述、安装、配置、仓库、私服详解
目录 1、Maven 概述 1.1 Maven 的定义 1.2 Maven 解决的问题 1.3 Maven 的核心特性与优势 2、Maven 安装 2.1 下载 Maven 2.2 安装配置 Maven 2.3 测试安装 2.4 修改 Maven 本地仓库的默认路径 3、Maven 配置 3.1 配置本地仓库 3.2 配置 JDK 3.3 IDEA 配置本地 Ma…...
Fabric V2.5 通用溯源系统——增加图片上传与下载功能
fabric-trace项目在发布一年后,部署量已突破1000次,为支持更多场景,现新增支持图片信息上链,本文对图片上传、下载功能代码进行梳理,包含智能合约、后端、前端部分。 一、智能合约修改 为了增加图片信息上链溯源,需要对底层数据结构进行修改,在此对智能合约中的农产品数…...
在Ubuntu24上采用Wine打开SourceInsight
1. 安装wine sudo apt install wine 2. 安装32位库支持,SourceInsight是32位程序 sudo dpkg --add-architecture i386 sudo apt update sudo apt install wine32:i386 3. 验证安装 wine --version 4. 安装必要的字体和库(解决显示问题) sudo apt install fonts-wqy…...
IP如何挑?2025年海外专线IP如何购买?
你花了时间和预算买了IP,结果IP质量不佳,项目效率低下不说,还可能带来莫名的网络问题,是不是太闹心了?尤其是在面对海外专线IP时,到底怎么才能买到适合自己的呢?所以,挑IP绝对是个技…...
代码规范和架构【立芯理论一】(2025.06.08)
1、代码规范的目标 代码简洁精炼、美观,可持续性好高效率高复用,可移植性好高内聚,低耦合没有冗余规范性,代码有规可循,可以看出自己当时的思考过程特殊排版,特殊语法,特殊指令,必须…...
