2019年1月18日 星期五

A tool to Modify #define Macro value in c/c++ file (implement in Python)

This article is an implement of:

an exe(windows application)/scripts to
1.search for an #define Macro 
2.change the value (type: number, string, regular expression...etc)

finally, it will be like this:

Steps to Use it:
-1 open the file you want to modify.


-2 type keyword(a few character is OK), then press"start" to search for this Macro.
(show Exception message if not exist!!)


-3 type in the Macro value, then press"save the change in Macro Value".




-4 if open the file, you can see the change result.


the fallowing sections describe how to implement:

0.Project configuration:

Python Interpreter Settings:
- python 3.6
- PyQT5 5.11.2
- PyInstaller 3.1.0



Install Package in Python 3.6:

from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
from PyQt5.QtGui import *

from ui_textReader_ver1 import Ui_Form

import sys
from shutil import copyfile
import datetime

1. Graphic User Interface Source Code (create by Qt version 5.9.1)

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

# Form implementation generated from reading ui file 'ui_textReader.ui'
#
# Created by: PyQt5 UI code generator 5.11.2
#
# WARNING! All changes made in this file will be lost!

from PyQt5 import QtCore, QtGui, QtWidgets

class Ui_Form(object):
    def setupUi(self, Form):
        Form.setObjectName("Form")
        Form.resize(703, 559)
        self.groupBox_2 = QtWidgets.QGroupBox(Form)
        self.groupBox_2.setGeometry(QtCore.QRect(0, 10, 691, 531))
        self.groupBox_2.setObjectName("groupBox_2")
        self.verticalLayoutWidget = QtWidgets.QWidget(self.groupBox_2)
        self.verticalLayoutWidget.setGeometry(QtCore.QRect(20, 20, 651, 501))
        self.verticalLayoutWidget.setObjectName("verticalLayoutWidget")
        self.verticalLayout = QtWidgets.QVBoxLayout(self.verticalLayoutWidget)
        self.verticalLayout.setContentsMargins(0, 0, 0, 0)
        self.verticalLayout.setObjectName("verticalLayout")
        self.gridLayout_2 = QtWidgets.QGridLayout()
        self.gridLayout_2.setObjectName("gridLayout_2")
        self.s1_bt_1 = QtWidgets.QPushButton(self.verticalLayoutWidget)
        self.s1_bt_1.setObjectName("s1_bt_1")
        self.gridLayout_2.addWidget(self.s1_bt_1, 0, 1, 1, 1)
        self.s1_text_1 = QtWidgets.QLineEdit(self.verticalLayoutWidget)
        self.s1_text_1.setObjectName("s1_text_1")
        self.gridLayout_2.addWidget(self.s1_text_1, 5, 1, 1, 3)
        self.s1_text_2 = QtWidgets.QLineEdit(self.verticalLayoutWidget)
        self.s1_text_2.setObjectName("s1_text_2")
        self.gridLayout_2.addWidget(self.s1_text_2, 0, 3, 1, 1)
        self.s1_lb_2 = QtWidgets.QLabel(self.verticalLayoutWidget)
        self.s1_lb_2.setObjectName("s1_lb_2")
        self.gridLayout_2.addWidget(self.s1_lb_2, 5, 0, 1, 1)
        self.s1_lb_3 = QtWidgets.QLabel(self.verticalLayoutWidget)
        self.s1_lb_3.setObjectName("s1_lb_3")
        self.gridLayout_2.addWidget(self.s1_lb_3, 0, 2, 1, 1)
        self.s1_lb_1 = QtWidgets.QLabel(self.verticalLayoutWidget)
        self.s1_lb_1.setObjectName("s1_lb_1")
        self.gridLayout_2.addWidget(self.s1_lb_1, 0, 0, 1, 1)
        self.s1_lb_6 = QtWidgets.QLabel(self.verticalLayoutWidget)
        self.s1_lb_6.setObjectName("s1_lb_6")
        self.gridLayout_2.addWidget(self.s1_lb_6, 8, 0, 1, 1)
        self.s1_lb_7 = QtWidgets.QLabel(self.verticalLayoutWidget)
        self.s1_lb_7.setObjectName("s1_lb_7")
        self.gridLayout_2.addWidget(self.s1_lb_7, 9, 0, 1, 1)
        self.s1_text_3 = QtWidgets.QLineEdit(self.verticalLayoutWidget)
        self.s1_text_3.setObjectName("s1_text_3")
        self.gridLayout_2.addWidget(self.s1_text_3, 8, 1, 1, 3)
        self.s1_lb_4 = QtWidgets.QLabel(self.verticalLayoutWidget)
        self.s1_lb_4.setObjectName("s1_lb_4")
        self.gridLayout_2.addWidget(self.s1_lb_4, 10, 0, 1, 1)
        self.s1_bt_2 = QtWidgets.QPushButton(self.verticalLayoutWidget)
        self.s1_bt_2.setObjectName("s1_bt_2")
        self.gridLayout_2.addWidget(self.s1_bt_2, 10, 1, 1, 1)
        self.s1_text_4 = QtWidgets.QLineEdit(self.verticalLayoutWidget)
        self.s1_text_4.setObjectName("s1_text_4")
        self.gridLayout_2.addWidget(self.s1_text_4, 9, 1, 1, 3)
        self.verticalLayout.addLayout(self.gridLayout_2)
        self.s1_bt_3 = QtWidgets.QPushButton(self.verticalLayoutWidget)
        self.s1_bt_3.setObjectName("s1_bt_3")
        self.verticalLayout.addWidget(self.s1_bt_3)
        self.s1_lb_5 = QtWidgets.QLabel(self.verticalLayoutWidget)
        self.s1_lb_5.setObjectName("s1_lb_5")
        self.verticalLayout.addWidget(self.s1_lb_5)
        self.textBrowser = QtWidgets.QTextBrowser(self.verticalLayoutWidget)
        self.textBrowser.setObjectName("textBrowser")
        self.verticalLayout.addWidget(self.textBrowser)
        self.gridLayout = QtWidgets.QGridLayout()
        self.gridLayout.setObjectName("gridLayout")
        self.verticalLayout.addLayout(self.gridLayout)

        self.retranslateUi(Form)
        QtCore.QMetaObject.connectSlotsByName(Form)

    def retranslateUi(self, Form):
        _translate = QtCore.QCoreApplication.translate
        Form.setWindowTitle(_translate("Form", "Form"))
        self.groupBox_2.setTitle(_translate("Form", "Content"))
        self.s1_bt_1.setText(_translate("Form", "open"))
        self.s1_text_1.setText(_translate("Form", "config.h"))
        self.s1_lb_2.setText(_translate("Form", "File Path"))
        self.s1_lb_3.setText(_translate("Form", "System Time"))
        self.s1_lb_1.setText(_translate("Form", "File"))
        self.s1_lb_6.setText(_translate("Form", "Define Macro"))
        self.s1_lb_7.setText(_translate("Form", "MacroValue"))
        self.s1_lb_4.setText(_translate("Form", "Search in File"))
        self.s1_bt_2.setText(_translate("Form", "start"))
        self.s1_bt_3.setText(_translate("Form", "save the change in Macro Value"))
        self.s1_lb_5.setText(_translate("Form", "Status"))

