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