Category Archives: tkinter

[Python] mybatis sql 자동생성 프로그램 (tkinter + pymysql)

이름은 거창하게 했는데 사실 별거 아니다.

mybatis를 하면 column이름을 camelcase로 바꾸고 (물론 자동으로 알아서 가져가지만 as 로 명시적으로 써주고 있다.)

그대로 vo 만들고 update문에 if문 걸어주고 암튼 여간 귀찮은 일이 아니다.

그래서 이를 자동화 해주는 스크립트를 짜보았따.

이걸 쓰면 서 배웠던 것은 pymysql활용과 tkinter활용정도가 아닐까 싶다.

tkinter를 exe로 배포하지는 않았따.

이 프로그램이 아무도 필요하지 않기 때문이다. 이미 내가 다 만들었기 때문이다.

 

라이브러리

import pymysql


def get_connection():
    connection = pymysql.connect(host='127.0.0.1',
                                 port=53306,
                                 user='user',
                                 password='user',
                                 db='user',
                                 charset='utf8mb4',
                                 cursorclass=pymysql.cursors.DictCursor)
    return connection


def get_column_list(table_name):
    connection = get_connection()
    result_list = []

    try:
        with connection.cursor() as cursor:
            # Read a single record
            sql = '''
            select COLUMN_NAME, COLUMN_DEFAULT, DATA_TYPE
              from information_schema.`COLUMNS`
             where TABLE_NAME = %s
            '''
            cursor.execute(sql, (table_name,))
            result_set = cursor.fetchall()
            for row in result_set:
                result_list.append(row)
    finally:
        connection.close()

    return result_list


def mybatis_camelcase_converter(str):
    """
    COLUMN_TYPE 이런 형식의 컬럼을 앞이 소문자인 camelcase로 바꿔준다.
    :param str: column name
    :return: camelcase str
    """
    result = str.title()
    result = result.replace('_', '')
    result = result[0].lower() + result[1:]
    return result


def create_insert_statement(table_name):
    """
    테이블 명을 받아서 insert 구문을 만든다.
    :param table_name: 테이블명
    :return: str 형식의 insert 구문
    """
    result_dict = get_column_list(table_name)

    result_str = 'INSERT INTO ' + table_name
    column_str = '('

    for i in result_dict:
        if i['COLUMN_DEFAULT'] is None:
            column_str = column_str + '\n' + i['COLUMN_NAME'] + ","

    column_str = column_str[0:len(column_str) - 1]
    column_str += '\n)\nVALUES'

    values_str = '('

    for i in result_dict:
        if i['COLUMN_DEFAULT'] is None:
            values_str = values_str + '\n' + "#{" + mybatis_camelcase_converter(i['COLUMN_NAME']) + "},"

    values_str = values_str[0:len(values_str) - 1]
    values_str += '\n)\n'

    insert_statement = result_str + column_str + values_str
    return insert_statement


def create_update_statement(table_name):
    """
    테이블 명을 받아서 update 구문을 만든다.
    :param table_name: 테이블명
    :return: str 형식의 update 구문    
    """
    result_dict = get_column_list(table_name)
    result_str = 'UPDATE ' + table_name + "\n"
    result_str += '<set>\n'
    first = True
    for i in result_dict:
        org_str = i['COLUMN_NAME']
        cvt_str = mybatis_camelcase_converter(i['COLUMN_NAME'])

        if i['COLUMN_DEFAULT'] is None:
            result_str += '<if test="' + cvt_str + ' != null and ' + cvt_str + ''' != ''">\n'''
            result_str += org_str + " = " + '#{' + cvt_str + '},\n'
            result_str += '</if>\n'

    result_str += '</set>'
    return result_str


