Python自动生成代码(二)- 基于MVP架构的C/C++ 代码生成器实例

  • 背景
  • 代码解析 - BuildCode_MVP Class
  • 代码解析 - Generatexxx()
  • 代码解析 - ReplaceStrinFile()
  • 代码解析 -Tkinter 图形界面
  • 完整代码
  • 其他好玩的python脚本
  • 博主热门文章推荐:

背景

上篇博客 Python自动生成代码 - 通过tkinter图形化操作生成代码框架 简单介绍了下自动生成代码(代码生成器)及其实现,下面继续通过一个应用案例来探讨:

在写代码过程中,如果有大量频繁重复性的编码、修改操作,或者可以Reuse的各类代码,可以通过Python写一个脚本,自动生成这类代码,就不用每次手写、或者copy了,提高效率。

这里以基于MVP(Model View Presenter)架构 的C/C++代码 作为实例:

在基于MVP架构实际coding中,因为做GUI开发时有非常多的页面,而每一个页面都需要 M/V/P层的代码,这些代码都是一个架构,只是代码中的一些类、变量、页面、头文件等命名不同而已,所以通过代码生成器来一键生成。。。这样做新的界面,只要生成相应代码,再往里面写应用即可

这样就非常方便,对我来说开发上也提高了很多效率,这里分享一下,代码中的模板因为涉及公司已经去掉,如果使用根据自己的代码需要,改变code_Template 即可。

应用场景举例:比如新建固定的代码框架、添加一些既定的软件逻辑,通讯协议、消息模板等等,当需要根据同样架构 再编写一套代码时,或者一个Function时,每次使通过脚本一键生成代码,就不需要每次都写一遍了,同时可以把相关软件逻辑放进去,也能避免出错。

惯例先上图:

部分生成代码:(仅Demo用,在代码中对codeTemplate中根据实际待Generate代码替换)

代码解析 - BuildCode_MVP Class

BuildCode_MVP Class 是通过Generate 创建相关cpp/hpp代码文件及目录,并放在同一目录下。



class BuildCode_MVP:
    ' Generate code class of Model/View/Presenter Code '

    def __init__(self, KeyWord = 'TestInputScreenName'):
        self.CmdKeyWord = KeyWord

    def Generate(self):
        Chpwd = self.CmdKeyWord + 'Screen'
        mkdir(Chpwd)
        os.chdir(Chpwd)

        self.GeneratePresenterCpp()
        self.GeneratePresenterHpp()
        self.GenerateViewCpp()
        self.GenerateViewHpp()
        self.GenerateIViewHpp()

        os.chdir('../')

    TemplateScreenName = "YourCodeName"
    TemplateScreenName_View = "YourCodeName"

代码解析 - Generatexxx()

Generatexxx 既创建相关cpp/hpp代码文件,例如下面的GeneratePresenterCpp(),主要是将codeTemplate通过ReplaceStrinFile 替换 template里的字符,将代码模板里的methods、变量、头文件等名字 转换为 输入的字符self.CmdKeyWord作为替换名 。

这里删减了很多和代码逻辑相关的,具体可以根据需要添加 替换、增加code 等功能

    def GeneratePresenterCpp(self):
        fileName_cpp = self.CmdKeyWord +'Presenter.cpp'

        mycode = []  

        codeTemplate = ''' 这里是需要替换的Generator代码模板
//Generated By Python3 Script          
//#include <iostream>
/***************************************************************************//**
@brief
*******************************************************************************/
        '''
        mycode.append(codeTemplate)
        WritetoFile(fileName_cpp,mycode)

        print('Presenter: '+ fileName_cpp + ' Generate OK!')

        #Replace Template str
        ReplaceStrinFile(fileName_cpp, self.TemplateScreenName, self.CmdKeyWord)
        ReplaceStrinFile(fileName_cpp, self.TemplateScreenName_View, self.CmdKeyWord.lower())

        return(mycode)

代码解析 - ReplaceStrinFile()

ReplaceStrinFile就是遍历文件内容,找到要替换的str 并且通过re.sub执行替换操作

def ReplaceStrinFile(FileName, OldStr, NewStr):
    file = open(FileName, 'r')
    alllines = file.readlines()
    file.close()
    file = open(FileName, 'w+')
    for eachline in alllines:
        a = re.sub(OldStr, NewStr, eachline)
        file.writelines(a)
    file.close()

代码解析 -Tkinter 图形界面

界面很简单,主要通过CommandInput输入框, Btn 和text显示。

这个UI代码结构也是我之前其他工具沿用的。。


