workloadinfos.py 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533
  1. import datetime
  2. import json
  3. import os
  4. import time
  5. import openpyxl
  6. from PyQt6 import QtCore
  7. from PyQt6.QtCore import Qt , QTimer
  8. from PyQt6.QtGui import QTextDocument , QTextCursor , QTextCharFormat , QColor , QTextOption , QPalette
  9. from PyQt6.QtWidgets import QWidget , QMessageBox , QTableWidgetItem , QFileDialog , QStyledItemDelegate , QStyle , \
  10. QMenu
  11. from UI.workloadinfos import Ui_workloadinfos
  12. from .LogerinTxt import app_logger
  13. from Functions.apiFunction import api_fc
  14. filename = "D:\\flightinfo\\logs\\peoples.json"
  15. class Workloadinfos(Ui_workloadinfos, QWidget):
  16. def __init__(self, parent=None):
  17. super().__init__(parent)
  18. self.setupUi(self)
  19. self.ipinfo = None
  20. self.user = None
  21. self.selectedbc=None
  22. self.selectedtime=None
  23. self.doubleclickLock = 0
  24. self.headerLabel = ["序号", "交", "姓名", "备", "角色", "备", "授权", "备", "工作量", "备", "当前工作量", "备", "总工作量", "备",
  25. "总工时", "备", "当前工作", "备", "修正工时", "备", "修正详情", "备注信息", "备", "备", "备", "备", "备", "备",
  26. "1","2","3","4","5","6","7","8","9","10","11","12","13","14","15","16","17","18","19","20"]
  27. self.tableWidgetWorkload.setSortingEnabled(True)
  28. self.tableWidgetWorkload.setColumnCount(len(self.headerLabel))
  29. self.tableWidgetWorkload.verticalHeader().setVisible(False)
  30. self.tableWidgetWorkload.setHorizontalHeaderLabels(self.headerLabel)
  31. self.refresh.clicked.connect(self.refreshnow)
  32. self.changeupdate.clicked.connect(self.update)
  33. self.clear.clicked.connect(self.clearselect)
  34. self.autoupdateMode=False
  35. self.autoupdate_check.stateChanged.connect(self.on_checkbox_changed)
  36. # 保存当前排序状态
  37. self.current_sort_column = -1
  38. self.current_sort_order = Qt.SortOrder.AscendingOrder
  39. self.searchinfo.returnPressed.connect(self.search_table)
  40. self.searchinfo.textChanged.connect(self.search_table_empty)
  41. self.selectall.clicked.connect(self.selectall_funciton)
  42. self.load_pushbutton.clicked.connect(self.get_load)
  43. self.tableWidgetWorkload.setContextMenuPolicy(Qt.ContextMenuPolicy.CustomContextMenu)
  44. self.tableWidgetWorkload.customContextMenuRequested.connect(self.generateMenu)
  45. self.tableWidgetWorkload.horizontalHeader().sortIndicatorChanged.connect(self.on_sort_changed)
  46. self.changeupdate.clicked.connect(self.changeupdate_Function)
  47. self.tableWidgetWorkload.cellDoubleClicked.connect(self.itemclick2)
  48. def start(self):
  49. self.show()
  50. self.peopleresold = self.read_json_to_list()
  51. try:
  52. # ip = "http://" + str(self.ipinfo) + "/static/getPglistInDatabase"
  53. # self.peopleres = requests.get(url=ip, timeout=30).json()['返回值']
  54. self.peopleres = api_fc.request_get("/static/getPglistInDatabase")['返回值']
  55. #print(self.peopleres)
  56. except Exception as e:
  57. QMessageBox.warning(self, "提示", "服务器连接超时,请联系管理员检查服务器!")
  58. app_logger.log_error(e)
  59. self.peopleres = []
  60. self.peopleselect=self.peopleresold if self.peopleresold else self.peopleres
  61. self.comboBox_peoplelist.addItems(self.peopleres)
  62. self.set_initial_selection()
  63. self.refreshnow()
  64. self.serchTimer = QTimer()
  65. self.serchTimer.start(120 * 1000)
  66. self.serchTimer.timeout.connect(self.autoupdate)
  67. def itemclick2(self, row, column):
  68. try:
  69. # print(self.doubleclickLock)
  70. if column == 21 and self.doubleclickLock == 0:
  71. self.doubleclickLockUpdate=1
  72. self.clickrow = row
  73. self.clickcolumn = column
  74. self.olditem = self.tableWidgetWorkload.item(row, column).text()
  75. item = self.tableWidgetWorkload.item(row, column)
  76. # 如果单元格对象不存在,则返回默认的背景色
  77. if not item:
  78. self.color=self.tableWidgetWorkload.palette().color(QPalette.Base)
  79. else:
  80. # 获取并返回单元格的背景色
  81. self.color = item.background()
  82. self.tableWidgetWorkload.cellChanged.connect(self.cellchanged)
  83. else:
  84. self.doubleclickLockUpdate = 0
  85. except Exception as e:
  86. app_logger.log_error(e)
  87. def cellchanged(self, row, column):
  88. if self.clickrow == row and self.clickcolumn == column :
  89. self.clickrow = None
  90. self.clickcolumn = None
  91. try:
  92. self.doubleclickLock = 1
  93. self.newitem = self.tableWidgetWorkload.item(row, column).text().replace("\"","*").replace("\\",";").replace("\'","*").replace("/","、").replace("#","*")
  94. name = self.tableWidgetWorkload.item(row, 2).text()
  95. if column == 21 and self.olditem != self.newitem:
  96. item = self.tableWidgetWorkload.item(row, column)
  97. if item.text() != "":
  98. text = item.text().replace("\"", "*").replace("\\", ";").replace("\'", "*").replace("/", "、").replace("#", "*")
  99. else:
  100. text = "清空项目12345678987654321"
  101. # ip = "http://" + str(self.ipinfo) + "/static/updateNoteItem/" + "%s/" % str(text) + "%s" % str(name)
  102. # # print(ip)
  103. # ip2 = "http://" + str(self.ipinfo) + "/static/insertLogs2/" + "排班辅助%s的备注模块:由%s变更为%s/" % (name,self.olditem, self.newitem) + "%s/" % str(self.user) + "%s" % str(datetime.datetime.now())
  104. try:
  105. # requests.get(url=ip, timeout=30).json()
  106. # requests.get(url=ip2, timeout=30).json()
  107. api_fc.request_post('/static/updateNoteItem', text=str(text), name=str(name))
  108. api_fc.request_post('/static/insertLogs2',
  109. text="排班辅助%s的备注模块:由%s变更为%s" % (name,self.olditem, self.newitem),
  110. name=str(self.user),
  111. time=str(datetime.datetime.now()))
  112. self.tableWidgetWorkload.setItem(int(row), column, QTableWidgetItem(str(self.newitem)))
  113. self.tableWidgetWorkload.item(int(row), column).setBackground(QColor("yellow"))
  114. self.tableWidgetWorkload.item(int(row), column).setTextAlignment(Qt.AlignmentFlag.AlignCenter)
  115. except Exception as e:
  116. app_logger.log_error(e)
  117. QMessageBox.warning(self, "提示", "服务器连接超时,请联系管理员检查服务器!")
  118. self.doubleclickLockUpdate = 0
  119. self.doubleclickLock = 0
  120. self.tableWidgetWorkload.cellChanged.disconnect(self.cellchanged)
  121. except Exception as e:
  122. self.doubleclickLockUpdate = 0
  123. self.clickrow = None
  124. self.clickcolumn = None
  125. app_logger.log_error(e)
  126. try:
  127. self.tableWidgetWorkload.cellChanged.disconnect(self.cellchanged)
  128. except:
  129. pass
  130. self.tableWidgetWorkload.setItem(int(row), column, QTableWidgetItem(str(self.olditem)))
  131. self.tableWidgetWorkload.item(int(row), column).setBackground(QColor(self.color))
  132. self.tableWidgetWorkload.item(int(row), column).setTextAlignment(Qt.AlignmentFlag.AlignCenter)
  133. app_logger.log_error(e)
  134. self.doubleclickLock = 0
  135. def changeupdate_Function(self):
  136. day=datetime.datetime.now().strftime("%Y%m%d")
  137. info = self.changeinfo.text().strip().lower()
  138. people = self.changepeople.text().strip()
  139. costtime = self.changevalue.text().strip().lower()
  140. try:
  141. costtime = float(costtime)
  142. if info and people and costtime:
  143. timestr=str(int(time.time()*1000))
  144. id= f"人工修正-{info}-{timestr}-{people}"
  145. data0={
  146. "id": id,
  147. 'people':people,
  148. 'costtime':costtime,
  149. 'info':info,
  150. 'day':day
  151. }
  152. try:
  153. # ip = "http://" + str(self.ipinfo) + "/static/manchangecosttime"
  154. # data = json.dumps(data0)
  155. # res = requests.post(url=ip, data=data, timeout=30).json()
  156. res = api_fc.request_post('/static/manchangecosttime', data=data0)
  157. if res["返回值"] == "ok":
  158. QMessageBox.information(self, "提示", "操作成功!")
  159. self.refreshnow()
  160. else:
  161. QMessageBox.warning(self, "提示", "操作失败!")
  162. except Exception as e:
  163. QMessageBox.warning(self, "提示", "服务器连接超时,请联系管理员检查服务器!")
  164. app_logger.log_error(e)
  165. else:
  166. QMessageBox.warning(self,"警告", "请填写完整信息!!!")
  167. except ValueError:
  168. QMessageBox.warning(self,"警告", "工时内请填写数字!!!")
  169. def generateMenu(self, pos):
  170. try:
  171. nowDay = datetime.date.today().strftime("%Y%m%d")
  172. a_str = nowDay + " 09:00:00"
  173. b_str = nowDay + " 20:30:00"
  174. a = datetime.datetime.strptime(a_str, "%Y%m%d %H:%M:%S")
  175. b = datetime.datetime.strptime(b_str, "%Y%m%d %H:%M:%S")
  176. nowtime = datetime.datetime.now()
  177. passday = (nowtime - datetime.timedelta(days=4)).strftime("%Y%m%d")
  178. if a < nowtime < b:
  179. for i in self.tableWidgetWorkload.selectionModel().selection().indexes():
  180. menu = QMenu()
  181. item = menu.addAction('上个班未交班')
  182. item2 = menu.addAction('上个班交班')
  183. screenPos = self.tableWidgetWorkload.mapToGlobal(pos)
  184. action = menu.exec(screenPos)
  185. rowIndex = i.row()
  186. taskids = self.tableWidgetWorkload.item(rowIndex, 3).text()
  187. handstsNow=self.tableWidgetWorkload.item(rowIndex, 1).text()
  188. name=self.tableWidgetWorkload.item(rowIndex, 2).text()
  189. data={
  190. "ids":taskids,
  191. "type":handstsNow,
  192. "name":name,
  193. "date":passday,
  194. }
  195. if action == item:
  196. if handstsNow == "Y":
  197. self.handoverflight(data)
  198. self.refreshnow()
  199. else:
  200. QMessageBox.warning(self, "警告", f"未查询到{name}交班记录,无需取消交班")
  201. return
  202. elif action == item2:
  203. if handstsNow != "Y":
  204. self.handoverflight(data)
  205. self.refreshnow()
  206. else:
  207. QMessageBox.warning(self, "警告", f"经查询{name}已被记录交班,不用重复记录")
  208. return
  209. else:
  210. return
  211. except Exception as e:
  212. app_logger.log_error(e)
  213. def get_load(self):
  214. try:
  215. peoples = []
  216. fileName, _ = QFileDialog.getOpenFileName(self, '打开文件', '/')
  217. ext = os.path.splitext(fileName)[1].lower()
  218. if ext == ".xlsx":
  219. workbook = openpyxl.load_workbook(filename=fileName)
  220. mysheet = workbook.active
  221. myrows = list(mysheet.values)
  222. if myrows[0][0] != ""and myrows[0][0] != None:
  223. for i in myrows:
  224. if i[0] != ""and i[0] != None:
  225. peoples.append(i[0])
  226. if peoples:
  227. self.peopleselect=peoples
  228. self.write_list_to_json(peoples)
  229. self.set_initial_selection()
  230. self.refreshnow()
  231. else:
  232. QMessageBox.information(self, "提示", "人员名单导入为空,请重新录入")
  233. else:
  234. QMessageBox.warning(self, "警告", "请使用xlsx文件,在第一列输入人名再导入,人名需要带字母")
  235. return
  236. except Exception as e:
  237. app_logger.log_error(e)
  238. def selectall_funciton(self):
  239. self.peopleselect=self.peopleres
  240. self.write_list_to_json(self.peopleselect)
  241. self.set_initial_selection()
  242. def search_table_empty(self):
  243. """根据搜索框内容过滤表格数据"""
  244. search_text = self.searchinfo.text().strip().lower()
  245. if not search_text:
  246. # 如果搜索框为空,显示所有行
  247. for row in range(self.tableWidgetWorkload.rowCount()):
  248. self.tableWidgetWorkload.setRowHidden(row, False)
  249. return
  250. def search_table(self):
  251. """根据搜索框内容过滤表格数据"""
  252. search_text = self.searchinfo.text().strip().lower()
  253. if not search_text:
  254. # 如果搜索框为空,显示所有行
  255. for row in range(self.tableWidgetWorkload.rowCount()):
  256. self.tableWidgetWorkload.setRowHidden(row, False)
  257. return
  258. # 遍历表格所有单元格进行搜索
  259. for row in range(self.tableWidgetWorkload.rowCount()):
  260. row_hidden = True
  261. for col in range(self.tableWidgetWorkload.columnCount()):
  262. item = self.tableWidgetWorkload.item(row, col)
  263. if item and search_text in item.text().lower():
  264. row_hidden = False
  265. break
  266. self.tableWidgetWorkload.setRowHidden(row, row_hidden)
  267. def on_sort_changed(self, logicalIndex, order):
  268. """记录当前的排序状态"""
  269. self.current_sort_column = logicalIndex
  270. self.current_sort_order = order
  271. def on_checkbox_changed(self, state):
  272. if state != 0:
  273. self.autoupdateMode = True
  274. else:
  275. self.autoupdateMode = False
  276. def ensure_file_exists(self):
  277. """确保文件存在,不存在则创建空文件"""
  278. directory = os.path.dirname(filename)
  279. # 如果目录不存在,则创建目录
  280. if directory and not os.path.exists(directory):
  281. os.makedirs(directory)
  282. # 如果文件不存在,则创建空文件
  283. if not os.path.exists(filename):
  284. with open(filename, 'w', encoding='utf-8') as f:
  285. f.write("[]") #
  286. def write_list_to_json(self,data_list):
  287. """将 Python 列表写入 JSON 文件"""
  288. try:
  289. self.ensure_file_exists() # 确保文件存在
  290. with open(filename, 'w', encoding='utf-8') as f:
  291. json.dump(data_list, f, ensure_ascii=False)
  292. #print(f"数据已成功写入 {filename}")
  293. except Exception as e:
  294. app_logger.log_error(e)
  295. #print(f"写入文件时出错: {e}")
  296. def read_json_to_list(self):
  297. """从 JSON 文件读取数据并转换为 Python 列表"""
  298. try:
  299. self.ensure_file_exists() # 确保文件存在
  300. with open(filename, 'r', encoding='utf-8') as f:
  301. data = json.load(f)
  302. return data
  303. except json.JSONDecodeError:
  304. #print(f"文件 {filename} 格式不是有效的 JSON,将重置为空列表")
  305. # 重置文件为空白列表
  306. self.write_list_to_json([])
  307. return None
  308. except Exception as e:
  309. return None
  310. def set_initial_selection(self):
  311. """设置多选ComboBox的初始选中值"""
  312. for i in range(self.comboBox_peoplelist.model().rowCount()):
  313. item = self.comboBox_peoplelist.model().item(i)
  314. if item.text() in self.peopleselect:
  315. item.setCheckState(QtCore.Qt.CheckState.Checked)
  316. else:
  317. item.setCheckState(QtCore.Qt.CheckState.Unchecked)
  318. self.comboBox_peoplelist.update_text()
  319. def getdatas(self):
  320. try:
  321. # ip = "http://" + str(self.ipinfo) + "/static/getworkload"
  322. # data = json.dumps(self.peopleselect)
  323. # res = requests.post(url=ip, data=data, timeout=30).json()
  324. res = api_fc.request_post('/static/getworkload', names=self.peopleselect)["data"]
  325. return res
  326. except Exception as e:
  327. QMessageBox.warning(self, "提示", "服务器连接超时,请联系管理员检查服务器!")
  328. app_logger.log_error(e)
  329. def handoverflight(self,data0):
  330. try:
  331. # ip = "http://" + str(self.ipinfo) + "/static/handoverflight"
  332. # data = json.dumps(data0)
  333. # res = requests.post(url=ip, data=data, timeout=30).json()
  334. res = api_fc.request_post('/static/handoverflight', data=data0)
  335. if res["返回值"] == "ok":
  336. QMessageBox.information(self, "提示", "操作成功!")
  337. else:
  338. QMessageBox.warning(self, "提示", "操作失败!")
  339. except Exception as e:
  340. QMessageBox.warning(self, "提示", "服务器连接超时,请联系管理员检查服务器!")
  341. app_logger.log_error(e)
  342. def autoupdate(self):
  343. if self.autoupdateMode:
  344. res = self.getdatas()
  345. self.tableDisplay(res)
  346. if self.searchinfo.text():
  347. self.search_table()
  348. def tableDisplay(self,displaydata):
  349. try:
  350. if displaydata:
  351. self.tableWidgetWorkload.setSortingEnabled(False)
  352. # 清除原有数据
  353. self.tableWidgetWorkload.setRowCount(0)
  354. # 设置列数(假设数据是二维列表,第一行为表头)
  355. self.tableWidgetWorkload.setRowCount(len(displaydata))
  356. for row in range(len(displaydata)):
  357. for col in range(len(self.headerLabel)):
  358. item = QTableWidgetItem(str(displaydata[str(row)][str(col)]))
  359. self.tableWidgetWorkload.setItem(row, col, item)
  360. if col == 16 and "无" not in str(displaydata[str(row)][str(col)]):
  361. item.setForeground(QColor("red"))
  362. font = item.font()
  363. item.setFont(font)
  364. elif col == 8 :
  365. item.setForeground(QColor("green"))
  366. font = item.font()
  367. font.setBold(True)
  368. item.setFont(font)
  369. elif col == 21 and str(displaydata[str(row)][str(col)]) != "" :
  370. item.setBackground(QColor("yellow"))
  371. elif col == 1 and str(displaydata[str(row)][str(col)]) == "Y":
  372. item.setBackground(QColor("yellow"))
  373. if col not in [5]:
  374. self.tableWidgetWorkload.item(int(row), col).setTextAlignment(Qt.AlignmentFlag.AlignCenter)
  375. else:
  376. self.tableWidgetWorkload.item(int(row), col).setTextAlignment(Qt.AlignmentFlag.AlignTop | Qt.AlignmentFlag.AlignCenter)
  377. self.tableWidgetWorkload.setRowHeight(int(row), 35)
  378. settips = {1: "3", 14: "20"}
  379. for key, value in settips.items():
  380. if self.tableWidgetWorkload.item(int(row), key):
  381. self.tableWidgetWorkload.item(int(row), key).setToolTip("%s" % (str(displaydata[str(row)][str(value)])))
  382. self.tableWidgetWorkload.setItemDelegate(ColoredTextDelegate())
  383. self.tableWidgetWorkload.resizeColumnsToContents()
  384. self.tableWidgetWorkload.setWordWrap(True)
  385. self.tableWidgetWorkload.setColumnWidth(6, 100)
  386. for i in range(len(self.headerLabel)):
  387. if i in [3,5,7,9,11,13,15,17,19,22,23,24,25,26,27,10,12,18,20]:
  388. self.tableWidgetWorkload.setColumnHidden(i, True)
  389. else:
  390. self.tableWidgetWorkload.setColumnHidden(i, False)
  391. self.tableWidgetWorkload.setSortingEnabled(True)
  392. except Exception as e:
  393. app_logger.log_error(e)
  394. self.tableWidgetWorkload.setSortingEnabled(True)
  395. def refreshnow(self):
  396. self.peopleselect = self.comboBox_peoplelist.get_selected_items()
  397. self.write_list_to_json(self.peopleselect)
  398. res=self.getdatas()
  399. self.tableDisplay(res)
  400. if self.searchinfo.text():
  401. self.search_table()
  402. def clearselect(self):
  403. self.peopleselect=[]
  404. self.write_list_to_json(self.peopleselect)
  405. self.set_initial_selection()
  406. class ColoredTextDelegate(QStyledItemDelegate):
  407. def paint(self, painter, option, index):# 保存painter状态
  408. painter.save()
  409. # 设置交替行颜色
  410. palette = option.palette
  411. if index.row() % 2 == 1:
  412. bg_color = palette.color(QPalette.ColorRole.AlternateBase)
  413. else:
  414. bg_color = palette.color(QPalette.ColorRole.Base)
  415. # 绘制背景(考虑选中状态)
  416. if option.state & QStyle.StateFlag.State_Selected:
  417. painter.fillRect(option.rect, palette.highlight())
  418. else:
  419. painter.fillRect(option.rect, bg_color)
  420. if index.column() in [28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47]:
  421. # 获取单元格文本
  422. text = index.data(Qt.ItemDataRole.DisplayRole) or ""
  423. if "预排" in text:
  424. painter.fillRect(option.rect, QColor("yellow"))
  425. # 设置文本绘制选项
  426. painter.save()
  427. text_option = QTextOption()
  428. text_option.setAlignment(Qt.AlignmentFlag.AlignCenter)
  429. doc = QTextDocument()
  430. doc.setDefaultTextOption(text_option)
  431. doc.setTextWidth(option.rect.width())
  432. default_format = QTextCharFormat()
  433. default_format.setForeground(QColor("gray"))
  434. font = default_format.font()
  435. font.setPointSize(8)
  436. default_format.setFont(font)
  437. balck_format = QTextCharFormat()
  438. balck_format.setForeground(QColor("balck"))
  439. font = balck_format.font()
  440. font.setPointSize(10)
  441. balck_format.setFont(font)
  442. # 插入带格式文本
  443. cursor = QTextCursor(doc)
  444. cursor.insertText(text[:5], balck_format)
  445. cursor.insertText(text[5:], default_format)
  446. # 绘制文档
  447. painter.save()
  448. painter.translate(option.rect.topLeft())
  449. doc.drawContents(painter)
  450. painter.restore()
  451. elif index.column() == 8:
  452. text = index.data(Qt.ItemDataRole.DisplayRole) or ""
  453. start = text.find("(")
  454. end = text.find(")")
  455. # 设置文本绘制选项
  456. painter.save()
  457. text_option = QTextOption()
  458. text_option.setAlignment(Qt.AlignmentFlag.AlignCenter)
  459. doc = QTextDocument()
  460. doc.setDefaultTextOption(text_option)
  461. doc.setTextWidth(option.rect.width())
  462. default_format = QTextCharFormat()
  463. default_format.setForeground(QColor("red"))
  464. font = default_format.font()
  465. font.setBold(True)
  466. default_format.setFont(font)
  467. balck_format = QTextCharFormat()
  468. balck_format.setForeground(QColor("balck"))
  469. # 插入带格式文本
  470. cursor = QTextCursor(doc)
  471. cursor.insertText(text[:start+1], balck_format)
  472. cursor.insertText(text[start+1:end], default_format)
  473. cursor.insertText(text[end:], balck_format)
  474. # 计算垂直居中位置
  475. text_height = doc.size().height()
  476. y_offset = (option.rect.height() - text_height) / 2
  477. # 绘制文本
  478. painter.translate(option.rect.x(), option.rect.y() + y_offset)
  479. doc.drawContents(painter)
  480. painter.restore()
  481. else:
  482. # 其他单元格使用默认绘制
  483. super().paint(painter, option, index)
  484. def sizeHint(self, option, index):
  485. # 确保单元格有合适的大小
  486. if index.column() == 8:
  487. text = index.data(Qt.ItemDataRole.DisplayRole) or ""
  488. doc = QTextDocument()
  489. doc.setPlainText(text)
  490. doc.setTextWidth(option.rect.width())
  491. return doc.size().toSize()
  492. return super().sizeHint(option, index)