Example #1
0
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()
Example #2
0
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