#Tkinter code below
root = Tk()
root.geometry('1000x600')
root.title('MVP Code Generator Tools')
root.config(bg='#f0ffff')

#Lable

Lb_CommandInput = Label(root,text='输入 MVP Screen Name:',\
                      # bg='#d3fbfb',\
                       fg='red',\
                       font=('华文新魏',10),\
                       width=28,\
                       height=2,\
                       relief=RIDGE)

Lb_CommandInput.place(relx=0.125, rely=0.237, relwidth=0.15, relheight=0.05)

Lb_CommandInput = Label(root,text='输入 Signal Name:',\
                      # bg='#d3fbfb',\
                       fg='red',\
                       font=('华文新魏',10),\
                       width=28,\
                       height=2,\
                       relief=RIDGE)

Lb_CommandInput.place(relx=0.125, rely=0.537, relwidth=0.15, relheight=0.05)

intro = Label(root,text='请在下方输入MVP要生成的ScreenName,然后点击Generate',\
                       bg='#d3fbfb',\
                       fg='red',\
                       font=('华文新魏',14),\
                       width=20,\
                       height=2,\
                       relief=RIDGE)

intro.place(relx=0.1, rely=0.1, relwidth=0.8, relheight=0.1)


ContactLable = Label(root, text='联系人:HowardXue H191748',\
                      # bg='#d3fbfb',\
                       fg='red',\
                       font=('',12),\
                       width=20,\
                       height=2,\
                       relief=RIDGE)
ContactLable.place(relx=0.7, rely=0.03, relwidth=0.25, relheight=0.05)



#Output
txt = Text(root, font = ('',10))
txt.place(relx=0.3, rely=0.2, relwidth=0.6, relheight=0.625)

#lable

#Entry
Et_CommandInput = Entry(root, bd =5, relief=GROOVE)

Et_CommandInput.place(relx=0.125, rely=0.3, relwidth=0.15, relheight=0.05)


Et_SignalCommandInput = Entry(root, bd =5, relief=GROOVE)

Et_SignalCommandInput.place(relx=0.125, rely=0.6, relwidth=0.15, relheight=0.05)

#Button

bt_bin2json = Button(root, text='MVP Code Generate', command=OnCommandInput, fg ='blue')
bt_bin2json.place(relx=0.1, rely=0.38, relwidth=0.2, relheight=0.1)


signalButton = Button(root, text='Signal PVID Code Generate', command=OnSignalButton, fg ='blue')
signalButton.place(relx=0.1, rely=0.68, relwidth=0.2, relheight=0.1)

# bt_clear = Button(root, text='Clear', command=clearSendContent, fg ='blue')
# bt_clear.place(relx=0.15, rely=0.825, relwidth=0.2, relheight=0.1)

bt_clear_rcv = Button(root, text='Clear', command=clearRcvContent, fg ='blue')
bt_clear_rcv.place(relx=0.45, rely=0.825, relwidth=0.2, relheight=0.1)

#comboxlist.pack()

root.mainloop()

完整代码

MVPCodeBuilder_Blog.py

#conding=utf-8
import sys
import os
import re

def mkdir(path):
    import os
    path = path.strip()
    path = path.rstrip("\\")
    isExists = os.path.exists(path)
    if not isExists:
        os.makedirs(path)
        print(path + ' Create Success')
        return True
    else:
        print(path + ' Folder already exist')
        return False
        
def ReplaceStrinFile(FileName, OldStr, NewStr):
    file = open(FileName, 'r')
    alllines = file.readlines()
    file.close()
    file = open(FileName, 'w+')
    for eachline in alllines:
        a = re.sub(OldStr, NewStr, eachline)
        file.writelines(a)
    file.close()
    
def WritetoFile(FileName,Data):
    with open(FileName,'w') as record:
        strr = "\n"
        content = strr.join(Data)
        record.write(content+'\n')
        
