Bottle UiKit Python实现的前后端交互代码实现

文章目录

  • 项目所在 GitHub 地址
  • 1、项目整体思路
  • 2、项目实现技术
  • 3、前端界面实现
  • 4、后端服务器框架 bottle 简单介绍
  • 5、python 代码实现
    • 5.1 main.py
    • 5.2 Callibration.py
  • 6、前后端交互的重点以及难点
    • 6.1、实现前后端交互之上传文件
    • 6.2、实现前后端交互之实现参数的传递
    • 6.3、实现前后端交互之前端点击按钮后端实现相应的操作
    • 6.4、实现前后端交互之实现静态文件的传递
    • 6.5、点击按钮实现图片的展示
  • 7 其他项目中用到的小程序
    • 7.1、使用python 进行文件的压缩以及文件拷贝到指定文件夹

项目所在 GitHub 地址

https://github.com/YIMEng-0/PythonSoftwareGISHomeWork.git

1、项目整体思路

在前端的网页中进行文件的上传,在后端对于前端上传的文件进行数据处理,返回处理结果。在这个开发小项目中,返回的处理结果是数据处理产生的中间文件以及后端数据经过处理得到的图片文件;

说明:

中间文件 文本文件
图片文件 png 文件

简单来讲,整个项目就是输入–>处理–> 输出的简单逻辑。

2、项目实现技术

整体技术 具体使用
前端语言 html css javascript
前端UI框架 uikit
后端服务器框架 bottle
后端编程语言 Python

3、前端界面实现

Bottle UiKit Python实现的前后端交互代码实现_第1张图片

4、后端服务器框架 bottle 简单介绍

轻量级服务器框架,国内的查询资料较少,谨慎使用

5、python 代码实现

5.1 main.py

import Calibration
# 读取文件
High = []
Mid = []
Low = []

# 前端读取到的文件可以保存在一个文件夹下面就好了,这样下面的代码就不用修改了

path = 'Spectral_Data/'
for i in range(1, 8):
    filename_high = path + 'Spectrum_' + str(i) + "_0_1.sps"
    filename_mid = path + 'Spectrum_' + str(i) + "_1.sps"
    filename_low = path + 'Spectrum_' + str(i) + "_1_1.sps"
    High.append(Calibration.import_file(filename_high))
    Mid.append(Calibration.import_file(filename_mid))
    Low.append(Calibration.import_file(filename_low))
diff_high = Calibration.cal_dif(High, 7)
diff_mid = Calibration.cal_dif(Mid, 7)
diff_low = Calibration.cal_dif(Low, 7)

# 开始拟合
# 计算多项式系数
my_M = 2
my_N = 3
my_lamda = 0
Polynomial_Coefficients = []  # 拟合的多项式系数
result = []  # 中等光强下,定标后的结果

# 计算结果的导出文件夹路径的设置
savepath = "out/"
for row in range(0, 6):
    temp_args = []
    temp_result = []
    temp_args1 = []
    temp_result1 = []
    args_dis = []
    result_dis = []
    for col in range(0, len(diff_low[0])):
        my_x_n = [diff_low[row][col], diff_mid[row][col], diff_high[row][col]]
        my_y_n = [Low[row + 1][col], Mid[row + 1][col], High[row + 1][col]]

        # 方法1Numpy自带的多项式拟合函数
        temp_args.append(Calibration.np.polyfit(my_y_n, my_x_n, 2))
        func = Calibration.np.poly1d(temp_args[col])
        temp_result.append(Mid[row+1][col] - func(Mid[row+1][col]))
        # print("numpy自带的Polyfit:", temp_args[col])
        # print("numpy自带的Polyld :", temp_result[col])

        # 方法2:自己写的多项式拟合函数
        temp_args1.append(Calibration.ployfit(my_M, my_N, my_y_n, my_x_n, my_lamda))
        temp_result1.append(Mid[row+1][col] - Calibration.ployval(temp_args[col], Mid[row + 1][col]))
        # print("自己写的:", temp_args1[col])
        # print("自己写的:", temp_result1[col])

        dis_args = [temp_args[col][0]-temp_args1[col][2], temp_args[col][1]-temp_args1[col][1], temp_args[col][2]-temp_args1[col][0]]
        args_dis.append(dis_args)
        result_dis.append(temp_result[col] - temp_result1[col])

    Polynomial_Coefficients.append(temp_args)   # 保存的多项式参数:a*x^2 + b*x + c中的a,b,c
    result.append(temp_result)                  # 保存的定标结果
    Calibration.export_file(Polynomial_Coefficients[row], savepath, "Light_" + str(row + 1) + ".conf")