def create_vo_variables(table_name):
    """
    테이블 명을 받아서 vo에 쓸 변수 리스트를 뽑아낸다.
    :param table_name: 테이블명 str
    :return: 변수리스트 str
    """
    result_dict = get_column_list(table_name)
    result_str = ''

    for i in result_dict:
        org_str = i['COLUMN_NAME']
        cvt_str = mybatis_camelcase_converter(i['COLUMN_NAME'])
        data_type = ''
        double = False

        if i['DATA_TYPE'] == 'int':
            data_type = 'int'
        elif i['DATA_TYPE'] == 'date':
            data_type = 'Date'
        elif i['DATA_TYPE'] == 'datetime':
            data_type = 'Timestamp'
        elif i['DATA_TYPE'] in ['longtext', 'varchar']:
            data_type = 'String'
        elif i['DATA_TYPE'] == 'point':
            double = True
            data_type = 'double'
        elif i['DATA_TYPE'] == 'tinyint':
            data_type = 'boolean'

        if double:
            result_str += 'private ' + data_type + ' x' + cvt_str[0].upper() + cvt_str[1:] + ';\n'
            result_str += 'private ' + data_type + ' y' + cvt_str[0].upper() + cvt_str[1:] + ';\n'
        else:
            result_str += 'private ' + data_type + ' ' + cvt_str + ';\n'

    return result_str


if __name__ == "__main__":
    table_name = 'sms'
    print(get_column_list(table_name))
    print(create_insert_statement(table_name))
    print(create_update_statement(table_name))
    print(create_vo_variables(table_name))

실행 결과

[{'COLUMN_NAME': 'sms_id', 'COLUMN_DEFAULT': None, 'DATA_TYPE': 'int'}, {'COLUMN_NAME': 'auth_number', 'COLUMN_DEFAULT': None, 'DATA_TYPE': 'int'}, {'COLUMN_NAME': 'is_used', 'COLUMN_DEFAULT': None, 'DATA_TYPE': 'tinyint'}, {'COLUMN_NAME': 'created_at', 'COLUMN_DEFAULT': 'CURRENT_TIMESTAMP', 'DATA_TYPE': 'datetime'}, {'COLUMN_NAME': 'updated_at', 'COLUMN_DEFAULT': 'CURRENT_TIMESTAMP', 'DATA_TYPE': 'datetime'}]
INSERT INTO sms(
sms_id,
auth_number,
is_used
)
VALUES(
#{smsId},
#{authNumber},
#{isUsed}
)

UPDATE sms
<set>
<if test="smsId != null and smsId != ''">
sms_id = #{smsId},
</if>
<if test="authNumber != null and authNumber != ''">
auth_number = #{authNumber},
</if>
<if test="isUsed != null and isUsed != ''">
is_used = #{isUsed},
</if>
</set>
private int smsId;
private int authNumber;
private boolean isUsed;
private Timestamp createdAt;
private Timestamp updatedAt;

 

설명이 필요 없을 것 같다. 소스가 병맛인건 내가 졸면서 했기 때문이다.

댓글로 욕하지 마시고 그냥 혀만 차고 가시길 바란다.

 

Application 부분

import tkinter as tk
from tkinter import ttk
from tkinter import scrolledtext
import mysqlconnector

win = tk.Tk()

win.title("mysql_creator")

win.resizable(0, 0)

radio_val = tk.StringVar()

# label
a_label = ttk.Label(win, text="INSERT TABLE NAME: ")
a_label.grid(column=0, row=0)

# input box
name = tk.StringVar()
text_edit = ttk.Entry(win, width=10, textvariable=name)
text_edit.grid(column=1, row=0)


# 라이브러리와 연동하여 결과를 가져옴.
def get_result(event):
    if len(name.get()) > 0:
        result_str = ''
        if radio_val.get() == "i":
            result_str = mysqlconnector.create_insert_statement(name.get())
            scr.delete('0.0', tk.END)
            scr.insert(tk.INSERT, result_str)
        elif radio_val.get() == "u":
            result_str = mysqlconnector.create_update_statement(name.get())
            scr.delete('0.0', tk.END)
            scr.insert(tk.INSERT, result_str)
        elif radio_val.get() == "v":
            result_str = mysqlconnector.create_vo_variables(name.get())
            scr.delete('0.0', tk.END)
            scr.insert(tk.INSERT, result_str)
        # 결과를 클립보드에 자동으로 복사함.
        win.clipboard_clear()
        win.clipboard_append(result_str)
        return
    else:
        a_label.configure(foreground='red')

# 엔터키에 이벤트 바인딩
text_edit.bind('<Return>', get_result)
events = tk.Event
submit_button = ttk.Button(win, text="create it!")
# 클릭에 이벤트 바인딩
submit_button.bind('<Button>', get_result)
submit_button.grid(column=2, row=0)

