workloadinfos.py 24 KB

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