print("计算结束!")

Calibration.draw_pic(High, "1-High Light Intensity", 1)
Calibration.draw_pic(Mid, "1-Middle Light Intensity", 2)
Calibration.draw_pic(Low, "1-Low Light Intensity", 3)
Calibration.draw_pic(diff_low, "2-Low Light Difference", 4)
Calibration.draw_pic(diff_mid, "2-Middle Light Difference", 5)
Calibration.draw_pic(diff_high, "2-High Light Difference", 6)
Calibration.draw_pic(result, "Middle Light Calibration Difference(Numpy - Myself)", 7)


# 图片可以保存在其他的路径中
# 后期进行代码的重构,使得保存图片可以保存在给定的问价夹路径中

5.2 Callibration.py

# 读取数据文件,计算定标参数,保存定标参数
# Yangkai,2021/11/19
import numpy as np
import matplotlib.pyplot as plt
import numpy.linalg


def cal_dif(matrix_data, light_number):
    """
    计算传入的二位列表,以第1列为标准,计算其它列到第1列的差值
    :param matrix_data: 浮点数,二位列表,每一列代表一根光纤的测量值
    :param light_number: 整数,光纤个数
    :return: 返回光纤个数-1的二维列表,其它光纤同波段下距第1根光纤的差值
    """
    difference = []
    try:
        for row1 in range(1, light_number):
            dis = []
            for col1 in range(0, len(matrix_data[0])):
                temp_dis = matrix_data[row1][col1] - matrix_data[0][col1]
                dis.append(temp_dis)
                # print("temp_dis:", temp_dis)
            difference.append(dis)
            # print(dis)
        # print(difference)
    except IndexError:
        print("Error: 索引超出列表范围!请检查传入列表的维度")
    finally:
        return difference


def import_file(filename):
    """
    读取sps光谱数据,忽略波段,只读取DN值
    :param filename: 文件名
    :return: 返回该文件DN值的一维列表
    """
    fo = open(filename, "r+")
    data = []
    try:
        print("当前正在读取:", fo.name)
        for row1 in fo.readlines()[26:]:  # 从26行开始读取,跳过文件头
            line = row1.strip()  # strip函数删掉每一行数字前面的空格
            data.append(float(line[13:]))  # 一个文件的数据放进一个列表
    except IOError:
        print("Error: 没有找到文件或读取失败")
    except IndexError:
        print("Error: 索引超出列表范围,请检查文件内容!")
    else:
        print("文件读取成功!")
    finally:
        fo.close()
        return data


def ployfit(m, n, x, y, lamda):
    """
    对输入的参数进行M次多项式的拟合,返回拟合的多项式系数
    :param m: 拟合的多项式阶数
    :param n: 输入拟合数据的个数
    :param x: 待拟合的x,即各光照强度
    :param y: 待拟合的y,即各光照强度下的差值Δ
    :param lamda: 正则项系数,默认为0,即无正则项
    :return: 多项式系数的列表
    """
    # print("\n输入参数为:M=%d, N=%d, x_n=(%.4f,%.4f,%.4f), t_n=(%.4f,%.4f,%.4f)" % (
    # M, N, x_n[0], x_n[1], x_n[2], t_n[0], t_n[1], t_n[2]))
    order = np.arange(m + 1)        # 生成一个从0-m的列表
    order = order[:, np.newaxis]    # 提取所有行,升维,变成m*1的二维矩阵
    e = np.tile(order, [1, n])      # 在行方向上复制1次,列方向上复制n次
    x_t = np.power(x, e)            # 对传进来的x,求矩阵
    X = np.transpose(x_t)
    a = np.matmul(x_t, X) + lamda * np.identity(m + 1)  # X.T * X
    b = np.matmul(x_t, y)   # X.T * T
    # numpy.linalg中的函数solve可以求解形如 Ax = b 的线性方程组
    w = numpy.linalg.solve(a, b)
    # w = np.matmul(np.linalg.inv(a), b)  # aW = b => (X.T * X) * W = X.T * T
    return w