result_txt = tk.StringVar()
scr = scrolledtext.ScrolledText(win, width=100, height=30, wrap=tk.WORD, undo=True)
scr.grid(column=0, columnspan=15)

MODES = [
    ("Insert", "i"),
    ("update", "u"),
    ("VO", "v"),
]

# 라디오 버튼 초기화
radio_val.set("i")  

i = 3
for text, mode in MODES:
    b = tk.Radiobutton(win, text=text, variable=radio_val, value=mode)
    b.grid(column=i, row=0)
    i += 1

win.mainloop()

 

어려운 것은 없다. tkinter처음 써보는 분들이 참고 하시면 도움이 될 것 같기도 하다.

근데 요즘 누가 os application을 만드려나 모르겠다.

 

완성하면

mysqlcreator1

mysqlcreator2

mysqlcreator3

mysqlcreator4

 

마무리

  •  원래는 beautify 까지 해주려고 했는데, 귀찮아서 포기했다.
  •  또 vo 에서 getter, setter, tostring 등도 자동 생성해주려고 했는데 그냥 intellij에서 alt+insert 를 쓰자
  •  간단하게 내가 필요한 프로그램을 빠르게 만든다는 점에서는 굉장히 즐거운 작업이었다.
  •  그러나 누군가에게 내 소스코드를 까보여주었을 때의 부끄러움도 앞으로 고려해봐야겠다.

 

[Python] tkinter 엔터키에 이벤트 주기

같은 이벤트를 각각 버튼과 엔터키에 바인딩 하려면 어떻게 해야할까?

보통 컴포넌트를 생성하면서 command=event_name
이런식으로 이벤트를 준다.

깔끔하게 이벤트 처리를 하기 위해선, command를 쓰는 것 보다 bind를 쓰는게 낫다.

# 엔터키에 이벤트가 바인딩 된다.
text_edit.bind('<Return>', submit_button_by_enter)
# 마우스 클릭에 이벤트가 바인딩 된다.
submit_button.bind('<Button>', submit_button_by_enter)

이런식으로 bind 메소드를 활용하여 깔끔하게 이벤트를 바인딩 하자.

마우스클릭 이벤트와 엔터이벤트를 구분하면서 한 function으로 해결하려면 이방법 을 써도 된다.

 

참고

"""Container for the properties of an event.

    Instances of this type are generated if one of the following events occurs:

    KeyPress, KeyRelease - for keyboard events
    ButtonPress, ButtonRelease, Motion, Enter, Leave, MouseWheel - for mouse events
    Visibility, Unmap, Map, Expose, FocusIn, FocusOut, Circulate,
    Colormap, Gravity, Reparent, Property, Destroy, Activate,
    Deactivate - for window events.

    If a callback function for one of these events is registered
    using bind, bind_all, bind_class, or tag_bind, the callback is
    called with an Event as first argument. It will have the
    following attributes (in braces are the event types for which
    the attribute is valid):

        serial - serial number of event
    num - mouse button pressed (ButtonPress, ButtonRelease)
    focus - whether the window has the focus (Enter, Leave)
    height - height of the exposed window (Configure, Expose)
    width - width of the exposed window (Configure, Expose)
    keycode - keycode of the pressed key (KeyPress, KeyRelease)
    state - state of the event as a number (ButtonPress, ButtonRelease,
                            Enter, KeyPress, KeyRelease,
                            Leave, Motion)
    state - state as a string (Visibility)
    time - when the event occurred
    x - x-position of the mouse
    y - y-position of the mouse
    x_root - x-position of the mouse on the screen
             (ButtonPress, ButtonRelease, KeyPress, KeyRelease, Motion)
    y_root - y-position of the mouse on the screen
             (ButtonPress, ButtonRelease, KeyPress, KeyRelease, Motion)
    char - pressed character (KeyPress, KeyRelease)
    send_event - see X/Windows documentation
    keysym - keysym of the event as a string (KeyPress, KeyRelease)
    keysym_num - keysym of the event as a number (KeyPress, KeyRelease)
    type - type of the event as a number
    widget - widget in which the event occurred
    delta - delta of wheel movement (MouseWheel)
    """