最近有一個需求,在現有生產系統上的人員庫中增加一個此人員關鍵資訊的二維碼,支援文字版和跳轉版兩種方式,與報表工具關聯,可列印。以windows服務方式,定時檢查,只要發現某人員沒有此二維碼資訊,就生成並寫入人員庫。
決定使用Python。
在此之前,沒有用Python實現過其中的任何一個工作。對於習慣於微軟開發環境下的程式設計師,使用開源系統,幾乎一步一個坎,不過確實挺簡單的。
整體設想,主要工作有以下幾個步驟:
1.生成二維碼
2.建立連線,讀取資料
3.根據讀取的資料生成二維碼
4.二維碼寫入資料庫
5.與報表工具關聯,顯示在報表上
6.寫日誌
7.生成windows服務
下面分步敘述過程。
1. 生成二維碼
使用QRCode
安裝命令pip install QRCode,會自動下載並安裝與當前版本匹配外掛。
程式碼如下(來自網路,稍加改動,保留原作者宣告,下同):
# coding: utf-8 """ filename: qrcode.py Created by Tacey Wong at 16-9-22 下午10:34 """ #import zbar import qrcode from PIL import Image import os, sys def gen_qrcode(string, path, logo=""): """ 生成中間帶logo的二維碼 需要安裝qrcode, PIL庫 @引數 string: 二維碼字串 @引數 path: 生成的二維碼儲存路徑 @引數 logo: logo檔案路徑 @return: None """ qr = qrcode.QRCode( version=2, error_correction=qrcode.constants.ERROR_CORRECT_H, box_size=8, border=1 ) qr.add_data(string) qr.make(fit=True) img = qr.make_image() #img = img.convert("RGBA") img = img.convert("RGB") if logo and os.path.exists(logo): try: icon = Image.open(logo) img_w, img_h = img.size except(Exception) as e: print(e) sys.exit(1) factor = 4 size_w = int(img_w / factor) size_h = int(img_h / factor) icon_w, icon_h = icon.size if icon_w > size_w: icon_w = size_w if icon_h > size_h: icon_h = size_h icon = icon.resize((icon_w, icon_h), Image.ANTIALIAS) w = int((img_w - icon_w) / 2) h = int((img_h - icon_h) / 2) #icon = icon.convert("RGBA") #png影象使用 icon = icon.convert("RGB") img.paste(icon, (w, h), icon) img.save(path) # 呼叫系統命令開啟圖片 # xdg - open(opens a file or URL in the user's preferred application) #os.system('xdg-open %s' % path) ##def decode_qrcode(path): ## """ ## 解析二維碼資訊 ## @引數 path: 二維碼圖片路徑 ## @return: 二維碼資訊 ## """ ## # 建立圖片掃描物件 ## scanner = zbar.ImageScanner() ## # 設定物件屬性 ## scanner.parse_config('enable') ## # 開啟含有二維碼的圖片 ## img = Image.open(path).convert('L') ## # 獲取圖片的尺寸 ## width, height = img.size ## # 建立zbar圖片物件並掃描轉換為位元組資訊 ## qrCode = zbar.Image(width, height, 'Y800', img.tobytes()) ## scanner.scan(qrCode) ## # 組裝解碼資訊 ## data = '' ## for s in qrCode: ## data += s.data ## # 刪除圖片物件 ## del img ## # 輸出解碼結果 ## return data def SetQRCode(info,imgFileName=""): #if __name__ == "__main__": #info = """""" #pic_path = "QR" + imgFileName + ".jpg" pic_path = "QR.jpg" icon_path = "logo.png" logo_path = "" gen_qrcode(info, pic_path,logo_path ) #print(decode_qrcode(pic_path))
2. 建立連線,讀取資料
本步驟使用cx_Oracle,安裝方法同上
>>>pip install cx_Oracle
需要oracle客戶端,下載地址:https://oracle.github.io/odpi/doc/installation.html#windows,找到適合你的版本,下載zip版本以後,解壓,把oracle客戶端路徑加到系統路徑中。
程式碼如下:
import cx_Oracle import logging import inspect import os #建立和資料庫系統的連線 conn = cx_Oracle.connect('使用者名稱', '密碼', '資料庫伺服器IP/oracle服務名') #建立連線,3 個引數分開寫 cursor = conn.cursor() def updateData(fid,value): param={'fieldname1': fieldname1, 'fieldname2': fieldname2} cursor.execute("""UPDATE tablename SET FQRCODE=:fieldname1 WHERE DICT_OID=:fieldname2 """,param) #cursor.close(); conn.commit(); #conn.close(); def openImage(): file=open("qr.jpg",'rb'); img=file.read(); file.close() return img def searchAll(): import gqrcode cursor.execute(''' SELECT * FROM tablename n ''') #personInfoTuple=cursor.fetchone() #獲取一條 #personInfoTuple=cursor.fetchmany(2) #獲取2條 personInfoTuple = cursor.fetchall() #獲取全部 import datetime; import time; listIds=[] for x in personInfoTuple: authcode = x[5] strInfo = "網址” now = datetime.datetime.now(); now = now.strftime('%Y%m%d%H%M%S%MS') clock = time.clock(); filename=x[4]+now gqrcode.SetQRCode(strInfo) #生成二維碼
3. 根據讀取的資料生成二維碼
此處需要安裝PIL,即pillow
>>>pip install pillow
imgStream=openImage(); #以二進位制流開啟圖片 listIds.append(x[0])
4. 二維碼寫入資料庫
updateData(x[0], imgStream);#入庫
5. 與報表工具關聯,顯示在報表上
以二進位制流的方式入庫以後,在報表工具中,以圖片控制元件讀取欄位即可,大多數報表工具都支援。我用的是如意報表。
6. 寫日誌
Python裡面另有loggning模組,因為任務的時間緊迫,我沒有仔細研究,直接寫文字檔案。
//日誌 def logger(msg): try: logFileName="logging_test.log" logging.basicConfig(filename=logFileName,filemode='a',level=logging.DEBUG) logging.debug(msg) except Exception as ex: logging.debug(ex);
7. 生成windows服務
這一步我浪費了很多時間。啟動服務時,在有些機器上報錯,有些不報錯,沒有找到規律,如果有人在事件檢視器中發現“等待服務的連線超時(30000)毫秒”,我沒有解決。
這一步需要安裝pywin32,理論上用pip install pywin32即可,但是,我用的是p3.6.3,PyInstaller的官方網站似乎沒有此版本,我沒找到。我在CSDN上找到的此版本,說是官方的。連結:http://download.csdn.net/download/battlewolf_hua/9827297
我為了檢測此服務需要多少記憶體,需要下載安裝psutil
>>>pip install psutil
程式碼如下:
#encoding=utf-8 import win32serviceutil import win32service import win32event import os import logging import inspect import servicemanager import sys import datetime import oracleTest import psutil class ServiceTest (win32serviceutil.ServiceFramework): info = psutil.virtual_memory() _totalMemory = round(info.total /1024 /1024/1024,2) #總記憶體GB _thisMemory = psutil.Process(os.getpid()).memory_info().rss #本程式使用記憶體KB _now = datetime.datetime.now(); _prefix = _now.strftime('%Y%m%d') _svc_name_ = "ConstructorNoBuildingService" #服務名 #_svc_name_ = "PythonService" #服務名 _svc_display_name_ = "ServiceTest" #服務在windows系統中顯示的名稱 _svc_description_ = "二維碼服務" #服務的描述 def __init__(self, args): win32serviceutil.ServiceFramework.__init__(self, args) self.hWaitStop = win32event.CreateEvent(None, 0, 0, None) self.logger = self._getLogger() self.run = True def _getLogger(self): logger = logging.getLogger('[ServiceTest]') this_file = inspect.getfile(inspect.currentframe()) dirpath = os.path.abspath(os.path.dirname(this_file)) handler = logging.FileHandler(os.path.join(dirpath, "service" + self._prefix + ".log")) formatter = logging.Formatter('%(asctime)s %(name)-12s %(levelname)-8s %(message)s') handler.setFormatter(formatter) logger.addHandler(handler) logger.setLevel(logging.DEBUG) return logger _hour = _now.strftime('%H') def SvcDoRun(self): import time ## self.logger.info("service is run....") while self.run : ## self.logger.info("I am runing....") listUpdatedIds = oracleTest.searchAll() if(len(listUpdatedIds)==0): self.logger.info("無資料,服務空載") else: self.logger.info(listUpdatedIds) self.logger.info("使用記憶體"+str(self._thisMemory/1024)+"KB") time.sleep(30) #執行任務間隔,相當於timer控制元件中的interval def SvcStop(self): self.logger.info("service was stopped.") self.ReportServiceStatus(win32service.SERVICE_STOP_PENDING) win32event.SetEvent(self.hWaitStop) self.run = False if __name__=='__main__': print("主程式啟動") ## print(len(sys.argv)); logger(len(sys.argv)); if len(sys.argv) == 1: try: evtsrc_dll = os.path.abspath(servicemanager.__file__) servicemanager.PrepareToHostSingle(ServiceTest) servicemanager.Initialize(ServiceTest, evtsrc_dll) servicemanager.StartServiceCtrlDispatcher() except win32service.error as details: import winerror if details == winerror.ERROR_FAILED_SERVICE_CONTROLLER_CONNECT: print("錯誤發生") win32serviceutil.usage() else: print("否則,執行win32serviceutil.HandleCommandLine(ServiceTest)") try: win32serviceutil.HandleCommandLine(ServiceTest) except Exception as e: print(e) win32serviceutil.usage()
8. 安裝服務
命令列,原始檔所在目錄
Python serviceTest.py install
Good luck