class BuildCode_MVP:
    ' Generate code class of Model/View/Presenter Code '

    def __init__(self, KeyWord = 'TestInputScreenName'):
        self.CmdKeyWord = KeyWord

    def Generate(self):
        Chpwd = self.CmdKeyWord + 'Screen'
        mkdir(Chpwd)
        os.chdir(Chpwd)

        self.GeneratePresenterCpp()
        self.GeneratePresenterHpp()
        self.GenerateViewCpp()
        self.GenerateViewHpp()
        self.GenerateIViewHpp()

        os.chdir('../')

    TemplateScreenName = "YourCode"
    TemplateScreenName_View = "YourCode"

    def GetScreenIDEnum(self):
        return 'SCREEN_'+self.CmdKeyWord.upper()
    def GetIncludeHeader_Presenter(self):
        return '#include "'+self.CmdKeyWord+'Screen/'+self.CmdKeyWord+'Presenter.hpp"'

    def GetIncludeHeader_View(self):
        return '#include "' + self.CmdKeyWord + 'Screen/' + self.CmdKeyWord + 'View.hpp"'

    def GetWrapper_BackBtnClicked(self):
        return self.GetScreenIDEnum() + self.CmdKeyWord;

    def GeneratePresenterCpp(self):
        fileName_cpp = self.CmdKeyWord +'Presenter.cpp'

        mycode = []  

        codeTemplate = ''' 这里是需要替换的Generator代码模板
//Generated By Python3 Script          
//#include <iostream>
/***************************************************************************//**
@brief
*******************************************************************************/
        '''
        mycode.append(codeTemplate)
        WritetoFile(fileName_cpp,mycode)

        print('Presenter: '+ fileName_cpp + ' Generate OK!')

        #Replace Template str
        ReplaceStrinFile(fileName_cpp, self.TemplateScreenName, self.CmdKeyWord)
        ReplaceStrinFile(fileName_cpp, self.TemplateScreenName_View, self.CmdKeyWord.lower())

        return(mycode)

############### HPP ####################

    def GeneratePresenterHpp(self):
        fileName_hpp = self.CmdKeyWord + 'Presenter.hpp'

        mycode = []

        mycode.append('\n//Generated By Python3 Script ')
        mycode.append('#ifndef _'+ self.CmdKeyWord.upper() +'_PRESENTER_H_ ')
        mycode.append('#define _'+ self.CmdKeyWord.upper() + '_PRESENTER_H_ ')
        codeTemplate = ''' 这里是需要替换的Generator代码模板


#endif

        '''
        mycode.append(codeTemplate)
        WritetoFile(fileName_hpp, mycode)

        print('Presenter: ' + fileName_hpp + ' Generate OK!')
        # Replace Template str
        ReplaceStrinFile(fileName_hpp, self.TemplateScreenName, self.CmdKeyWord)
        ReplaceStrinFile(fileName_hpp, self.TemplateScreenName_View, self.CmdKeyWord.lower())
        return (mycode)


    def GenerateViewCpp(self):
        fileName_cpp = self.CmdKeyWord + 'View.cpp'

        mycode = []

        codeTemplate = ''' 这里是需要替换的Generator代码模板
//Generated By Python3 Script          

#ifdef __cplusplus
  extern "C"
  {
#endif
        '''
        mycode.append(codeTemplate)

        WritetoFile(fileName_cpp, mycode)

        print('View: ' + fileName_cpp + ' Generate OK!')

        # Replace Template str
        ReplaceStrinFile(fileName_cpp, self.TemplateScreenName, self.CmdKeyWord)
        ReplaceStrinFile(fileName_cpp, self.TemplateScreenName_View, self.CmdKeyWord.lower())
        return (mycode)

    ############### HPP ####################


    def GenerateViewHpp(self):
        fileName_hpp = self.CmdKeyWord + 'View.hpp'

        mycode = []

        mycode.append('\n//Generated By Python3 Script ')
        mycode.append('#ifndef _' + self.CmdKeyWord.upper() + '_VIEW_H_ ')
        mycode.append('#define _' + self.CmdKeyWord.upper() + '_VIEW_H_ ')
        codeTemplate = ''' 这里是需要替换的Generator代码模板

#endif 

        '''
        mycode.append(codeTemplate)
        WritetoFile(fileName_hpp, mycode)

        print('View: ' + fileName_hpp + ' Generate OK!')
        # Replace Template str
        ReplaceStrinFile(fileName_hpp, self.TemplateScreenName, self.CmdKeyWord)
        ReplaceStrinFile(fileName_hpp, self.TemplateScreenName_View, self.CmdKeyWord.lower())
        return (mycode)

    def GenerateIViewHpp(self):
        fileName_hpp = 'I'+self.CmdKeyWord + 'View.hpp'

        mycode = []

        mycode.append('\n//Generated By Python3 Script ')
        mycode.append('#ifndef _I_' + self.CmdKeyWord.upper() + '_VIEW_H_ ')
        mycode.append('#define _I_' + self.CmdKeyWord.upper() + '_VIEW_H_ ')
        codeTemplate = ''' 
 
#endif 

        '''
        mycode.append(codeTemplate)
        WritetoFile(fileName_hpp, mycode)

        print('IView: ' + fileName_hpp + ' Generate OK!')
        # Replace Template str
        ReplaceStrinFile(fileName_hpp, self.TemplateScreenName, self.CmdKeyWord)
        ReplaceStrinFile(fileName_hpp, self.TemplateScreenName_View, self.CmdKeyWord.lower())
        return (mycode)




