Software Develop
Qt5 官方函数:http://doc.qt.io/qt-5/classes.html
- 一、控件类
- 1、文本框QLineedit
- 2、信号与槽(3种方法)
- A、connect连接方法
- B、槽函数使用装饰器
- C、在实例控件的时候传入参数实现
- 3、对话框QxxxDialog
- 4、提示框QMessageBox
- 5、键鼠事件获取Event
- 6、下拉选择框QCombobox
- 7、spinbox值的限制
- 8、创建自己的控件类
- 二、二维图形显示
- 1、绘图view、scene、item关系
- 1)体系结构
- GraphicsView视图
- GraphicsScene场景
- GraphicsItem图形
- 2)坐标系统
- GraphicsView视图坐标
- GraphicsScene场景坐标
- GraphicsItem图形坐标
- 1)体系结构
- 2、图表QChart
- 1、绘图view、scene、item关系
- 三、Table表格
- 1、QTableView
- 2、QTableWidget
- 两者区别比较
- 四、pyqt 程序结构
- 1、主程序
- 2、test gui
- 测试gui实验
- 1、库
- 2、整体思路
- 3、结论
- 4、代码(测试部分)
- 测试gui实验
- 五、自定义样式qss
- 原理
- 盒子模型
- 语法
- filter
- 伪类(伪状态)
- 举个栗子
- qss参考
- 六、其他辅助
- 1、线程加进度框增加用户体验
- 2、language翻译
- 3、assistant文档
- 4、正则化
一、控件类
1、文本框QLineedit
1)对文本框进行赋值
self.lineedit_x.setText(“xxx”)
2)获取文本框内容
a = self.lineedit_x.text()
3)限制文本框值范围0-999
limit = QIntValidator(0,999,self)
self.lineEdit_x.setValidator(limit)
4)当文本框内数值改变会产生一个触发信号
self.lineEdit_x.textChanged()(const QString &text)
5)文本框内字符串改变时,作为信号,槽是editChange(self,text)函数,该函数的参数text是文本框的数值。
self.lineEdit_x.textChanged.connect(self.editChange)
6)设置可以输入最多字符数x
setMaxLength(x)
2、信号与槽(3种方法)
A、connect连接方法
信号.connect(槽函数)
button按下产生一个信号,槽为event_x,connect将两者相连,按钮按下执行event_x事件,槽函数代表的事件函数不需要加括号
self.pushButton_x.clicked.connect(self.event_x)
B、槽函数使用装饰器
class Dialog(Ui_SettingWidget, QWidget):
def __init__(self, parent=None):
super(Dialog, self).__init__(parent=parent)
self.setupUi(self)
self.checkBox.setObjectName("mycheckbox")
QMetaObject.connectSlotsByName(self)
#自动寻找满足要求的槽函数 on_控件objectname名称_信号触发事件
@pyqtSlot(bool, name="on_mycheckbox_toggled")
def on_mycheckbox_toggled(self, status):
print(status)
# print('click ckb', self.checkBox.isChecked())
C、在实例控件的时候传入参数实现
# 按钮点击函数
def doClicked(self):
print(self.sender(), 'clicked')
pushButton = QPushButton('按钮', self, clicked=self.doClicked, minimumHeight=40)
3、对话框QxxxDialog
1)t为文本框里的内容,ok是按下OK为true,按下cancel为False
t, ok = QInputDialog.getText(self, "用户输入对话框", "请输入内容")
2)s 储存了路径的地址文本
s = QFileDialog.getOpenFileName(self, "dialogname", "path", "python file(*py)" )
打印出路径
self.filelL.setText(s[0])
3)c存放了获取的颜色
c = QColorDialog.getColor(Qt.blue)
4)f存放了获取的字体种类,ok表示是否选择了字体,返回ture或false
f, ok = QFontDialog.getFont()
4、提示框QMessageBox
1)warring
QMessageBox.critical(self, "name", "contain")
2)
reply = QMessageBox.question(self, 'message', 'quit?', QMessageBox.Yes | QMessageBox.No, QMessageBox.No)
3)
msgBox = QMessageBox()
msgBox.setStandardButtons(QMessageBox.Ok)
4)获取当前产生事件的对象
self.sender()
5、键鼠事件获取Event
def mousePressEvent(self, event):
# 鼠标左键按下
if e.button() == QtCore.Qt.LeftButton:
self.setText("left")
# 鼠标右键按下
elif e.button() == QtCore.Qt.RightButton:
self.setText("right")
# 鼠标中键按下
elif e.button() == QtCore.Qt.MidButton:
self.setText("mid")
重载mouseXxxEvent(),假设当前的表格类继承QTableWidget来写
def mouseReleaseEvent(self, QMouseEvent):
#xxx操作
QTableWidget.mouseReleaseEvent(self, QMouseEvent)#保留重写之前的mouseReleaseEvent的所有功能,即在原有功能上加上xxx操作,但若无此函数,则为覆盖重写
键盘事件:
def keyPressEvent(self, QKeyEvent):
if QKeyEvent.key() == Qt.Key_Enter:
print("xxx")
6、下拉选择框QCombobox
历遍combobox里的所有item项目
item_amount = self.combobox.count()
for i in range(item_amount):
if self.combobox.itemText(i) == xxx
combobox可以包含data()数据,表边上让用户选择的是字符串,实际上可以拿到当前index对应的data()
对应的选项变更产生的触发信号有
currentTextChanged # 文本更改产生信号
currentIndexChanged # 选项行更改产生信号
activated # 鼠标选择后产生的信号
7、spinbox值的限制
很多时候,spinbox的范围并不是不变的,而是随着算法的运行变化,此时就要动态设置其范围。
获取spinbox输入完成状态的有xxxspinbox.editingFinished信号与xxxspinbox.valueChanged信号,editingFinished信号是在输入完成后需要按下回车键在发送信号,而valueChanged是有输入变化就发送信号,例如输入1996,将分别在输入1,9,9,6的时候发送总共四个信号,而对其输入值的检查是否合法,应该在完全输入完之后才是合理的。
以下组合选择第三种
1)valueChanged与在valueChanged connect的函数下进行自定义函数checkvalue
假若范围为200-300,要输入255,输入2时会强制转为200,导致手动输入不到255,要通过鼠标才实现
2)editingFinished与在editingFinished connect的函数下进行自定义函数checkvalue
假若输入完没按下回车,即进行其他操作,数据未保留
3)valueChanged与对spinbox.setRange()
8、创建自己的控件类
当一个widget复用到多个dialog内时,可以创建自己的控件类减少代码量。这时,widget只有ui的框架,内容通过dialog读取,widget emit signal同时携带数据与dialog进行交互,此番操作是为了解耦,增强程序复用性。可以参考ColorMatch与ColorSegment。
在设计ui文件时候,在右方与下方添加水平的spacer与竖直防线的spacer,为了在不同大小的界面控件下所包含的控件占用同样的空间大小。
二、二维图形显示
1、绘图view、scene、item关系
1)体系结构
Graphics View提供了一个界面,它既可以管理大数量的定制2D graphical items,又可与它们交互,有一个view widget可以把这些项绘制出来,并支持旋转与缩放。这个柜架也包含一个事件传播结构,对于在scene中的这些items,它具有双精度的交互能力。Items能处理键盘事件,鼠标的按,移动、释放、双击事件,也可以跟踪鼠标移动。Graphics View使用BSP树来提供对item的快速查找,使用这种技术,它可以实时地绘制大规模场景,甚至以百万items计。Graphics View在Qt 4.2中被引用,它替代了它的前辈QCanvas。
GraphicsView视图
QGraphicsView提供了视图部件,它可视化场景中的内容。你可以联结多个视图到同一个场景,对这个相同的数据集提供几个视口。视口部件是一个滚动区域,它提供了滚动条以对大场景进行浏览。(如果使用需要OpenGL,你应该调用QGraphicsView.setViewport()来把一个QGLWidget设为视口。)
GraphicsScene场景
为管理大量的items提供一个快速的接口。 传播事件到每个item。 管理item的状态,例如选择,焦点处理。 提供未经变换的渲染功能,主要用于打印。 场景作为QGraphicsItem对象的容器。通过调用QgraphicsScene::addItem()把这些Items加入到场景中。可以使用众多的查找函数来获取特定的items。QGraphicsScene:items()与它的许多重载函数可获取那些与点、矩形,多边形,向量路径等相交或是有包含有关系的items。QGraphicsScene::itemAt()返回特定上最顶端的item。所有的item查找函数都以出栈序列返回(也就是说,第一个返回的是最顶端的,最后一个返回的是最底端的)。
QGraphicsScene scene;
QGraphicsRectItem *rect=scene.addRect(QRectF(0,0,100,100));
QGraphicsItem *item=scene.itemAt(50,50); //item==rect;
QGraphicsScene的事件传播结构会把场景事件投递到items,也管理多个items之间的传递。假如场景收到了鼠标在某个位置press事件,场景会把这个事件投递给处在那个位置的item。QGraphicsScene也管理某种item状态,像选择与焦点。你可以通过调用QGraphicsScene::setSelectionArea()来选择items,它需要提供一个任意的形状为参数。这个函数也作为在QGraphicsView实现橡皮筋选择功能的一个基础。为得到这些已经被选择的items,调用QGraphicsScene::selectedItem()。另一个状态处理是是否一个item拥有键盘输入焦点。你可以调用QGraphicsScene::setFocusItem()或QGraphics::setFocus()来设定焦点,也可用QGraphicsScene::focusItem()来得到当前拥有焦点的那个item。最后,QGraphicsScene允许你通过调用QGraphicsScene::render()函数把部分场景送到绘图设备进行渲染。
GraphicsItem图形
QGraphicsItem 是场景中图形items的基类。Graphics View 提供了一些标准的、用于典型形状的items。像矩形(QGraphicsRectItem),椭圆(QGraphicsEllipseItem),文本(QGraphicsTextItem),当你写定制的item时,那些最有用的一些QGraphicsItem特性也是有效的。除此这外,QGraphicsItem支持以下特性: 鼠标按、移动、释放、双击事件,鼠标悬停事件,滚轮事件,弹出菜单事件。 键盘输入焦点,键盘事件。 拖拽 组,包括父子关系,使用QGraphicsItemGroup *碰撞检测 Items如同QGraphicsView一样,位于本地坐标系,它也为item与场景之间,item与item之间的坐标转换提供许多工具函数。而且,也像QGraphicsView一样,它使用矩阵来变换它的坐标系统:QGraphicsItem::matrix()。它对旋转与缩放单个的Item比较有用。 Items可以包含别的items(孩子)。父items的转换被它的子孙所继承。然而,它的所有函数
QGraphicsItem::contains();
QGraphicsItem::boundingRect();
QGraphicsItem::collidesWith();
不会积累这些转换,依然在本地坐标下工作。 QGraphicsItem通过QGraphicsItem::shape(),QGraphicsItem::collideWith())来支持碰撞检测。这两个都是虚函数。从shape()返回你的item的形状(以本地坐标QPainterPath表示),QGraphicsItem会为你处理所有的碰撞检测。假如你想提供自己的碰撞检测,你应该重新实现QGraphicsItem::collideWith()。
2)坐标系统
Graphics View基于笛卡尔坐标系。item在场景中的位置与几何形状通过x,y坐标表示。当使用未经变形的视图来观察场景时,场景中的一个单位等于屏幕上的一个像素。在Graphics View中有三个有效的坐标系统:Item坐标系,场景坐标系,视图坐标系。为了简化你的实现,Graphics View提供了方便的函数,允许三个坐标系之间相互映射。 当渲染时,Graphics View的场景坐标对应于QPainter的逻辑坐标,视图坐标与设备坐标相同。
GraphicsView视图坐标
视图坐标是widget的坐标,视图坐标中每个单位对应一个像素。这种坐标的特殊之处在于它是相对于widget或是视口的,不会被所观察的场景所影响。QGraphicsView的视口的左上角总是(0,0),右下角总是(视口宽,视口高)。所有的鼠标事件与拖拽事件,最初以视图坐标表示,就应该把这些坐标映射到场景坐标以便与item交互。
GraphicsScene场景坐标
场景坐标系统描述了每个最顶级item的位置,也是从视图向场景投递场景事件的基础。场景中的每个item有场景位置与包围矩形(QGraphicsItem::scenePos(),QGraphicsItem::sceneBoundingRect()), 另外,它有自己本地item位置与包围矩形。场景位置描述了item在场景坐标下的位置,它的场景包围矩形则用于QGraphicsScene决定场景中哪块区域发生了变化。场景中的变化通过QGraphicsScene::changed()信号来通知,它的参数是场景矩形列表。
GraphicsItem图形坐标
Items位于它们自己的坐标系中。它的坐标都以点(0,0)为中心点,这也是所有变换的中心点。在item坐标系中的几何图元,经常被称为item点,item线,item矩形。当创建一个定制的item,item坐标是所需要考虑的。QGraphicsScene与QGraphicsView可以为你执行所有转换,这使得实现定制的item变得容易。举例来说,假如你收到鼠标按或是拖进入事件,事件的位置以item坐标的形式给出。QGraphicsItem::contain()虚函数,当某个点的位置在你的item范围内时,返回true,否则返回false。这个点参数使用item坐标,相似地,item的包围矩形与形状也使用item坐标。 Item位置指的是item的中心点在它父亲的坐标系中的坐标。以这种思想来看,场景指的就是那些祖先最少的item的“父亲”。最上级的Item位置就是在场景中的位置。 子坐标与父坐标之间是相关的,假如孩子未经变换,子坐标与父坐标之间的差值等于在父坐标系下,父item与子item之间的距离。例如,假如一个未经变换的子item位置与其父item的中心重合,那么这两个item的坐标系统完全相同。如果孩子的位置是(10,0),那么孩子坐标系中的(0,10)点,对应于父坐标系中的(10,10)点。 因为item的位置与变换是相对于父item的,子item的坐标不会被父亲的变换影响,尽管父item的变换隐含地对子item做了变换。在上面的例子中,即使父item旋转,缩放,子item的(0,10)点依然对应于父item的(10,10)点。然而,相对于场景来讲,子item会遵循父item的变换。假如父item被缩放(2X,2X),子item的位置在场景中的坐标是(20,0),它的(10,0)点则与场景中的(40,0)对应 。除了QGraphicsItem::pos(),QGraphicsItem的函数以Item坐标工作,如一个item’s包围矩形总是以item坐标的形式给出。
3)实际使用样例
①、
import sys
from PyQt5.QtCore import Qt
from PyQt5.QtGui import QPixmap, QColor, QPainterPath
from PyQt5.QtWidgets import QApplication, QGraphicsItem, QGraphicsLineItem, QGraphicsRectItem, QGraphicsEllipseItem,
QGraphicsPixmapItem, QGraphicsTextItem, QGraphicsPathItem, QGraphicsScene, QGraphicsView
class Demo(QGraphicsView):
def __init__(self):
super(Demo, self).__init__()
# 1
self.resize(300, 300)
# 2
self.scene = QGraphicsScene()
self.scene.setSceneRect(0, 0, 300, 300)
# 3
self.line = QGraphicsLineItem()
self.line.setLine(100, 10, 200, 10)
# self.line.setLine(QLineF(100, 10, 200, 10))
# 4
self.rect = QGraphicsRectItem()
self.rect.setRect(100, 30, 100, 30)
# self.rect.setRect(QRectF(100, 30, 100, 30))
# 5
self.ellipse = QGraphicsEllipseItem()
self.ellipse.setRect(100, 80, 100, 20)
# self.ellipse.setRect(QRectF(100, 80, 100, 20))
# 6
self.pic = QGraphicsPixmapItem()
self.pic.setPixmap(QPixmap('0.png').scaled(60, 60))
self.pic.setFlags(QGraphicsItem.ItemIsSelectable | QGraphicsItem.ItemIsMovable)
self.pic.setOffset(100, 120)
# self.pic.setOffset(QPointF(100, 120))
# 7
self.text1 = QGraphicsTextItem()
self.text1.setPlainText('Hello PyQt5')
self.text1.setDefaultTextColor(QColor(66, 222, 88))
self.text1.setPos(100, 180)
self.text2 = QGraphicsTextItem()
self.text2.setPlainText('Hello World')
self.text2.setTextInteractionFlags(Qt.TextEditorInteraction)
self.text2.setPos(100, 200)
self.text3 = QGraphicsTextItem()
self.text3.setHtml('<a href="https://baidu.com">百度</a>')
self.text3.setOpenExternalLinks(True)
self.text3.setTextInteractionFlags(Qt.TextBrowserInteraction)
self.text3.setPos(100, 220)
# 8
self.path = QGraphicsPathItem()
self.tri_path = QPainterPath()
self.tri_path.moveTo(100, 250)
self.tri_path.lineTo(130, 290)
self.tri_path.lineTo(100, 290)
self.tri_path.lineTo(100, 250)
self.tri_path.closeSubpath()
self.path.setPath(self.tri_path)
# 9
self.scene.addItem(self.line)
self.scene.addItem(self.rect)
self.scene.addItem(self.ellipse)
self.scene.addItem(self.pic)
self.scene.addItem(self.text1)
self.scene.addItem(self.text2)
self.scene.addItem(self.text3)
self.scene.addItem(self.path)
# 10
self.setScene(self.scene)
if __name__ == '__main__':
app = QApplication(sys.argv)
demo = Demo()
demo.show()
sys.exit(app.exec_())
运行效果:
②、
from PyQt5.QtCore import Qt, QPoint, QPointF
from PyQt5.QtGui import QPen, QBrush
from PyQt5.QtWidgets import QApplication, QGraphicsItem, QGraphicsRectItem, QGraphicsEllipseItem, QGraphicsScene,
QGraphicsView, QGraphicsItemGroup
class ResizeRegion(QGraphicsEllipseItem):
def __init__(self, pos, parent, width=10, typeName=None):
super(ResizeRegion, self).__init__(parent)
assert isinstance(pos, (QPoint, QPointF)), "point type error!"
assert isinstance(width, int), "width value error!"
self.initAppearance(pos=pos, width=width, color=Qt.red, typeName=typeName)
def initAppearance(self, pos, width, color, typeName=None, isVisible=False):
self.setPos(pos)
self.setVisible(isVisible)
self.width = width
brush = self.brush()
brush.setStyle(Qt.SolidPattern)
brush.setColor(color)
self.setBrush(brush)
pen = self.pen()
pen.setColor(color)
self.setPen(pen)
self.setRect(-width/2, -width/2, width, width)
self.setAcceptHoverEvents(True)
self.setFlag(QGraphicsItem.ItemIgnoresTransformations)
self.type_name = typeName
def resetPos(self, pos):
assert isinstance(pos, (QPoint, QPointF)), "point type error!"
self.setPos(pos)
def resetColor(self, color):
pen = self.pen()
pen.setColor(color)
self.setPen(pen)
brush = self.brush()
brush.setColor(color)
self.setBrush(brush)
def resetWidth(self, width):
assert isinstance(width, int), "width value error!"
self.width = width
self.setRect(-width / 2, -width / 2, width, width)
def __repr__(self):
return str(self.pos())
class MoveRegion(QGraphicsRectItem):
def __init__(self, pos, parent, width=10, typeName=None):
super(MoveRegion, self).__init__(parent)
assert isinstance(pos, (QPoint, QPointF)), "point type error!"
assert isinstance(width, int), "width value error!"
self.initAppearance(pos=pos, width=width, color=Qt.red, typeName=typeName)
def initAppearance(self, pos, width, color, typeName=None, isVisible=False):
self.setPos(QPoint(pos.x(), pos.y()))
self.setVisible(isVisible)
self.width = width
init_color = color
brush = self.brush()
brush.setStyle(Qt.SolidPattern)
brush.setColor(init_color)
self.setBrush(brush)
pen = self.pen()
pen.setColor(init_color)
self.setPen(pen)
self.setRect(-width / 2, -width / 2, width, width)
self.setAcceptHoverEvents(True)
self.setFlag(QGraphicsItem.ItemIgnoresTransformations)
self.type_name = typeName
def resetPos(self, pos):
assert isinstance(pos, (QPoint, QPointF)), "point type error!"
self.setPos(QPoint(pos.x(), pos.y()))
def resetColor(self, color):
pen = self.pen()
pen.setColor(color)
self.setPen(pen)
brush = self.brush()
brush.setColor(color)
self.setBrush(brush)
def resetWidth(self, width):
assert isinstance(width, int), "width value error!"
self.width = width
self.setRect(-width/2, -width/2, width, width)
def __repr__(self):
return str(self.pos())
def demo():
from PyQt5.QtWidgets import QGraphicsScene, QGraphicsView, QApplication
import sys
app = QApplication(sys.argv)
view = QGraphicsView()
view.resize(600, 600)
scene = QGraphicsScene()
scene.setSceneRect(0, 0, 300, 300)
item1 = QGraphicsRectItem()
item1.setRect(-1, -1, 30, 30)
item2 = ResizeRegion(pos=QPoint(30, 30), parent=item1)
item2.setVisible(True)
item3 = MoveRegion(pos=QPoint(0, 30), parent=item1)
item3.setVisible(True)
scene.addItem(item1)
# scene.addItem(item2)
# scene.addItem(item3)
view.setScene(scene)
view.show()
sys.exit(app.exec_())
if __name__ == '__main__':
demo()
tips:item的原点坐标是item的左上角,为了让红色正方形对谁左下角,需要将其原点坐标设置为(-width/2, -width/2)
运行效果:
2、图表QChart
Qt Charts Examples: https://doc.qt.io/qt-5/qtcharts-examples.html
继承关系 QChart<=QGraphicsWidget<=QGraphicsObject<=QGraphicsItem
新建一个图表类MyChart继承自QChart,当做一个item项,当需要显示,插入scene中。
折线图需要显示的数据实例QLineSeries,例如:
def createNewSeries(self, name, data):
series_temp = QLineSeries(self)
series_temp.setName(str(name))
for x, y in enumerate(data):
series_temp.append(x, y)
self.addSeries(series_temp)
return series_temp
即在表格中加入和一个线段图,通过addSeries可以在同坐标系的chart中加入多个lineseries。
相同形状相通坐标轴范围的两个chart原点不对齐:将Y轴隐藏后原点对齐
如何在chart显示所在位置的实时位置?
1)得到鼠标在scene的坐标scencePos
2)转到item坐标系下坐标itemPos = chart.mapFromScene(scencePos)
3)转到chart的坐标轴xy值chartPos = chart.mapToValue(itemPos)
同理相反:chart的坐标轴xy值转到scene 坐标值
1)chart坐标为QPointF(x, y)
2)chart=>item:item_pos = chart.mapToPosition(QPointF(x, y))
2)item=>scene:item_in_scene_pos = chart.mapToScene(item_pos)
获取当前scene下所有item:
if self.chart not in self.scene.items():
self.scene.addItem(self.chart)
11、tableview、tablewidget
自适应列宽 :
self.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch)
不可编辑:
self.setEditTriggers(QAbstractItemView.NoEditTriggers)
三、Table表格
通常使用表格显示数据的的类有QTableView, QTableWidget...
1、QTableView
父类及其关系:
QWidget->QFrame->QAbstractScrollArea->QAbstractItemView->QTableView
顶层结构为QWidget,通常使用方法放进layout里外包一层Groupbox实现长宽的动态变化大小。
其内部需要一个存放数据的容器—model。数据的修改,设置都通过model操作,当然需要在开始调用setModel方法将model与tableview绑定。
2、QTableWidget
巧妙的是QTableWidget是对QTableView的高度封装,不需要自行设置model。
对于交互性高的表格,个人更喜好此种方法。
每当有修改操作时候,禁用本身的点击即修改
item.setFlags(Qt.ItemIsEnabled | Qt.ItemIsSelectable)
数据显示可以用list存放,轮一遍用item承载;数据修改可以在每个cell里相应自定义的单击或双击的事件,通过创建spinbox或combobox来增加交互性,获取finish更改结束信号来更新数据参数。
这里不提供的使用教程,具体参照官方文档
两者区别比较
| 比较项目 | QTableView | QTableWidget | | ------------------ | ------------------------------------------------------------ | -------------------------------- | | 继承关系 | Dad | Son | | mvc结构 | 模型(Model)表示数据,视图(View)是用户界面,控制(Controler)定义了用户在界面上的操作,通过使用MVC模式,有效的分离了数据和用户界面,使得设计更为灵活,更能适应变化。 | 无 | | 交互性 | 单独显示的情况较好使用 | 需要修改即交互性强的情况较为合适 | | 绑定QSqlTableModel | 能 | 不能 |
四、pyqt 程序结构
1、主程序
由qtdesigner设计所生成的文件为1.ui, 通过命令转成1.py文件,
pyuic5 1.ui -o 1.py
该文件包含了qtdesigner中所设计的所有控件,但是缺少了承载控件的窗体,如QDialog、QWidget或QMainwindow。因此需要另一份文件为1run.py的文件,该文件下显示窗体的类要同时继承1.py下的控件类与窗体类名(QDialog、QWidget或QMainwindow)
1)同时继承两个类:
class SettingWidget2(Ui_SettingWidget, QWidget):
def __init__(self, alg, parent=None):
super(SettingWidget2, self).__init__(parent)
self.setupUi(self)
if __name__ == '__main__':
app = QApplication(sys.argv)
alg = AffineTransformTool('affine')
frame = SettingWidget2(alg)
frame.show()
sys.exit(app.exec_())
2)继承一个、实例化一个:
class SettingWidget2(Ui_SettingWidget):
def __init__(self, alg, parent=None):
super(SettingWidget2, self).__init__(parent)
self.carry_widget = QWidget()
self.setupUi(self.carry_widget)
if __name__ == '__main__':
app = QApplication(sys.argv)
alg = AffineTransformTool('affine')
frame = SettingWidget2(alg)
frame.show()
sys.exit(app.exec_())
2、test gui
测试gui实验
1、库
unittest,QTest
2、整体思路
使用unittest框架搭建,与算法的test类似,只是增加了模拟鼠标键盘的函数
QTest.mouseClick(self.form.pushButton, Qt.LeftButton)
xxx.setvalue()
QTest模拟pushButton按钮的鼠标左键按下,或者使用gui函数直接对控件值进行set
3、结论
对于pushbutton按钮,radiobutton单选按钮可以使用QTest提供的模拟鼠标键盘函数,而对于combobox下拉框,lineedit文本框,表格、标签页等只能使用gui函数直接操作。
1)可以测试的部分:控件的connect事件,控件的数值是否符合预期
2)不能测试的部分:改变右边region=>左边控件参数变化,图形graphics的显隐,需要肉眼判断的部分
3)疑问
①是否要验证gui控件值与算法变量的对应关系,以imageread为例:gui的’Image Color‘对应alg中的 ’cv2.IMREAD_COLOR‘;
②对于报错弹框如何测试;
③测试代码都是手动操作gui后ok的操作再用测试代码复现,是否有必要;
④目前只能实现单个tool_gui的测试,对于block中多个tool连入无法测试;
4、代码(测试部分)
pass
五、自定义样式qss
原理
QT样式表可以让你自定义任何一个QT控件的样式包括颜色、边框、背景等等(你想的到样式都可以),Qt样式表的概念、术语和语法与HTML的CSS样式表类似。
能够实现快速的自定义界面风格。
Qt 样式表的思想很大程度上是来自于HTML的层叠式样式表(CSS),通过调用QWidget::setStyleSheet()或 QApplication::setStyleSheet(),你可以为一个独立的子部件、整个窗口,甚至是整个个应用程序指定一个样式表。
样式表是通过QStyle的一个叫做QStyleSheetStyle的特殊子类来实现的。这个特殊的子类实际上是其他的系统特定风格类的包裹类,它会把通过样式表指定的自定义外观风格应用在底层的系统特定风格之上。 Qt 4.2包含了一个叫做stylesheet的例子来帮助你学习样式表,这个例子自带了两个样式:Coffee和Pagefold。 上面的Coffee风格自定义了push button、frames和tooltip,但使用了下层的风格 (例如这里是Windows XP风格)来绘制checkbox,combobox和radio button。 Pagefold风格完全重新定义了对话框中使用的所有控件的外观,从而实现了一种独特的,平台无关的外观。
盒子模型
在CSS中,"box model"这一术语是用来设计和布局时使用。
CSS盒模型本质上是一个盒子,封装周围的HTML元素,它包括:边距,边框,填充,和实际内容。
盒模型允许我们在其它元素和周围元素边框之间的空间放置元素。
下面的图片说明了盒子模型(Box Model):
- Margin(外边距) - 清除边框外的区域,外边距是透明的。
- Border(边框) - 围绕在内边距和内容外的边框。
- Padding(内边距) - 清除内容周围的区域,内边距是透明的。
- Content(内容) - 盒子的内容,显示文本和图像。
边距,边框宽度和填充属性都默认为零。在这种情况下,所有四个矩形(边距,边框,填充和内容)都完全一致。
语法
Qt样式表与CSS的语法规则几乎完全相同,一个样式表由一系列的样式规则构成。每个样式规则都有着下面的形式:
selector {attribute:value}
选择器(selector)部分通常是一个类名(例如QComboBox),当然也还有其他的语法形式。 属性(attribute)部分是一个样式表属性的名字,值(value)部分是赋给该属性的值。
可以同时为与M个选择器相匹配的部件设置N种属性。例如:
QCheckBox, QComboBox, QSpinBox {
color: red;
background-color: white;
font: bold;
}
filter
QCheckBox[ischeck:True]:hover {
color: blue;
}
只对选择的ckbox设置style
伪类(伪状态)
CSS 伪类 是添加到选择器的关键字,指定要选择的元素的特殊状态。例如,:hover
可被用于在用户将鼠标悬停在按钮上时改变按钮的颜色。
button:hover {
color: blue;
}
举个栗子
pass
qss参考
qt官方qss定义
QSS 基础
CSS(层叠样式表)
PyQt5图形和特效之Qt样式表设置
六、其他辅助
1、线程加进度框增加用户体验
waitingdialog待添加
线程example:
#coding=utf-8
import threading
from time import ctime, sleep
def music(func):
for i in range(2):
print("I was listening to %s. %s" %(func,ctime()))
sleep(4)
def move(func):
for i in range(2):
print("I was at the %s! %s" %(func,ctime()))
sleep(5)
threads = []
t1 = threading.Thread(target=music, args=(u'爱情买卖',))
threads.append(t1)
t2 = threading.Thread(target=move, args=(u'阿凡达',))
threads.append(t2)
if __name__ == '__main__':
for t in threads:
t.setDaemon(True)
t.start()
t.join()
print("all over %s" %ctime())
>>>
'''
I was listening to 爱情买卖. Tue Nov 27 16:40:37 2018
I was at the 阿凡达! Tue Nov 27 16:40:37 2018
I was listening to 爱情买卖. Tue Nov 27 16:40:41 2018
I was at the 阿凡达! Tue Nov 27 16:40:42 2018
all over Tue Nov 27 16:40:47 2018
'''
QT线程:
class AlgRunThread(QThread):
isFinishSignal = pyqtSignal()
isErrorSignal = pyqtSignal(str)
def __init__(self, alg, parent=None):
super(AlgRunThread, self).__init__(parent)
self.alg = alg
def run(self):
try:
self.alg.run()
self.isFinishSignal.emit()
except Exception as e:
self.isErrorSignal.emit(str(e))
self.run_alg_thread = AlgRunThread(self.alg)
self.run_alg_thread.start() # 跑算法的run
进度条:
1)QDialog内添加QProgressBar
class ProcessWidget(QDialog):
def __init__(self, parent):
super(ProcessWidget, self).__init__(parent=parent, flags=Qt.Tool | Qt.X11BypassWindowManagerHint )
self.all_time_ms = 4000
self.initUI()
self.doAction()
def restart(self):
self.step = 0
self.doAction()
def initUI(self):
self.step = 0
self.timer = QBasicTimer()
self.pbar = QProgressBar(self)
self.pbar.setGeometry(30, 40, 200, 25)
self.setWindowTitle('Calculating ...')
def timerEvent(self, e):
if self.step >= 99:
self.timer.stop()
return
self.step = self.step + 1
self.pbar.setValue(self.step)
def doAction(self, value=None):
self.timer.start(self.all_time_ms/100, self)
2)QProgressDialog:
self.progress_dialog = QProgressDialog("init project", "wait", 0, 1000, self)
# # self.progress_dialog.setWindowFlags(Qt.Tool | Qt.X11BypassWindowManagerHint)
self.progress_dialog.setCancelButton(None)
self.progress_dialog.setValue(500)
self.progress_dialog.show()
3)使用QThread
先继承QThread重写一个线程类,其中的run方法里可以放置想要在线程里执行的程序
class AlgRunThread(QThread):
isFinishSignal = pyqtSignal(bool)
isErrorSignal = pyqtSignal(str)
def __init__(self, alg, parent=None):
super(AlgRunThread, self).__init__(parent)
self.alg = alg
def run(self):
try:
self.alg.run() #这个线程类就表示在线程里跑算法的run
self.isFinishSignal.emit(True)
except Exception as e:
self.isErrorSignal.emit(str(e))
self.run_alg_thread = AlgRunThread(alg)
self.run_alg_thread.start() #运行线程里的run函数
算法进程开始的时候gui的进度条也同时开启,当进程算法运行完会抛出isFinishSignal信号,根据这个信号来结束进度条,而当算法运行异常会抛出isErrorSignal信号,gui接这个信号并弹框显示信息。
2、language翻译
1)xx.ui=>xx.py
pyuic5 demo.ui -o demo.py
2)xx.py=>xx.ts
pylupdate5 demo.py -ts demo.ts
# 也可以直接将*.ui->*.ts
3)將xx.ts文件拖動到Qt Linguist 進行文字的手動替換,release 後,生成xx.qm文件
4)代碼塊中加入
self.translate = QTranslator()
self.translate.load('demo.qm')
app.installTranslator(self.translate)
self.setupUi(self)
visiontool 中新增语言file的操作
1)xx.ui=>xx.py
pyuic5 demo.ui -o demo.py
2)xx.py=>xx.ts
pylupdate5 demo.py -ts demo.ts
# 也可以直接将*.ui->*.ts
3)將xx.ts文件拖動到Qt Linguist 進行文字的手動替換,release 後,生成xx.qm文件
4)xxxwidget中的构造函数中加入
self.retranslateUi(self)
5)动态更改语言:
LanguageSupport类下的changeLanguage方法,传入语言缩写字母['cn', 'eng', 'korea'],即可更改当前界面。
3、assistant文档
file:
*.qhp:Qt help project—负责组织实际用到的帮助文件(通常为HTML文件,即需要在Qt Assistant中浏览的文件)
*.qch:Qt Compressed help—是Qt Assistant能够识别的文档最小单元
*.qhcp:Qt help collection project—将qch二进制文件组织成为一个collection,定制客户化的Assistant
*.qhc:qhc文件中是qch文件的集合,打开Assistant时,通过指定当前collection即可注册多个帮助文档。
command:
qhelpgenerator xxx.qhp -o xxx.qch
qcollectiongenerator xxx.qhcp -o xxx.qhc
assistant -collectionfile xxx.qhc
4、正则化
regular = QRegExp("^(50|[0-4]?d(.d{1,2})?)$")
#限定0.00-50.00,精度小数点后两位
#"^"和"$"限定字符串的开始和结尾
#管道符"|"表示平行分组
#"[0-4]"表示十位范围0-4
#"?"表示区分位数,
#"d"表示表示匹配一位数字
#"."表示小数点
#"d{1,2}"表示小数点两位,数字随意
regular_value = QRegExpValidator(regular)
self.read_timeout_combobox.setValidator(regular_value)