Integrating Database, Python, GUI [KOR]
데이터베이스, 파이썬, GUI를 묶어서 결과물을 내보자
SQLite : 개인용 RDBMS, 모바일기기, 임베디드 시스템, 소규모 웹사이트 운영에 효과적
In [2]:
import sqlite3   # sqlite 임포트 하기
conn1 = sqlite3.connect('exam.db')  # 현재 모듈과 같은 경로에 db 파일이 저장. 연결 객체가 생성됨
conn2 = sqlite3.connect(':memory:')  # RAM에 db가 저장 : 휘발성. 연결 객체가 생성됨
try:
    cur = conn2.cursor()  # SQL을 실행 가능 객체
    
    # 자료 입력
    cur.execute("create table if not exists friends(name text, phone text, addr text)")  
    cur.execute("insert into friends values('홍길동','111-1111','역삼1동')")
    cur.execute("insert into friends values('고길동','222-1111','서초1동')")
    
    inputData = ('손오공', '333-1111', '압구정동')
    cur.execute("insert into friends values(?,?,?)", inputData)  # 이런방식으로도 넣을 수 있다.
    
    conn2.commit()
    
    # 자료 읽기
    cur.execute("select * from friends")
    print(cur.fetchone())  # 레코드 포인터가 있는 지점의 레코드를 참조, 튜플형식으로 출력
    print(cur.fetchone())
    
    print(cur.fetchall())  # 레코드 포인터 위치부터 전체 레코드 출력, 튜플을 리스트로 묶어서 출력
    
    for r in cur:
        print(r[0], ' ', r[1], ' ', r[2])  # 원하는 방법으로 출력
     
    
except Exception as e:
    print('err : ', e)
    conn2.rollback()
    
    
finally:
    conn2.close()
('홍길동', '111-1111', '역삼1동')
('고길동', '222-1111', '서초1동')
[('손오공', '333-1111', '압구정동')]
데이터 연동 프로그램
In [8]:
def dbFunc(dbName):
    try:
        conn = sqlite3.connect(dbName)
        cur = conn.cursor()
        
        # 테이블 생성
        cur.execute("drop table if exists jiktab")
        cur.execute("create table jiktab(id integer primary key, name text)")
        
        # insert (튜플, 리스트 가능)
        cur.execute("insert into jiktab values(1,'홍길동')")
        
        tdata = 2,'고길동'  # 튜플타입
        cur.execute("insert into jiktab values(?,?)", tdata)
        
        ldata = [3,'김밥']  # 리스트타입
        cur.execute("insert into jiktab values(?,?)", ldata)
        
        ddata = {'id':4, 'name':'공기밥'}
        cur.execute("insert into jiktab values(:id,:name)", ddata)  # 딕셔너리 타입, 물음표를 쓰면 안됨
        
        conn.commit()
        
        # update
        updata = ('박치기', 1)
        cur.execute("update jiktab set name=? where id=?", updata)
        
        # delete
        deldata = (2,)
        cur.execute("delete from jiktab where id=?", deldata)
        
        conn.commit()
        
        # select
        cur.execute("select * from jiktab")
        for r in cur:
            print(str(r[0]) + " "+ r[1])
        
        print()
        cur.execute("select * from jiktab where id <= 2")
        for r in cur:
            print(str(r[0]) + ' '+ r[1])
            
        print()
        cur.execute("select * from jiktab")
        print('건수 : '+ str(cur.fetchone()[0]))
        
        conn.commit()
        
        
    except Exception as e:
        print('err : ', e)
        conn.rollback()
        
        
    finally:
        pass
    
    
if __name__ == '__main__':
    dbFunc('test.db')
1 박치기 3 김밥 4 공기밥 1 박치기 건수 : 1
2.1 원격 MySQL / MariaDB 랑 연동하기¶
순서 : 1. Driver file 설치
       2. Module 로딩
       3. 원격DB와 연결 (Connection 객체)
       4. cursor 개체 (SQL문 사용)Driver file 설치
Anaconda Prompt 에서 pip install mysqlclient 를 친다
안되면 https://www.lfd.uci.edu/~gohlke/pythonlibs/#mysqlclient 에서 MySQL Client 다운로드Module 로딩
In [16]:
# 1번 방법
import MySQLdb
conn = MySQLdb.connect(host = '127.0.0.1', user = 'root', password='123', database='test')  # dict 형식으로 값을 줘야함
conn.close()
In [36]:
# 2번 방법
import MySQLdb
config = {
    'host':'127.0.0.1',
    'user':'root',
    'password':'123',
    'database':'test',
    'port':3306,
    'charset':'utf8',
    'use_unicode':True
}
try:
    conn = MySQLdb.connect(**config)  # 원래 config는 이런식으로 하지 않고, 파일로 빼서 암호를 걸고 파일을 부른다.
    cursor = conn.cursor()
    
    # 자료 추가
    sql = "insert into sangdata(code,sang,su,dan) values(5,'상품1',7,1000)"
    cursor.execute(sql)
    
    ins_data = ('10','상품2','120',5000)   
    sql = "insert into sangdata(code,sang,su,dan) values(%s,%s,%s,%s)"
    cursor.execute(sql, ins_data)  # 파이썬에만 데이터가 들어와있음 
    
    conn.commit()  # 파이썬은 auto commit으로 설정되있다
    
    # 자료 수정
    sql = "update sangdata set sang=%s,su=%s where code=%s"  # code는 pk이므로 수정에서 제외
    up_data = ('파이썬',33,10)
    cursor.execute(sql, up_data)
    
    conn.commit()
    
    # 자료 삭제
    sql = "delete from sangdata where code=%s"
    del_data = ('10',)
    cursor.execute(sql, del_data)
    conn.commit()
    
    # 자료 읽기
    sql = "select code,sang,su,dan from sangdata"
    cursor.execute(sql)
    for data in cursor.fetchall():
        print(data)
    
    """
    # 다른 읽기 방법1
    for r in cursor:
        print(r[0], r[1], r[2], r[3])
  
    # 다른 읽기 방법2
    for (code, sang, su, dan) in cursor:
        print(code, sang, su, dan)
    """
    
