一. 需求
公司要做一个H5手机端适配页面,因技术问题所以H5是外包的,每次前端给我们源码,我们把源码传到服务器让其他人访问看是否存在bug,这个不是很麻烦吗?有人说,可以让前端在他们的服务器上先托管,等我们验收了后在给源码不结了嘛,是的呀!所有的人都愿意这样,but……
要是能在本地搭建环境直接访问是不是更好的。问题是我们这边程序此刻没时间与H5前端对接,既浪费我们时间又浪费他们时间。所以开发一个service服务器让测试人员直接参与,这样就可以把我们完美分工了。
二.Python搭建web服务器
Python自带一个http.serrver包可以简单的搭建web服务器
参考:
http://www.cnblogs.com/xuxn/archive/2011/02/14/build-simple-web-server-with-python.html
http://blog.csdn.net/kevin_darkelf/article/details/40979545
http://blog.csdn.net/tianmohust/article/details/7689414
# return Html page class MyHttpBaseHandler(BaseHTTPRequestHandler): def do_GET(self): print(self.path) enc = "UTF-8" encoded = ''.join(self.path).encode(enc) f = io.BytesIO() f.write(encoded) f.seek(0) self.send_response(200) self.send_header("Content-type", "text/html; charset=%s" % enc) self.send_header("Content-Length", str(len(encoded))) self.end_headers() shutil.copyfileobj(f, self.wfile) # This method serves the 'POST' request type, only allowed for CGI scripts. def do_POST(self): pass # return static -m eg:python -m http.server 8080 class MyHttpSimpleHandler(SimpleHTTPRequestHandler): pass
httpd = HTTPServer((self.addressIP, self.port), MyHttpSimpleHandler)
print("Server started on " + self.addressIP + ",port " + str(self.port) + ".....")
httpd.serve_forever()
三. 绘制UI,生成二维码
之前写过
Pyqt+QRcode 生成 识别 二维码
直接参考生成二维码
绘制UI
class MainWidgetUI(QDialog): def __init__(self, parent=None): super(MainWidgetUI, self).__init__(parent) self.setFixedSize(640, 480) # PyQT禁止调整窗口大小 self.setWindowTitle('Python 创建本地服务器环境生成二维码') self.setWindowIcon(QtGui.QIcon("favicon.ico")) # Main布局 main_layout = QVBoxLayout() self.methodtype = QComboBox() self.methodTopLayout = QHBoxLayout() self.methodtype.addItem('文件', QVariant(1)) # 使用 QVariant保存Key self.methodtype.addItem('地址', QVariant(2)) self.methodtype.setFixedWidth(90) # 设置固定不变的宽度为90px self.pushButton = QPushButton("选择文件") self.Url = QLineEdit() self.Url.hide() self.methodTopLayout.addWidget(self.methodtype) # 添加一个挂件 self.methodTopLayout.addWidget(self.Url) # 添加一个挂件 self.methodTopLayout.addSpacing(10) # 添加一个10px的空间距离 且不带弹性 self.methodTopLayout.addWidget(self.pushButton) self.qrcodeGroup = QVBoxLayout() self.groupBox = QGroupBox("二维码") self.QrLabel = QLabel(self.groupBox) self.qrcodeGroup.addWidget(self.groupBox) main_layout.addLayout(self.methodTopLayout) # 添加一个布局 main_layout.addLayout(self.qrcodeGroup) self.setLayout(main_layout) self.QrLabel.setGeometry(QRect(30, 30, 540, 380)) # 设置qrLabel 的图形位置 # self.QrLabel.setScaledContents(True) # 按比例缩放二维码显示内容 self.pushButton.clicked.connect(self.FileOperator) # 点击按钮 self.methodtype.currentIndexChanged.connect(self.comboxchange) # 下拉框改变时候事件 self.Url.textChanged.connect(self.comboxchange) # 当地址文本框内容改变时触发
生成二维码
# 获取服务器(地址) 生成二维码 def ShowQrCode(self, strings): if not strings: # 参数为空,pixmap为空 self.QrLabel.setPixmap(QtGui.QPixmap("")) else: qr = qrcode.QRCode(version=None, error_correction=qrcode.constants.ERROR_CORRECT_L, box_size=10, border=2, ) qr.add_data(strings) qr.make(fit=True) img = qr.make_image() qraddr=tempDir+'qr.png' print(qraddr) img.save(qraddr)
很简单,通过选择文件获取路径,将文件copy到当前目录,将当前目录设置为http.server服务目录,二维码则为服务地址+文件名
四.注意的事项
1. 获取当前局域网IP
addressIP = socket.gethostbyname_ex(socket.gethostname())[-1][0] # 获取局域网IP
2.获取windows临时目录
tempDir = tempfile.gettempdir() + '/' # 获取临时temp目录
3. copy文件
copy文件有两种,一种为Python自带,另一种为Pyqt中方法
shutil.copy(filePath, "./") # 文件=>目录
QFile.copy(filePath, sys.path[0]+"\"+file) # QFile.copy 必须文件对=>文件
4. 使用线程开启服务
直接在UI中开启httpd.serve_forever() 会导致UI堵塞,所以使用QtCore.QThread 线程开启http服务
5.动态调整UI大小
参数不同导致生成的二维码大小不同,所以要动态修改UI大小以便将整个二维码显示全
qrsize = Image.open(qraddr).size if qrsize[0] > 400: # 二维码的像素值大于400的时候动态修改窗体的大小 dsize = qrsize[0] // 2 # 取整数部分 self.setFixedSize(640 + dsize, 480 + dsize) self.QrLabel.setGeometry(QRect(30, 30, 540 + dsize, 380 + dsize)) else: self.setFixedSize(640, 480) self.QrLabel.setGeometry(QRect(30, 30, 540, 380)) self.QrLabel.setPixmap(QtGui.QPixmap(qraddr))
6. 通过判断文件的md5对比文件是否为最新版文件
if filePath: file = filePath.split('/')[-1] isfExist = os.path.exists(file) if not isfExist: # 不存在文件 shutil.copy(filePath, "./") # 文件对=>目录 else: # 已经存在文件,对比文件md5 判断是否为最新文件 md5FilePath = self.getFileMD5(filePath) md5File = self.getFileMD5(file) if md5File != md5FilePath: shutil.copy(filePath, "./") # 获取文件的MD5值,适用于小文件 def getFileMD5(self, filepath): f = open(filepath, 'rb') md5obj = hashlib.md5() md5obj.update(f.read()) hash = md5obj.hexdigest() f.close() return str(hash).upper()
7.生成debug.log 日志
def delog(string='--'): debugFile=open("debog.txt",'a',1,'utf-8') debugFile.writelines(string+' ') debugFile.close()
五.完整代码
# -*- coding: UTF8 -*- import io, shutil, sys, os from http.server import HTTPServer, BaseHTTPRequestHandler, SimpleHTTPRequestHandler from PyQt5.QtWidgets import * from PyQt5.QtCore import * from PyQt5 import QtGui import qrcode from PIL import Image import socket import hashlib import tempfile port = 8080 # 默认端口 addressIP = socket.gethostbyname_ex(socket.gethostname())[-1][0] # 获取局域网IP tempDir = tempfile.gettempdir() + '/' # 获取临时temp目录 class MainWidgetUI(QDialog): def __init__(self, parent=None): super(MainWidgetUI, self).__init__(parent) self.setFixedSize(640, 480) # PyQT禁止调整窗口大小 self.setWindowTitle('Python 创建本地服务器环境生成二维码') self.setWindowIcon(QtGui.QIcon("favicon.ico")) # Main布局 main_layout = QVBoxLayout() self.methodtype = QComboBox() self.methodTopLayout = QHBoxLayout() self.methodtype.addItem('文件', QVariant(1)) # 使用 QVariant保存Key self.methodtype.addItem('地址', QVariant(2)) self.methodtype.setFixedWidth(90) # 设置固定不变的宽度为90px self.pushButton = QPushButton("选择文件") self.Url = QLineEdit() self.Url.hide() self.methodTopLayout.addWidget(self.methodtype) # 添加一个挂件 self.methodTopLayout.addWidget(self.Url) # 添加一个挂件 self.methodTopLayout.addSpacing(10) # 添加一个10px的空间距离 且不带弹性 self.methodTopLayout.addWidget(self.pushButton) self.qrcodeGroup = QVBoxLayout() self.groupBox = QGroupBox("二维码") self.QrLabel = QLabel(self.groupBox) self.qrcodeGroup.addWidget(self.groupBox) main_layout.addLayout(self.methodTopLayout) # 添加一个布局 main_layout.addLayout(self.qrcodeGroup) self.setLayout(main_layout) self.QrLabel.setGeometry(QRect(30, 30, 540, 380)) # 设置qrLabel 的图形位置 # self.QrLabel.setScaledContents(True) # 按比例缩放二维码显示内容 self.pushButton.clicked.connect(self.FileOperator) # 点击按钮 self.methodtype.currentIndexChanged.connect(self.comboxchange) # 下拉框改变时候事件 self.Url.textChanged.connect(self.comboxchange) # 当地址文本框内容改变时触发 # 文件操作 def FileOperator(self): filePath = self.selectFile() if filePath: file = filePath.split('/')[-1] isfExist = os.path.exists(file) if not isfExist: # 不存在文件 shutil.copy(filePath, "./") # 文件对=>目录 # delog(a) # 打包exe调试日志log # QFile.copy(filePath, sys.path[0]+"\"+file) QFile.copy 必须文件对=>文件 else: # 已经存在文件,对比文件md5 判断是否为最新文件 md5FilePath = self.getFileMD5(filePath) md5File = self.getFileMD5(file) if md5File != md5FilePath: shutil.copy(filePath, "./") # 拼接二维码参数 address = "http://" + addressIP + ':' + str(port) + '/' + file self.ShowQrCode(address) # 显示二维码 print('生成qrcord') self.Theading = TheadingPost((addressIP, port)) # 开进程打开服务-直接启动http.service 会导致UI进程无响应 self.Theading.start() # 线程开始 # 获取文件的MD5值,适用于小文件 def getFileMD5(self, filepath): f = open(filepath, 'rb') md5obj = hashlib.md5() md5obj.update(f.read()) hash = md5obj.hexdigest() f.close() return str(hash).upper() # 选择文件 def selectFile(self): # getOpenFileName 只能选择一个 getOpenFileNames 可多个选择 files = QFileDialog.getOpenFileName(self, "请选择播放文件", '', "*.*") if files[0] == '': QMessageBox.warning(self, u'错误提示!', "请选择文件", QMessageBox.Yes) else: return files[0] # 下拉框选择 def comboxchange(self): currentIndex = self.methodtype.currentIndex() # currentIndex 索引是从0开始自增 key = self.methodtype.itemData(currentIndex) # QVariant 保存的 key # self.methodtype.currentText() # 文本 if key == 1: # 文件 self.pushButton.show() self.Url.hide() self.ShowQrCode("") elif key == 2: # 地址 self.pushButton.hide() self.Url.show() url = self.Url.text() self.ShowQrCode(url) # 获取服务器(地址) 生成二维码12 def ShowQrCode(self, strings): if not strings: # 参数为空,pixmap为空 self.QrLabel.setPixmap(QtGui.QPixmap("")) else: qr = qrcode.QRCode(version=None, error_correction=qrcode.constants.ERROR_CORRECT_L, box_size=10, border=2, ) qr.add_data(strings) qr.make(fit=True) img = qr.make_image() qraddr=tempDir+'qr.png' print(qraddr) img.save(qraddr) qrsize = Image.open(qraddr).size if qrsize[0] > 400: # 二维码的像素值大于400的时候动态修改窗体的大小 dsize = qrsize[0] // 2 # 取整数部分 self.setFixedSize(640 + dsize, 480 + dsize) self.QrLabel.setGeometry(QRect(30, 30, 540 + dsize, 380 + dsize)) else: self.setFixedSize(640, 480) self.QrLabel.setGeometry(QRect(30, 30, 540, 380)) self.QrLabel.setPixmap(QtGui.QPixmap(qraddr)) # return Html page class MyHttpBaseHandler(BaseHTTPRequestHandler): def do_GET(self): print(self.path) enc = "UTF-8" encoded = ''.join(self.path).encode(enc) f = io.BytesIO() f.write(encoded) f.seek(0) self.send_response(200) self.send_header("Content-type", "text/html; charset=%s" % enc) self.send_header("Content-Length", str(len(encoded))) self.end_headers() shutil.copyfileobj(f, self.wfile) # This method serves the 'POST' request type, only allowed for CGI scripts. def do_POST(self): pass # return static -m eg:python -m http.server 8080 class MyHttpSimpleHandler(SimpleHTTPRequestHandler): pass # 启动服务 class TheadingPost(QThread): def __init__(self, list): super(TheadingPost, self).__init__() self.addressIP = list[0] self.port = list[1] def run(self): httpd = HTTPServer((self.addressIP, self.port), MyHttpSimpleHandler) print("Server started on " + self.addressIP + ",port " + str(self.port) + ".....") httpd.serve_forever() def delog(string='--'): debugFile=open("debog.txt",'a',1,'utf-8') debugFile.writelines(string+' ') debugFile.close() if __name__ == "__main__": app = QApplication(sys.argv) main_widget = MainWidgetUI() main_widget.show() sys.exit(app.exec_())