2. Functions and Main

#這就是blogger: c 語言宏修改小工具 by Alex Kuan 的EXAMPLE CODE @2019/1/18

from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
from PyQt5.QtGui import *

from ui_textReader_ver1 import Ui_Form

import sys
from shutil import copyfile
import datetime

class textReader_MainWindow(QMainWindow, Ui_Form):
    def __init__(self, parent=None):
        super(textReader_MainWindow, self).__init__(parent)
        self.setupUi(self)

        self.setWindowTitle("C++ define Modifier by Alex Kuan")
        self.setWindowIcon(QIcon("./image/alex2.jpg"))
        self.time_format = 0

        self.parameter_change = ''
        self.parameter_loc = 0xff
        self.parameter_find = False
        self.f_line = 0

        self.filenames = 0

        self.func_init()

    def func_init(self):
        self.real_time_stamp()

        self.s1_bt_1.clicked.connect(self.open_file)
        self.s1_bt_2.clicked.connect(self.check_parameter)
        self.s1_bt_3.clicked.connect(self.change_parameter)


    def real_time_stamp(self):
        self.sys_timer = QTimer(self)
        self.sys_timer.timeout.connect(self.real_time_update)
        self.sys_timer.start(1000)

    def real_time_update(self):
        self.time_format = datetime.datetime.now().strftime("%Y-%m-%d_%H:%M:%S")
        self.s1_text_2.setText(str(self.time_format))

    def open_file(self):
        fname = QFileDialog()
        fname.setFileMode(QFileDialog.AnyFile)
        fname.setFilter(QDir.Files)

        if fname.exec_():
            self.filenames = fname.selectedFiles()
            copyfile(self.filenames[0],"./config_file.txt")
            self.s1_text_1.setText(self.filenames[0])

            f = open(self.filenames[0], 'r')
            self.f_line = f.readlines()

            for i in range(0, len(self.f_line)):
                self.textBrowser_print(2,self.f_line[i])

    def check_parameter(self):
        self.parameter_change = self.s1_text_3.text()
        self.parameter_loc = 0xff
        self.parameter_find = False
        line_cnt = 0


        for li in self.f_line:
            if self.parameter_change in li:
                self.parameter_find = True
                self.parameter_loc = line_cnt
            else:
                line_cnt = line_cnt+1

        if False == self.parameter_find:
            QMessageBox.critical(self, "Config Error", "This Parameter not exist!")
        else:
            self.textBrowser.clear()
            self.textBrowser_print(1,"find %s at line %d\r\n"%(self.f_line[self.parameter_loc],
                                                           self.parameter_loc))

    def change_parameter(self):
        if False != self.parameter_find:
            print(self.filenames[0])
            f_write = open(self.filenames[0], 'w+')
            self.f_line[int(self.parameter_loc)] = '#define '+self.parameter_change+' ('+self.s1_text_4.text()+')\n'

            f_write.writelines(self.f_line)

            f_write.close()
        else:
            QMessageBox.critical(self, "Config Error", "can't find this parameter!")



    def textBrowser_print(self, msg_type, char):
        if 0 == msg_type:
            char_add = "[app]:" + char

        elif 1 == msg_type:
            char_add = "[data]:" + char
        else:
            char_add = char

        self.textBrowser.insertPlainText(char_add)
        textCursor = self.textBrowser.textCursor()
        textCursor.movePosition(textCursor.End)
        self.textBrowser.setTextCursor(textCursor)