except Exception as e:
    print('err : ', e)
    
finally:
    conn.close()
(1, '장갑', 3, 10000) (2, '벙어리장갑', 2, 12000) (3, '가죽장갑', 10, 50000) (4, '가죽점퍼', 5, 650000) (5, '상품1', 7, 1000) (10, '파이썬', 33, 5000)
2.2 응용 (GUI 사용)
MySQLdb 임포트 후 config 입력
wxPython GUI 에서 출력 실행 (wxFormBuilder 사용)
2.2.1 연습1
In [1]:
import wx
import wx.xrc
import MySQLdb
config = {
    'host':'127.0.0.1',
    'user':'root',
    'password':'123',
    'database':'test',
    'port':3306,
    'charset':'utf8',
    'use_unicode':True
}
class MyFrameLogin ( wx.Frame ):
    
    def __init__( self, parent ):
        wx.Frame.__init__ ( self, parent, id = wx.ID_ANY, title = u"직원의 관리고객", pos = wx.DefaultPosition, size = wx.Size( 499,400 ), style = wx.DEFAULT_FRAME_STYLE|wx.TAB_TRAVERSAL )
        
        # self.SetSizeHintsSz( wx.DefaultSize, wx.DefaultSize )  # 항상 직접 주석 달아주기
        self.SetBackgroundColour( wx.SystemSettings.GetColour( wx.SYS_COLOUR_MENU ) )
        
        bSizer1 = wx.BoxSizer( wx.VERTICAL )
        
        self.m_panel1 = wx.Panel( self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.TAB_TRAVERSAL )
        bSizer2 = wx.BoxSizer( wx.HORIZONTAL )
        
        self.m_staticText1 = wx.StaticText( self.m_panel1, wx.ID_ANY, u"사번 : ", wx.DefaultPosition, wx.DefaultSize, 0 )
        self.m_staticText1.Wrap( -1 )
        bSizer2.Add( self.m_staticText1, 0, wx.ALL, 5 )
        
        self.txtNo = wx.TextCtrl( self.m_panel1, wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, wx.Size( 50,-1 ), 0 )
        bSizer2.Add( self.txtNo, 0, wx.ALL, 5 )
        
        self.m_staticText2 = wx.StaticText( self.m_panel1, wx.ID_ANY, u"직원명 : ", wx.DefaultPosition, wx.DefaultSize, 0 )
        self.m_staticText2.Wrap( -1 )
        bSizer2.Add( self.m_staticText2, 0, wx.ALL, 5 )
        
        self.txtName = wx.TextCtrl( self.m_panel1, wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, wx.DefaultSize, 0 )
        bSizer2.Add( self.txtName, 0, wx.ALL, 5 )
        
        self.btnLogin = wx.Button( self.m_panel1, wx.ID_ANY, u"로그인", wx.DefaultPosition, wx.DefaultSize, 0 )
        bSizer2.Add( self.btnLogin, 0, wx.ALL, 5 )
        
        
        self.m_panel1.SetSizer( bSizer2 )
        self.m_panel1.Layout()
        bSizer2.Fit( self.m_panel1 )
        bSizer1.Add( self.m_panel1, 0, wx.EXPAND |wx.ALL, 5 )
        
        self.m_panel2 = wx.Panel( self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.TAB_TRAVERSAL )
        bSizer3 = wx.BoxSizer( wx.VERTICAL )
        
        self.staMsg = wx.StaticText( self.m_panel2, wx.ID_ANY, u"관리고객 ", wx.DefaultPosition, wx.DefaultSize, 0 )
        self.staMsg.Wrap( -1 )
        bSizer3.Add( self.staMsg, 0, wx.ALL, 5 )
        
        
        self.m_panel2.SetSizer( bSizer3 )
        self.m_panel2.Layout()
        bSizer3.Fit( self.m_panel2 )
        bSizer1.Add( self.m_panel2, 0, wx.EXPAND |wx.ALL, 5 )
        
        self.m_panel3 = wx.Panel( self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.TAB_TRAVERSAL )
        bSizer4 = wx.BoxSizer( wx.VERTICAL )
        
        self.lstGogek = wx.ListCtrl( self.m_panel3, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LC_REPORT )
        bSizer4.Add( self.lstGogek, 0, wx.ALL|wx.EXPAND, 5 )
        
        
        self.m_panel3.SetSizer( bSizer4 )
        self.m_panel3.Layout()
        bSizer4.Fit( self.m_panel3 )
        bSizer1.Add( self.m_panel3, 1, wx.EXPAND |wx.ALL, 5 )
        
        self.m_panel4 = wx.Panel( self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.TAB_TRAVERSAL )
        bSizer5 = wx.BoxSizer( wx.HORIZONTAL )
        
        self.staCount = wx.StaticText( self.m_panel4, wx.ID_ANY, u"인원수 : ", wx.DefaultPosition, wx.DefaultSize, 0 )
        self.staCount.Wrap( -1 )
        bSizer5.Add( self.staCount, 0, wx.ALL, 5 )
        
        
        self.m_panel4.SetSizer( bSizer5 )
        self.m_panel4.Layout()
        bSizer5.Fit( self.m_panel4 )
        bSizer1.Add( self.m_panel4, 1, wx.EXPAND |wx.ALL, 5 )
        
        
        self.SetSizer( bSizer1 )
        self.Layout()
        
        self.Centre( wx.BOTH )
        
        # lstGogek 객체에 제목달기 (직접 추가 해줘야 함)
        self.lstGogek.InsertColumn(0, '고객번호', width=100)
        self.lstGogek.InsertColumn(1, '고객명', width=150)
        self.lstGogek.InsertColumn(2, '고객전화', width=200)
        
        # Connect Events
        self.btnLogin.Bind( wx.EVT_BUTTON, self.OnLogin )
    
    def __del__( self ):
        pass
    
    
    # Virtual event handlers, overide them in your derived class
    def OnLogin( self, event ):
        if self.txtNo.GetValue() == '':  # 사번 입력 검사
            wx.MessageBox('사번을 입력하시오', '알림', wx.OK)
            self.txtNo.SetFocus()  # 커서 깜빡이게 한다
            return
        
        if self.txtName.GetValue() == '':  # 직원명 검사
            wx.MessageBox('직원을 입력하시오', '알림', wx.OK)
            self.txtName.SetFocus()
            return
        
        self.LoginCheck()  # 입력자료가 만족되는 로그인 처리 메소드를 호출
        
    def LoginCheck(self):  # DB와 연동을 하는 시점
        try:
            conn = MySQLdb.connect(**config) 
            cursor = conn.cursor()
            no = self.txtNo.GetValue()
            name = self.txtName.GetValue()
            
            sql = """
            select count(jikwon_no) from jikwon
            where jikwon_no={0} and jikwon_name='{1}'
            """.format(no, name)
            
            cursor.execute(sql)
            count = cursor.fetchone()[0]  # 정보가 맞으면 1, 틀리면 0
            if count == 0:
                wx.MessageBox('로그인 실패', '알림', wx.OK)
                return
            
            else:
                self.staMsg.SetLabelText(no + '번 직원의 관리고객 목록')
                self.DisplayData(no)
                
        except Exception as e:
            wx.MessageBox('로그인 체크 에러:', str(e))
        
        finally:  # 끝나면 꼭 닫아준다
            cursor.close()
            conn.close()
        
    def DisplayData(self, no):
        try:
            conn = MySQLdb.connect(**config)
            cursor = conn.cursor()
            sql = """
            select gogek_no,gogek_name,gogek_tel from gogek where gogek_damsano={}
            """.format(no)
                
            cursor.execute(sql)
            gogekDatas = cursor.fetchall()
                
            self.lstGogek.DeleteAllItems()  # 고객목록 창 초기화
            for gdata in gogekDatas:
                i = self.lstGogek.InsertItem(1000, 0)    # 최대 행수를 적어준다
                self.lstGogek.SetItem(i, 0, str(gdata[0]))    # i행 0열
                self.lstGogek.SetItem(i, 1, gdata[1])
                self.lstGogek.SetItem(i, 2, gdata[2])
            self.staCount.SetLabelText('인원 수 : ' + str(len(gogekDatas)))
                
        except Exception as e:
            wx.MessageBox('고객 출력 에러 : ', str(e))
                
        finally:
            cursor.close()
            conn.close()
        
