一次使用Python連線資料庫生成二維碼並安裝為windows服務的工作任務

王明輝發表於2017-12-21

最近有一個需求,在現有生產系統上的人員庫中增加一個此人員關鍵資訊的二維碼,支援文字版和跳轉版兩種方式,與報表工具關聯,可列印。以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.3PyInstaller的官方網站似乎沒有此版本,我沒找到。我在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

 

 

 

相關文章