if __name__ == "__main__":
    print("system start")
    app = QApplication(sys.argv)
    ui = textReader_MainWindow()
    ui.show()
    sys.exit(app.exec())


3.Project file
image and path 
the "config_file.txt" is a copy of your file before it modified.
change its name to recover if you need older version.

the bellow folders is optional:
./image
./check_file
./save_photo

the bellow folders are made by Pyinstaller, not programmer:
./dist
./build
./idea

4.Compile as Windows Application

python pyinstaller -F ./text_reader_v1_main.py 


6.Note & Reference
- pyqt5:https://www.books.com.tw/products/0010787989
- https://medium.com/pyladies-taiwan/python-%E5%B0%87python%E6%89%93%E5%8C%85%E6%88%90exe%E6%AA%94-32a4bacbe351





2018年8月31日 星期五

python 3 with serial :dynamic update /save MCU uart value with csv file

This article is an implement of:

1. dynamic update chart by matplotlib
2. raw data input by serial (COM port)
3. start/pause function for save value array
4. save as .csv format with system time as file name.

finally, it will be like this:































my Python Settings:


























Install Package in Python 3.6:

from __future__ import print_function
import matplotlib
import matplotlib.pyplot as plt
from matplotlib.gridspec import GridSpec
from matplotlib.widgets import Button
import numpy as np

import serial

import datetime
import csv



STEP 0. check your COM port number at Device manager:
my MCU now is COM61


STEP 1. check uart input format
For this example, I define my MCU input in this format:





















'@' (0x40) as begin, '#' (0x23) as end
5 random values from 0 to 4096 , seperated by ',' (0x2c)

and I want to dynamic show on plot


STEP 2. new Python project file, install package
at the front of this article, I post every package this project need.

from __future__ import print_function
import matplotlib
import matplotlib.pyplot as plt
from matplotlib.gridspec import GridSpec
from matplotlib.widgets import Button
import numpy as np

import serial

import datetime
import csv


STEP 3. add these file into project file path

main.py
replace

COM = 'COM61'
as your COM number checked in STEP 0.


from __future__ import print_function
import matplotlib
import matplotlib.pyplot as plt
from matplotlib.gridspec import GridSpec
from matplotlib.widgets import Button

import datetime
import csv

import class_serial_input as serial
import class_line         as line

UPDATE_INTERVAL_IN_MSEC = 80

SERIAL_DATA_IN = 1
SERIAL_DATA_STOP = 0

serial_data_flag = SERIAL_DATA_IN

COM = 'COM61'


def start_func(label):
 print(" in start func")
 
 global serial_data_flag
 serial_data_flag = SERIAL_DATA_IN


def stop_func(label):
 print(" in stop func")
 
 global serial_data_flag
 serial_data_flag = SERIAL_DATA_STOP