if __name__ == '__main__':
    app = wx.App()
    MyFrameLogin(None).Show()
    app.MainLoop()
In [2]:
# 출력 되는 창
with open('dbGUI1.gif','rb') as file:
    display(Image(file.read()))
2.2.2 연습2
In [ ]:
import wx
import wx.xrc
import MySQLdb
config = {
    'host':'127.0.0.1',
    'user':'root',
    'password':'123',
    'database':'test',
    'port':3306,
    'charset':'utf8',
    'use_unicode':True
}
class JikwonInfo( wx.Frame ):
    
    def __init__( self, parent ):
        wx.Frame.__init__ ( self, parent, id = wx.ID_ANY, title = wx.EmptyString, pos = wx.DefaultPosition, size = wx.Size( 400,300 ), style = wx.DEFAULT_FRAME_STYLE|wx.TAB_TRAVERSAL )
        
        # self.SetSizeHintsSz( wx.DefaultSize, wx.DefaultSize )
        self.SetBackgroundColour( wx.SystemSettings.GetColour( wx.SYS_COLOUR_MENU ) )
        
        bSizer1 = wx.BoxSizer( wx.VERTICAL )
        
        self.m_panel1 = wx.Panel( self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.TAB_TRAVERSAL )
        bSizer3 = wx.BoxSizer( wx.HORIZONTAL )
        
        self.m_staticText1 = wx.StaticText( self.m_panel1, wx.ID_ANY, u"직급입력 : ", wx.DefaultPosition, wx.DefaultSize, 0 )
        self.m_staticText1.Wrap( -1 )
        bSizer3.Add( self.m_staticText1, 0, wx.ALL, 5 )
        
        self.txtJik = wx.TextCtrl( self.m_panel1, wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, wx.DefaultSize, 0 )
        bSizer3.Add( self.txtJik, 0, wx.ALL, 5 )
        
        self.btnSearch = wx.Button( self.m_panel1, wx.ID_ANY, u"검색", wx.DefaultPosition, wx.DefaultSize, 0 )
        bSizer3.Add( self.btnSearch, 0, wx.ALL, 5 )
        
        
        self.m_panel1.SetSizer( bSizer3 )
        self.m_panel1.Layout()
        bSizer3.Fit( self.m_panel1 )
        bSizer1.Add( self.m_panel1, 0, wx.EXPAND |wx.ALL, 5 )
        
        
        self.m_panel2 = wx.Panel( self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.TAB_TRAVERSAL )
        bSizer4 = wx.BoxSizer( wx.VERTICAL )
        
        self.lstJikwon = wx.ListCtrl( self.m_panel2, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LC_REPORT )
        bSizer4.Add( self.lstJikwon, 0, wx.ALL|wx.EXPAND, 5 )
        
        
        self.m_panel2.SetSizer( bSizer4 )
        self.m_panel2.Layout()
        bSizer4.Fit( self.m_panel2 )
        bSizer1.Add( self.m_panel2, 1, wx.EXPAND |wx.ALL, 5 )
        
        self.m_panel3 = wx.Panel( self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.TAB_TRAVERSAL )
        bSizer5 = wx.BoxSizer( wx.HORIZONTAL )
        
        self.m_staticText2 = wx.StaticText( self.m_panel3, wx.ID_ANY, u"인원수 : ", wx.DefaultPosition, wx.DefaultSize, 0 )
        self.m_staticText2.Wrap( -1 )
        bSizer5.Add( self.m_staticText2, 0, wx.ALL, 5 )
        
        self.staCount = wx.StaticText( self.m_panel3, wx.ID_ANY, u"      ", wx.DefaultPosition, wx.DefaultSize, 0 )
        self.staCount.Wrap( -1 )
        bSizer5.Add( self.staCount, 0, wx.ALL, 5 )
        
        self.m_staticText3 = wx.StaticText( self.m_panel3, wx.ID_ANY, u"연봉평균 : ", wx.DefaultPosition, wx.DefaultSize, 0 )
        self.m_staticText3.Wrap( -1 )
        bSizer5.Add( self.m_staticText3, 0, wx.ALL, 5 )
        
        self.staPayavg = wx.StaticText( self.m_panel3, wx.ID_ANY, u"          ", wx.DefaultPosition, wx.DefaultSize, 0 )
        self.staPayavg.Wrap( -1 )
        bSizer5.Add( self.staPayavg, 0, wx.ALL, 5 )
 
        self.btnExit = wx.Button( self.m_panel3, wx.ID_ANY, u"종료", wx.DefaultPosition, wx.DefaultSize, 0 )
        bSizer5.Add( self.btnExit, 0, wx.ALL, 5 ) 
        
        
        self.m_panel3.SetSizer( bSizer5 )
        self.m_panel3.Layout()
        bSizer5.Fit( self.m_panel3 )
        bSizer1.Add( self.m_panel3, 1, wx.EXPAND |wx.ALL, 5 )
        
        
        self.SetSizer( bSizer1 )
        self.Layout()
        
        self.Centre( wx.BOTH )
        
        # lstJikwon 객체에 제목달기
        self.lstJikwon.InsertColumn(0, '사번', width=50)
        self.lstJikwon.InsertColumn(1, '직원명', width=130)
        self.lstJikwon.InsertColumn(2, '부서명', width=130)
        self.lstJikwon.InsertColumn(3, '성별', width=50)
              
        # Connect Events
        self.btnSearch.Bind( wx.EVT_BUTTON, self.OnSearch )
        self.btnExit.Bind( wx.EVT_BUTTON, self.Exit )
    
    def __del__( self ):
        pass
    # 구조 설명
    """
    OnSearch 에서는 입력 여부를 판단한다. 아무것도 입력이 안되있으면 전체직원정보 출력, 입력되있으면 SearchCheck으로 넘긴다
    SearchCheck 에서는 입력한 직급이 있는직급인지 없는직급인지 판단한다. 없으면 창 출력, 있으면 DisplayData로 넘긴다
    DisplayData 에서는 깔끔하게 출력하면 끝 ~~~~
    """
    def OnSearch( self, event ):
        
        # 직급 입력안했을시 전체 정보 출력
        if self.txtJik.GetValue() == '':  
            conn = MySQLdb.connect(**config) 
            cursor = conn.cursor()
            # 평균연봉도 구하기 위해 jikwon_pay 까지 일단 받아둔다
            sql = """
            select jikwon_no,jikwon_name,buser_name,jikwon_gen,jikwon_pay from jikwon 
            left outer join buser on buser_num=buser_no
            """
            cursor.execute(sql)   
            
            jikDatas = cursor.fetchall()
            self.lstJikwon.DeleteAllItems()  # 직원목록 창 초기화
            
            for jdata in jikDatas:
                i = self.lstJikwon.InsertItem(1000, 0)    # 최대 행수를 적어준다
                self.lstJikwon.SetItem(i, 0, str(jdata[0]))  # i행 0열
                self.lstJikwon.SetItem(i, 1, jdata[1])
                self.lstJikwon.SetItem(i, 2, jdata[2])
                self.lstJikwon.SetItem(i, 3, jdata[3])
            
            # 인원 수 출력
            self.staCount.SetLabelText(str(len(jikDatas)))
                
            # 평균 연봉 계산 후 출력
            pay = 0
            for i in jikDatas:
                pay += i[4]
            payavg = round(pay / len(jikDatas))
            self.staPayavg.SetLabelText(str(payavg))        
            return  # 함수 끝낸다
            
        self.SearchCheck()  # 무언가가 입력되있고 검색을 눌렀을때 실행
    
    
    # 직급이 있는 직급인지 없는 직급인지 확인후, 없으면 알림을 띄운다, 있으면 display 한다
    def SearchCheck(self):
        try:
            conn = MySQLdb.connect(**config) 
            cursor = conn.cursor()
            
            jik = self.txtJik.GetValue()
            sql = """
            select count(jikwon_jik) from jikwon
            where jikwon_jik='{}'
            """.format(jik)
            cursor.execute(sql)
            count = cursor.fetchone()[0]  # 직급이 없으면 0 반환
            if count == 0:
                wx.MessageBox('없는 직급 입니다', '알림', wx.OK)
                
            else:
                self.DisplayData(jik)
        
        except Exception as e:
            wx.MessageBox('검색 에러')
            
        finally:
            cursor.close()
            conn.close()
    
    
    # 깔끔하게 출력
    def DisplayData(self, jik):
        try:
            conn = MySQLdb.connect(**config) 
            cursor = conn.cursor()
            
            # 나중에 평균연봉도 구하기 위해 jikwon_pay 까지 일단 받아둔다
            sql = """
            select jikwon_no,jikwon_name,buser_name,jikwon_gen,jikwon_pay from jikwon 
            left outer join buser on buser_num=buser_no where jikwon_jik='{}'
            """.format(jik)
            cursor.execute(sql)
            jikDatas = cursor.fetchall()
            self.lstJikwon.DeleteAllItems()  # 직원목록 창 초기화
            
            for jdata in jikDatas:
                i = self.lstJikwon.InsertItem(1000, 0)    # 최대 행수를 적어준다
                self.lstJikwon.SetItem(i, 0, str(jdata[0]))  # i행 0열
                self.lstJikwon.SetItem(i, 1, jdata[1])
                self.lstJikwon.SetItem(i, 2, jdata[2])
                self.lstJikwon.SetItem(i, 3, jdata[3])
            
            # 인원 수 출력
            self.staCount.SetLabelText(str(len(jikDatas)))
                
            # 평균 연봉 계산 후 출력
            pay = 0
            for i in jikDatas:
                pay += i[4]
            payavg = round(pay / len(jikDatas))
            self.staPayavg.SetLabelText(str(payavg))
            
        except Exception as e:
            wx.MessageBox('직원 출력 에러')
        
        finally:  # 끝나면 꼭 닫아준다
            cursor.close()
            conn.close()
    
    # 종료 메소드
    def Exit(self, event):
        dlg = wx.MessageDialog(self, '정말 종료할까요?', '알림', wx.YES_NO)   
        imsi = dlg.ShowModal()
        if imsi == wx.ID_YES:
            dlg.Destroy()    # 대화 상자 닫기
            self.Close()    # Frame 닫기
        
        
