【Python开发】PyQt5应用与实践
一个典型的GUI应用程序可以抽象为:主界面(菜单栏、工具栏、状态栏、内容区域),二级界面(模态、非模态),信息提示(Tooltip),程序图标等组成。本篇根据作者使用PyQt5编写的一个工具,介绍如何使用PyQt5构建一个典型的GUI应用。
1. 主界面
QMainWindow类提供一个有菜单条、锚接窗口(例如工具条)和一个状态条的主应用程序窗口。主窗口通常用在提供一个大的中央窗口部件(例如文本编辑或者绘制画布)以及周围菜单、工具条和一个状态条。QMainWindow常常被继承,因为这使得封装中央部件、菜单和工具条以及窗口状态变得更容易。
菜单栏
创建菜单的代码如下:
self.addMenu = self.menuBar().addMenu("&添加")
self.addMenu.addAction(self.addAvatarAct)
self.addMenu.addAction(self.addAvatarSetAct)
self.addMenu.addAction(self.addAvatarDecorationAct)
self.modifyMenu = self.menuBar().addMenu("&修改")
self.modifyMenu.addAction(self.modifyAvatarAct)
self.modifyMenu.addAction(self.modifyAvatarSetAct)
self.settingMenu = self.menuBar().addMenu("&设置")
self.settingMenu.addAction(self.settingAct)
其中每个菜单项,关联一个QAction,定义了图标、菜单名、回调函数、快捷键等等,这里没有设置快捷键。
self.addAvatarAct = QAction(QIcon("res/ico/addAvatar.ico"), "&Add Avatar", self, triggered=self.addAvatar)
self.addAvatarSetAct = QAction(QIcon("res/ico/addAvatarSet.ico"), "&Add AvatarSet", self, triggered=self.addAvatarSet)
self.addAvatarDecorationAct = QAction(QIcon("res/ico/addAvatarDecoration.ico"), "&Add AvatarDecoration", self, triggered=self.addAvatarDecoration)
self.modifyAvatarAct = QAction(QIcon("res/ico/modifyAvatar.ico"), "&Modify Avatar or Decoration", self, triggered=self.modifyAvatar)
self.modifyAvatarSetAct = QAction(QIcon("res/ico/modifyAvatarSet.ico"), "&Modify AvatarSet", self, triggered=self.modifyAvatarSet)
self.settingAct = QAction(QIcon("res/ico/settingPath.ico"), "&路径", self, triggered=self.settingPath)
self.homeAct = QAction(QIcon("res/ico/home.ico"), "&首页", self, triggered=self.homePage)
说明:QAction类提供了一个可以同时出现在菜单和工具条上的抽象用户界面操作。
在图形用户界面应用程序中很多命令可以通过菜单选项、工具条按钮和键盘快捷键调用。因为同一个操作将会被执行,而与它的调用方法无关,并且因为菜单和工具条必须保持同步,所以提供一个操作这样的命令很有用。一个操作可以被添加到菜单和工具条中并且将会自动使它们同步。例如,如果用户按下“加粗”工具条按钮,“加粗”菜单项将会自动被选中。
QAction可以包含图标、菜单文本、快捷键、状态条文本、这是什么文本和工具提示。它们可以分别通过setIconSet()、setText()、setMenuText()、setToolTip()、setStatusTip()、setWhatsThis()和setAccel()来设置。
工具栏
创建工具栏的代码如下:
self.toolbar = self.addToolBar('Home')
self.toolbar.addAction(self.homeAct)
self.toolbar = self.addToolBar('AddAvatar')
self.toolbar.addAction(self.addAvatarAct)
self.toolbar = self.addToolBar('AddAvatarDecoration')
self.toolbar.addAction(self.addAvatarDecorationAct)
self.toolbar = self.addToolBar('AddAvatarSet')
self.toolbar.addAction(self.addAvatarSetAct)
self.toolbar = self.addToolBar('ModifyAvatar')
self.toolbar.addAction(self.modifyAvatarAct)
self.toolbar = self.addToolBar('ModifyAvatarSet')
self.toolbar.addAction(self.modifyAvatarSetAct)
工具栏项也需要关联一个QAction,可以和菜单项共用一个QAction,即一个QAction可以被关联到多个地方。
状态栏
设置状态栏,只需要:
self.statusBar().showMessage("数据加载完成")
第一次调用self.statusBar()获取工具栏时,会初始化工具栏实例,后面再次调用不会在创建新的实例。
程序图标
程序图标分为2个:程序窗口图标;执行文件的图标。
l setWindowIcon(QIcon(“res/ico/icon.ico”))设置程序窗口的图标
l 执行文件的图标,通过打包工具设置
2. UI布局
PyQt的布局系统提供了一个规定子窗口部件布局的简单的和强有力的方式。当你一旦规定了合理的布局,你就会获得如下利益:
l 布置子窗口部件。
l 最高层窗口部件可感知的默认大小。
l 最高层窗口部件可感知的最小大小。
l 调整大小的处理。
l 当内容改变的时候自动更新:
n 字体大小、文本或者子窗口部件的其它内容。
n 隐藏或者显示子窗口部件。
n 移去一些子窗口部件。
PyQt支持的布局方式有很多,如下表所示:
布局相关类 |
作用 |
QBoxLayout |
Lines up child widgets horizontally or vertically |
QButtonGroup |
Container to organize groups of button widgets |
QFormLayout |
Manages forms of input widgets and their associated labels |
QGraphicsAnchor |
Represents an anchor between two items in a QGraphicsAnchorLayout |
QGraphicsAnchorLayout |
Layout where one can anchor widgets together in Graphics View |
QGridLayout |
Lays out widgets in a grid |
QGroupBox |
Group box frame with a title |
QHBoxLayout |
Lines up widgets horizontally |
QLayout |
The base class of geometry managers |
QLayoutItem |
Abstract item that a QLayout manipulates |
QSizePolicy |
Layout attribute describing horizontal and vertical resizing policy |
QSpacerItem |
Blank space in a layout |
QStackedLayout |
Stack of widgets where only one widget is visible at a time |
QStackedWidget |
Stack of widgets where only one widget is visible at a time |
QVBoxLayout |
Lines up widgets vertically |
QWidgetItem |
Layout item that represents a widget |
其中使用比较多的是以下布局方式(或者说是我使用比较多,不代表大家):
- 水平布局 QHBoxLayout
- 垂直布局 QVBoxLayout
- 网格布局 QGridLayout
水平布局
水平布局(QHBoxLayout)顾名思义,将空间水平切成多段,然后通过addWidget、addItem将widget填充指定的位置。如下代码即实现了上图中,适合角色选择的水平布局:
hbox = QHBoxLayout()
self.roleChkBoxGroup.setLayout(hbox)
for _, v in sorted(ParseKeywords.profession.items()):
checkBox = QRadioButton(v["cname"] + " " + str(v["value"]))
hbox.addWidget(checkBox)
删除一个控件,使用removeWidget,或者调用QWidget.hide()一样可以从布局中删除,直到QWidget.show()被调用。下面的垂直布局、网格布局,甚至其他布局都是注意的。
垂直布局
垂直布局(QVBoxLayout)顾名思义,将空间垂直切成多段,然后通过addWidget、addItem将widget填充指定的位置。如下代码即实现了上图中,细节信息的垂直布局(垂直布局中,还嵌套了水平布局):
vbox = QVBoxLayout()
groupBox.setLayout(vbox)
count = QWidget()
hbox = QHBoxLayout()
countLabel = QLabel("细节数目:")
hbox.addWidget(countLabel)
self.countSpineBox = QSpinBox()
self.countSpineBox.setRange(0, 10)
self.countSpineBox.valueChanged.connect(self.countSpineValueChanged)
hbox.addWidget(self.countSpineBox)
hbox.addStretch()
count.setLayout(hbox)
vbox.addWidget(count) #垂直布局,添加widget1
self.detailTable = QTableWidget()
self.detailTable.setColumnCount(9)
self.detailTable.setHorizontalHeaderLabels(
['有效期', '货币类型', '价格',
'普通折扣价', '蓝钻价', '蓝钻折扣价', '超级蓝钻折扣价',
'赠送礼包ID', '快捷购买'])
vbox.addWidget(self.detailTable) #垂直布局,添加widget2
垂直布局中,还嵌套了水平布局。
说明:QHBoxLayout、QVBoxLayout都是继承自QBoxLayout,为了更好的控制布局,都继承了以下方法:
l QBoxLayout.addSpacing (size)
添加一个不能伸缩的空间(一个QSpacerItem),其宽度设置为size到布局末尾。框布局提供了默认的边距margin和spacing,这是额外添加的空间。
l QBoxLayout.addStretch(stretch)
添加一个可伸缩的空间(一个QSpacerItem),设0为最小值并且伸缩因子为stretch直到布局末尾
网络布局
网格布局(QGridLayout)顾名思义,将空间划分成多行多列的网络,然后通过addWidget、addItem将widget填充到指定的单元格(cell)。这个比较像网页中使用table布局的思路。下面的代码即创建上图中的网格布局:
位-2,第2~3位-表示适用角色,第4~5位-挂点位置,第6~8位-序号)"),
0, 2)
grid.addWidget(subidLabel, 1, 0)
grid.addWidget(self.subidLineEdit, 1, 1)
grid.addWidget(QLabel("(套装包含的物品,多个物品适用逗号分隔;必须在套装之前添加)"), 1, 2)
grid.addWidget(fashionLabel, 2, 0)
grid.addWidget(self.fashionLineEdit, 2, 1)
grid.addWidget(nameLabel, 3, 0)
grid.addWidget(self.nameLineEdit, 3, 1)
grid.addWidget(descLabel, 4, 0)
grid.addWidget(self.descLineEdit, 4, 1)
grid.addWidget(marketTagLabel, 5, 0)
grid.addWidget(self.tagCombox, 5, 1)
grid.addWidget(recommendLabel, 6, 0)
grid.addWidget(self.recommendCombox, 6, 1)
grid.addWidget(roleLabel, 8, 0)
grid.addWidget(self.roleChkBoxGroup, 8, 1)
grid.addWidget(beginLabel, 9, 0)
grid.addWidget(self.beginTime, 9, 1)
grid.addWidget(endLabel, 10, 0)
grid.addWidget(self.endTime, 10, 1)
gridWidget = QWidget()
gridWidget.setLayout(grid)
上述往网格中添加的widget都是占一个单元格的情况,其实还支持占用几个单元格。如下代码,往网格中的第二行、第一列添加一个widget,占用1行、2列:
grid.addWidget(self.createDetail(), 1, 0, 1, 2)
网格布局默认是均分每列,为了更好的控制布局,QGridLayout为每列提供了最小宽度(setColumnMinimumWidth())、伸缩因子(setColumnStretch()),为每行提供了最小高度(setRowMinimumHeight())、伸缩因子(setRowStretch())。最小宽/高度很好理解,伸缩因子如下面代码,设置了第二列和三列的比例是1:2。
layout.setColumnStretch(1, 10)
layout.setColumnStretch(2, 20)
3. 二级弹窗
QDialog类是对话框窗口的基类。对话框窗口是主要用于短期任务以及和用户进行简要通讯的顶级窗口。QDialog可以是模态对话框也可以是非模态对话框。QDialog支持扩展性并且可以提供返回值。它们可以有默认按钮。
内置对话框
内置常用的对话框有:QColorDialog、QErrorMessage、QFileDialog、QFontDialog、QInputDialog、QMessageBox、QProgressDialog、QTabDialog、QWizard。
内置的对话框提供了一些常用的功能,使用起来也必将遍历。编写该工具使用到了,选择文件、目录的对话框QFileDialog。
自定义对话框
如果内置的对话框不能满足需求,可以自定义对话框(继承自QDialog)。如下定义了一个设置路径的对话框:
class SettingDialog(QDialog):
def __init__(self, parent=None):
super(SettingDialog, self).__init__(parent)
self.path = Global.path
self.initUI()
self.setWindowIcon(QIcon("res/ico/settingPath.ico"))
self.setWindowTitle("设置")
self.resize(240, 100)
def initUI(self):
grid = QGridLayout()
grid.addWidget(QLabel("路径:"), 0, 0)
self.pathLineEdit = QLineEdit()
self.pathLineEdit.setFixedWidth(200)
self.pathLineEdit.setText(Global.path)
grid.addWidget(self.pathLineEdit, 0, 1)
button = QPushButton("更改")
button.clicked.connect(self.changePath)
grid.addWidget(button, 0, 2)
grid.addWidget(QLabel("<font color='#ff0000'>包含Keywords.xml、Avatar,AvatarSet,Market.xls的路径</font>"),
1, 0, 1, 3)
buttonBox = QDialogButtonBox()
buttonBox.setOrientation(Qt.Horizontal) # 设置为水平方向
buttonBox.setStandardButtons(QDialogButtonBox.Ok|QDialogButtonBox.Cancel)
buttonBox.accepted.connect(self.accept) # 确定
buttonBox.rejected.connect(self.reject) # 取消
grid.addWidget(buttonBox, 2, 1)
self.setLayout(grid)
def changePath(self):
open = QFileDialog()
self.path = open.getExistingDirectory()
self.pathLineEdit.setText(self.path)
print(self.path)
使用对话框,只需要:
dialog = SettingDialog()
if dialog.exec_():
# -----
4. 常用组件
下面介绍编写工具过程中使用到的组件的一些注意事项。
QTableWidget
列自适应
如果有很多列,QTableWidget出出现水平滚动条,但是有不希望有滚动条可以通过设置列自适应方式:
tw.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch)
保证所以列都能显示,不会出现水平滚动条,这样有的单元格显示会被截断显示,如图中的"青年套装下装"-->"青年套装...",这时可以设置单元的tooltip提供完整显示的途径。
禁止编辑
编写工具时,有要求QTableWidget展示出来的数据不能编辑,是通过以下方式实现:
tw.setEditTriggers(QAbstractItemView.NoEditTriggers)
QAbstractItemView还定义了其它的模式,如下表所示:
Constant |
Value |
Description |
QAbstractItemView.NoEditTriggers |
0 |
No editing possible. |
QAbstractItemView.CurrentChanged |
1 |
Editing start whenever current item changes. |
QAbstractItemView.DoubleClicked |
2 |
Editing starts when an item is double clicked. |
QAbstractItemView.SelectedClicked |
4 |
Editing starts when clicking on an already selected item. |
QAbstractItemView.EditKeyPressed |
8 |
Editing starts when the platform edit key has been pressed over an item. |
QAbstractItemView.AnyKeyPressed |
16 |
Editing starts when any key is pressed over an item. |
QAbstractItemView.AllEditTriggers |
31 |
Editing starts for all above actions. |
按行选择
设置QTableWidget按行选择:
tw.setSelectionBehavior(QAbstractItemView::SelectRows); //整行选中的方式
QAbstractItemView还定义了其它的模式,如下表所示:
Constant |
Value |
Description |
QAbstractItemView.SelectItems |
0 |
Selecting single items. |
QAbstractItemView.SelectRows |
1 |
Selecting only rows. |
QAbstractItemView.SelectColumns |
2 |
Selecting only columns. |
表头排序
如果希望单击QTableWidget表头进行数据排序,可以简单通过以下接口实现:
tw.setSortingEnabled(True)
但是,排序需要注意的2个问题:
l 点了下qtablewidget 的标题,它排序正常,修改数据,在查询,数据显示有问题
重新获取数据之前先关闭可排序性,获取到数据之后再开启排序性
l 排序规则问题,默认使用字母排序
使用以下方式设置单元格,会使用字母排序
item = QTableWidgetItem()
item.setData(Qt.DisplayRole, "xxx")
或者
item = QTableWidgetItem()
item.setText("xxx")
如果需要按照数值排序需要使用以下方式设置单元格
item = QTableWidgetItem()
item.setData(Qt.DisplayRole, int(1212))
自定义单元格控件
可以对QTableWidget自定义(添加)widget,如下为QTableWidget设置单元格为一个下拉选择的QCombox
combox = QComboBox()
for _, v in ParseKeyword.currencyType.items():
combox.addItem(v["cname"], v["value"])
combox.setCurrentText("点券")
tw.setCellWidget(row, 1, combox)
效果如下图所示:
QDateTimeEdit
显示格式
默认的时间显示格式(如2015/1/16 17:42),可能不满足需求,可以通过setDisplayFormat()设置显示格式来定制。格式选项如下所示:
这些是可能用到的日期表达式:
l d - 没有前置0的数字的天(1-31)
l dd - 前置0的数字的天(01-31)
l ddd - 缩写的日名称(Mon-Sun)。使用QDate.shortDayName()。
l dddd - 长的日名称(Monday-Sunday)。使用QDate.longDayName()。
l M - 没有前置0的数字的月(1-12)
l MM - 前置0的数字的月(01-12)
l MMM - 缩写的月名称(Jan-Dec)。使用QDate.shortMonthName()。
l MMMM - 长的月名称(January-December)。使用QDate.longMonthName()。
l yy - 两位数字的年(00-99)
l yyyy - 四位数字的年(0000-9999)
这些是可能用到的时间表达式:
l h - 没有前置0的数字的小时(0-23或者如果显示AM/PM时,1-12)
l hh - 前置0的数字的小时(00-23或者如果显示AM/PM时,01-12)
l m - 没有前置0的数字的分钟(0-59)
l mm - 前置0的数字的分钟(00-59)
l s - 没有前置0的数字的秒(0-59)
l ss - 前置0的数字的秒(00-59)
l z - 没有前置0的数字的毫秒(0-999)
l zzz - 前置0的数字的毫秒(000-999)
l AP - 切换为AM/PM显示。AP将被“AM”或“PM”替换。
l ap - 切换为am/pm显示。ap将被“am”或“pm”替换。
如工具中使用的格式为:
setDisplayFormat("yyyy-MM-dd hh:mm:ss")
显示效果如下图所示:
弹出日期选择窗口
希望点击QDateTimeEdit可以弹出日期选择窗口,可以简单的通过setCalendarPopup(True)实现,非常的简单。
5. 打包
python常用的打包工具有py2exe、pyinstaller、cx_freeze,而且现在都开始支持python3,py2exe可以打包成单exe文件,一般简单的东西都是用它来打包供其他人使用。但是使用py2exe打包PyQt5时,碰到了不少错误,后面干脆使用cx_freeze打包一次成功(不足之处,就是不能打包成单个exe)。下面简单介绍编写setup.py几个关键的点,详细的参考官方文档(http://cx-freeze.readthedocs.org/en/latest/index.html)。
l 默认只会打包代码文件,如果程序有非代码文件,如配置、资源文件需要打包,需要显示指定。如"include_files": ["setting.ini", "res"],打包时会将setting.ini文件、res资源目录拷贝到exe目录下。
l cx_freeze会自动检测依赖文件,但是有时候会抽风,可以通过"packages": ["os", "xlrd3", "xlwt3", "lxml"]显示包含。同时对不要的包,可以"excludes": ["tkinter"]指定不要编译到最终的软件包中。
l 指定文件名需要带exe后缀,cx_freeze是不会自动添加exe后缀的。
l 如果需要一次编译多个exe,可以在executables数组中列出多个,例如:
executables = [
Executable("main.py", base=base, targetName="Joker3DAvatarMgr.exe", compress=True, icon="res/ico/icon.ico"),
Executable("test.py", base=base, targetName="test.exe", compress=True, icon="res/ico/test.ico")
]
完整的setup.py文件如下所示:
import sys
from cx_Freeze import setup, Executable
# GUI applications require a different base on Windows (the default is for a
# console application).
base = None
if sys.platform == "win32":
base = "Win32GUI"
# Dependencies are automatically detected, but it might need fine tuning.
build_exe_options = {
"packages": ["os", "xlrd3", "xlwt3", "lxml"],
"excludes": ["tkinter"],
"include_files": ["setting.ini", "res"]
}
#
executables = [
Executable("main.py", base=base, targetName="Joker3DAvatarMgr.exe", compress=True, icon="res/ico/icon.ico")
]
setup( name = "setup",
version = "0.1",
description = "Joker3D prop manager tool!",
author = "tylerzhu",
author_email = "saylor.zhu@gmail.com",
options = {"build_exe": build_exe_options},
executables = executables,
)
编写好setup.py之后,可以通过python setup.py build打包。
网上有不少人反馈打包之后,放到没有按照PyQt的PC上执行,会报以下错误:“This application failed to start because it could not find or load the Qt platform plugin windows”
这个问题,我以前也碰到过,但是这次我用的Python3.4 + cx_freeze 4.3.4 + PyQt5-5.4-gpl-Py3.4-Qt5.4.0-x32.exe并没有出现这个问题。如果出现了这个问题也不要紧,通过以下方法可以解决:将PyQt5安装目录(Lib\site-packages\PyQt5)下的libGLESv2.dll拷到打包的exe目录下即可。
作者:吴秦
出处:http://www.cnblogs.com/skynet/
本文基于署名 2.5 中国大陆许可协议发布,欢迎转载,演绎或用于商业目的,但是必须保留本文的署名吴秦(包含链接).
【Python开发】PyQt5应用与实践的更多相关文章
- 【转】Python开发指南:最佳实践精选
总体原则 价值 “为别人开发你也想要使用的工具.” ——Kenneth Reitz "简洁总是胜过可用." ——Pieter Hintjens "满足90%的使用场景.忽 ...
- Python开发GUI工具介绍,实战:将图片转化为素描画!【华为云技术分享】
版权声明:本文为博主原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明. 本文链接:https://blog.csdn.net/devcloud/article/detai ...
- 【Machine Learning】Python开发工具:Anaconda+Sublime
Python开发工具:Anaconda+Sublime 作者:白宁超 2016年12月23日21:24:51 摘要:随着机器学习和深度学习的热潮,各种图书层出不穷.然而多数是基础理论知识介绍,缺乏实现 ...
- 通过数据分析告诉你北京Python开发的现状
相信各位同学多多少少在拉钩上投过简历,今天突然想了解一下北京Python开发的薪资水平.招聘要求.福利待遇以及公司地理位置.既然要分析那必然是现有数据样本.本文通过爬虫和数据分析为大家展示一下北京Py ...
- Python开发GUI工具介绍,实战:将图片转化为素描画!
欢迎添加华为云小助手微信(微信号:HWCloud002 或 HWCloud003),输入关键字"加群",加入华为云线上技术讨论群:输入关键字"最新活动",获取华 ...
- Python开发GUI实战:图片转换素描画工具!
奋斗没有终点 好好学习72变,因为将来 没有人能替你阻挡81难 . 生如蝼蚁,当有鸿鹄之志: 命如纸薄,应有不屈之心 . 今天被这句话触动了,所以开篇分享给大家.鸡汤有毒,但有时大家却靠它激励自己 ...
- 拉勾网python开发要求爬虫
#今日目标 **拉勾网python开发要求爬虫** 今天要爬取的是北京python开发的薪资水平,招聘要求,福利待遇以及公司的地理位置. 通过实践发现除了必须携带headers之外,拉勾网对ip访问频 ...
- Python开发WebService:REST,web.py,eurasia,Django
Python开发WebService:REST,web.py,eurasia,Django 博客分类: Python PythonRESTWebWebServiceDjango 对于今天的WebSe ...
- Python+Selenium基础入门及实践
Python+Selenium基础入门及实践 32018.08.29 11:21:52字数 3220阅读 23422 一.Selenium+Python环境搭建及配置 1.1 selenium 介绍 ...
- 在python开发工具PyCharm中搭建QtPy环境(详细)
在python开发工具PyCharm中搭建QtPy环境(详细) 在Python的开发工具PyCharm中安装QtPy5(版本5):打开“File”——“Settings”——“Project Inte ...
随机推荐
- codeforces613E
Puzzle Lover CodeForces - 613E Oleg Petrov loves crossword puzzles and every Thursday he buys his fa ...
- js输入密文弹出数字键盘
我们经常被产品要求,在移动端的web页面上的输入框输入密码时要弹出数字键盘,而不是全键盘,这个该怎么实现呢? 1.首先要弹出数字键盘,我们只能把input框的type从password改为tel 2. ...
- 树状数组(BIT)
树状数组 树状数组是在线段树的结构上改造而来数据结构,主要用于完成: 给定一个初始值全为0的数列 ①给定i,计算返回a1+a2+--+ai的值 ②给定i和x,执行ai+=x BIT的求和 ll sum ...
- AJAX-基础-1
概述 AJAX = Asynchronous JavaScript And XML(异步 JavaScript 及 XML) AJAX 是 Asynchronous JavaScript And XM ...
- 新版uni-app 在微信小工具调试遇到报错解决方案
问题描述:我在运行到微信小程序是运行报错打不开微信小程序报错如下图 结局方案:将微信小程序安全设置开启如下图
- OUC_Summer Training_ DIV2_#5
这是做的最好的一次了一共做了4道题 嘻嘻~ A - Game Outcome Time Limit:2000MS Memory Limit:262144KB 64bit IO For ...
- Java如何对一个对象进行深拷贝?
在Java语言里,当我们需要拷贝一个对象时,有两种类型的拷贝:浅拷贝与深拷贝.浅拷贝只是拷贝了源对象的地址,所以源对象的值发生变化时,拷贝对象的值也会发生变化.而深拷贝则是拷贝了源对象的所有值,所以即 ...
- SRS之信号的管理:SrsSignalManager
1. 综述 SRS 中使用了 State Threads 协程库,该库对信号的处理是将信号事件转换为 I/O 事件.主要做法是:对关注的信号设置同样地信号处理函数 sig_catcher(),该函数捕 ...
- yarn application命令介绍
yarn application 1.-list 列出所有 application 信息 示例:yarn application -list 2.-appStates <Stat ...
- DP&图论 DAY 3 上午
DP&图论 DAY 3 上午 状态压缩dp >状态压缩dp ◦状态压缩是设计dp状态的一种方式.◦当普通的dp状态维数很多(或者说维数与输入数据有关),但每一维总量很少是,可以将多维 ...