class WebUI(BaseWebUI): def __init__(self, app, hub, debug=False): BaseWebUI.__init__(self, "index.html", app, hub, debug) self.html = index.html self.agent = '%s v%s' % (USER_AGENT, '.'.join(str(v) for v in VERSION)) log("Starting [%s]..." % self.agent, LEVEL_INFO) # Setup the system tray icon if sys.platform == 'darwin': tray_icon = 'ombre_16x16.png' elif sys.platform == "win32": tray_icon = 'ombre_16x16.png' else: tray_icon = 'ombre_32x32.png' self.trayIcon = QSystemTrayIcon(self._getQIcon(tray_icon)) self.trayIcon.setToolTip(tray_icon_tooltip) # Setup the tray icon context menu self.trayMenu = QMenu() self.showAppAction = QAction('&Show %s' % APP_NAME, self) f = self.showAppAction.font() f.setBold(True) self.showAppAction.setFont(f) self.trayMenu.addAction(self.showAppAction) self.aboutAction = QAction('&About...', self) self.trayMenu.addAction(self.aboutAction) self.trayMenu.addSeparator() self.exitAction = QAction('&Exit', self) self.trayMenu.addAction(self.exitAction) # Add menu to tray icon self.trayIcon.setContextMenu(self.trayMenu) # connect signals self.trayIcon.activated.connect(self._handleTrayIconActivate) self.exitAction.triggered.connect(self.handleExitAction) self.aboutAction.triggered.connect(self.handleAboutAction) self.showAppAction.triggered.connect(self._handleShowAppAction) self.app.aboutToQuit.connect(self._handleAboutToQuit) # Setup notification support self.system_tray_running_notified = False self.notifier = Notify(APP_NAME) self.trayIcon.show() def run(self): # load user's pool list # load_pools(self.app.property("AppPath")) self.view.loadFinished.connect(self._load_finished) # self.view.load(qt_core.QUrl(self.url)) self.view.setHtml(index.html, qt_core.QUrl(self.url)) self.resetWindowSize() self.center() self.timer = QTimer(self) self.timer.timeout.connect(self._updateHashRate) self.timer.start(2000) self.wait(1) self.timer2 = QTimer(self) self.timer2.timeout.connect(self._reportError) self.timer2.start(2000) self.trayIcon.show() self.show() def closeEvent(self, event): """ Override QT close event """ event.ignore() self.hide() if not self.system_tray_running_notified: self.notify("%s is still running at system tray." % APP_NAME, "Running Status") self.system_tray_running_notified = True def resetWindowSize(self): ws = qt_core.QSize( WINDOW_WIDTH, HEAD_ROW_HEIGHT + POOL_ROW_HEIGHT * (len([p for p in self.hub.pools.all_pools if not p['is_hidden']])) + BOTTOM_MARGIN) self.setFixedSize(ws) def _getQIcon(self, icon_file): _icon_path = os.path.join(self.app.property("ResPath"), 'icons', icon_file) return QIcon(_icon_path) def _handleTrayIconActivate(self, reason): if reason == QSystemTrayIcon.DoubleClick: self.showNormal() self.activateWindow() def handleExitAction(self, show_confirmation=False): reply = QMessageBox.No if show_confirmation: reply = QMessageBox.question(self, 'Exit %s?' % APP_NAME, "Are you sure to exit %s?" % APP_NAME, QMessageBox.Yes, QMessageBox.No) if not show_confirmation or reply == QMessageBox.Yes: self.trayIcon.hide() QTimer.singleShot(250, self.app.quit) def _handleShowAppAction(self): self.showNormal() self.activateWindow() def handleAboutAction(self): self.showNormal() self.about() def _reportError(self): for pool_info in self.hub.pools.all_pools: if 'error' in pool_info: if pool_info['error'] is not None: self.hub.report_error(pool_info['id'], pool_info['error']) else: self.hub.report_error(pool_info['id'], 'ERROR_END') pool_info.pop("error", None) def _updateHashRate(self): _sum_hashrates = 0. for pool_info in self.hub.pools.all_pools: _json = {'pool_id': pool_info['id']} hash_rates = pool_info[ 'hash_report'] if 'hash_report' in pool_info else {} if len(hash_rates) > 0: _hash_rates = dict(hash_rates) _total_hash_rate = reduce( lambda x, y: x + y, [_hash_rates[k] for k in _hash_rates]) _json['hash_rate'] = _total_hash_rate _sum_hashrates += _total_hash_rate pool_info['total_hashrate'] = _total_hash_rate else: _json['hash_rate'] = 0.0 # reset hashrate if 'hash_report' in pool_info and 'thr_list' in pool_info: if pool_info['thr_list'] is not None: for thr in pool_info['thr_list']: pool_info['hash_report'].update( {'%d' % thr._thr_id: 0.0}) work_report = pool_info[ 'work_report'] if 'work_report' in pool_info else {} if 'work_submited' in work_report and work_report[ 'work_submited'] > 0: _json['shares_good'] = work_report[ 'work_accepted'] if 'work_accepted' in work_report else 0 _json['shares_total'] = work_report['work_submited'] _json['shares_pct'] = "%.2f%%" % ( _json['shares_good'] * 100.0 / _json['shares_total'], ) else: _json['shares_good'] = 0 _json['shares_total'] = 0 _json['shares_pct'] = "0.00%" if 'difficulty' in work_report: _json['difficulty'] = "%.f" % work_report['difficulty'] else: _json['difficulty'] = "0" self.hub.update_hashrate(json.dumps(_json)) self.trayIcon.setToolTip( "%s\nHashrate: %s" % (tray_icon_tooltip, human_readable_hashrate(_sum_hashrates))) def _load_finished(self): #This is the actual context/frame a webpage is running in. # Other frames could include iframes or such. main_page = self.view.page() main_frame = main_page.mainFrame() # ATTENTION here's the magic that sets a bridge between Python to HTML main_frame.addToJavaScriptWindowObject("app_hub", self.hub) if self.is_first_load: ## Avoid re-settings on page reload (if happened) change_setting = main_page.settings().setAttribute settings = web_core.QWebSettings change_setting(settings.DeveloperExtrasEnabled, self.debug) change_setting(settings.LocalStorageEnabled, True) change_setting(settings.OfflineStorageDatabaseEnabled, True) change_setting(settings.OfflineWebApplicationCacheEnabled, True) change_setting(settings.JavascriptCanOpenWindows, True) change_setting(settings.PluginsEnabled, False) # Show web inspector if debug on if self.debug: self.inspector = web_core.QWebInspector() self.inspector.setPage(self.view.page()) self.inspector.show() #Tell the HTML side, we are open for business main_frame.evaluateJavaScript("app_ready()") # send pool list to HTML for rendering self.hub.create_pool_list() # Resize main window to fit web content (avoid scroll bars showed) main_page.setViewportSize(main_frame.contentsSize()) #self.setFixedSize(860, 360) # resume mining jobs for p in self.hub.pools.all_pools: if 'is_mining' in p and p['is_mining']: self.hub.start_stop_mining(p['id']) self.is_first_load = False def _handleAboutToQuit(self): log("%s is about to quit..." % APP_NAME, LEVEL_INFO) for pool_info in self.hub.pools.all_pools: if not 'thr_list' in pool_info or pool_info['thr_list'] is None: pool_info['is_mining'] = False else: # shut down threads for thr in pool_info['thr_list']: thr.shutdown() thr.join() # shut down RPC client pool_info['rpc'].shutdown() pool_info['rpc'].join() pool_info[ 'is_mining'] = True # save mining status to resume on next start if manager: manager.shutdown() # save pool list self.hub.pools.save_all() def notify(self, message, title="", icon=None, msg_type=None): if self.notifier.notifier is not None: self.notifier.notify(title, message, icon) else: self.showMessage(message, title, msg_type) def showMessage(self, message, title="", msg_type=None, timeout=2000): """Displays 'message' through the tray icon's showMessage function, with title 'title'. 'type' is one of the enumerations of 'common.MessageTypes'. """ if msg_type is None or msg_type == MSG_TYPE_INFO: icon = QSystemTrayIcon.Information elif msg_type == MSG_TYPE_WARNING: icon = QSystemTrayIcon.Warning elif msg_type == MSG_TYPE_CRITICAL: icon = QSystemTrayIcon.Critical title = "%s - %s" % (APP_NAME, title) if title else APP_NAME self.trayIcon.showMessage(title, message, icon, timeout) def about(self): QMessageBox.about(self, "About", \ u"%s <br><br>Copyright© 2017 - Sumokoin Projects<br><br>\ <b>www.sumokoin.org</b>" % self.agent) def wait(self, timeout=1): for _ in range(timeout * 10): sleep(0.1) self.app.processEvents()
class MainWindow(QDialog): def __init__(self, parent=None): super(MainWindow, self).__init__(parent) self.stopSign = 0 self.ws = 0 self.comment = '' self.clipboard = QApplication.clipboard() self.setWindowTitle(u'勤務表') self.setWindowIcon(QIcon('./img/tray.png')) self.systemTrayIcon = QSystemTrayIcon(self) self.systemTrayIcon.setIcon(QIcon('./img/tray.png')) self.systemTrayIcon.setVisible(True) self.systemTrayIcon.show() self.systemTrayIcon.activated.connect(self.on_systemTrayIcon_activated) self.tableLabel = QLabel('select *.xls file') self.pathText = QLineEdit() self.triggerBtn = QPushButton(u'檢查 / 開始') self.browseBtn = QPushButton(u' 瀏覽 ') self.stopBtn = QPushButton(u'停止') self.table = QTableWidget(26,0,self) self.setUpTable() self.hbox1 = QHBoxLayout() self.hbox2 = QHBoxLayout() self.hbox3 = QHBoxLayout() self.hbox4 = QHBoxLayout() self.hbox1.addWidget(self.pathText) self.hbox1.addWidget(self.browseBtn) self.hbox1.addWidget(self.triggerBtn) self.browseBtn.clicked.connect(self.OpenFile) self.triggerBtn.clicked.connect(self.setupTypeThread) self.stopBtn.clicked.connect(self.threadStop) self.status = QTreeWidget(self) self.status.setHeaderHidden(True) self.hbox2.addWidget(self.status) self.hbox3.addWidget(self.table) self.hbox4.addWidget(self.stopBtn) self.setGeometry(200, 200, 700, 400) self.status.setFixedHeight (80) self.layout = QVBoxLayout() self.layout.addWidget(self.tableLabel) self.layout.addLayout(self.hbox1) self.layout.addLayout(self.hbox2) self.layout.addLayout(self.hbox3) self.layout.addLayout(self.hbox4) self.setLayout(self.layout) self.stopBtn.setEnabled(False) def setUpTable(self): self.table.horizontalHeader().setVisible(False) for i in xrange(0, 26, 1): self.vhfont = QFont('Times', pointSize = 10, weight=QFont.Bold) timePos = i+6 if i == 0: item = QTableWidgetItem(u'[標題]') elif i == 1: item = QTableWidgetItem(u'[設定]') elif 2 < timePos < 24: item = QTableWidgetItem(('{0}~{1}').format(timePos, timePos+1)) else: item = QTableWidgetItem(('{0}~{1}').format(timePos-24, timePos-23)) item.setFont(self.vhfont) item.setTextAlignment(Qt.AlignCenter) self.table.setVerticalHeaderItem(i, item) def on_systemTrayIcon_activated(self, reason): if reason == QSystemTrayIcon.DoubleClick: if self.isHidden(): try: self.typeThread self.threadStop() except: pass self.show() else: self.hide() def setupTypeThread(self): self.clipboard.setText('') det = self.loadDetec() self.ws, vaild = self.checkTableValidation(det, self.table) ws = [] for col in self.ws: ws.append(col[0]) self.addStatus( u' 狀態: 檢查勤務表狀態....', 0) errStat = self.check(ws) if vaild != True and self.table.columnCount()!=0: self.addStatus( vaild, 1) elif self.table.columnCount() ==0: self.addStatus( u' 錯誤: 請載入勤務表', 1) self.typeThread = startType(self.ws) self.typeThread.threadDone.connect(self.showDoneMsg, Qt.QueuedConnection) self.typeThread.toTray.connect(self.toTray, Qt.QueuedConnection) self.typeThread.toCilpboard.connect(self.toCilpboard, Qt.QueuedConnection) self.typeThread.enableButtons.connect(self.enableButtons, Qt.QueuedConnection) self.typeThread.showErrMsg.connect(self.showErrMsg, Qt.QueuedConnection) self.typeThread.addStatus.connect(self.addStatus, Qt.QueuedConnection) if not self.typeThread.isRunning() and vaild == True and errStat == True: self.addStatus( u' 狀態: 檢查通過,開始進行登打作業....', -1) self.browseBtn.setEnabled(False) self.triggerBtn.setEnabled(False) self.stopBtn.setEnabled(True) self.typeThread.start() def toTray(self, state): if state: pass self.hide() self.systemTrayIcon.showMessage(u'輸入中',u'勤務表背景輸入中....\n\n雙擊圖示可暫停程序', msecs=1000000) else: self.show() def toCilpboard(self, text): self.clipboard.setText(text) def threadStop(self): self.typeThread.stopSign = 1 if self.typeThread.isRunning(): self.browseBtn.setEnabled(True) self.triggerBtn.setEnabled(True) self.stopBtn.setEnabled(False) def addStatus(self, text, err): subTreeItem = QTreeWidgetItem(self.status) subTreeItem.setText(0, text) self.activateWindow() if err == 1: font = QFont('Serif', 10, QFont.Bold) subTreeItem.setFont(0, font) subTreeItem.setForeground(0, QBrush(Qt.white)) subTreeItem.setBackground(0, QBrush(QColor(150,0,0))) elif err == 0: font = QFont('Serif', 10, QFont.Bold) subTreeItem.setFont(0, font) subTreeItem.setForeground(0, QBrush(Qt.black)) subTreeItem.setBackground(0, QBrush(Qt.white)) else: font = QFont('Serif', 10, QFont.Bold) subTreeItem.setFont(0, font) subTreeItem.setForeground(0, QBrush(Qt.white)) subTreeItem.setBackground(0, QBrush(QColor(0,150,0))) self.status.scrollToItem(subTreeItem, QAbstractItemView.PositionAtCenter) def showErrMsg(self, title, msg): self.addStatus( u'錯誤: ' + msg, 1) QMessageBox.warning(self, title, msg, QMessageBox.Ok) def showDoneMsg(self): self.addStatus( u'完成: 完成!', 1) QMessageBox.warning(self, u'完成!', u'完成!', QMessageBox.Ok) def enableButtons(self): self.clipboard.setText(self.comment) self.browseBtn.setEnabled(True) self.triggerBtn.setEnabled(True) self.stopBtn.setEnabled(False) self.show() def OpenFile(self): fileName = QFileDialog.getOpenFileName(self, "Open File.", "/home") self.comment = '' ext = fileName[0].split('.')[-1] if ext == 'xls' or ext == 'xlsx': self.pathText.setText(fileName[0]) sheet = open_workbook(fileName[0]).sheets()[0] self.setFixedHeight(550) self.addStatus( u' 狀態: 載入勤務表: [' + fileName[0] + ']', -1) for col in xrange(self.table.columnCount()-1,-1,-1): self.table.removeColumn(col) ws, header, headerNum = self.loadWS(sheet) self.appendTable(header, ws) self.check(ws) self.comment += self.yieldcomment(sheet) self.ws = ws else: self.showErrMsg(u'錯誤',u'選取檔案不是EXCEL檔') self.ws = 0 def checkTableValidation(self, detCol, table): # .[ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9] ws = [[], [], [], [], [], [], [], [], [], []] errStat = True if len(detCol) == 0 or (0 in detCol): errStat = u' 狀態: 勤務表標頭錯誤,點選 [ --請選擇-- ] 選取有效標頭' for i in xrange(len(detCol)-1): if sorted(detCol)[i] == sorted(detCol)[i+1]: errStat = u' 狀態: 勤務表標頭重複' print detCol for c in xrange(table.columnCount()): col = [] colNum = detCol[c] for r in xrange(2, 26, 1): col.append(table.item(r,c).text()) ws[colNum-1].append(col) for i in xrange(len(ws)): if len(ws[i]) == 0: ws[i].append(['','','','','','','','','','','','','','','','','','','','','','','','']) return (ws), errStat def loadWS(self, sheet): header, headerNum, ws= [],[],[] for c in xrange(3, 26 ,1): title = (sheet.cell_value(3, c)) if len(title) != 0 and len(header) <6: header.append(title) col = [] for m in xrange(7, 31, 1): try: col.append(str(sheet.cell_value(m, c)).strip('()').replace('.0', '').replace('.', ',')) except: col.append(u'error') ws.append(col) return ws, header, headerNum def appendTable(self, header, ws): try: font = QFont('TypeWriter', pointSize = 10, weight=QFont.Bold) for text in header: self.table.insertColumn(0) for c in xrange(len(header)): det = self.determine(header[c]) item = QTableWidgetItem(header[c]) if det == 0: item.setBackground(QBrush(QColor('#FF8D00'))) else: item.setBackground(QBrush(QColor('#005588'))) item.setFont(font) item.setTextAlignment(Qt.AlignCenter) self.table.setItem(0, c, item) nComboBox = self.newCombo() nComboBox.setCurrentIndex(det) self.table.setCellWidget(1, c, (nComboBox)) for r in xrange(2,26,1): item = QTableWidgetItem(ws[c][r-2]) item.setFont(font) item.setTextAlignment(Qt.AlignCenter) self.table.setItem(r, c, item) self.addStatus( u' 狀態: 勤務表預覽成功', -1) return 0 except: self.addStatus( u' 狀態: 勤務表預覽失敗', 1) return 'error' def loadDetec(self): det = [] for col in xrange(self.table.columnCount()): det.append( self.table.cellWidget(1, col).currentIndex()) return det def newCombo(self): taskList = [u'--請選擇--', u'值班', u'救護勤務', u'備勤', u'待命服勤', u'水源查察', u'消防查察', u'宣導勤務', u'訓(演)練', u'專案勤務', u'南山救護站'] nComboBox = QComboBox() nComboBox.addItems(taskList) for i in xrange(len(taskList)): if i == 0: nComboBox.setItemData(i, QColor('#550000'), Qt.BackgroundColorRole) nComboBox.setItemData(i, Qt.AlignCenter, Qt.TextAlignmentRole) nComboBox.setStyleSheet("text-align: right; font: bold 13px;") return nComboBox def setTableErr(self, row, state): if state == 'illegal' : color = '#CC0033' elif state == 'repeated' : color = '#FF8D00' elif state == 'legal' : color = '#201F1F' for col in xrange(self.table.columnCount()): self.table.item(row,col).setBackground(QBrush(QColor(color))) def check(self, ws): errStat = True for m in xrange(2,26): self.setTableErr(m, 'legal') for i in xrange(24): ary = [] for j in xrange(len(ws)): for each in ws[j][i].replace(' ', '').split(','): try: if each == "A": ary.append(-1) elif each == '' : pass else: each == ary.append(int(each)) except: errStat = False timePos = i+8 rptErrMSG = u" 錯誤: 於 {0} ~ {1} 時 數字: {2} --不合法, 請修正" self.setTableErr(i+2, 'illegal') print timePos if timePos < 23: print 'a' self.addStatus(rptErrMSG.format(timePos, timePos+1, each), 1) else: self.addStatus(rptErrMSG.format(timePos-24, timePos-23, each), 1) ary = sorted(ary) for idx in xrange(len(ary)-1): if ary[idx] == ary[idx+1]: errStat = False timePos = i+8 rptErrMSG = u" 錯誤: 於 {0} ~ {1} 時 數字: {2} --番號重複, 請修正" self.setTableErr(i+2, 'repeated') if timePos < 23: self.addStatus(rptErrMSG.format(timePos, timePos+1, str(ary[idx]).replace('-1', 'A')), 1) else: self.addStatus(rptErrMSG.format(timePos-24, timePos-23, str(ary[idx]).replace('-1', 'A')), 1) return errStat def determine(self, title): cmpfactor = 0 smp = [u'值班', u'救護分隊務', u'備', u'待命服', u'水源', u'消防', u'宣導', u'訓演練', u'專案其它', u'南山站'] for index, each in zip(xrange(len(smp)),smp): for text in each: for elem in title: cmpfactor += ( elem == text ) if cmpfactor > 0: return index+1 return 0 def yieldcomment(self, sheet): comment0, comment1 = '', '' # for i,j in [[24,27], [27,27], [29,27], [29,35]]: # try: # comment0 += (smart_str(sheet.cell_value(i, j)) + '\n') # except: # pass for i,j in [[31,3], [32,3], [33,3], [34,3]]: try: comment1 += (sheet.cell_value(i, j) + '\n') except: pass return comment1