if __name__ == '__main__':
    app = wx.App()
    JikwonInfo(None).Show()
    app.MainLoop()
In [3]:
# 출력 되는 창
with open('dbGUI2.gif','rb') as file:
    display(Image(file.read()))
2.2.3 레코드 이동 (id를 이용)
'mariadb.txt' 를 만들어서 안에다가 서버 주소를 dict 형식으로 넣어준다
In [8]:
display(Image("dbGUItext.jpg"))
In [ ]:
import wx
import wx.xrc
import ast  # sting 파일을 dict로 읽는다
import MySQLdb
# 주소 파일 읽어오기
with open('mariadb.txt', 'r') as f:
    config = ast.literal_eval(f.read())
    
datas = []
rec_r = 0  # 레코드 번호
    
class MyFrame1 ( wx.Frame ):
    
    def __init__( self, parent ):
        wx.Frame.__init__ ( self, parent, id = wx.ID_ANY, title = u"레코드 이동", pos = wx.DefaultPosition, size = wx.Size( 517,205 ), style = wx.DEFAULT_FRAME_STYLE|wx.TAB_TRAVERSAL )
        
        # self.SetSizeHintsSz( wx.DefaultSize, wx.DefaultSize )
        self.SetBackgroundColour( wx.SystemSettings.GetColour( wx.SYS_COLOUR_MENU ) )
        
        bSizer1 = wx.BoxSizer( wx.VERTICAL )
        
        self.m_panel1 = wx.Panel( self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.TAB_TRAVERSAL )
        bSizer2 = wx.BoxSizer( wx.HORIZONTAL )
        
        self.m_staticText1 = wx.StaticText( self.m_panel1, wx.ID_ANY, u"사번 : ", wx.DefaultPosition, wx.DefaultSize, 0 )
        self.m_staticText1.Wrap( -1 )
        bSizer2.Add( self.m_staticText1, 0, wx.ALL, 5 )
        
        self.txtNo = wx.TextCtrl( self.m_panel1, wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, wx.DefaultSize, 0 )
        bSizer2.Add( self.txtNo, 0, wx.ALL, 5 )
        
        self.m_staticText2 = wx.StaticText( self.m_panel1, wx.ID_ANY, u"이름 : ", wx.DefaultPosition, wx.DefaultSize, 0 )
        self.m_staticText2.Wrap( -1 )
        bSizer2.Add( self.m_staticText2, 0, wx.ALL, 5 )
        
        self.txtName = wx.TextCtrl( self.m_panel1, wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, wx.DefaultSize, 0 )
        bSizer2.Add( self.txtName, 0, wx.ALL, 5 )
        
        
        self.m_panel1.SetSizer( bSizer2 )
        self.m_panel1.Layout()
        bSizer2.Fit( self.m_panel1 )
        bSizer1.Add( self.m_panel1, 1, wx.EXPAND |wx.ALL, 5 )
        
        self.m_panel2 = wx.Panel( self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.TAB_TRAVERSAL )
        bSizer3 = wx.BoxSizer( wx.HORIZONTAL )
        
        self.btn1 = wx.Button( self.m_panel2, wx.ID_ANY, u"<<<", wx.DefaultPosition, wx.DefaultSize, 0 )
        bSizer3.Add( self.btn1, 0, wx.ALL, 5 )
        
        self.btn2 = wx.Button( self.m_panel2, wx.ID_ANY, u"<", wx.DefaultPosition, wx.DefaultSize, 0 )
        bSizer3.Add( self.btn2, 0, wx.ALL, 5 )
        
        self.btn3 = wx.Button( self.m_panel2, wx.ID_ANY, u">", wx.DefaultPosition, wx.DefaultSize, 0 )
        bSizer3.Add( self.btn3, 0, wx.ALL, 5 )
        
        self.btn4 = wx.Button( self.m_panel2, wx.ID_ANY, u">>>", wx.DefaultPosition, wx.DefaultSize, 0 )
        bSizer3.Add( self.btn4, 0, wx.ALL, 5 )
        
        
        self.m_panel2.SetSizer( bSizer3 )
        self.m_panel2.Layout()
        bSizer3.Fit( self.m_panel2 )
        bSizer1.Add( self.m_panel2, 1, wx.EXPAND |wx.ALL, 5 )
        
        
        self.SetSizer( bSizer1 )
        self.Layout()
        
        self.Centre( wx.BOTH )
        
        # 수정이 불가하게 한다
        self.txtNo.SetEditable(False)
        self.txtName.SetEditable(False)
        
        # Connect Events
        # id 는 직접 입력
        self.btn1.id = 1
        self.btn2.id = 2
        self.btn3.id = 3
        self.btn4.id = 4
        self.btn1.Bind( wx.EVT_BUTTON, self.BtnProcess )
        self.btn2.Bind( wx.EVT_BUTTON, self.BtnProcess )
        self.btn3.Bind( wx.EVT_BUTTON, self.BtnProcess )
        self.btn4.Bind( wx.EVT_BUTTON, self.BtnProcess )
    
        self.DbLoad()  # db와 연결 메소드
        
    # Virtual event handlers, overide them in your derived class
    def BtnProcess( self, event ):
        id = event.GetEventObject().id
        global rec_r
        
        if id == 1:  # <<< 버튼은 처음 값 보여준다
            rec_r = 0
        elif id == 2:  # < 버튼은 이전 값 보여준다
            rec_r = rec_r - 1
        elif id == 3:  # > 버튼은 이후 값 보여준다
            rec_r = rec_r + 1
            if rec_r > (len(datas) - 1):rec_r = len(datas) - 1
        elif id == 4:  # >>> 버튼은 마지막 값 보여준다
            rec_r = len(datas) - 1
        
        self.ShowData(rec_r)
    
    # db 연결
    def DbLoad(self):
        try:
            conn = MySQLdb.connect(**config)
            cursor = conn.cursor()
            sql = "select * from jikwon"
            cursor.execute(sql)
            global datas
            datas = cursor.fetchall()
           
            self.ShowData(rec_r)
            
        except Exception as e:
            wx.MessageBox('연결 에러', '에러', wx.OK)
            
        finally: 
            cursor.close()
            conn.close()
        
    def ShowData(self, r):
        self.txtNo.SetLabel(str(datas[r][0]))
        self.txtName.SetLabel(str(datas[r][1]))
    
    