def ployval(args, x):
    """
    根据多项式系数,计算x处的y值
    :param args: 多项式系数,一维列表
    :param x: 待求点的x
    :return:  待求点的y
    """
    y = 0.0
    try:
        if len(args) == 0:
            print("输入的阶数为0!无法拟合!")
        else:
            # y1 = args[0] + args[1]*x + args[2]*x*x
            for rank in range(0, len(args)):
                y += args[rank] * (x ** (len(args)-rank-1))     # y = (w_0 * x^2) + (w_1 * x^1) + (w_2 * x^0)
    except IndexError:
        print("Error: 索引超出列表范围!")
    finally:
        return y


# 画图
def draw_pic(matrix_data, title, pic_num):
    """
    画图函数
    :param matrix_data: 传入的二维矩阵
    :param title: 图标题和保存的文件名,用同一个字符串
    :param pic_num: 是第几副图
    """
    font = {'family': 'Times New Roman', 'size': 30, 'weight': 'bold'}
    n = list(range(350, 1021))
    plt.figure(num=pic_num, figsize=(15, 10))
    for i in range(0, len(matrix_data)):
        plt.plot(n, matrix_data[i], linewidth=3, label='NO.'+str(i+2))
    plt.tick_params(labelsize=15)
    plt.xticks(np.linspace(350, 1020, 10))
    plt.xlim(340, 1030)
    plt.grid(linestyle=":", color="k")
    plt.xlabel('Wavelength(nm)', font)
    plt.ylabel('Intensity(DN)', font)
    plt.title(title, font)
    plt.legend(loc='upper right', frameon=True, fontsize=25)
    plt.savefig("./" + title + ".png", dpi=600)
    plt.show()
    print("绘制成功!图片已保存")


# 输出到文件
def export_file(matrix_data, filepath, filename):
    """
    输出到文件
    :param matrix_data: 要保存的数据
    :param filepath:    文件保存路径
    :param filename:    希望保存的文件名
    """
    try:
        with open(filepath+filename, 'w+') as fid:
            for row in range(0, len(matrix_data)):
                fid.write("%20.15f %20.15f %20.15f\n" % (matrix_data[row][0], matrix_data[row][1], matrix_data[row][2]))
    except IOError:
        print("ERROR: 输出失败!请检查路径、文件名以及数据!")
    else:
        print("文件保存成功!")
    finally:
        print("输出函数调用结束")


6、前后端交互的重点以及难点

6.1、实现前后端交互之上传文件

前端代码:

            <p class="uk-heading-line uk-text-center J-block-title"><span>文件上传span>p>
            <div class="js-upload uk-placeholder uk-text-center">
                <span uk-icon="icon: cloud-upload">span>
                <span class="uk-text-middle">拖动文件 orspan>
                <div uk-form-custom>
                    <input type="file" multiple='multiple'>
                    <span class="uk-link">点击选择标准光谱文件span>
                div>
                <progress id="js-progressbar" class="uk-progress" value="0" max="100" hidden>progress>
            div>

            <div class="js-upload uk-placeholder uk-text-center">
                <span uk-icon="icon: cloud-upload">span>
                <span class="uk-text-middle">拖动文件 orspan>
                <div uk-form-custom>
                    <input type="file" multiple='multiple'>
                    <span class="uk-link">点击选择观测光谱文件span>
                div>
                <progress id="js-progressbar1" class="uk-progress" value="0" max="100" hidden>progress>
            div>
        <script>
            var bar = document.getElementById('js-progressbar');
            UIkit.upload('.js-upload', {

                url: '/1',
                multiple: true,
                name: 'Spectral_Data',

                beforeSend: function () {
                    console.log('beforeSend', arguments);
                },
                beforeAll: function () {
                    console.log('beforeAll', arguments);
                },
                load: function () {
                    console.log('load', arguments);
                },
                error: function () {
                    console.log('error', arguments);
                },
                complete: function () {
                    console.log('complete', arguments);
                },

                loadStart: function (e) {
                    console.log('loadStart', arguments);

                    bar.removeAttribute('hidden');
                    bar.max = e.total;
                    bar.value = e.loaded;
                },

                progress: function (e) {
                    console.log('progress', arguments);

                    bar.max = e.total;
                    bar.value = e.loaded;
                },

                loadEnd: function (e) {
                    console.log('loadEnd', arguments);

                    bar.max = e.total;
                    bar.value = e.loaded;
                },

                completeAll: function () {
                    console.log('completeAll', arguments);

                    setTimeout(function () {
                        bar.setAttribute('hidden', 'hidden');
                    }, 1000);

                    alert('Upload Completed');
                }

            });
        </script>