class BuildCode_Signal:
    ' Generate Signal code '

    def __init__(self, KeyWord='TestPVIDValue'):
        self.CmdKeyWord = 'Signal_' + KeyWord
        self.CmdKeyWord_Input = KeyWord

    TemplateSignalName = "TestSignalName"

    def GetSignal_PVIDName(self):
        return 'PVID_' + self.CmdKeyWord.upper()

    def GetSignal_InitCode(self):
        codeTemplate = '''

'''
        rst = re.sub(self.TemplateSignalName, self.CmdKeyWord, codeTemplate)

        return rst

    def GetSignal_PVIDCode(self):
        codeTemplate = '''

        '''

        template_mdlsetPVID = "mdl_SetxxxxxxxxScreen_xxxxxxxx"
        Generate_mdlsetPVID = "mdl_SetxxxxxxxxScreen_" + self.CmdKeyWord_Input

        rst = re.sub(template_mdlsetPVID, Generate_mdlsetPVID, codeTemplate)

        template_mdlgetPVID = "mdl_GetxxxxxxxxxxxScreen_xxxxxxx"
        Generate_mdlgetPVID = "mdl_GetxxxxxxxxxxxScreen_" + self.CmdKeyWord_Input

        rst = re.sub(template_mdlgetPVID, Generate_mdlgetPVID, rst)

        return rst_Gen

    def GetIncludeHeader_Presenter(self):
        return '#include "' + self.CmdKeyWord + 'Screen/' + self.CmdKeyWord + 'Presenter.hpp"'





if __name__ == '__main__':
    if(sys.argv[1:] == []):
        print('Not input parameter , Use Test Data')
        CmdKeyWord = 'TestInput'
    else:
        CmdKeyWord = sys.argv[1]
        
    
# code = BuildCode_MVP(CmdKeyWord)
#code.Generate()


MVPGenerator_Blog.py

# -*- coding: utf-8 -*-

from tkinter import *

from MVPCodeBuilder_Blog import *

print('GUI start...')

def ShowSendtoDUT(wlcmd):
    SendDut = '\n\r >>[Failed Log] >>:\n\r'
    inputData.insert(END, SendDut)
    inputData.insert(END, wlcmd)

def ShowRcvfromDUT(rst):
    # RcvDut = ' \n\r >>[MVP Code Generator] >>:\n\r'
    # txt.insert(END, RcvDut)
    txt.insert(END, rst)


def OnCommandInput():
    InputCmd = Et_CommandInput.get()
    if(len(InputCmd) == 0):
        ShowRcvfromDUT("\n\r未输入内容!")
    else:
        code = BuildCode_MVP(InputCmd)
        code.Generate()

        ShowRcvfromDUT('\n\rStatus: All MVP File Generated OK!\n\r')
        ShowRcvfromDUT('Here is code for reference use:')

def OnSignalButton():
    InputCmd = Et_SignalCommandInput.get()
    if (len(InputCmd) == 0):
        ShowRcvfromDUT("\n\r未输入内容!")
    else:
        code = BuildCode_Signal(InputCmd)

        ShowRcvfromDUT('Here is code for reference use:')
        ShowRcvfromDUT(code.GetSignal_InitCode())

        ShowRcvfromDUT(code.GetSignal_PVIDCode())



def clearSendContent():
     inputData.delete(1.0, END)

def clearRcvContent():
     txt.delete(1.0, END)



#Tkinter code below
root = Tk()
root.geometry('1000x600')
root.title('MVP Code Generator Tools')
root.config(bg='#f0ffff')

#Lable

Lb_CommandInput = Label(root,text='输入 MVP Screen Name:',\
                      # bg='#d3fbfb',\
                       fg='red',\
                       font=('华文新魏',10),\
                       width=28,\
                       height=2,\
                       relief=RIDGE)

Lb_CommandInput.place(relx=0.125, rely=0.237, relwidth=0.15, relheight=0.05)

Lb_CommandInput = Label(root,text='输入 Signal Name:',\
                      # bg='#d3fbfb',\
                       fg='red',\
                       font=('华文新魏',10),\
                       width=28,\
                       height=2,\
                       relief=RIDGE)

Lb_CommandInput.place(relx=0.125, rely=0.537, relwidth=0.15, relheight=0.05)