if __name__ == '__main__':
    app = wx.App()
    MyFrame1(None).Show()
    app.MainLoop()
In [9]:
# 출력 되는 창
with open('dbGUI3.gif','rb') as file:
    display(Image(file.read()))
In [ ]:
import wx
import wx.xrc
import ast
import MySQLdb
with open('mariadb.txt', 'r') as f:
    config = ast.literal_eval(f.read())
    
class MyFrame ( wx.Frame ):
    
    def __init__( self, parent ):
        wx.Frame.__init__ ( self, parent, id = wx.ID_ANY, title = u"CRUD_test", pos = wx.DefaultPosition, size = wx.Size( 451,492 ), style = wx.DEFAULT_FRAME_STYLE|wx.TAB_TRAVERSAL )
        
        # self.SetSizeHintsSz( wx.DefaultSize, wx.DefaultSize )
        self.SetBackgroundColour( wx.SystemSettings.GetColour( wx.SYS_COLOUR_MENU ) )
        
        bSizer1 = wx.BoxSizer( wx.VERTICAL )
        
        self.m_panel1 = wx.Panel( self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.TAB_TRAVERSAL )
        bSizer2 = wx.BoxSizer( wx.HORIZONTAL )
        
        self.m_staticText1 = wx.StaticText( self.m_panel1, wx.ID_ANY, u"번호 : ", wx.DefaultPosition, wx.DefaultSize, 0 )
        self.m_staticText1.Wrap( -1 )
        bSizer2.Add( self.m_staticText1, 0, wx.ALL, 5 )
        
        self.txtNo = wx.TextCtrl( self.m_panel1, wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, wx.DefaultSize, 0 )
        bSizer2.Add( self.txtNo, 0, wx.ALL, 5 )
        
        self.btnInsert = wx.Button( self.m_panel1, wx.ID_ANY, u"등록", wx.DefaultPosition, wx.DefaultSize, 0 )
        bSizer2.Add( self.btnInsert, 0, wx.ALL, 5 )
        
        
        self.m_panel1.SetSizer( bSizer2 )
        self.m_panel1.Layout()
        bSizer2.Fit( self.m_panel1 )
        bSizer1.Add( self.m_panel1, 0, wx.EXPAND |wx.ALL, 5 )
        
        self.m_panel2 = wx.Panel( self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.TAB_TRAVERSAL )
        bSizer3 = wx.BoxSizer( wx.HORIZONTAL )
        
        self.m_staticText2 = wx.StaticText( self.m_panel2, wx.ID_ANY, u"이름 : ", wx.DefaultPosition, wx.DefaultSize, 0 )
        self.m_staticText2.Wrap( -1 )
        bSizer3.Add( self.m_staticText2, 0, wx.ALL, 5 )
        
        self.txtName = wx.TextCtrl( self.m_panel2, wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, wx.DefaultSize, 0 )
        bSizer3.Add( self.txtName, 0, wx.ALL, 5 )
        
        self.btnUpdate = wx.Button( self.m_panel2, wx.ID_ANY, u"수정", wx.DefaultPosition, wx.Size( 70,-1 ), 0 )
        bSizer3.Add( self.btnUpdate, 0, wx.ALL, 5 )
        
        self.btnConfirm = wx.Button( self.m_panel2, wx.ID_ANY, u"확인", wx.DefaultPosition, wx.Size( 70,-1 ), 0 )
        bSizer3.Add( self.btnConfirm, 0, wx.ALL, 5 )
        
        
        self.m_panel2.SetSizer( bSizer3 )
        self.m_panel2.Layout()
        bSizer3.Fit( self.m_panel2 )
        bSizer1.Add( self.m_panel2, 0, wx.EXPAND |wx.ALL, 5 )
        
        self.m_panel3 = wx.Panel( self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.TAB_TRAVERSAL )
        bSizer4 = wx.BoxSizer( wx.HORIZONTAL )
        
        self.m_staticText3 = wx.StaticText( self.m_panel3, wx.ID_ANY, u"전화 : ", wx.DefaultPosition, wx.DefaultSize, 0 )
        self.m_staticText3.Wrap( -1 )
        bSizer4.Add( self.m_staticText3, 0, wx.ALL, 5 )
        
        self.txtTel = wx.TextCtrl( self.m_panel3, wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, wx.DefaultSize, 0 )
        bSizer4.Add( self.txtTel, 0, wx.ALL, 5 )
        
        self.btnDelete = wx.Button( self.m_panel3, wx.ID_ANY, u"삭제", wx.DefaultPosition, wx.DefaultSize, 0 )
        bSizer4.Add( self.btnDelete, 0, wx.ALL, 5 )
        
        
        self.m_panel3.SetSizer( bSizer4 )
        self.m_panel3.Layout()
        bSizer4.Fit( self.m_panel3 )
        bSizer1.Add( self.m_panel3, 0, wx.EXPAND |wx.ALL, 5 )
        
        self.m_panel4 = wx.Panel( self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.TAB_TRAVERSAL )
        bSizer5 = wx.BoxSizer( wx.VERTICAL )
        
        self.lstMember = wx.ListCtrl( self.m_panel4, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LC_REPORT )
        bSizer5.Add( self.lstMember, 1, wx.ALL|wx.EXPAND, 5 )
        
        
        self.m_panel4.SetSizer( bSizer5 )
        self.m_panel4.Layout()
        bSizer5.Fit( self.m_panel4 )
        bSizer1.Add( self.m_panel4, 1, wx.EXPAND |wx.ALL, 5 )
        
        self.m_panel5 = wx.Panel( self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.TAB_TRAVERSAL )
        bSizer6 = wx.BoxSizer( wx.HORIZONTAL )
        
        self.m_staticText4 = wx.StaticText( self.m_panel5, wx.ID_ANY, u"인원수 : ", wx.DefaultPosition, wx.DefaultSize, 0 )
        self.m_staticText4.Wrap( -1 )
        bSizer6.Add( self.m_staticText4, 0, wx.ALL, 5 )
        
        self.lblCnt = wx.StaticText( self.m_panel5, wx.ID_ANY, u"0", wx.DefaultPosition, wx.DefaultSize, 0 )
        self.lblCnt.Wrap( -1 )
        bSizer6.Add( self.lblCnt, 0, wx.ALL, 5 )
        
        
        self.m_panel5.SetSizer( bSizer6 )
        self.m_panel5.Layout()
        bSizer6.Fit( self.m_panel5 )
        bSizer1.Add( self.m_panel5, 0, wx.EXPAND |wx.ALL, 5 )
        
        
        self.SetSizer( bSizer1 )
        self.Layout()
        
        self.Centre( wx.BOTH )
        
        # lstMember에 제목주기
        self.lstMember.InsertColumn(0, '번호', width=50)
        self.lstMember.InsertColumn(1, '이름', width=100)
        self.lstMember.InsertColumn(2, '전화', width=150)
        
    
        # Connect Events
        self.btnInsert.id = 1
        self.btnUpdate.id = 2    # 실행중에 id를 변경. 이유는 수정,취소 두가지 작업
        self.btnConfirm.id = 3
        self.btnDelete.id = 4
        self.btnInsert.Bind( wx.EVT_BUTTON, self.BtnProcess )
        self.btnUpdate.Bind( wx.EVT_BUTTON, self.BtnProcess )
        self.btnConfirm.Bind( wx.EVT_BUTTON, self.BtnProcess )
        self.btnDelete.Bind( wx.EVT_BUTTON, self.BtnProcess )
        
        self.btnConfirm.Enable(enable=False)  # 수정 확인 버튼은 비활성화 상태에서 출발
        self.ShowData()
        
    def __del__( self ):
        pass
    
    def BtnProcess( self, event ):
        id = event.GetEventObject().id
        
        if id == 1:
            self.MemInsert()  # 등록처리 메소드 호출
            
        elif id == 2:
            self.MemUpdateReady()  # 수정준비 메소드 호출
            
        elif id == 3:
            self.MemUpdateProcess()  # 수정처리 메소드 호출
            
        elif id == 4:
            self.MemDelete()  # 삭제처리 메소드 호출
        elif id == 5:
            self.MemUpdateCancel()  # 수정취소 메소드 호출           
            
    def ShowData(self):
        try:
            conn = MySQLdb.connect(**config)
            cursor = conn.cursor()
            
            sql = "select * from pymem"
            cursor.execute(sql)
            
            self.lstMember.DeleteAllItems()  # 초기화
            count = 0
            for data in cursor:
                i = self.lstMember.InsertItem(10000, 0)  # 동적인 번호 부여
                self.lstMember.SetItem(i, 0, str(data[0]))  # 번호
                self.lstMember.SetItem(i, 1, data[1])  # 이름
                self.lstMember.SetItem(i, 2, data[2])  # 전화
                count += 1
                
            self.lblCnt.SetLabel(str(count))
            
        except Exception as e:
            wx.MessageBox('읽기 오류 : ' + str(e), '에러', wx.OK)
        
        finally:
            cursor.close()
            conn.close()
 
    def MemInsert(self):  # 회원추가
        no = self.txtNo.GetValue()
        name = self.txtName.GetValue()
        tel = self.txtTel.GetValue()
        
        if no == '' or name == '' or tel == '':  # 값을 입력 안했을 때
            wx.MessageBox('자료를 입력하시오', '알림', wx.OK)
            return
        
        try:
            conn = MySQLdb.connect(**config)
            cursor = conn.cursor()
            
            # 새로운 번호의 등록이 가능한지 확인 작업 
            data = self.SelectData(no)
            if data != None:
                wx.MessageBox('이미 등록된 번호입니다', '알림', wx.OK)
                self.txtNo.SetFocus()  # 맨위로 커서를 올림
                return
            
            # 등록이 가능한 상태라면 등록작업 계속 진행
            sql = "insert into pymem values(%s,%s,%s)"
            cursor.execute(sql, (no, name, tel))
            conn.commit()  # commit을 해야 데이터 베이스에 등록이 됨
            self.ShowData()  # 결과 띄우기
            
            self.txtNo.SetValue('')  # 화면에 입력한 자료 초기화
            self.txtName.SetValue('')
            self.txtTel.SetValue('')
            
        except Exception as e:
            wx.MessageBox('추가 오류 : ' + str(e), '에러', wx.OK )
            conn.rollback()
        
        finally:
            cursor.close()
            conn.close()
        
    def MemUpdateReady(self):  # 회원수정 준비
        textDlg = wx.TextEntryDialog(None, '수정할 번호를 입력하시오', '알림')  # 입력창 띄우기
        if textDlg.ShowModal() == wx.ID_OK:
            if textDlg.GetValue() == '':
                return
            upno = textDlg.GetValue()
            data = self.SelectData(upno)
            if data == None:
                wx.MessageBox(upno + "번은 등록된자료가 아닙니다")
                return
            
            # 수정 준비작업 계속
            self.txtNo.SetValue(str(data[0]))  # 수정할 자료 화면에 표시
            self.txtName.SetValue(data[1])
            self.txtTel.SetValue(data[2])
            
            # 번호칸 비활성화
            self.txtNo.SetEditable(False)  
            
            # 수정에서 취소로 변경
            self.btnUpdate.SetLabel('취소')  
            self.btnUpdate.id = 5 
            
            # 확인버튼 활성화
            self.btnConfirm.Enable(enable = True)
            
        else:  # 취소 눌렀을 때 
            return
        textDlg.Destroy()  # TextEntryDialog 닫기 마무리 처리함
            
    def MemUpdateProcess(self):  # 회원수정 처리
        no = self.txtNo.GetValue()
        name = self.txtName.GetValue()
        tel = self.txtTel.GetValue()
        
        if name == '' or tel == '':
            wx.MessageBox('수정 자료를 입력하시오', '알림', wx.OK)
            return
        
        try:
            conn = MySQLdb.connect(**config)
            cursor = conn.cursor()
            
            sql = "update pymem set irum=%s, junhwa=%s where bun=%s"
            cursor.execute(sql, (name, tel, no))
            conn.commit()
            self.ShowData()
            self.MemUpdateCancel()  # 수정 후 화면 초기화
            
        except Exception as e:
            wx.MessageBox('수정 에러 : ' + str(e), '알림', wx.OK)
            conn.rollback()
            
        finally:
            cursor.close()
            conn.close()
    
    def MemUpdateCancel(self):  # 회원수정 준비를 취소
        self.txtNo.SetValue('')
        self.txtName.SetValue('')
        self.txtTel.SetValue('')
        
        self.txtNo.SetEditable(True)  # 이름칸 다시 작성 가능
        self.btnUpdate.SetLabel('수정')
        self.btnUpdate.id = 2
        self.btnConfirm.Enable(enable=False)  # 확인칸 다시 비활성화
    
    def MemDelete(self):  # 회원삭제
        textDlg = wx.TextEntryDialog(None, '삭제할 번호를 입력하시오', '알림')  # 입력창 띄우기
        if textDlg.ShowModal() == wx.ID_OK:
            if textDlg.GetValue() == '':
                return
            
            delno = textDlg.GetValue()
            data = self.SelectData(delno)
            if data == None:
                wx.MessageBox(delno + "번은 등록된자료가 아닙니다", '알림', wx.OK)
                return
            
            # 삭제 작업 계속 진행
            try:
                conn = MySQLdb.connect(**config)
                cursor = conn.cursor()
                sql = "delete from pymem where bun={}".format(delno)
                cursor.execute(sql)
                conn.commit()
                self.ShowData()
                
            
            except Exception as e:
                wx.MessageBox("삭제 에러 : " + str(e), '알림', wx.OK)
            
        else:  # 취소 눌렀을 때 
            return
        textDlg.Destroy()  # TextEntryDialog 닫기 마무리 처리함
    
    def SelectData(self, no):  # 레코드 읽는 메소드
        try:
            conn = MySQLdb.connect(**config)
            cursor = conn.cursor()
            
            sql = "select * from pymem where bun={0}".format(no)
            cursor.execute(sql)
            data = cursor.fetchone()
            return data
            
        except Exception as e:
            wx.MessageBox('추가오류 :' + str(e), '에러', wx.OK)
            
        finally:
            cursor.close()
            conn.close()
                           
if __name__ == '__main__':
    app = wx.App()
    MyFrame(None).Show()
    app.MainLoop()
    
    
In [10]:
# 출력 되는 창
with open('dbGUI4.gif','rb') as file:
    display(Image(file.read()))
  Categories :
  
  
      Practice