后端代码:

# 这个是和前端的上传文件控件进行的绑定,可以设置文件保存的路径
@route('/1', method='POST')
def backData():
    upload = request.files.get('Spectral_Data')
    upload.save(r'./Spectral_Data')
    return 'ok'

注意有一个 url 需要配置,这个必须和后端进行绑定
在后端服务器中 get(‘Spectral_Data’) 得到到前端的
name :Spectral_Data,这样子前后端就能实现通讯了,也就是可以进行上传文件了;
Bottle UiKit Python实现的前后端交互代码实现_第2张图片

6.2、实现前后端交互之实现参数的传递

前端代码:

            <div class="form-group" uk-margin style="text-align: center;">
                <form class="uk-form-horizontal" action="/obsconfig" method="post">
                    <div class="uk-margin">
                        <label class="uk-form-label" for="form_ftp1">定标轮次label>
                        <div class="uk-inline J-modity-input">
                            <span class="uk-form-icon uk-form-icon-flip">span>
                            <input class="uk-input" type="text" id="DingBiaoLunCi" name="DingBiao"
                            >
                        div>
                    div>

                    <div class="uk-margin">
                        <label class="uk-form-label" for="form_ftp2">光纤数目label>
                        <div class="uk-inline J-modity-input">
                            <span class="uk-form-icon uk-form-icon-flip">span>
                            <input class="uk-input" type="text" id="form_ftp_usrname" name="GuangQian"
                            >
                        div>
                    div>


                    <p uk-margin style="margin: 45px 0 30px;">
                        <button class="uk-button uk-button-primary uk-width-1-1 uk-margin-small-bottom" type="submit">参数输入完毕点击确定
                        button>
                    p>
                form>
            div>

后端代码:

# 进行配置参数的获取
@route('/obsconfig', method='POST')
def tem():
    # 使用数组或者某种存储容器存储表格中传递过来的相关参数
    # form = 'html' 里面的 name 进行获取
    forms = {}
    forms['DingBiao'] = request.forms.get('DingBiao')
    forms['GuangQian'] = request.forms.get('GuangQian')
    print(forms)

    return template('templates/光谱仪定标.html')

需要注意:
下图中在前后端中这两个位置的参数必须是一致的,这样子后端才能接受到前端传递的参数,相当于快递员快递,总得知道自己从什么地方出发,到什么地方去,计算机就是机械的进行执行命令;
Bottle UiKit Python实现的前后端交互代码实现_第3张图片

6.3、实现前后端交互之前端点击按钮后端实现相应的操作

前端代码:

            <p class="uk-heading-line uk-text-center J-block-title"><span>相关操作span>p>
            
            <div class="js-upload uk-placeholder uk-text-center">
                <p uk-margin style="text-align: center;">
                    <button class="uk-button uk-button-default" id='getCalibration'>光谱仪定标button>
                <div class="uk-inline" style="display:inline-block">
                    <button class="uk-button uk-button-default" type="button">导出button>
                    <div uk-dropdown>
                        <p><a href="http://localhost:8088/result/out.zip" download="result">导出定标参数a>p>
                    div>
                div>
                p>
            div>
        <script>
            var btnFLD = document.getElementById("getCalibration");
            btnFLD.onclick = function () {
                var httpRequest = new XMLHttpRequest();//第一步:创建需要的对象
                httpRequest.open('POST', 'http://localhost:8088/getCalibration', true); //第二步:打开连接
                httpRequest.setRequestHeader("Content-type", "application/x-www-form-urlencoded");//设置请求头 注:post方式必须设置请求头(在建立连接后设置请求头)
                httpRequest.send();//发送请求 将情头体写在send中
            }

            const commodity_image = document.createElement("img");
            let imageURL = "/images?skuid=" + r[j]["skuid"];
            commodity_image.src = imageURL;
        </script>

后端代码:

# 这个是和前端的按钮进行的绑定,点击按钮调用后端的 main.py 的脚本文件,进行图片的生成
@route('/getCalibration', method='POST')
def getCalibration():
    os.system("python main.py")

需要注意:
前后端的这两个参数需要相同,实现前后端的绑定,实现动态交互
Bottle UiKit Python实现的前后端交互代码实现_第4张图片