def save_chart_csv(label):
 print(" save chart as csv")
 
 i_line_data = i_line.get_i_line()
 
 with open('output_%u_%u_%u_%u_%u_%u.csv' % (now_time.year, now_time.month, now_time.day,
                                             now_time.hour, now_time.minute, now_time.second),
           'w', newline='') as csvfile:
  writer = csv.writer(csvfile, delimiter=',')
  writer.writerow(i_line_data)
  


if __name__ == "__main__":
 print("start up \r\n")
 
 #default_port = serial_data.Serial_data("random_data" sys.argv[1])
 default_port = serial.serial_input("random_data",com=COM)
 
 #init matplot interface
 fig = plt.figure('random serial value chart power by Agatha Kuan', figsize=(10, 8))
 gs = GridSpec(14, 14, left=0.01, bottom=0.01, right=1 - 0.01, top=1 - 0.01, wspace=1, hspace=1)
 
 i_chart_axes = plt.subplot(gs[1:8, 6:14])
 i_chart_axes.axis([0, 4, 0, 4096])
 
 plt.ion()
 
 plt.ylabel("random value", color='#8d734b', size=12, family='monospace')
 plt.title("signal chart", color='#8d734b', size=15, family='monospace')
 
 plt.grid(True)
 
 #init line
 i_line = line.Init_line("line_1")
 
 #init button
 point_start = plt.subplot(gs[1, :4])
 button_start = Button(point_start, 'start', color='0.95', hovercolor='0.6')
 button_start.label.set_fontsize(11)
 button_start.on_clicked(start_func)
 
 point_stop = plt.subplot(gs[2, :4])
 button_stop = Button(point_stop, 'pause', color='0.95', hovercolor='0.6')
 button_stop.label.set_fontsize(11)
 button_stop.on_clicked(stop_func)
 
 point_save_csv = plt.subplot(gs[4, :2])
 button_save_csv = Button(point_save_csv, 'save as .CSV', color='0.95', hovercolor='0.6')
 button_save_csv.label.set_fontsize(11)
 button_save_csv.on_clicked(save_chart_csv)
 
 now_time = datetime.datetime.now()
 sys_start_time = datetime.datetime.now()
 
 time_stamp = plt.subplot(gs[7:11, :2])
 time_stamp.axis('off')
 time_stamp_text = plt.text(0.01, 0.05, " ", color='#8d734b', size=12, family='monospace',
                            transform=time_stamp.transAxes)
 
 
 
 while 1 :
  if SERIAL_DATA_STOP != serial_data_flag:
   default_port.get_serial_data()
   i_tmp = default_port.get_data()
  
   i_line.draw_update_thershold()
   i_line.draw_update_signal_line(i_tmp)
  
   
   i_chart_axes.relim(True)
   i_chart_axes.autoscale_view(True, 'y', True)
  
  now_time = datetime.datetime.now()
  time_stamp_text.set_text("start at %u/%u %u:%u:%u \n\n"
                           "System Time %u/%u %u:%u:%u \n"%(sys_start_time.month,sys_start_time.day,sys_start_time.hour,
                                                            sys_start_time.minute,sys_start_time.second,
                                                            now_time.month, now_time.day,now_time.hour,
                                                            now_time.minute, now_time.second))
  
  fig.canvas.draw()
  plt.pause(float(UPDATE_INTERVAL_IN_MSEC) / 1000.0)
  
  default_port.clear_data()
  
  

class_serial_input.py

import serial

import numpy as np

POINT_TO_UPDATE = 5
SEPERATE_SIGN = 0x2C
DATA_MAX_LEN = (2+(1+4)*(POINT_TO_UPDATE+1))*2

__metaclass__ = type