intro = Label(root,text='请在下方输入MVP要生成的ScreenName,然后点击Generate',\
                       bg='#d3fbfb',\
                       fg='red',\
                       font=('华文新魏',14),\
                       width=20,\
                       height=2,\
                       relief=RIDGE)

intro.place(relx=0.1, rely=0.1, relwidth=0.8, relheight=0.1)


ContactLable = Label(root, text='联系人:HowardXue ',\
                      # bg='#d3fbfb',\
                       fg='red',\
                       font=('',12),\
                       width=20,\
                       height=2,\
                       relief=RIDGE)
ContactLable.place(relx=0.7, rely=0.03, relwidth=0.25, relheight=0.05)



#Output
txt = Text(root, font = ('',10))
txt.place(relx=0.3, rely=0.2, relwidth=0.6, relheight=0.625)

#lable

#Entry
Et_CommandInput = Entry(root, bd =5, relief=GROOVE)

Et_CommandInput.place(relx=0.125, rely=0.3, relwidth=0.15, relheight=0.05)


Et_SignalCommandInput = Entry(root, bd =5, relief=GROOVE)

Et_SignalCommandInput.place(relx=0.125, rely=0.6, relwidth=0.15, relheight=0.05)

#Button

bt_bin2json = Button(root, text='MVP Code Generate', command=OnCommandInput, fg ='blue')
bt_bin2json.place(relx=0.1, rely=0.38, relwidth=0.2, relheight=0.1)


signalButton = Button(root, text='Signal PVID Code Generate', command=OnSignalButton, fg ='blue')
signalButton.place(relx=0.1, rely=0.68, relwidth=0.2, relheight=0.1)

# bt_clear = Button(root, text='Clear', command=clearSendContent, fg ='blue')
# bt_clear.place(relx=0.15, rely=0.825, relwidth=0.2, relheight=0.1)

bt_clear_rcv = Button(root, text='Clear', command=clearRcvContent, fg ='blue')
bt_clear_rcv.place(relx=0.45, rely=0.825, relwidth=0.2, relheight=0.1)

#comboxlist.pack()

root.mainloop()

其他好玩的python脚本

Python实现自动发送邮件 --自动抓取博客/网站中留言的邮箱并发送相应邮件
Python自动生成代码 - 通过tkinter图形化操作并生成代码框架
Python解析CSV数据 - 通过Pandas解析逻辑分析仪导出的CSV数据
Python通过Django搭建网站执行Lua脚本 (实现数据解析)


博主热门文章推荐:

一篇读懂系列:

  • 一篇读懂无线充电技术(附方案选型及原理分析)
  • 一篇读懂:Android/iOS手机如何通过音频接口(耳机孔)与外设通信
  • 一篇读懂:Android手机如何通过USB接口与外设通信(附原理分析及方案选型)

LoRa Mesh系列:

  • LoRa学习:LoRa关键参数(扩频因子,编码率,带宽)的设定及解释
  • LoRa学习:信道占用检测原理(CAD)
  • LoRa/FSK 无线频谱波形分析(频谱分析仪测试LoRa/FSK带宽、功率、频率误差等)

网络安全系列:

  • ATECC508A芯片开发笔记(一):初识加密芯片
  • SHA/HMAC/AES-CBC/CTR 算法执行效率及RAM消耗 测试结果
  • 常见加密/签名/哈希算法性能比较 (多平台 AES/DES, DH, ECDSA, RSA等)
  • AES加解密效率测试(纯软件AES128/256)–以嵌入式Cortex-M0与M3 平台为例

嵌入式开发系列:

  • 嵌入式学习中较好的练手项目和课题整理(附代码资料、学习视频和嵌入式学习规划)
  • IAR调试使用技巧汇总:数据断点、CallStack、设置堆栈、查看栈使用和栈深度、Memory、Set Next Statement等
  • Linux内核编译配置(Menuconfig)、制作文件系统 详细步骤
  • Android底层调用C代码(JNI实现)
  • 树莓派到手第一步:上电启动、安装中文字体、虚拟键盘、开启SSH等
  • Android/Linux设备有线&无线 双网共存(同时上内、外网)

AI / 机器学习系列:

  • AI: 机器学习必须懂的几个术语:Lable、Feature、Model…
  • AI:卷积神经网络CNN 解决过拟合的方法 (Overcome Overfitting)
  • AI: 什么是机器学习的数据清洗(Data Cleaning)
  • AI: 机器学习的模型是如何训练的?(在试错中学习)
  • 数据可视化:TensorboardX安装及使用(安装测试+实例演示)

更多推荐

Python自动生成代码(二)- 基于MVP架构的C/C++ 代码生成器实例