qxz_zhijian_simple.py 42 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070
  1. # -*- coding: utf-8 -*-
  2. # Form implementation generated from reading ui file '.\ui文件\气象站质检工具.ui'
  3. #
  4. # Created by: PyQt5 UI code generator 5.15.4
  5. #
  6. # WARNING: Any manual changes made to this file will be lost when pyuic5 is
  7. # run again. Do not edit this file unless you know what you are doing.
  8. # pyinstaller -n "气象站(v3.2)" -D -w f:\image_down_code\qxz_zhijian.py -i .\LOGO.ico
  9. from cgi import print_arguments
  10. from multiprocessing.managers import DictProxy
  11. from operator import delitem
  12. from unittest import result
  13. from PyQt5 import QtCore, QtGui, QtWidgets
  14. import hashlib
  15. import os
  16. import sys
  17. import re
  18. import ast
  19. from urllib import parse
  20. import json
  21. import time
  22. import datetime
  23. import requests
  24. import uuid
  25. import pymongo
  26. import pymysql
  27. from collections import defaultdict, Counter
  28. from xlrd import open_workbook
  29. from xlsxwriter.workbook import Workbook
  30. import openpyxl
  31. from aliyunsdkcore.vendored.requests.auth import HTTPBasicAuth
  32. save_filename = ""
  33. pwd_str = "yf6021"
  34. toji_format = {
  35. 'font_name' : '宋体',
  36. 'font_size': 14,
  37. 'font_color': 'black',
  38. 'text_wrap': True,
  39. 'bold': False,
  40. 'fg_color': '92D050',
  41. 'align': 'center',
  42. 'valign': 'vcenter',
  43. 'border': 1,
  44. 'top': 1,
  45. 'left': 1,
  46. 'right': 1,
  47. 'bottom': 1
  48. }
  49. title_format = {
  50. 'font_name' : '宋体',
  51. 'font_size': 12,
  52. 'bold': True,
  53. 'align': 'center',
  54. 'valign': 'vcenter',
  55. 'border': 1,
  56. 'top': 1,
  57. 'left': 1,
  58. 'right': 1,
  59. 'bottom': 1
  60. }
  61. merge_title_format = {
  62. 'font_name' : '宋体',
  63. 'font_size': 22,
  64. 'bold': True,
  65. 'align': 'center',
  66. 'valign': 'vcenter',
  67. "fg_color": "8DB4E2",
  68. 'border': 1,
  69. 'top': 1,
  70. 'left': 1,
  71. 'right': 1,
  72. 'bottom': 1
  73. }
  74. explain_formal = {
  75. 'font_name' : '宋体',
  76. 'font_size': 11,
  77. 'font_color': 'black',
  78. 'text_wrap': True,
  79. 'align': 'justify',
  80. 'valign': 'vcenter',
  81. 'border': 1,
  82. 'top': 1,
  83. 'left': 1,
  84. 'right': 1,
  85. 'bottom': 1
  86. }
  87. formal_format = {
  88. 'font_name' : '宋体',
  89. 'font_size': 11,
  90. 'font_color': 'black',
  91. 'fg_color': '77E88C',
  92. 'text_wrap': True,
  93. 'align': 'center',
  94. 'valign': 'vcenter',
  95. 'border': 1,
  96. 'top': 1,
  97. 'left': 1,
  98. 'right': 1,
  99. 'bottom': 1
  100. }
  101. common_format = {
  102. 'font_name' : '宋体',
  103. 'font_size': 11,
  104. 'font_color': 'black',
  105. "fg_color": 'E7EC73',
  106. 'text_wrap': True,
  107. 'align': 'center',
  108. 'valign': 'vcenter',
  109. 'border': 1,
  110. 'top': 1,
  111. 'left': 1,
  112. 'right': 1,
  113. 'bottom': 1
  114. }
  115. error_format = {
  116. 'font_name' : '宋体',
  117. 'font_size': 11,
  118. 'font_color': 'black',
  119. "fg_color": 'F4746A',
  120. 'text_wrap': True,
  121. 'align': 'center',
  122. 'valign': 'vcenter',
  123. 'border': 1,
  124. 'top': 1,
  125. 'left': 1,
  126. 'right': 1,
  127. 'bottom': 1
  128. }
  129. default_formal = {
  130. 'font_name': '宋体',
  131. 'font_size': 11,
  132. 'font_color': 'black',
  133. 'text_wrap': True,
  134. 'align': 'center',
  135. 'valign': 'vcenter',
  136. 'border': 1,
  137. 'top': 1,
  138. 'left': 1,
  139. 'right': 1,
  140. 'bottom': 1
  141. }
  142. class QxzCand:
  143. def __init__(self):
  144. self.func_dict = {
  145. "空气温度": self.get_cond_101,
  146. "空气湿度": self.get_cond_102,
  147. "土壤温度": self.get_cond_106,
  148. "土壤含水率": self.get_cond_107,
  149. "风速": self.get_cond_108,
  150. "风向": self.get_cond_109,
  151. "降雨量累计": self.get_cond_115,
  152. "光合有效辐射": self.get_cond_123,
  153. "气压": self.get_cond_127,
  154. "盐分": self.get_cond_154,
  155. "电导率": self.get_cond_211,
  156. "磷": self.get_cond_156,
  157. "日照时数": self.get_cond_113,
  158. "PM2.5": self.get_cond_145,
  159. "PM10": self.get_cond_146,
  160. "总辐射": self.get_cond_105,
  161. "负氧离子": self.get_cond_162,
  162. "二氧化碳": self.get_cond_120,
  163. "蒸发量": self.get_cond_110,
  164. "土壤PH": self.get_cond_128,
  165. "紫外辐射": self.get_cond_138,
  166. "水质pH": self.get_cond_228,
  167. "水位": self.get_cond_230,
  168. "水质电导率": self.get_cond_178,
  169. "光照度": self.get_cond_112,
  170. "硫化氢": self.get_cond_219,
  171. "水温": self.get_cond_144,
  172. "浊度": self.get_cond_151,
  173. "氨气": self.get_cond_153,
  174. "氧气": self.get_cond_220,
  175. "PM100": self.get_cond_203,
  176. "露点温度": self.get_cond_202,
  177. "氮": self.get_cond_155,
  178. "钾": self.get_cond_157,
  179. "溶解氧": self.get_cond_148,
  180. }
  181. self.cond_msg_dict = {}
  182. for k, v in self.func_dict.items():
  183. self.cond_msg_dict[k] = self.get_cond_msg
  184. self.head_dict = {
  185. "ID": "/",
  186. "检验项目": "判定标准",
  187. "经度": "合格条件(绿色):\n113°46′13″±30″范围内",
  188. "纬度": "合格条件(绿色):\n35°1′45″±30″范围内",
  189. "固件版本号": "合格条件(绿色):\n用检验时的输入作为导入的标准.\n/ 无法显示,替换为 -",
  190. "电压": "合格条件(绿色):\n11~15",
  191. "信号强度": "合格条件(绿色):\n>14",
  192. "上传数据条数": "合格条件(绿色):\n1、大于7条\n2、两条数据时间间隔小于20分钟的出现频次2次以内、两条数据间隔大于40分钟出现频率2次以内",
  193. "风速": "合格条件(绿色):\n1、0<示值<10\n2、有1条合格 即可",
  194. "风向": "合格条件(绿色):\n1、0<示值<360\n2、有1条合格即可",
  195. "降雨量累计": "合格条件(绿色):\n1、0<示值\n2、00:00示值归零\n3、有1条合格即可",
  196. "土壤含水率": "合格条件(绿色):\n1、0<示值<=100\n2、有1条合格即可",
  197. "土壤温度": "合格条件(绿色):\n1.≠0且在-5~35°C之间\n2、有1条合格即可",
  198. "盐分": "合格条件(绿色):\n1、0<示值<1000#2、有1条合格即可",
  199. "电导率": "合格条件(绿色):\n1、0<示值\n2、有1条合格即",
  200. "氮": "合格条件(绿色):\n1、0<示值\n2、有1条合格即可",
  201. "磷": "合格条件(绿色):\n1、0<示值且不能为负值\n2、有1条合格即可",
  202. "光合有效辐射": "合格条件(绿色):\n1、0<示值且不能为负值\n2、有1条合格即可",
  203. "日照时数": "合格条件(绿色):\n1、0.1≤示值\n2、有1条合格即可",
  204. "PM2.5": "合格条件(绿色):\n1、0<示值且不能为负值\n2、有1条合格即可",
  205. "PM10": "合格条件(绿色):\n1、0<示值\n2、有1条合格即可",
  206. "负氧离子": "合格条件(绿色):\n1、0<示值<1000\n2、有1条合格即可",
  207. "总辐射": "合格条件(绿色):\n1、0<示值\n2、有1条合格即可",
  208. "二氧化碳": "合格条件(绿色):\n1、400<示值<1500\n2、有1条合格即可",
  209. "蒸发量": "合格条件(绿色):\n1、0<示值\n2、有1条合格即可",
  210. "土壤PH": "合格条件(绿色):\n1、6~8\n2、有1条合格即可",
  211. "钾": "合格条件(绿色):\n1、0<示值\n2、有1条合格即可",
  212. "紫外辐射": "合格条件(绿色):\n1、0<示 值\n2、有1条合格即可",
  213. "空气温度": "合格条件(绿色):\n1.-10~35°C,但不允许为0°C\n2、有1条合格即可",
  214. "空气湿度": "合格条件(绿色):\n1、0<示值<100\n2、有1条合格即可",
  215. "水质pH": "合格条件(绿色):\n1、6≤示值≤8\n2、有1条合格即可",
  216. "水位": "合格条件(绿色):\n1、0<示值\n2、有1条合格即可",
  217. "水质电导率": "合格条件(绿色):\n1、0<示值\n2、有1条合格即可",
  218. "气压": "合格条件(绿色):\n1、900<示值<1150\n2、有1条合格即可",
  219. "光照度": "合格条件(绿色):\n1、0<示值<200000\n2、有1条合格即可",
  220. "硫化氢": "合格条件(绿色):\n1、示值=0\n2、有1条合格即可",
  221. "水温": "合格条件(绿色):\n1.0~30°C,但不允许为0°C\n2、 有1条合格即可",
  222. "溶解氧": "合格条件(绿色):\n1、0<示值\n2、有1条合格即可",
  223. "浊度": "合格条件(绿色):\n1、0<示值\n2、有1条合格即可",
  224. "氨气": "合格条件(绿色):\n1、示值=0\n2、有1条合格即可",
  225. "氧气": "合格条件(绿色):\n1、0<示值\n2、有1条合格即可",
  226. "PM100": "合格条件(绿色):\n1、0<示值\n2 、有1条合格即可",
  227. "露点温度": "合格条件(绿色):\n1.-10~35°C,但不允许为0°C\n2、有1条合格即可",
  228. "sim卡信息": "合格条件(绿色):\n1、大于等于180天单元格显示绿色\n2、小于180天大于等于30天单元格显示黄色、\n3、小于30天单元格显示为红色",
  229. "位置信息": "合格条件(绿色):显示河南省新乡市原阳县为合格,否则不合格",
  230. "单台合格数": "合格条件(绿色):\n所有显示数值全部在合格范围内,则判定为合格"
  231. }
  232. def get_cond_lat_msg(self, info):
  233. return {
  234. "status": int(info["status"]),
  235. "msg": f"纬度为: {info['lat']}"
  236. }
  237. def get_cond_lng_msg(self, info):
  238. return {
  239. "status": int(info["status"]),
  240. "msg": f"经度为: {info['lng']}"
  241. }
  242. def get_cond_volt_msg(self, info):
  243. return self.get_cond_msg(info)
  244. def get_cond_rssi_msg(self, info):
  245. return self.get_cond_msg(info)
  246. def get_cond_version_msg(self, info):
  247. status = info["status"]
  248. value = info["dver_num"]
  249. old_value = info["old_value"]
  250. device_version = value.replace('/', '-')
  251. old_version = old_value.replace('/', '-')
  252. return {
  253. "status": status,
  254. "msg": f"设备:{device_version}, 输入:{old_version}"
  255. }
  256. def get_cond_sim_msg(self, info):
  257. return info
  258. def get_time_uptime_msg(self, info):
  259. total_count = int(info["total_count"])
  260. uptime_20 = int(info["uptime_20"])
  261. uptime_40 = int(info["uptime_40"])
  262. msg = f"数据量:{total_count},间隔<20分钟:{uptime_20},间隔>40分钟:{uptime_40}"
  263. status = 0
  264. if total_count > 7 and uptime_20 <= 2 and uptime_40 <= 2:
  265. status = 1
  266. return {
  267. "status": status,
  268. "msg": msg
  269. }
  270. def get_cond_msg(self, info):
  271. """获取输出结果"""
  272. max_value = info['max_value']
  273. min_value = info['min_value']
  274. status = int(info['status'])
  275. msg = f"最大值:{max_value},最小值:{min_value}"
  276. return {"status": status, "msg": msg}
  277. def template_func(self, ek, cond_str):
  278. """获取模板函数"""
  279. cond = {
  280. "$function": {
  281. "args": [f"${ek}"],
  282. "lang": "js",
  283. "body": f"""
  284. function(values_list){{
  285. let max_value = Math.max(...values_list);
  286. let min_value = Math.min(...values_list);
  287. let status = 0;
  288. for (let i = 0; i < values_list.length; i++){{
  289. let v = values_list[i];
  290. if ({cond_str}){{
  291. status = 1;
  292. break;
  293. }}
  294. }}
  295. return {{"max_value": max_value, "min_value": min_value, "status": status}}
  296. }}
  297. """
  298. }
  299. }
  300. return cond
  301. def get_cond_101(self, ek):
  302. """获取空气温度条件"""
  303. cond = self.template_func(ek, "v != 0 && -10 <= v && v <= 35")
  304. return cond
  305. def get_cond_102(self, ek):
  306. """获取空气湿度条件"""
  307. cond = self.template_func(ek, "0 < v && v < 100")
  308. return cond
  309. def get_cond_106(self, ek):
  310. """获取土壤温度条件"""
  311. cond = self.template_func(ek, "v != 0 && -5 <= v && v <= 35")
  312. return cond
  313. def get_cond_107(self, ek):
  314. """获取土壤含水率条件"""
  315. cond = self.template_func(ek, "0 < v && v <= 100")
  316. return cond
  317. def get_cond_108(self, ek):
  318. """获取风速条件"""
  319. cond = self.template_func(ek, "0 < v && v < 10")
  320. return cond
  321. def get_cond_109(self, ek):
  322. """获取风向条件"""
  323. cond = self.template_func(ek, "0 < v && v < 360")
  324. return cond
  325. def get_cond_115(self, ek):
  326. """获取降雨量累计条件"""
  327. cond = self.template_func(ek, "0 < v")
  328. return cond
  329. def get_cond_123(self, ek):
  330. """获取光合有效辐射条件"""
  331. cond = self.template_func(ek, "0 < v")
  332. return cond
  333. def get_cond_127(self, ek):
  334. """获取气压条件"""
  335. cond = self.template_func(ek, "900 < v && v < 1150")
  336. return cond
  337. def get_cond_154(self, ek):
  338. """获取盐分条件"""
  339. cond = self.template_func(ek, "0 < v && v < 1000")
  340. return cond
  341. def get_cond_211(self, ek):
  342. """获取电导率条件"""
  343. cond = self.template_func(ek, "0 < v")
  344. return cond
  345. def get_cond_155(self, ek):
  346. """获取氮条件"""
  347. cond = self.template_func(ek, "0 < v")
  348. return cond
  349. def get_cond_156(self, ek):
  350. """获取磷条件"""
  351. cond = self.template_func(ek, "0 < v")
  352. return cond
  353. def get_cond_113(self, ek):
  354. """获取日照时数条件"""
  355. cond = self.template_func(ek, "0.1 <= v")
  356. return cond
  357. def get_cond_145(self, ek):
  358. """获取PM2.5条件"""
  359. cond = self.template_func(ek, "0 < v")
  360. return cond
  361. def get_cond_146(self, ek):
  362. """获取PM10条件"""
  363. cond = self.template_func(ek, "0 < v")
  364. return cond
  365. def get_cond_105(self, ek):
  366. """获取总辐射条件"""
  367. cond = self.template_func(ek, "0 < v")
  368. return cond
  369. def get_cond_162(self, ek):
  370. """获取负氧离子条件"""
  371. cond = self.template_func(ek, "0 < v && v < 1000")
  372. return cond
  373. def get_cond_120(self, ek):
  374. """获取二氧化碳条件"""
  375. cond = self.template_func(ek, "400 < v && v < 1500")
  376. return cond
  377. def get_cond_110(self, ek):
  378. """获取蒸发量条件"""
  379. cond = self.template_func(ek, "0 < v")
  380. return cond
  381. def get_cond_128(self, ek):
  382. """获取土壤PH条件"""
  383. cond = self.template_func(ek, "6 <= v && v <= 8")
  384. return cond
  385. def get_cond_157(self, ek):
  386. """获取钾条件"""
  387. cond = self.template_func(ek, "0 < v")
  388. return cond
  389. def get_cond_138(self, ek):
  390. """获取紫外辐射条件"""
  391. cond = self.template_func(ek, "0 < v")
  392. return cond
  393. def get_cond_228(self, ek):
  394. """获取水质pH条件"""
  395. cond = self.template_func(ek, "6 <= v && v <= 8")
  396. return cond
  397. def get_cond_230(self, ek):
  398. """获取水位条件"""
  399. cond = self.template_func(ek, "0 < v")
  400. return cond
  401. def get_cond_178(self, ek):
  402. """获取水质电导率条件"""
  403. cond = self.template_func(ek, "0 < v")
  404. return cond
  405. def get_cond_112(self, ek):
  406. """获取光照度条件"""
  407. cond = self.template_func(ek, "0 < v && v < 200000")
  408. return cond
  409. def get_cond_219(self, ek):
  410. """获取硫化氢条件"""
  411. cond = self.template_func(ek, "v == 0")
  412. return cond
  413. def get_cond_144(self, ek):
  414. """获取水温条件"""
  415. cond = self.template_func(ek, "0 < v && v <= 30")
  416. return cond
  417. def get_cond_148(self, ek):
  418. """获取溶解氧条件"""
  419. cond = self.template_func(ek, "0 < v")
  420. return cond
  421. def get_cond_151(self, ek):
  422. """获取浊度条件"""
  423. cond = self.template_func(ek, "0 < v")
  424. return cond
  425. def get_cond_153(self, ek):
  426. """获取氨气条件"""
  427. cond = self.template_func(ek, "v == 0")
  428. return cond
  429. def get_cond_220(self, ek):
  430. """获取氧气条件"""
  431. cond = self.template_func(ek, "0 < v")
  432. return cond
  433. def get_cond_203(self, ek):
  434. """获取PM100条件"""
  435. cond = self.template_func(ek, "0 < v")
  436. return cond
  437. def get_cond_202(self, ek):
  438. """获取露点温度条件"""
  439. cond = self.template_func(ek, "-10 < v && v < 35")
  440. return cond
  441. class Ui_MainWindow(object):
  442. """GUI界面"""
  443. def setupUi(self, MainWindow):
  444. MainWindow.setObjectName("MainWindow")
  445. MainWindow.resize(701, 644)
  446. font = QtGui.QFont()
  447. font.setFamily("Arial")
  448. font.setPointSize(12)
  449. MainWindow.setFont(font)
  450. icon = QtGui.QIcon("logo.ico")
  451. MainWindow.setWindowIcon(icon)
  452. self.centralwidget = QtWidgets.QWidget(MainWindow)
  453. self.centralwidget.setObjectName("centralwidget")
  454. self.pageTitleLabel = QtWidgets.QLabel(self.centralwidget)
  455. self.pageTitleLabel.setGeometry(QtCore.QRect(139, 50, 429, 42))
  456. font = QtGui.QFont()
  457. font.setFamily("楷体")
  458. font.setPointSize(28)
  459. self.pageTitleLabel.setFont(font)
  460. self.pageTitleLabel.setTextFormat(QtCore.Qt.AutoText)
  461. self.pageTitleLabel.setObjectName("pageTitleLabel")
  462. self.inputFileLabel = QtWidgets.QLabel(self.centralwidget)
  463. self.inputFileLabel.setGeometry(QtCore.QRect(180, 130, 121, 21))
  464. self.inputFileLabel.setObjectName("inputFileLabel")
  465. self.inputFileEdit = QtWidgets.QLineEdit(self.centralwidget)
  466. self.inputFileEdit.setGeometry(QtCore.QRect(310, 130, 151, 21))
  467. self.inputFileEdit.setFocusPolicy(QtCore.Qt.NoFocus)
  468. font = QtGui.QFont()
  469. font.setFamily("Arial")
  470. font.setPointSize(8)
  471. self.inputFileEdit.setFont(font)
  472. self.inputFileEdit.setObjectName("inputFileEdit")
  473. self.inputFileTool = QtWidgets.QToolButton(self.centralwidget)
  474. self.inputFileTool.setGeometry(QtCore.QRect(470, 130, 71, 21))
  475. font = QtGui.QFont()
  476. font.setFamily("Arial")
  477. font.setPointSize(10)
  478. self.inputFileTool.setFont(font)
  479. self.inputFileTool.setObjectName("inputFileTool")
  480. self.savePathLabel = QtWidgets.QLabel(self.centralwidget)
  481. self.savePathLabel.setGeometry(QtCore.QRect(180, 170, 121, 21))
  482. self.savePathLabel.setObjectName("savePathLabel")
  483. self.savePathEdit = QtWidgets.QLineEdit(self.centralwidget)
  484. self.savePathEdit.setGeometry(QtCore.QRect(310, 170, 151, 21))
  485. self.savePathEdit.setFocusPolicy(QtCore.Qt.NoFocus)
  486. font = QtGui.QFont()
  487. font.setFamily("Arial")
  488. font.setPointSize(8)
  489. self.savePathEdit.setFont(font)
  490. self.savePathEdit.setObjectName("savePathEdit")
  491. self.savePathTool = QtWidgets.QToolButton(self.centralwidget)
  492. self.savePathTool.setGeometry(QtCore.QRect(470, 170, 71, 21))
  493. font = QtGui.QFont()
  494. font.setFamily("Arial")
  495. font.setPointSize(10)
  496. self.savePathTool.setFont(font)
  497. self.savePathTool.setObjectName("savePathTool")
  498. self.startTimeLabel = QtWidgets.QLabel(self.centralwidget)
  499. self.startTimeLabel.setGeometry(QtCore.QRect(180, 210, 121, 21))
  500. self.startTimeLabel.setObjectName("startTimeLabel")
  501. self.startTimeEdit = QtWidgets.QDateTimeEdit(self.centralwidget)
  502. self.startTimeEdit.setGeometry(QtCore.QRect(310, 210, 151, 21))
  503. self.startTimeEdit.setMaximumDateTime(QtCore.QDateTime(QtCore.QDate(2025, 12, 31), QtCore.QTime(23, 59, 59)))
  504. self.startTimeEdit.setMinimumDateTime(QtCore.QDateTime(QtCore.QDate(2023, 8, 1), QtCore.QTime(8, 30, 0)))
  505. self.startTimeEdit.setObjectName("startTimeEdit")
  506. self.endTimeLabel = QtWidgets.QLabel(self.centralwidget)
  507. self.endTimeLabel.setGeometry(QtCore.QRect(180, 250, 121, 21))
  508. self.endTimeLabel.setObjectName("endTimeLabel")
  509. self.endTimeEdit = QtWidgets.QDateTimeEdit(self.centralwidget)
  510. self.endTimeEdit.setGeometry(QtCore.QRect(310, 250, 151, 21))
  511. self.endTimeEdit.setMaximumDateTime(QtCore.QDateTime(QtCore.QDate(2025, 12, 31), QtCore.QTime(23, 59, 59)))
  512. self.endTimeEdit.setMinimumDateTime(QtCore.QDateTime(QtCore.QDate(2023, 8, 1), QtCore.QTime(9, 0, 0)))
  513. self.endTimeEdit.setObjectName("endTimeEdit")
  514. self.platLabel = QtWidgets.QLabel(self.centralwidget)
  515. self.platLabel.setGeometry(QtCore.QRect(180, 290, 121, 21))
  516. self.platLabel.setObjectName("dianjiLabel")
  517. self.platBox = QtWidgets.QComboBox(self.centralwidget)
  518. self.platBox.setGeometry(QtCore.QRect(310, 290, 151, 21))
  519. self.platBox.addItems(['大数据平台','四情平台'])
  520. font = QtGui.QFont()
  521. font.setFamily("Arial")
  522. font.setPointSize(11)
  523. self.platBox.setFont(font)
  524. self.platBox.setObjectName("platBox")
  525. # self.stm8vsLabel = QtWidgets.QLabel(self.centralwidget)
  526. # self.stm8vsLabel.setGeometry(QtCore.QRect(180, 330, 121, 21))
  527. # self.stm8vsLabel.setObjectName("stm8vsLabel")
  528. # self.stm8vsEdit = QtWidgets.QLineEdit(self.centralwidget)
  529. # self.stm8vsEdit.setGeometry(QtCore.QRect(310, 330, 151, 21))
  530. font = QtGui.QFont()
  531. font.setFamily("Arial")
  532. font.setPointSize(11)
  533. # self.stm8vsEdit.setFont(font)
  534. # self.stm8vsEdit.setObjectName("stm8vsEdit")
  535. font = QtGui.QFont()
  536. font.setFamily("Arial")
  537. font.setPointSize(11)
  538. self.orderLabel = QtWidgets.QLabel(self.centralwidget)
  539. self.orderLabel.setGeometry(QtCore.QRect(180, 370, 121, 21))
  540. self.orderLabel.setObjectName("dverLabel")
  541. self.orderEdit = QtWidgets.QLineEdit(self.centralwidget)
  542. self.orderEdit.setGeometry(QtCore.QRect(310, 370, 151, 21))
  543. font = QtGui.QFont()
  544. font.setFamily("Arial")
  545. font.setPointSize(11)
  546. self.orderEdit.setFont(font)
  547. self.orderEdit.setObjectName("dverEdit")
  548. self.pushButton = QtWidgets.QPushButton(self.centralwidget)
  549. self.pushButton.setGeometry(QtCore.QRect(300, 410, 111, 41))
  550. self.pushButton.setObjectName("pushButton")
  551. self.progressBar = QtWidgets.QProgressBar(self.centralwidget)
  552. self.progressBar.setGeometry(QtCore.QRect(130, 470, 491, 31))
  553. self.progressBar.setProperty("value", 0)
  554. self.progressBar.setVisible(False)
  555. self.progressBar.setObjectName("progressBar")
  556. MainWindow.setCentralWidget(self.centralwidget)
  557. self.retranslateUi(MainWindow)
  558. self.inputFileTool.clicked.connect(self.input_file_path)
  559. self.savePathTool.clicked.connect(self.out_save_location)
  560. self.pushButton.clicked.connect(self.on_click)
  561. QtCore.QMetaObject.connectSlotsByName(MainWindow)
  562. def retranslateUi(self, MainWindow):
  563. _translate = QtCore.QCoreApplication.translate
  564. MainWindow.setWindowTitle(_translate("MainWindow", "气象站质检工具"))
  565. self.savePathTool.setText(_translate("MainWindow", "选择文件夹"))
  566. self.inputFileTool.setText(_translate("MainWindow", "选择文件"))
  567. self.pageTitleLabel.setText(_translate("MainWindow", "<html><head/><body><p><span style=\" font-weight:600; color:#3a45aa;\">云飞气象站设备质检工具</span></p></body></html>"))
  568. self.savePathLabel.setText(_translate("MainWindow", "|输出文件位置:"))
  569. self.inputFileLabel.setText(_translate("MainWindow", "|输入文件位置:"))
  570. self.startTimeLabel.setText(_translate("MainWindow", "|开始时间:"))
  571. self.endTimeLabel.setText(_translate("MainWindow", "|结束时间:"))
  572. self.platLabel.setText(_translate("MainWindow", "|检验平台:"))
  573. # self.stm8vsLabel.setText(_translate("MainWindow", "|主板版本号:"))
  574. self.orderLabel.setText(_translate("MainWindow", "|任务单号:"))
  575. self.pushButton.setText(_translate("MainWindow", "开始导出"))
  576. def input_file_path(self):
  577. file_path = QtWidgets.QFileDialog.getOpenFileName(None,"选取文件","./","All Files (*.xlsx;*.xls);;Text Files (*.txt);;ALL(*)")
  578. self.inputFileEdit.setText(file_path[0])
  579. self.inputFileEdit.setStyleSheet("color:black;")
  580. def out_save_location(self):
  581. fname = QtWidgets.QFileDialog.getExistingDirectory(None, '选取文件夹', './')
  582. self.savePathEdit.setText(fname)
  583. self.savePathEdit.setStyleSheet("color:black;")
  584. def on_click(self):
  585. file_path = self.inputFileEdit.text()
  586. save_path = self.savePathEdit.text()
  587. start_time = self.startTimeEdit.dateTime().toPyDateTime()
  588. end_time = self.endTimeEdit.dateTime().toPyDateTime()
  589. set_plat = self.platBox.currentText()
  590. set_stm8vs = "1"
  591. set_order = self.orderEdit.text()
  592. if not all ([file_path,save_path,start_time,end_time,set_stm8vs,set_order]):
  593. QtWidgets.QMessageBox.information(None, "提示", "选项未填写完整")
  594. elif end_time < start_time:
  595. QtWidgets.QMessageBox.information(None, "提示", "结束日期应在开始日期之后")
  596. else:
  597. read_dict = {}
  598. if file_path.split(".")[-1] == "xlsx":
  599. wb = openpyxl.load_workbook(file_path)
  600. sheet = wb[wb.sheetnames[0]]
  601. row_num = sheet.max_row
  602. d_list = []
  603. for row in range(2, row_num + 1):
  604. cell = sheet.cell(row, 1)
  605. try:
  606. cell_value = str(cell.value).strip()
  607. if cell_value:
  608. d_list.append(cell.value)
  609. except Exception as e:
  610. continue
  611. read_dict["设备ID"] = d_list
  612. else:
  613. xls = open_workbook(file_path)
  614. sheet_object = xls.sheets()[0]
  615. ncols = sheet_object.ncols
  616. d_list = []
  617. col_value = sheet_object.col_values(0)
  618. for d_i in col_value[1:]:
  619. try:
  620. d_i_v = str(d_i).strip()
  621. d_list.append(d_i_v)
  622. except Exception as e:
  623. continue
  624. read_dict["设备ID"] = d_list
  625. device_list = read_dict.get("设备ID")
  626. if device_list:
  627. self.inputFileTool.setEnabled(False)
  628. self.savePathTool.setEnabled(False)
  629. self.startTimeEdit.setEnabled(False)
  630. self.endTimeEdit.setEnabled(False)
  631. self.platBox.setEnabled(False)
  632. # self.stm8vsEdit.setEnabled(False)
  633. self.orderEdit.setEnabled(False)
  634. self.pushButton.setEnabled(False)
  635. self.pushButton.setText("执行中...")
  636. self.runThread = SCDThread(device_list,save_path,start_time,end_time,set_plat,set_stm8vs,set_order)
  637. self.runThread.proess_signal.connect(self.set_progressbar_value)
  638. self.runThread.start()
  639. self.progressBar.setVisible(True)
  640. else:
  641. QtWidgets.QMessageBox.information(None, "提示", "输入文件无'设备ID'列或该列无数据")
  642. def set_progressbar_value(self, value):
  643. self.progressBar.setValue(value)
  644. if value == 100:
  645. QtWidgets.QMessageBox.information(None, "提示", "文件导出完毕!导出文件名:\n{}".format(save_filename))
  646. self.inputFileTool.setEnabled(True)
  647. self.inputFileEdit.setText("")
  648. self.savePathTool.setEnabled(True)
  649. self.savePathEdit.setText("")
  650. self.startTimeEdit.setEnabled(True)
  651. self.endTimeEdit.setEnabled(True)
  652. self.platBox.setEnabled(True)
  653. # self.stm8vsEdit.setEnabled(True)
  654. # self.stm8vsEdit.setText("")
  655. self.orderEdit.setEnabled(True)
  656. self.orderEdit.setText("")
  657. self.pushButton.setEnabled(True)
  658. self.pushButton.setText("开始导出")
  659. self.progressBar.setVisible(False)
  660. self.progressBar.setValue(0)
  661. return
  662. class SCDThread(QtCore.QThread):
  663. """涉及进度条需主界面动态执行GUI,线程执行业务逻辑,避免主页面卡死"""
  664. proess_signal = QtCore.pyqtSignal(int)
  665. def __init__(self,device_list,save_path,start_time,end_time,set_plat,set_stm8vs,set_order):
  666. super(SCDThread, self).__init__()
  667. self.save_path = save_path
  668. self.start_time = time.mktime(start_time.timetuple())
  669. self.end_time = time.mktime(end_time.timetuple())
  670. self.start_time_str = start_time.strftime("%y-%m-%d %H:%M:%S")
  671. self.end_time_str = end_time.strftime("%y-%m-%d %H:%M:%S")
  672. self.set_plat = set_plat
  673. self.set_stm8vs = set_stm8vs
  674. self.set_order = set_order
  675. self.user = parse.quote_plus("root")
  676. self.passwd = parse.quote_plus("yfkj@6020")
  677. self.myclient = pymongo.MongoClient("mongodb://{0}:{1}@114.115.147.140:27017/".format(self.user,self.passwd))
  678. self.db = self.myclient.smartfarming
  679. self.device_collection = self.db.sa_device
  680. self.qxz_collection = self.db.sa_device_qxz_data
  681. self.qxz_base_info_collection = self.db.sa_qxz_base_info
  682. self.config = {
  683. 'host': '120.27.222.26',
  684. 'port': 3306,
  685. 'user': 'yfwlw',
  686. 'password': 'sql_yfkj_6019',
  687. 'db': 'yfwlw',
  688. 'charset': 'utf8mb4',
  689. 'cursorclass': pymysql.cursors.DictCursor,
  690. }
  691. self.connection = pymysql.connect(**self.config)
  692. self.cursor = self.connection.cursor()
  693. self.cond = QxzCand()
  694. self.mongo_ping()
  695. device_list_tp = []
  696. for d in device_list:
  697. d_id, device_id, platform = self.device_their_platform(d)
  698. device_list_tp.append(device_id)
  699. self.device_list = device_list_tp
  700. def mongo_ping(self):
  701. """mongo-ping预防连接失效"""
  702. # try:
  703. # self.myclient.admin.command('ping')
  704. # except: # "ConnectionFailure"
  705. self.myclient = pymongo.MongoClient("mongodb://{0}:{1}@114.115.147.140:27017/".format(self.user,self.passwd))
  706. self.db = self.myclient.smartfarming
  707. self.device_collection = self.db.sa_device
  708. self.qxz_collection = self.db.sa_qxz_data
  709. self.qxz_info_record_collection = self.db.sa_qxz_info_record
  710. self.qxz_base_info_collection = self.db.sa_qxz_base_info
  711. # self.sa_qxz_conf = self.db.
  712. def sql_ping(self):
  713. """mysql-ping 预防连接失效"""
  714. try:
  715. self.connection.ping()
  716. except:
  717. self.connection = pymysql.connect(**self.config)
  718. self.cursor = self.connection.cursor()
  719. def __time_dif(self,checkdatetime):
  720. """计算时间差"""
  721. nowdatetime = datetime.datetime.now()
  722. checkdatetime = datetime.datetime.strptime(checkdatetime, "%Y-%m-%d %H:%M:%S")
  723. timedif = checkdatetime - nowdatetime
  724. return timedif.days
  725. def device_their_platform(self,shortId):
  726. """确定设备所在平台以及完整设备号"""
  727. self.mongo_ping()
  728. self.sql_ping()
  729. regex = re.compile('.*{}$'.format(shortId))
  730. bd_device_dict = self.device_collection.find_one(
  731. filter = {"device_id":regex,"device_type_id":5},
  732. projection = {'_id': 0},
  733. sort = [('uptime', pymongo.DESCENDING)]
  734. )
  735. device_sql = "SELECT * FROM AppInfoManage_qxzstatus_new WHERE equip_id_id LIKE '%{}' ORDER BY upl_time DESC LIMIT 1;".format(shortId)
  736. self.cursor.execute(device_sql)
  737. sq_device_dict = self.cursor.fetchone()
  738. if bd_device_dict and sq_device_dict:
  739. bd_upltime = bd_device_dict["uptime"]
  740. sq_upltime = time.mktime(sq_device_dict["upl_time"].timetuple())
  741. if bd_upltime >= sq_upltime:
  742. d_id = bd_device_dict["id"]
  743. deviceId = bd_device_dict["device_id"]
  744. platform = "大数据平台"
  745. else:
  746. d_id = ""
  747. deviceId = sq_device_dict["equip_id_id"]
  748. platform = "四情平台"
  749. elif bd_device_dict and not sq_device_dict:
  750. d_id = bd_device_dict["id"]
  751. deviceId = bd_device_dict["device_id"]
  752. platform = "大数据平台"
  753. return d_id,deviceId,platform
  754. elif not bd_device_dict and sq_device_dict:
  755. d_id = ""
  756. deviceId = sq_device_dict["equip_id_id"]
  757. platform = "四情平台"
  758. else:
  759. d_id = ""
  760. deviceId = "平台无此设备"
  761. platform = "未知"
  762. return d_id,deviceId,platform
  763. def sim_updata(self, iccid):
  764. # 时间戳 用于获取sign
  765. timestamp = int(time.time())
  766. current_milli_time = lambda: int(round(time.time() * 1000))
  767. data_1 = "appid=%s&iccid=%s&timestamp=%s%s"%("102420177762",iccid,current_milli_time(),"6397d7e6a56589f1d93284e9800493e1")
  768. sign = hashlib.sha256(data_1.encode('utf-8')).hexdigest()
  769. data = {"appid": "102420177762", "iccid": iccid, "timestamp":current_milli_time(),"sign":sign}
  770. url = "https://api.simboss.com/2.0/device/detail"
  771. try:
  772. status = 1
  773. ret = requests.post(url, data=data)
  774. code = json.loads(ret.text)["code"]
  775. if code == "0":
  776. status = 1
  777. else:
  778. url = 'http://sim.brlink.cn/api/open/iotcard/card'
  779. appkey = "iaO2DKgS8KdlnVgU"
  780. appsecret = "qzKgO4sBdzMrjRwv9H22S9ufepNv8Hl5ehPqkYVD31DCICjyKwqUdj7zihQQKfgx"
  781. status = 2
  782. ret = requests.post(url,json={'iccid':iccid},auth=HTTPBasicAuth(appkey,appsecret),timeout=(5,10))
  783. print(ret)
  784. codes = json.loads(ret.text)["code"]
  785. if codes == 0:
  786. status = 2
  787. else:
  788. url = "https://jsnl.xmnengjia.com/open/api/module/cards"
  789. data = {"iccids":[iccid]}
  790. data = json.dumps(data)
  791. ret = requests.post(url,data=data,timeout=(10,30))
  792. print(ret.text)
  793. status = 3
  794. except:
  795. status = 0
  796. ret = 0
  797. return status,ret
  798. def get_position(self, lng, lat):
  799. if lng and lat:
  800. try:
  801. ret = requests.post("http://api.map.baidu.com/geocoder?location=%s,%s&coord_type=gcj02&output=json"%(lat,lng))
  802. ret_json = json.loads(ret.text)
  803. province, city, district = ret_json["result"]["addressComponent"]["province"], \
  804. ret_json["result"]["addressComponent"]["city"], \
  805. ret_json["result"]["addressComponent"]["district"]
  806. return province + city + district
  807. except Exception as e:
  808. return False
  809. else:
  810. return False
  811. def run(self):
  812. """主业务逻辑,涉及进度条不能模块化,慢慢捋"""
  813. title_name_list = ["ID#", "检验项目#", "土壤温度#", "土壤湿度#", "地理位置#", "流量卡有效期#", "数据条数#", "判定#"]
  814. proess = 0
  815. now_time = datetime.datetime.now()
  816. global save_filename
  817. save_filename = self.set_order + "_" + now_time.strftime("%m%d") + ".xlsx"
  818. save_path = os.path.join(self.save_path,save_filename)
  819. workbook = Workbook(save_path)
  820. worksheet = workbook.add_worksheet()
  821. merge_title_style = workbook.add_format(merge_title_format)
  822. toji_style = workbook.add_format(toji_format)
  823. default_style = workbook.add_format(default_formal)
  824. red_style = workbook.add_format(error_format)
  825. green_style = workbook.add_format(formal_format)
  826. yellow_style = workbook.add_format(common_format)
  827. style_dict = {
  828. 0: red_style,
  829. 1: green_style,
  830. 2: yellow_style,
  831. 3: default_style
  832. }
  833. head_list = [i.split("#")[0] for i in title_name_list]
  834. worksheet.merge_range(0, 0, 0, len(head_list), "采集仪检验原始记录单", merge_title_style)
  835. for index, k in enumerate(title_name_list):
  836. value = k.split('#')[0]
  837. worksheet.write(2, index, value, default_style)
  838. tit = [
  839. "/",
  840. "/",
  841. "显示数据且数据不能为0",
  842. "显示数据且数据不能为0",
  843. "显示地理位置为河南省新乡县",
  844. "显示数据,不作为考评项目",
  845. "数据条数>=92,且两条数据间隔为30±2分钟(显示数据条数,若不符合进行描述)",
  846. "/"
  847. ]
  848. for index, k in enumerate(tit):
  849. worksheet.write(3, index, k, default_style)
  850. row_index = 4
  851. qualified = 0
  852. for device_id in self.device_list:
  853. worksheet.write(row_index, 0, device_id, default_style)
  854. worksheet.write(row_index, 1, device_id, default_style)
  855. at, ah, position, sim, cbd_data_count = None, None, None, None, None
  856. self.mongo_ping()
  857. device = self.device_collection.find_one({"device_id": device_id}, {'_id':0,'id':0})
  858. device_conf = self.db.sa_qxz_conf.find_one({"device_id": device_id}, {'_id':0,'id':0})
  859. device_data = self.db.sa_qxz_data.find({"device_id": device_id, "uptime": {"$gte": self.start_time, "$lte": self.end_time}})
  860. device_base_info = self.qxz_base_info_collection.find_one({"device_id": device_id})
  861. device_conf = dict(device_conf)
  862. device_data = list(device_data)
  863. # 获取温度与湿度
  864. tp = {}
  865. for k, v in device_conf.items():
  866. if v:
  867. if "土壤含水率" in str(v):
  868. tp["ah"] = k
  869. if "土壤温度" in str(v):
  870. tp["at"] = k
  871. print(tp)
  872. at_lst = []
  873. ah_lst = []
  874. if device_data:
  875. at_lst = []
  876. ah_lst = []
  877. for i in device_data:
  878. if tp.get("at"):
  879. temp = float((i.get(tp.get("at"))).split("#")[0])
  880. at_lst.append(temp)
  881. if tp.get("ah"):
  882. temp = float(i.get(tp.get("ah")).split("#")[0])
  883. ah_lst.append(float(temp))
  884. print(at_lst)
  885. print(ah_lst)
  886. if at_lst:
  887. if 0.0 in at_lst:
  888. at = [0, f"{min(at_lst)} ~ {max(at_lst)}" ]
  889. else:
  890. at = [1, f"{min(at_lst)} ~ {max(at_lst)}" ]
  891. else:
  892. at = [0, "暂无数据"]
  893. if ah_lst:
  894. if 0.0 in ah_lst:
  895. ah = [0, f"{min(ah_lst)} ~ {max(ah_lst)} 有 0 存在记录中" ]
  896. else:
  897. ah = [1, f"{min(ah_lst)} ~ {max(ah_lst)} 有 0 存在记录中" ]
  898. else:
  899. ah = [0, "暂无数据"]
  900. # 数据条数量及数据间隔
  901. range_time_lst = [i.get("uptime") for i in device_data]
  902. diff_list = [range_time_lst[i] - range_time_lst[i-1] for i in range(1, len(range_time_lst))]
  903. rg_time = [i for i in diff_list if i > 32 * 60 or i < 28 * 60]
  904. if len(diff_list) >= 92 and len(rg_time) == 0:
  905. cbd_data_count = f"数据条件为{len(diff_list)}"
  906. cbd_data_count = [1, cbd_data_count]
  907. else:
  908. cbd_data_count = f"数据条件为{len(diff_list)},数据间隔不在30±2之间的数据有{len(rg_time)}条"
  909. cbd_data_count = [0, cbd_data_count]
  910. # 获取地理位置
  911. province = device.get("province")
  912. city = device.get("city")
  913. district = device.get("district")
  914. if province and city and district:
  915. position = province + city + district
  916. pos = [1, position]
  917. else:
  918. lng = device.get("lng")
  919. lat = device.get("lat")
  920. if lng and lat:
  921. position = self.get_position(lng, lat)
  922. if position:
  923. pos = [1, position]
  924. else:
  925. pos = [0, ""]
  926. else:
  927. pos = [0, ""]
  928. # 获取SIM卡信息
  929. smf = device_base_info.get("iccid")
  930. if smf:
  931. url = "http://114.115.147.140:8002/api/api_gateway?method=forecast.send_control.sim_query"
  932. response = requests.post(url=url, data={"iccid": smf})
  933. response = json.loads(response.text)
  934. s_date = response.get("data", {}).get("data", {}).get("data").get("expireDate")
  935. day = self.__time_dif(s_date)
  936. sim = [1, str(day)]
  937. else:
  938. sim = [0, ""]
  939. print(at, ah, pos, sim, cbd_data_count)
  940. is_true = True
  941. for index, value in enumerate([at, ah, pos, sim, cbd_data_count]):
  942. if index != 3 and value[0] == 0:
  943. is_true = False
  944. worksheet.write(row_index, index + 2, value[1], style_dict[value[0]])
  945. vk = "合格" if is_true else "不合格"
  946. vi = 1 if is_true else 0
  947. worksheet.write(row_index, 7, vk, style_dict[vi])
  948. row_index += 1
  949. if is_true:
  950. qualified += 1
  951. toji_data = [
  952. "任务单号", self.set_order, "检验时间", self.start_time_str, self.end_time_str,
  953. "报告日期", now_time.strftime("%y-%m-%d %H:%M:%S"), "合格数", qualified
  954. ]
  955. for i in range(len(head_list) - len(toji_data)):
  956. toji_data.append(" ")
  957. for index, k in enumerate(toji_data):
  958. value = str(k)
  959. worksheet.write(1, index, value, toji_style)
  960. c_n = len(value) + 15
  961. worksheet.set_column(index, index, c_n)
  962. worksheet.protect(pwd_str)
  963. workbook.close()
  964. self.cursor.close()
  965. self.connection.close()
  966. self.myclient.close()
  967. self.proess_signal.emit(100)
  968. if __name__== "__main__":
  969. QtWidgets.QApplication.setAttribute(QtCore.Qt.AA_EnableHighDpiScaling)
  970. QtGui.QGuiApplication.setAttribute(QtCore.Qt.HighDpiScaleFactorRoundingPolicy.PassThrough)
  971. app = QtWidgets.QApplication(sys.argv)
  972. MainWindow = QtWidgets.QMainWindow()
  973. ui = Ui_MainWindow()
  974. ui.setupUi(MainWindow)
  975. MainWindow.show()
  976. sys.exit(app.exec_())