class serial_input:
 def __init__(self,name,com='COM61',baudrate=115200):
  
  
  self.ser = serial.Serial(com, baudrate)
  
  self.port = com
  self.baudrate = baudrate
  
  self.num_array = np.repeat(0x00, POINT_TO_UPDATE)
 
 def get_serial_data(self):
  self.ser.flushInput()
  
  char_in = self.ser.read(DATA_MAX_LEN)
  char_in = bytearray(char_in)
  
  temp = np.repeat(0x00, DATA_MAX_LEN)
  valid_tmp = np.repeat(0x00, DATA_MAX_LEN)
  
  for i in range (DATA_MAX_LEN):
   temp[i] = int(char_in[i])
   
  start_addr  = np.argwhere(temp == 0x40)
  end_addr    = np.argwhere(temp == 0x23)
  
  
  if(int(start_addr[0]) < int(end_addr[0])):
   start = int(start_addr[0])
   end   = int(end_addr[0])
   for i in range (end-start):
    valid_tmp[i] = temp[start+1+i]
  else:
   start = int(start_addr[0])
   end = int(end_addr[1])
   for i in range(end - start):
    valid_tmp[i] = temp[start + 1 + i]
   
  common_addr = np.argwhere(valid_tmp == 0x2c)
  
  for i in range (int(common_addr[0])):
   self.num_array[0] = self.num_array[0]+(valid_tmp[i]-0x30)*pow(10, int(common_addr[0])-1-i)
  
  for i in range(1,4):
   start = int(common_addr[i-1])
   end   = int(common_addr[i])
   
   for j in range(0,end-start-1):
    self.num_array[i] = self.num_array[i] + (valid_tmp[start+1+j]-0x30)*pow(10,end-start-2-j)
  
  end_addr = np.argwhere(valid_tmp == 0x23)
  
  for i in range (int(end_addr[0])-int(common_addr[3])-1):
   self.num_array[4] = self.num_array[4]+(valid_tmp[int(common_addr[3])+1+i]-0x30)*pow(10, int(end_addr[0])-int(common_addr[3])-2-i)
   
  
  
 def get_data(self):
  
  print(self.num_array)
  return self.num_array
 
 def clear_data(self):
  self.num_array = np.repeat(0x00, POINT_TO_UPDATE)


class_line.py

#線條的顏色: https://www.cnblogs.com/darkknightzh/p/6117528.html

import matplotlib.pyplot as plt
import numpy as np

__metaclass__ = type


THRESHOLD_VALUE = 2000

TIME_WINDOW_WIDTH_IN_SEC = 2.0

POINTS_IN_ONE_UPDATE = 5
VIEW_POINT_IN_SEC    = 1*POINTS_IN_ONE_UPDATE


TIME_STAMP_ARRAY = np.arange(0, VIEW_POINT_IN_SEC, 1)


class Init_line:
 def __init__(self,name):
  global TIME_STAMP_ARRAY
  
  self.time_window_begin = 0.0
  self.time_window_end = self.time_window_begin + TIME_WINDOW_WIDTH_IN_SEC
  
  self.time_line_value = TIME_STAMP_ARRAY
  self.thershold_line_value = np.repeat(THRESHOLD_VALUE, len(self.time_line_value))
  
  freq = 5.0
  omega = 2.0 * np.pi * freq
  constant_for_signal_shift = 1
  self.i_raw_line_value = np.sin(omega * self.time_line_value) + constant_for_signal_shift
  
  self.thershold_line, = plt.plot(self.time_line_value, self.thershold_line_value,label="thershold",color='#0080FF')
  self.i_raw_line, = plt.plot(self.time_line_value, self.i_raw_line_value,label="I raw", color='#FF8000')
  
  plt.legend(loc='upper right')
  
  
 def draw_thershold(self):
  self.thershold_line, = plt.plot(self.time_line_value, self.thershold_line_value)
 
 def draw_update_thershold(self):
  global TIME_STAMP_ARRAY
  
  self.time_line_value = TIME_STAMP_ARRAY
  self.thershold_line.set_data(self.time_line_value, self.thershold_line_value)
  
 def draw_init_signal_line(self):
  self.i_raw_line, = plt.plot(self.time_line_value, self.i_raw_line_value)
  
 def draw_update_signal_line(self,update_i_temp):
  self.i_raw_line_value = np.roll(self.i_raw_line_value, -POINTS_IN_ONE_UPDATE)
  self.i_raw_line_value[-(POINTS_IN_ONE_UPDATE):] = update_i_temp
  
  self.i_raw_line.set_data(self.time_line_value, self.i_raw_line_value)
  
 def get_i_line(self):
  return self.i_raw_line_value
  
 
  
  
 
  
  
  


STEP 4.run the Python file































when you run this python script, the PLOT start to update.


STEP 5. SAVE as csv file with time stamp as file name


press pause button to pause the update plot, then
press save as csv button

you will see a csv file in project path with time stamp as file name














open the file  in EXCEL, you can see the raw data:














NOTE:

  • this implement only use matplotlib to draw figure and canvas, didn't use other graphic UI structure like TKinter or PyQT5
  • the speed of COM data update is limited cause I didn't use multi-thread programming.


reference:

  1. https://www.cnblogs.com/darkknightzh/p/6117528.html
  2. https://zhuanlan.zhihu.com/p/32019813
  3. https://tw.saowen.com/a/aeeef5a75c5a3589f64d9f31953295c1b21a60ff72902f6e93f16327be2d3d78