延迟3s后点击按钮:
# close dialog after 3s
btn_ok.animateClick(3000)
页面显示时,自动触发:
show
方法时def showEvent(self, a0: QShowEvent) -> None:
"""页面显示事件"""
print('show...')
# 初始化工作
如果想仅在手动调用show
方法时触发,最小化再打开后不触发:
def showEvent(self, evt):
"""页面手动调用show()方法时调用"""
# 判断事件是否是由用户手动调用show()方法触发的
if evt.spontaneous():
return
# 页面显示时的初始化操作
print('show...')
页面隐藏时,自动触发:
hide
方法时def hideEvent(self, evt):
"""页面隐藏事件"""
print('hide...')
如果想仅在手动调用show
方法时触发,最小化再打开后不触发:
def hideEvent(self, evt):
"""页面手动调用hide()方法时调用"""
# 判断事件是否是由用户手动调用hide()方法触发的
if evt.spontaneous():
return
# 页面显示时的初始化操作
print('hide...')
页面关闭时,自动触发:
调用close
方法时
点击右上角X
按钮时
def closeEvent(self, evt) -> None:
print('close...')
reply = QMessageBox.question(self, '提示',"确定关闭吗?",QMessageBox.Yes|QMessageBox.No,QMessageBox.No)
# evt.ignore()
参考
from PyQt5.QtCore import Qt, QRect, QSize, QPoint
from PyQt5.QtGui import QColor, QPainter, QTextFormat, QKeyEvent, QWheelEvent, QMouseEvent, QTextCursor
from PyQt5.QtWidgets import QWidget, QTextEdit, QPlainTextEdit
class QPlainTextEditWithNu(QPlainTextEdit):
def __init__(self, parent=None, rect=None):
super().__init__(parent)
# 尺寸
self.setGeometry(rect[0], rect[1], rect[2], rect[3])
self.setLineWrapMode(QPlainTextEdit.NoWrap) # 不自动换行
self.lineNumberArea = LineNumPaint(self)
self.document().blockCountChanged.connect(self.update_line_num_width)
self.document().cursorPositionChanged.connect(self.highlightCurrentLine) # 高亮当前行
self.verticalScrollBar().sliderMoved.connect(self.scroll_event) # 滚动条移动更新行号
self.update_line_num_width()
def wheelEvent(self, d: QWheelEvent) -> None:
self.scroll_event(d)
super().wheelEvent(d)
def scroll_event(self, event: QWheelEvent = None):
self.lineNumberArea.update()
def keyPressEvent(self, e: QKeyEvent) -> None:
super().keyPressEvent(e)
self.lineNumberArea.update()
def mousePressEvent(self, e: 'QMouseEvent') -> None:
super().mousePressEvent(e)
self.update()
self.highlightCurrentLine()
def lineNumberAreaWidth(self):
block_count = self.document().blockCount()
max_value = max(1, block_count)
d_count = len(str(max_value))
_width = self.fontMetrics().width('9') * d_count + 5
return _width
def update_line_num_width(self):
self.setViewportMargins(self.lineNumberAreaWidth(), 0, 0, 0)
def resizeEvent(self, event):
super().resizeEvent(event)
cr = self.contentsRect()
self.lineNumberArea.setGeometry(QRect(cr.left(), cr.top(), self.lineNumberAreaWidth(), cr.height()))
def highlightCurrentLine(self):
extraSelections = []
if not self.isReadOnly():
selection = QTextEdit.ExtraSelection()
lineColor = QColor(Qt.blue).lighter(190)
selection.format.setBackground(lineColor)
selection.format.setProperty(QTextFormat.FullWidthSelection, True)
selection.cursor = self.textCursor()
selection.cursor.clearSelection()
extraSelections.append(selection)
self.setExtraSelections(extraSelections)
def lineNumberAreaPaintEvent(self, event):
cursor = QTextCursor(self.document())
painter = QPainter(self.lineNumberArea)
painter.fillRect(event.rect(), Qt.lightGray)
line_height = self.fontMetrics().lineSpacing() # 包含行间距的行高
block_number = self.cursorForPosition(QPoint(0, int(line_height / 2))).blockNumber()
first_visible_block = self.document().findBlock(block_number)
blockNumber = block_number
cursor.setPosition(self.cursorForPosition(QPoint(0, int(line_height / 2))).position())
rect = self.cursorRect()
scroll_compensation = rect.y() - int(rect.y() / line_height) * line_height
top = scroll_compensation
last_block_number = self.cursorForPosition(QPoint(0, self.height() - 1)).blockNumber()
height = self.fontMetrics().height()
block = first_visible_block
while block.isValid() and (top <= event.rect().bottom()) and blockNumber <= last_block_number:
# cur_line_count = block.lineCount()
if block.isVisible():
number = str(blockNumber + 1)
painter.setPen(Qt.black)
# print((0, top, self.lineNumberArea.width(), height), number)
painter.drawText(0, top, self.lineNumberArea.width(), height, Qt.AlignCenter, number)
block = block.next()
top = top + line_height
blockNumber += 1
class LineNumPaint(QWidget):
def __init__(self, q_edit):
super().__init__(q_edit)
self.q_edit_line_num = q_edit
def sizeHint(self):
return QSize(self.q_edit.lineNumberAreaWidth(), 0)
def paintEvent(self, event):
self.q_edit_line_num.lineNumberAreaPaintEvent(event)
如果要将此控件作为其他Dialog的子控件,只需要将父控件的实例作为参数传递进去即可
self.te = QPlainTextEditWithNu(parent)
如果此控件本身就是一个窗口:
import sys
from PyQt5.QtWidgets import QApplication
from QPlainTextEditWithNu import QPlainTextEditWithNu
if __name__ == '__main__':
app = QApplication(sys.argv)
codeEditor = QPlainTextEditWithLineNum()
codeEditor.setWindowTitle("带行号的编辑器")
codeEditor.setGeometry(100, 100, 800, 600)
codeEditor.show()
sys.exit(app.exec_())
self.model.setHorizontalHeaderLabels(['id', 'username', 'create_time', 'status'])
通过下面的代码,可以实现自动将标题设置为df的列名,无需像上面一样手动指定
# 表格追加数据显示csv
df = pd.read_csv('./test.csv')
# 设置标题
self.model.setHorizontalHeaderLabels(df.columns.tolist())
# 渲染数据
for row in df.values.tolist():
self.model.appendRow([QStandardItem(str(item)) for item in row])
假设,后端传来的数据格式为:{'title':['id','username','status'], 'user_list':[xxx]}
,那么如何优雅地渲染?(至少不需要appendRow的时候手动指定key)
# tableView标题
self.model.setHorizontalHeaderLabels(data['title'])
# 刷新tableView
self.model.removeRows(0, self.model.rowCount())
for item in data['user_list']:
self.model.appendRow([QStandardItem(str(item[title])) for title in data['title']])
# clear不推荐,因为会清除标题HorizontalHeaderLabels
# self.model.clear()
# 推荐用法
self.model.removeRows(0, self.model.rowCount())
item = QStandardItem()
item.setData(user['username'], Qt.DisplayRole)
item.setData(user['id'], Qt.BackgroundRole)
比如,想要让item全部居中
定义
from PyQt5.Qt import QStandardItemModel, Qt
class MyQStandardItemModel(QStandardItemModel):
"""重写QStandardItemModel的data函数,使QTableView全部item居中"""
def data(self, index, role=None):
if role == Qt.TextAlignmentRole:
return Qt.AlignCenter
return QStandardItemModel.data(self, index, role)
调用
# 实例化Model
self.model = MyQStandardItemModel(self)
# TableView关联Modal
self.tableView.setModel(self.model)
def listView_doubleClick(self, idx):
"""双击条目"""
data = idx.data() # 获取选中的文本
data_bg = idx.data(Qt.BackgroundRole) # 获取bg角色的data
def tableView_doubleClick(self, idx):
"""双击条目"""
# (row, 0).data
data = self.page4_tableView.model().index(idx.row(), 0).data()
# (row, 1).data_bg
data_bg = self.page4_tableView.model().index(idx.row(), 1).data(Qt.BackgroundRole)
获取选中行对应的id,将id发送给后台接口进行某些操作(如删除数据、更新等):
selected_indexes = self.tableView.selectedIndexes()
if len(selected_indexes):
if prompt_question(self, '确认要xxx吗?'):
for selected_index in selected_indexes:
selected_row = selected_index.row()
id = self.tableView.model().index(selected_row, 0).data() # (row, 0)
print(id) # do something
只选中一行:
selected_indexes = self.tableView.selectedIndexes()
if len(selected_indexes):
if prompt_question(self, '确认要xxx吗?'):
selected_index = selected_indexes[0]
selected_row = selected_index.row()
id = self.page4_tableView.model().index(selected_row, 0).data()
print(id) # do something
获取选中行所有数据,组成列表(选中多行,就是二维列表):
selected_indexes = self.tableView.selectedIndexes()
if len(selected_indexes):
if prompt_question(self, '确认要xxx吗?'):
datas = []
for selected_index in selected_indexes:
selected_row = selected_index.row()
# 获取选中行对应的数据
data = []
for column in range(self.model.columnCount()):
item = self.tableView.model().index(selected_row, column).data()
data.append(item)
datas.append(data)
print(datas) # do something
避免索引重复:如果限定tableView的一行只能选中某一个单元格,那么上面两个示例是没问题的;但如果可以选中一行中的多个,上述代码的selected_row
就会重复。正确做法是,使用set()
存储选中的索引值:
# 使用set,避免tableView选中同一行的单元格,造成返回值重复的问题
selected_indexes = set()
for idx in self.page4_tableView.selectedIndexes():
selected_indexes.add(idx.row())
if len(selected_indexes):
if prompt_question(self, '确认要进行错误上报吗?'):
for selected_index in selected_indexes:
id = self.model.item(selected_row, 0).data() # (row, 0)
print(id) # do something
设置标题颜色为京东红,字体为微软雅黑,加粗
self.tableView.horizontalHeader().setStyleSheet("""
QHeaderView::section{
font: 10pt '微软雅黑';
font-weight: 600;
color: #f10215;
}""")
默认策略:双击、选中后按任意键都可编辑
禁用编辑:只能选中不能编辑
self.tableView.setEditTriggers(QAbstractItemView.NoEditTriggers)
ExtendedSelection
:默认策略,可以选中多行NoSelection
:不可选中SelectItems
:默认行为,只选中单元格SelectRows
:选中行SelectColumns
:选中列**默认:**不占满列、以最窄的宽度显示,但可自由伸缩
self.tableView.horizontalHeader().setSectionResizeMode(QHeaderView.Interactive)
宽度固定:不可拖动改变宽度
self.tableView.horizontalHeader().setSectionResizeMode(QHeaderView.Fixed)
每列等宽:水平方向上平分,每列等宽,不可拖动改变宽度
self.tableView.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch)
随内容自适应:但多列内容长度差别过大显得特别难看
self.tableView.horizontalHeader().setSectionResizeMode(QHeaderView.ResizeToContents)
各列自定义比例填充
setColumnWidth
方法来设置每一列的宽度,model.append(row)
之后操作,之前操作不生效!# 以下语句必须在model加载完数据之后
tableView.setColumnWidth(0, 500)
tableView.setColumnWidth(1, 100)
最后一列填满:在Qt Designer中,启用horizontalHeaderStretchLastSection
技巧:假如有n列,可以只设置前n-1列的宽度,然后指定最后一列填满。这样可以做到n列刚好填满tableView、不出现水平滚动条。但是这种方法仅适用于n不是很大的情况!
# 设置列宽
self.tableView.horizontalHeader().setStretchLastSection(True)
self.tableView.setColumnWidth(0, 100)
self.tableView.setColumnWidth(1, 250)
self.tableView.setColumnWidth(2, 350)
只需要使用request.post()
方法中,传入files
参数即可
服务端:
@app.route('/upload', methods=['POST'])
def upload():
file = request.files['upload_file']
filename = secure_filename(file.filename)
file.save(os.path.join(paths, filename))
return '文件上传成功'
客户端:
url = 'http://localhost:5000/upload'
files = {'upload_file': open('./HDFS_2k.log','rb')}
response = requests.post(url, files=files)
print(response)
客户端:如果需要上传文件的同时,传递参数(如user_id
),则需要在post()
方法中传递data
表单参数。
注意,其他参数必须通过form的方式(在post方法传递data=xxx
),而不是json(即不能在post方法传递json=xxx
)
url = 'http://localhost:5000/upload'
form_data = {'user_id': 1}
files = {'upload_file': open('./HDFS_2k.log','rb')}
response = requests.post(url, data=form_data, files=files)
print(response)
服务端:使用request.form[key]
接收form参数
注意,其他参数必须通过form方式(使用request.form[key]
接收),而不是json(即不能request.json[key]
接收)
@app.route('/upload', methods=['POST'])
def upload():
user_id = request.form['user_id']
file = request.files['upload_file']
# userId_rawFilename => 1_666.png
filename = '{}_{}'.format(user_id, secure_filename(file.filename))
file.save(os.path.join(paths, filename))
return '文件上传成功'
# LoadingDialog.py
from PyQt5.Qt import QProgressDialog
from PyQt5.QtCore import Qt
class LoadingDialog:
def __init__(self, root, message):
self.root = root
self.pd = QProgressDialog(message, '', 1, 100, root)
self.root.setEnabled(False)
# 无限加载效果
self.pd.setRange(0, 0)
# 居中显示
self.pd.move((self.root.width() - self.pd.width()) / 2, (self.root.height() - self.pd.height()) / 2)
# 隐藏"取消"按钮
self.pd.setCancelButton(None)
self.pd.setWindowFlags(Qt.CustomizeWindowHint)
self.pd.setStyleSheet("""background-color:#ddd;""")
# 必须使用show
self.pd.show()
def exec_(self):
self.pd.exec_()
def close(self):
self.pd.close()
self.root.setEnabled(True)
# 弹出模态框: 后台线程开始前
self.pd = LoadingDialog(root=self, message='请求中...')
# 关闭: 后台线程结束的槽函数中关闭
self.pd.close()
参考
import sys
import time
from PyQt5.QtCore import QThread, QObject, pyqtSignal, pyqtSlot
from PyQt5.QtWidgets import QApplication, QLabel, QWidget, QGridLayout, QPushButton
class Worker(QObject):
finished = pyqtSignal()
intReady = pyqtSignal(int)
@pyqtSlot()
def work(self):
for i in range(1, 100):
time.sleep(0.1)
print(i)
self.intReady.emit(i)
self.finished.emit()
class Form(QWidget):
def __init__(self):
super().__init__()
self.label = QLabel("0")
# 1 - create Worker and Thread inside the Form
self.worker = Worker()
self.thread = QThread()
self.worker.intReady.connect(self.updateLabel)
self.worker.moveToThread(self.thread)
self.worker.finished.connect(self.thread.quit)
self.thread.started.connect(self.worker.work)
# self.thread.finished.connect(app.exit)
self.initUI()
def start(self):
print('开始了......')
self.thread.start()
def initUI(self):
grid = QGridLayout()
self.setLayout(grid)
grid.addWidget(self.label, 0, 0)
self.setFixedSize(800, 600)
self.move(300, 150)
self.setWindowTitle('多线程经典案例')
btn = QPushButton('按钮', self)
btn.move(400, 300)
btn.clicked.connect(self.start)
def updateLabel(self, i):
self.label.setText("{}".format(i))
if __name__ == '__main__':
app = QApplication(sys.argv)
form = Form()
form.show()
sys.exit(app.exec_())
import sys
import time
from PyQt5.Qt import *
from ui_py.ui_loading import Ui_Form
class Runthread(QThread):
# 通过类成员对象定义信号对象
_signal = pyqtSignal(int)
def __init__(self):
super(Runthread, self).__init__()
def run(self):
for i in range(100):
time.sleep(0.05)
self._signal.emit(i) # 注意这里与_signal中的类型相同
self._signal.emit(100)
class Loading_Page(QWidget, Ui_Form):
# 信号
loading_end_signal = pyqtSignal(bool)
def __init__(self, parent=None, *args, **kwargs):
super().__init__(parent, *args, **kwargs)
self.setAttribute(Qt.WA_StyledBackground, True)
self.setupUi(self)
self.loading_thread()
def loading_thread(self):
self.thread = Runthread()
self.thread._signal.connect(self.loading)
self.thread.start()
def loading(self, val):
self.qpb.setValue(val)
if __name__ == '__main__':
app = QApplication(sys.argv)
root = Loading_Page()
root.show()
sys.exit(app.exec_())
import csv
import sys
from PyQt5 import QtCore, QtGui, QtWidgets
class MyWindow(QtWidgets.QWidget):
def __init__(self, fileName, parent=None):
super(MyWindow, self).__init__(parent)
self.fileName = fileName
self.model = QtGui.QStandardItemModel(self)
self.tableView = QtWidgets.QTableView(self)
self.tableView.setModel(self.model)
self.tableView.horizontalHeader().setStretchLastSection(True)
self.pushButtonLoad = QtWidgets.QPushButton(self)
self.pushButtonLoad.setText("Load Csv File")
self.pushButtonLoad.clicked.connect(self.on_pushButtonLoad_clicked)
self.pushButtonWrite = QtWidgets.QPushButton(self)
self.pushButtonWrite.setText("Write Csv File")
self.pushButtonWrite.clicked.connect(self.on_pushButtonWrite_clicked)
self.layoutVertical = QtWidgets.QVBoxLayout(self)
self.layoutVertical.addWidget(self.tableView)
self.layoutVertical.addWidget(self.pushButtonLoad)
self.layoutVertical.addWidget(self.pushButtonWrite)
def loadCsv(self, fileName):
with open(fileName, mode='r') as fp:
for row in csv.reader(fp):
items = [
QtGui.QStandardItem(field)
for field in row
]
self.model.appendRow(items)
def writeCsv(self, fileName):
with open(fileName, mode='w', newline='') as fp:
writer = csv.writer(fp)
for rowNumber in range(self.model.rowCount()):
fields = [
self.model.data(
self.model.index(rowNumber, columnNumber),
QtCore.Qt.DisplayRole
)
for columnNumber in range(self.model.columnCount())
]
writer.writerow(fields)
@QtCore.pyqtSlot()
def on_pushButtonWrite_clicked(self):
self.writeCsv(self.fileName)
@QtCore.pyqtSlot()
def on_pushButtonLoad_clicked(self):
self.loadCsv(self.fileName)
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
app.setApplicationName('MyWindow')
main = MyWindow("./gao.csv")
main.show()
sys.exit(app.exec_())
import sys
from PyQt5.QtGui import QFont, QTextDocument, QTextCursor
from PyQt5.QtPrintSupport import QPrinter, QPrintDialog, QPrintPreviewDialog
from PyQt5.QtWidgets import QApplication, QMainWindow, QLabel, QAction, QDialog
# 打印文本---《心经》
the_text = '''
观自在菩萨,行深般若波罗蜜多时,照见五蕴皆空,度一切苦厄。
舍利子,色不异空,空不异色,色即是空,空即是色,受想行识亦复如是。
舍利子,是诸法空相,不生不灭,不垢不净,不增不减。
是故空中无色,无受想行识,无眼耳鼻舌身意,无色声香味触法,无眼界乃至无意识界,无无明亦无无明尽,乃至无老死,亦无老死尽,无苦集灭道,无智亦无得。
以无所得故,菩提萨埵,依般若波罗蜜多故,心无挂碍;无挂碍故,无有恐怖,远离颠倒梦想,究竟涅槃。
三世诸佛,依般若波罗蜜多故,得阿耨多罗三藐三菩提。
故知般若波罗蜜多,是大神咒,是大明咒,是无上咒,是无等等咒,能除一切苦,真实不虚。
故说般若波罗蜜多咒,即说咒曰:揭谛揭谛,波罗揭谛,波罗僧揭谛,菩提萨婆诃。
'''
class MainWindow(QMainWindow):
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent)
self.setWindowTitle(self.tr("支持打印机Demo"))
self.createLabel()
self.createMenus()
def createLabel(self):
""" 创建文本框 """
self.label = QLabel()
self.label.setFont(QFont("Roman times", 12, QFont.Bold))
self.label.setText(the_text)
self.setCentralWidget(self.label)
def createMenus(self):
""" 创建菜单栏 """
# 创建动作一
self.printAction1 = QAction(self.tr("打印无预览"), self)
self.printAction1.triggered.connect(self.on_printAction1_triggered)
# 创建动作二
self.printAction2 = QAction(self.tr("打印有预览"), self)
self.printAction2.triggered.connect(self.on_printAction2_triggered)
# 创建动作三
self.printAction3 = QAction(self.tr("直接打印"), self)
self.printAction3.triggered.connect(self.on_printAction3_triggered)
# 创建动作四
self.printAction4 = QAction(self.tr("打印到PDF"), self)
self.printAction4.triggered.connect(self.on_printAction4_triggered)
# 创建菜单,添加动作
self.printMenu = self.menuBar().addMenu(self.tr("打印"))
self.printMenu.addAction(self.printAction1)
self.printMenu.addAction(self.printAction2)
self.printMenu.addAction(self.printAction3)
self.printMenu.addAction(self.printAction4)
def on_printAction1_triggered(self):
""" 动作一: 打印,无预览 """
printer = QPrinter()
printDialog = QPrintDialog(printer, self)
if printDialog.exec_() == QDialog.Accepted:
self.handlePaintRequest(printer)
def on_printAction2_triggered(self):
""" 动作二: 打印,有预览 """
dialog = QPrintPreviewDialog()
dialog.paintRequested.connect(self.handlePaintRequest)
dialog.exec_()
def on_printAction3_triggered(self):
""" 动作三: 直接打印 """
printer = QPrinter()
self.handlePaintRequest(printer)
def on_printAction4_triggered(self):
""" 动作四: 打印到pdf """
printer = QPrinter()
printer.setOutputFormat(QPrinter.PdfFormat)
printer.setOutputFileName("D:/pdf直接打印测试.pdf")
self.handlePaintRequest(printer)
def handlePaintRequest(self, printer):
""" 打印函数 """
document = QTextDocument()
cursor = QTextCursor(document)
cursor.insertText(self.label.text())
document.print(printer)
if __name__ == "__main__":
app = QApplication(sys.argv)
main = MainWindow()
main.show()
sys.exit(app.exec_())
下面是通用框架,点击btn_select按钮触发下面的槽函数,选择文件后将内容加载到te_log多行文本域中。
需要注意:必须是file_path, ok...
而不是self.file_path, ok...
,因为后者测试发现了BUG,即第1次选择了文件,第2次再次点击按钮弹出文件选择框但没有选文件时,self.file_path
就变成了''
,此时再点击"上传"按钮就会报错。
def btn_select_click(self):
file_path, ok = UIUtils.prompt_filedialog(self, '上传文件', './HDFS', '日志文件(*.log *.txt)')
# 选择了文件
if ok:
self.file_path = file_path
with open(self.file_path, mode='r', encoding='utf-8') as fp:
self.log_text = fp.read()
self.te_log.setPlainText(self.log_text)
self.statusbar.showMessage('当前选中:[{}]'.format(self.file_path))
# 启用btn_file_upload
self.btn_upload.setEnabled(True)
# ...
下面是通用框架,点击btn_save按钮触发下面的槽函数,填入文件名后将内容保存至本地txt文件。其中方法的参数3是directory
,传入一个目录表示保存在哪个文件夹下。
如果传入的是目录directory,必须手动填入文件名才可以报错,就像下面这样:
def btn_save_click(self):
save_path = QFileDialog.getSaveFileName(self, '保存文件', './models', '文本文件(*.txt)')[0]
if len(save_path):
print('成功保存文件: {}'.format(save_path))
# ...
如果传入的是一个文件file,则保存的文件名会给出默认提示,就像下面这样:
def btn_save_click(self):
save_path = QFileDialog.getSaveFileName(self, '保存文件', './models', '文本文件(*.txt)')[0]
if len(save_path):
print('成功保存文件: {}'.format(save_path))
# ...
**这是一个很容易出错的问题,情景如下:**某个Pyqt5应用由3个界面组成:win_register.py,win_login.py,win_main.py,它们的路径关系如下图:
在3个界面的py文件中,分别都指定了工作路径为项目logmeta-front的根目录,如下:
import os
os.chdir('../')
然后,在index.py中,将3个界面组合起来形成入口:
class Index:
def __init__(self):
self.init_ui()
# ...
def init_ui(self):
self.window_login = WinLogin()
self.window_register = WinRegister()
self.window_main = WinMain()
self.window_login_show()
现在出现的问题是,单独运行每个界面,资源加载都没问题
但运行入口文件index.py,则所有资源都丢失了:
出现上述BUG的原因是,每个界面文件都将工作路径重置为../
,但我们运行入口文件index.py时,界面文件重置工作路径的操作其实是相对于当前真正运行的文件(即index.py)的,所以修改后的工作路径就不是项目根目录了,而是根目录的上级目录(即index.py根目录的父目录),所以会造成上述错误。
解决方法:工作路径设置为绝对路径
import os
os.chdir(os.path.join(os.path.dirname(__file__), '../'))
def load_url_img(label, url):
"""加载网络图片"""
try:
img = QImage.fromData(requests.get(url).content)
label.setPixmap(QPixmap.fromImage(img))
label.setScaledContents(True)
label.repaint()
except Exception as _:
label.setPixmap(QPixmap(None))
load_url_img(self.lb_img, response['data']['img_url'])
import sys
import time
from PyQt5.Qt import *
from PyQt5 import QtCore
from resources.ui_loading import Ui_Form
app = QApplication(sys.argv)
class Runthread(QThread):
# 通过类成员对象定义信号对象
_signal = pyqtSignal(int)
def __init__(self):
super(Runthread, self).__init__()
def __del__(self):
self.wait()
def run(self):
for i in range(100):
time.sleep(0.05)
self._signal.emit(i)
self._signal.emit(100)
class Loading_Page(QWidget, Ui_Form):
# 信号
loading_end_signal = pyqtSignal(bool)
def __init__(self, parent=None, *args, **kwargs):
super().__init__(parent, *args, **kwargs)
self.setAttribute(Qt.WA_StyledBackground, True)
self.setupUi(self)
self.loading_thread()
def loading_thread(self):
self.thread = Runthread()
self.thread._signal.connect(self.loading)
self.thread.start()
def loading(self,val):
self.qpb.setValue(val)
if __name__ == '__main__':
root = Loading_Page()
root.show()
sys.exit(app.exec_())