6.4、实现前后端交互之实现静态文件的传递

前后端进行的图片展示传递的思路:
在服务器端会产生一系列的图片文件,每个文件会有自己的url;
在 标签中 的 src 属性中填写 图片的 url 即可;

6.5、点击按钮实现图片的展示

前端代码:

            <p class="uk-heading-line uk-text-center J-block-title"><span>结果span>p>
            <div class="js-upload uk-placeholder uk-text-center">
                
                <img id="myImage" src="https://bm.cugb.edu.cn/vis/upload/resources/image/2017/05/25/28332.jpg"/>
                <button onclick="handle()" class="uk-button uk-button-default" type="button">显示图片1button>

                <img id="myImage" src=""/>
                <button onclick="handle1()" class="uk-button uk-button-default" type="button">显示图片2button>

                <img id="myImage" src=""/>
                <button onclick="handle2()" class="uk-button uk-button-default" type="button">显示图片3button>

                <img id="myImage" src=""/>
                <button onclick="handle3()" class="uk-button uk-button-default" type="button">显示图片4button>

                <img id="myImage" src=""/>
                <button onclick="handle4()" class="uk-button uk-button-default" type="button">显示图片5button>

                <img id="myImage" src=""/>
                <button onclick="handle5()" class="uk-button uk-button-default" type="button">显示图片6button>

                <img id="myImage" src=""/>
                <button onclick="handle6()" class="uk-button uk-button-default" type="button">显示图片7button>
            div>
        <script>
            function handle() {
                document.getElementById('myImage').src = 'https://wx2.sinaimg.cn/mw2000/0071KLW1ly1gx0citc73pj36y04moe84.jpg';
            }

            function handle1() {
                document.getElementById('myImage').src = 'https://wx2.sinaimg.cn/mw2000/0071KLW1ly1gx0civjj8pj36y04mo1l0.jpg';
            }

            function handle2() {
                document.getElementById('myImage').src = 'https://wx3.sinaimg.cn/mw2000/0071KLW1ly1gx0cixoxrnj36y04mob2c.jpg';
            }

            function handle3() {
                document.getElementById('myImage').src = 'https://wx1.sinaimg.cn/mw2000/0071KLW1ly1gx0ciztf20j36y04mohdw.jpg';
            }

            function handle4() {
                document.getElementById('myImage').src = 'https://wx3.sinaimg.cn/mw2000/0071KLW1ly1gx0cj1xf29j36y04mohdw.jpg';
            }

            function handle5() {
                document.getElementById('myImage').src = 'https://wx1.sinaimg.cn/mw2000/0071KLW1ly1gx0cj3v8g6j36y04moqv6.jpg';
            }

            function handle6() {
                document.getElementById('myImage').src = 'https://wx4.sinaimg.cn/mw2000/0071KLW1ly1gx0cj5vcijj36y04mohdw.jpg';
            }

        </script>

注意:实现点击按钮进行图片的更换,处理函数的名字必须是一致的,这样子,才能把 html 与 javascript 进行绑定,实现相关的功能;
Bottle UiKit Python实现的前后端交互代码实现_第5张图片

7 其他项目中用到的小程序

7.1、使用python 进行文件的压缩以及文件拷贝到指定文件夹

import zipfile
import os
import shutil
# 将参数计算成功之后,将计算的结果使用压缩包的形式进行发送到前端

startdir = "./out"  #要压缩的文件夹路径
file_news = startdir +'.zip' # 压缩后文件夹的名字
z = zipfile.ZipFile(file_news,'w',zipfile.ZIP_DEFLATED) #参数一:文件夹名
for dirpath, dirnames, filenames in os.walk(startdir):
    fpath = dirpath.replace(startdir,'') #这一句很重要,不replace的话,就从根目录开始复制
    fpath = fpath and fpath + os.sep or ''#这句话理解我也点郁闷,实现当前文件夹以及包含的所有文件的压缩
    for filename in filenames:
        z.write(os.path.join(dirpath, filename),fpath+filename)
        print ('压缩成功')
z.close()

# 移动文件夹示例
# 将参数的文件移动到 result 文件夹中使用前端的按钮可以进行文件的下载
# 进行文件移动的代码的重写,向前端返回一个计算结束后面的压缩包
if not os.path.exists("./static/result/out.zip"):
    shutil.move("./out.zip", "./static/result/")

你可能感兴趣的:(Python,python,交互,前端)