1. tkinter 화면이동 구현

프로젝트를 진행할 때 모듈화를 진행하여 구현하기 위해 메인파일 내부에 화면이동을 위한 기능을 몰아넣기로 했다.
import tkinter as tk
from MainFrame import MF
from AdminFrame import AF
from AdminStock import AS
from AdminCollect import AC
# 메인 애플리케이션 클래스를 정의
class App(tk.Tk):
def __init__(self):
super().__init__()
self.configure(bg="#008080")
self.geometry("800x800") #창 크기를 지정하는 것이 가능하다.
self.title("20194035 자판기 앱 ") # 창 제목
self.frames = {} # 각 화면을 저장할 딕셔너리를 초기화
for F in (MF, AF, AS, AC): # MF, Admin, Admin1, Admin2 클래스를 순회함
page_name = F.__name__ # 클래스 이름을 가져옴
frame = F(parent=self, controller=self) # 클래스의 인스턴스를 생성함
self.frames[page_name] = frame # 인스턴스를 딕셔너리에 저장함
frame.grid(row=0, column=0, sticky="nsew") # 각 화면을 그리드에 배치함
self.show_frame("MF") # 초기 화면으로 Mainframe 화면을 표시함
# 화면을 전환하는 메서드를 정의합니다
def show_frame(self, page_name):
if self.frames.get(page_name):
frame = self.frames[page_name] # 전환할 화면을 딕셔너리에서 가져옴
frame.tkraise() # 해당 화면을 맨 앞으로 가져옵니다
if __name__ == "__main__":
app = App() # App 클래스의 인스턴스를 생성함
app.mainloop() # 애플리케이션의 메인 루프를 시작함
메인 앱클래스를 정의해서 프로그램을 실행하는 파일을 곧 이 파일로 지정했다. 메인 앱 클래스를 정의한 후, 각 화면을 담당하는 파일 4개를 가져왔다. 이후 화면 클래스를 반복시켜서 메인 기본 (빈 800x800) 화면 위에 4개의 슬라이드를 쌓아서 넣는다. 이후 show_frame 메서드를 통해서 화면 간 이동을 진행할 때 원하는 프레임을 딕셔너리에서 선택해서 화면을 맨 앞으로 가져오도록 구현을 진행했다. 마지막은 프로그램 메인루프를 실행시켜 구현이 되도록 하였다.
2. 메인 프레임 세부구현 코드 설명
첫 번째로 보이는 메인 프레임에 대한 세부 설명을 진행해 보자.

먼저 메인프레임 코드이다. 관리자 화면 로그인 기능, 음료 버튼을 눌렀을 때의 결제 처리, 금액 버튼을 눌렀을 때 현재 입력된 금액의 변경, 반환하기 시의 금액의 반환 기능을 인앱상에서 전역변수에 대한 연산이 가능하게 구현했다.
MainFrame.py
import tkinter as tk
import tkinter.font
from PIL import Image, ImageTk
from tkinter import messagebox
# 파일 모듈화
import drink_box_maker
import money_inout
import global_var
# 홈 화면을 정의하는 클래스입니다
class MF(tk.Frame):
def __init__(self, parent, controller):
super().__init__(parent)
self.controller = controller
self.configure(bg="#008080")
#이미지 객체를 변수에 저장
# 2x3 그리드 형태로 음료 상자를 배치
font = tkinter.font.Font(family="Consolas", size=12) # 폰트정의
#######################################################
# 음료 상자 프레임 생성
drink_frame = tk.Frame(self, bg="#008080")
drink_frame.grid(row=1, column=0, columnspan=3, padx=60, pady=10)
#######################################
# 관리자 버튼 생성
image_path = 'VM/img/admin.jpg' # 관리자 버튼 이미지 경로
image = Image.open(image_path)
resized_image = image.resize((35, 35), Image.LANCZOS)
photo = ImageTk.PhotoImage(resized_image)
# "Admin" 버튼을 생성하고 클릭 시 Admin 화면으로 전환함
# admin_button = tk.Button(self, image=photo, command=lambda: controller.show_frame("AF"))
admin_button = tk.Button(self, image=photo, command=lambda: show_password_window())
def show_password_window():
# 비밀번호 입력 창 생성
password_window = tk.Toplevel(self)
password_window.geometry("300x150")
password_window.title("관리자 로그인")
# 비밀번호 입력 필드 및 버튼 생성
password_label = tk.Label(password_window, text="로그인을 위해 비밀번호를 입력해 주세요")
password_label.pack(pady=10)
password_entry = tk.Entry(password_window, show="*")
password_entry.pack(pady=5)
def check_password():
# 사용자가 입력한 비밀번호를 가져옴
entered_password = password_entry.get()
# 비밀번호가 맞는지 확인
if entered_password == global_var.password:
messagebox.showinfo("로그인 성공", "환영합니다. 관리자님")
controller.show_frame("AF")
password_window.destroy() # 비밀번호 창을 닫음
else:
messagebox.showerror("Error", "비밀번호 잘못되었습니다. 다시 시도해주세요.")
submit_button = tk.Button(password_window, text="확인", command=check_password)
submit_button.pack(pady=20)
admin_button.image = photo
admin_button.grid(row=0, column=0, padx=10, pady=10, columnspan=3,sticky='nw')
####################################################
# 하단에 금액 입력 버튼을 배치할 프레임 생성
money_frame = tk.Frame(self, bg="#008080")
money_frame.grid(row=2, column=0, columnspan=5, padx=30, pady=10)
#################################################
#하단 프레임 구성 (반환하기, 금액 출력 버튼)
# 금액 입력 창 및 반환하기 버튼을 배치할 프레임 생성
bottom_frame = tk.Frame(self, bg="#008080")
bottom_frame.grid(row=4, column=0, columnspan=5, padx=10, pady=10, sticky="we")
money_label = tk.Label(bottom_frame, text=("현재 금액: "+str(money_inout.get_money_sum())+ " 원"), font=font, width = 40)
money_label.grid(row=0, column=0, padx=50, pady=20, sticky="w")
###################################################
# money frame 에 금액 버튼 생성하기
for i in range (1, 6):
money_inout.create_money_button(key=i, row=4, column=i-1, in_root=money_frame, change_target=money_label) # 4번째 행에 금액 버튼 배치
###################################################
# 음료버튼 생성하기
drk_img=drink_box_maker.drink_box(key=1,image_path='VM/img/'+global_var.drink_data[1]["음료"]+'.jpg', drink_name=global_var.drink_data[1]["음료"], drink_quantity=global_var.drink_data[1]["수량"],
drink_price=global_var.drink_data[1]["가격"], in_root=drink_frame, row = 1, column = 0, change_target=money_label)
drk_img=drink_box_maker.drink_box(key=2,image_path='VM/img/'+global_var.drink_data[2]["음료"]+'.jpg', drink_name=global_var.drink_data[2]["음료"], drink_quantity=global_var.drink_data[2]["수량"],
drink_price=global_var.drink_data[2]["가격"], in_root=drink_frame, row = 1, column = 1, change_target=money_label)
drk_img=drink_box_maker.drink_box(key=3,image_path='VM/img/'+global_var.drink_data[3]["음료"]+'.jpg', drink_name=global_var.drink_data[3]["음료"], drink_quantity=global_var.drink_data[3]["수량"],
drink_price=global_var.drink_data[3]["가격"], in_root=drink_frame, row = 1, column = 2, change_target=money_label)
drk_img=drink_box_maker.drink_box(key=4,image_path='VM/img/'+global_var.drink_data[4]["음료"]+'.jpg', drink_name=global_var.drink_data[4]["음료"], drink_quantity=global_var.drink_data[4]["수량"],
drink_price=global_var.drink_data[4]["가격"], in_root=drink_frame, row = 2, column = 0, change_target=money_label)
drk_img=drink_box_maker.drink_box(key=5,image_path='VM/img/'+global_var.drink_data[5]["음료"]+'.jpg', drink_name=global_var.drink_data[5]["음료"], drink_quantity=global_var.drink_data[5]["수량"],
drink_price=global_var.drink_data[5]["가격"], in_root=drink_frame, row = 2, column = 1, change_target=money_label)
drk_img=drink_box_maker.drink_box(key=6,image_path='VM/img/'+global_var.drink_data[6]["음료"]+'.jpg', drink_name=global_var.drink_data[6]["음료"], drink_quantity=global_var.drink_data[6]["수량"],
drink_price=global_var.drink_data[6]["가격"], in_root=drink_frame, row = 2, column = 2, change_target=money_label)
# 반환함수 선언하기
def return_money():
money_label.config(text="현재 금액: 0원")
print(str(money_inout.get_money_sum())+"원이 반환되었습니다.")
# 딕셔너리의 모든 값을 0으로 초기화
for key in global_var.current_amount:
global_var.current_amount[key] = 0
#반환하기 버튼 눌렀을 때 이벤트 처리 진행하기
return_button = tk.Button(bottom_frame, text="반환하기", command=return_money, font=font)
return_button.grid(row=0, column=1, padx=50, pady=20, sticky="w")
# 하단 영역의 각 열 크기를 조정하여 균형 있게 배치
bottom_frame.grid_columnconfigure(0, weight=1)
2. 1관리자 버튼 이벤트 처리
관리자 버튼 이미지 경로는 화면을 연결해 놓은 그대로이다. 다만 이때 show_password_window 함수를 이용해서 안내창을 나타내고, 이때의 비밀번호 라벨과 엔트리를 띄운 후, check_password 메서드를 선언해 로그인 확인 버튼의 이벤트 처리를 할 때 엔트리에서의 정보를 가져와 전역 변수로 선언 password에 대해 비교를 진행하여 로그인 성공 여부를 정해서 화면 이동 시켜준다.
2.2 음료구매 이벤트 처리
음료를 구매할 때 무슨 절차가 발생해야 할까? 음료를 누르면 drink_data에 접근해서 음료 가격을 알아내서 저장하고, 이를 계산하기 위해 자판기에 입력된 금액 current_amount 접근한 후, 음료의 가격이 자판기에 입력된 금액 보다 적다면 구매가 가능한 금액이 들어온 상태이므로 전체 잔돈데이터에 자판기에 입력된 금액을 추가시켜서 필요한 만큼의 잔돈만을 계산 음료의 수량을 1만큼 줄이고, 현재 주어진 잔돈만큼을 라벨에 출력시킨다. 이와 동시에 config를 통해서 음료자체의 ui도 실시간으로 함께 변경될 수 있도록 했다.
drink_box_maker.py
import tkinter as tk
import tkinter.font
from PIL import Image, ImageTk
import money_inout
from tkinter import messagebox
import global_var
# 현재금액 라벨은 money_label
#이미지경로, 음료이름, 음료 개수, 음료 가격, 그리드 뷰에서의 위치, 이벤트 처리 대상 객체(현재금액) 를 입력받아 시각화 하는 함수를 선언한다.
def drink_box(key, drink_name, drink_quantity, drink_price, in_root, image_path, row, column, change_target): # 프레임을 생성해 그 안에 데이터를 넣고 리턴시킨다.
# 이미지 로드
image = Image.open(image_path)
resized_image = image.resize((170, 170), Image.ANTIALIAS)
photo = ImageTk.PhotoImage(resized_image)
font = tkinter.font.Font(family="Consolas", size=12) # 폰트 지정하기
#음료 설명 추가 (음료이름, 음료 수량)
# 텍스트와 이미지를 포함하는 프레임 생성
frame = tk.Frame(in_root, width=250, height=250) # 루트에 대한 프레임을 생성한다.
frame.grid(row=row, column=column, padx=10, pady=10) # grid 메소드를 사용하여 그리드 형태로 배치
frame.pack_propagate(False) # 프레임 크기 고정
# 텍스트 라벨 생성
label1 = tk.Label(frame, text=drink_name, font = font)
label2 = tk.Label(frame, text="수량: "+str(drink_quantity), font = font)
# 라벨을 버튼 위에 배치
label1.grid(row=0, column=0, sticky="w") # 라벨을 왼쪽으로 정렬하여 그리드에 배치
label2.grid(row=0, column=1, sticky="w")
# 버튼 클릭 이벤트 핸들러 함수
# 음료 구매 이벤트 음료를 누르면 drink_data로 음료의 가격에 접근하여 이 가격에 대해서
# 현재 입력된금액 current_amount 에 접근한 후, 적절한 금액울 분배해 current_amount에서 필요한
# 금액만큼 제외한다.이후 change target에 최종 금액에 대한 업데이트를 진행한다.
# label1에서 슈량에 대한 데이터도 변경을 해야한다.
def on_button_click(change_target):
if(global_var.drink_data[key]["수량"] <= 0):
messagebox.showinfo(title = "음료가 없습니다.", message=f" 음료가 없습니다..ㅠㅠ")
return False
# 잔돈 금액과 동전 구성들리턴
change, updated_current_amount =calculate_change(current_amount=global_var.current_amount, drink_price=drink_price)
if change: #올바르게 코드가 처리 가능한 경우
messagebox.showinfo(title = "결제가 이루어집니다.",
message=f"금액을 {drink_price}원 현재 잔액{change}\
남은 지폐와 동전: {updated_current_amount}")
# 현재가 금액의 변경 자체는 이루어짐
# 음료 재고에 대한 업데이트와 음료 재고 라벨을 변경해야함
global_var.drink_data[key]["수량"] -= 1 # 음료수 수량을 1 줄인다.
label2.config(text= "수량: "+str(global_var.drink_data[key]["수량"]), font = font)
# 현재 금액 라벨 표시 변경
change_target.config(text="현재 금액: {} 원".format(money_inout.get_money_sum()))
else:
messagebox.showinfo(title = "금액을 만들 수 없습니다.", message=f" 현재 잔돈은 다음과 같습니다. {updated_current_amount}")
# 텍스트와 이미지를 포함하는 버튼 생성
#프레임을 대상으로 버튼 추가
button = tk.Button(frame, text=str(drink_price) + " 원", image=photo,
command=lambda:on_button_click(change_target=change_target), width = 200, height = 200, font = font,
compound = tk.TOP # 텍스트 위치를 바닥으로
)
#이미지 객체에 대한 참조를 유지
button.image = photo
button.grid(row=2, column=0, columnspan=2) # 버튼을 2행 0열에 배치하고, 2열에 걸쳐 표시
#거스름돈 계산 함수 거스름돈과 거스름돈 구성 리턴
def calculate_change(current_amount, drink_price):
#음료 수량이 0일때 예외처리
#애초에 돈이 부족한 경우 계산
if(money_inout.get_money_sum() < drink_price):
messagebox.showinfo(title="안내", message="금액이 부족합니다. ")
return None, current_amount
#####################################################################
#입력 금액이 음료를 사기 위한 금액은 넘었다.
# 기계내에 존재하는 잔돈을 합친 변수를 생성한다.
updated_current = global_var.money_data
# 이제 거스름돈을 계산해보자.
change_required = money_inout.get_money_sum() - drink_price
cge = change_required # 리턴할 거스름돈 저장 change_required는 차감시키면서 거스름돈 계산
tmp_current_amount = current_amount # 잘 안됐을 때 되돌리기 위해 존재
# 현재 입력된 금액을 업데이트를 진행해야 한다.
i = 1
# 결제된 금액(current amount)를 실제 잔돈의 수량인 updated_current에 적용함
for a, amount in global_var.current_amount.items():
updated_current[i]["수량"] += amount
i += 1
for key in current_amount:
current_amount[key] = 0
# 입력금액을 모두 통합 금액인 updated_current로 옮겼기 때문에 0으로 변경한다.
# current_amount = {10: 0, 50: 3, 100:0, 500:1, 1000:1}
# 적용한 updated_current = {1: {"금액": 10, "수량": 20}, 2: {"금액": 50, "수량": 23},
# 3: {"금액": 100, "수량": 20},4: {"금액": 500, "수량": 21}, 5: {"금액": 1000, "수량": 21},
# 와 같은 형식이 된다.
# 변경을 저장하는 변수
change_denominations = []
# 거스름돈 계산 부분 사용 가능 금액 선택 및 차감
# updated keys를 금액이 높은 순으로 정렬한다.
for key in sorted(updated_current.keys(), reverse=True):
denomination = updated_current[key]["금액"] # 현재 키에 해당하는 금액을 변수에 저장함
while change_required >= denomination and updated_current[key]["수량"] > 0:
# changed_required가 현재금액 보다 크거나 같고, 해당금액의 수량이 0보다 큰동안 루프실행
change_required -= denomination # 이조건이 참인동안 계속 현재 화폐를 거스름돈으로 씀
updated_current[key]["수량"] -= 1 # change_required에서 금액 차감하고 돈의 수량을 감소
change_denominations.append(denomination) # 리스트에 현재 금액 값을 추가한다.
# [100, 100, 50, 10] 과 같은 식으로 거스름돈리스트가 생기는것
if (change_required!= 0): # 돈이 딱 떨어지지 않은 경우 되돌린다.
# money_data에서 차감하기
current_amount = tmp_current_amount
# 잔돈 화폐가 부족합니다. 출력
messagebox.showinfo(title="안내", message="잔돈 갯수가 부족합니다. ")
return None, current_amount
else:
for denomination in change_denominations:
current_amount[denomination] += 1
# 0으로 변해 있는 current amount를 업데이트 시킨다.
global_var.money_data = updated_current # 변화를 적용한다.
print(global_var.money_data)
return cge, current_amount # 거스름돈과 거스름돈 구성 동전들 리턴
잔돈을 계산할 때 현재 금액을 변수에 저장하고, 내림차순 정렬된 잔돈 데이터가 금액 보다 적은 상태라면 최대한 넣어서 잔돈을 어떤 상황에서 표현이 가능하도록 구현하였다. 돈이 딱 떨어지지 않는 잔돈이 부족한 상황에서의 예외처리 역시 진행했다. change와 updated_current_amount 모두 참값(false가 아닌)이 들어와야 올바르게 계산이 된 것이라는 것을 알 수 있게 하여 클릭이 된다고 해서 무조건적으로 음료수가 감소된다거나 전역변수에 영향을 주는 상황을 분리했다.
2.3 반환 함수
반환의 구현은 현재 입력된 금액을 0으로 초기화 시키는 과정만 거치게 하면 된다.
3. 기능 작동 확인하기
초기 화면은 가장 위에 있다. 이제 이온음료 버튼을 눌러보자.


{1: {'금액': 10, '수량': 20}, 2: {'금액': 50, '수량': 19}, 3: {'금액': 100, '수량': 16}, 4: {'금액': 500, '수량': 20}, 5: {'금액': 1000, '수량': 21}} 다음과 같이 인앱 내 print를 확인하면 전체 거스름돈 데이터가 어떻게 구성되어 있는지 확인할 수 있다. 초기값을 20으로 지정해 놓은 상태여서 1000원이 한 장 늘고 나머지는 잔돈을 맞추기 위해 줄어들었다.
이제 1000원 버튼을 마구 눌러서 돈을 추가해주자.

관리자 화면으로 이동할 때 비밀번호를 입력하기 위한 기능이다. 비밀번호가 마스킹 처리된 상태이다.


관리자 화면으로 무사히 이동이 된 상태이다.
관리자 화면 - 비밀 번호 변경 기능
관리자 화면의 전체 코드이다.
import tkinter as tk # tkinter 모듈을 가져옵니다
import tkinter.font
import global_var
import admin_changePW
from tkinter import messagebox
from matplotlib.figure import Figure
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
import matplotlib.ticker as ticker
#그래프를 그리기 위한 데이터인 판매량 데이터를 초기화 시켜둠
sales_data = global_var.sales_data
# 관리자 화면을 정의하는 클래스입니다
class AF(tk.Frame):
def __init__(self, parent, controller):
super().__init__(parent)
self.controller = controller
self.configure(bg="#008080")
font = tkinter.font.Font(family="Consolas", size=12) # 폰트정의
#######################상단프레임###############################
top_frame = tk.Frame(self, bg="#008080")
top_frame.grid(row=0, column=0, columnspan=3, padx=10, pady=10, sticky = 'ew')
# "Back to Home" 버튼을 생성하고 클릭 시 MF 화면으로 전환
back_button = tk.Button(top_frame, text="돌아가기", font = font,
command=lambda: controller.show_frame("MF"))
back_button.grid(row=0, column=0, padx=(5,75), pady=10, sticky='nw')
# 관리자 화면 나타내기
label = tk.Label(top_frame, text="관리자 화면에 오신 것을 환영합니다.", font = font) # "Admin Screen"이라는 텍스트 레이블을 만듭니다
label.grid(row = 0, column= 1, pady= 10, padx = 100, sticky='nw')
##################중단 프레임 1 기능 선택및 화면 이동 버튼연결###########
mid_frame1 = tk.Frame(self, bg="#008080")
mid_frame1.grid(row=1, column=0, columnspan=2, padx=100, pady=10, sticky = 'ew')
# "Admin Stock " 버튼을 생성하고 클릭 시 Admin Stock 화면으로 전환
as_button = tk.Button(mid_frame1, text="재고 보충", font = font,
command=lambda: controller.show_frame("AS"))
as_button.grid(row=1, column=0, padx=120, pady=10, sticky='nw')
# "Admin Collect" 버튼을 생성하고 클릭 시 Admin Collect화면으로 전환
ac_button = tk.Button(mid_frame1, text="수금", font = font,
command=lambda: controller.show_frame("AC"))
ac_button.grid(row=1, column=1, padx=120, pady=10, sticky='nw')
##################중단 프레임 2 비밀번호 변경하기 ###########
mid_frame2 = tk.Frame(self, bg="#008080")
# 전체 프레임 기준(self) row 2 -> 3번쨰 프레임을 구성한다.
mid_frame2.grid(row=2, column=0, columnspan=3, padx=10, pady=5)
# 비밀번호 변경창 구성하기
label = tk.Label(mid_frame2, text="- - - - - - - - - - - 비밀번호 변경 - - - - - - - - - - -", font = font)
label.grid(row = 0, column= 0, pady= 10, columnspan=3, padx = 10)
# 현재 비밀번호 입력
mid_frame2.current_password_label = tk.Label(mid_frame2, text="현재 비밀번호", font= font)
mid_frame2.current_password_label.grid(row=1, column=0, padx=50, pady=10, sticky='nw')
mid_frame2.current_password_entry = tk.Entry(mid_frame2, show="*")
mid_frame2.current_password_entry.grid(row=1, column= 1, padx=50, pady=10, sticky='nw')
# 새로운 비밀번호 입력하기
mid_frame2.new_password_label = tk.Label(mid_frame2, text="새 비밀번호", font= font)
mid_frame2.new_password_label.grid(row=2, column=0, padx=50, pady=10, sticky='nw')
mid_frame2.new_password_entry = tk.Entry(mid_frame2, show="*")
mid_frame2.new_password_entry.grid(row=2, column= 1, padx=50, pady=10, sticky='nw')
# 새로운 비밀번호 확인하기
mid_frame2.confirm_password_label = tk.Label(mid_frame2, text="비밀번호 확인", font= font)
mid_frame2.confirm_password_label.grid(row=3, column=0, padx=50, pady=10, sticky='nw')
mid_frame2.confirm_password_entry = tk.Entry(mid_frame2, show="*")
mid_frame2.confirm_password_entry.grid(row=3, column= 1, padx=50, pady=10, sticky='nw')
# 확인 버튼
mid_frame2.change_button = tk.Button(mid_frame2, text="비밀번호 변경하기", font = font, command=lambda:admin_changePW.change_password(mid_frame2.current_password_entry, mid_frame2.new_password_entry, mid_frame2.confirm_password_entry))
mid_frame2.change_button.grid(row = 4, column= 0, pady= 10, columnspan=3, padx = 10)
##################하단프레임 그래프 선택하고 출력하기 ###########
bottom_frame = tk.Frame(self, bg="#008080")
# 전체 프레임 기준(self) row 3 4번째이자 하단 프레임을 구성한다.
# 그래프 출력창 구성하기
bottom_frame.grid(row=3, column=0, columnspan= 6, padx=10, pady=10)
label = tk.Label(bottom_frame, text="- - - - - - - - - - - - 음료, 날짜단위 별 매출 시각화 - - - - - - - - - - - -", font = font)
label.grid(row = 0, column= 0, pady= 10, columnspan=6)
###############하단프레임 그래프 생성함수 ###############
def plot_graph():
selected_drink = drink_selection.get() # 선택된 음료
days = day_selection.get()
if not selected_drink:
messagebox.showwarning("경고", "음료를 선택해주세요.")
return
# 선택된 음료가 일단위 => days 가 7이면
if(days == 7): # 최근 7일 만 출력하면 됨
len_data = 7
recent_data = sales_data[selected_drink][-days:]
else: # 월단위의 합을 저장한다.
recent_data = []
len_data = len(sales_data[selected_drink])
# 몇 달만큼의 데이터가 있는지를 인식하기
if (len_data < 30) : #데이터가 한달이 안되면
len_data = 1
recent_data = sum(sales_data[selected_drink])
else:
len_data = int(len_data/30)
for i in range(1, len_data+1):
recent_data.append(sum(sales_data[selected_drink][-(30*i):]))
#월별로 총합이 월 만큼 합해진다.
# Matplotlib 그래프 생성
fig = Figure(figsize=(5, 4), dpi=100)
ax = fig.add_subplot(111)
ax.bar(range(1, len_data+1), recent_data) # 최근 7일 데이터를 x축에 날짜로 표시
# 그래프 타이틀 및 축 라벨 설정
ax.set_title(f'Resent {days}')
ax.set_xlabel('days')
ax.set_ylabel('sales')
ax.xaxis.set_major_locator(ticker.MaxNLocator(integer=True))
# 새로운 Tkinter 윈도우 생성
graph_window = tk.Toplevel(bottom_frame)
graph_window.title(( selected_drink +" 판매량"))
# Matplotlib 그래프를 표시하는 캔버스 생성
canvas = FigureCanvasTkAgg(fig, master=graph_window)
canvas.draw()
canvas.get_tk_widget().pack(side=tk.TOP, fill=tk.BOTH, expand=1)
###################하단 프레임 구현 #############################################
# 음료 선택 라디오 버튼 생성
drink_selection = tk.StringVar()
col_num = 0
for drink in sales_data.keys():
rb = tk.Radiobutton(bottom_frame, text=drink, variable=drink_selection, value=drink, font = font)
rb.select()
rb.grid(row=1, column=col_num, padx=10, pady=10,sticky='w')
col_num += 1
# 날짜 단위 선택 라디오 버튼 생성하기
day_selection = tk.IntVar()
day_rb = tk.Radiobutton(bottom_frame, text="월 별", variable=day_selection, value=30,font=font)
day_rb.select()
day_rb.grid(row=2, column=0, padx=20, pady=30, sticky='w')
# 한달 단위
day_rb = tk.Radiobutton(bottom_frame, text="일주일", variable=day_selection, font = font,value=7)
day_rb.select()
day_rb.grid(row=2, column=1,padx=10, pady=10, sticky='w')
# 그래프 출력 버튼 생성
plot_button = tk.Button(bottom_frame, text="그래프 출력", font = font, command=plot_graph)
plot_button.grid(row=2, column=5,padx=10, pady=10, sticky='w')
전반적으로 상단 프레임 , 중단프레임 1, 2, 하단 프레임으로 나누고, 각각에 대한 기능 구현을 나누어서 진행했다.
비밀번호의 변경은 현재 비밀번호 입력창, 새 비밀번호 입력하기, 새 비밀번호 확인하기 입력 창 3개로 구성한 후, 비밀번호 변경하기 버튼이 눌렸을 때의 이벤트 처리를 함수를 통해서 하는 방식으로 구현했다.
admin_changePW.py
from tkinter import messagebox
import global_var
# 비밀번호 변경 이벤트 처리 함수 구성하기
import re
def is_valid_password(password):
# 비밀번호가 8자리 이상인지 확인
if len(password) < 8:
return False
# 특수문자 및 숫자가 각각 하나 이상 포함되어 있는지 확인하는 정규 표현식
has_special_char = re.search(r'[!@#$%^&*(),.?":{}|<>]', password)
has_digit = re.search(r'\d', password)
if has_special_char and has_digit:
return True
else:
return False
def change_password(current_password_entry, new_password_entry, confirm_password_entry):
current_password = current_password_entry.get()
new_password = new_password_entry.get()
confirm_password = confirm_password_entry.get()
# 여기에 현재 비밀번호와의 일치 여부를 확인하는 로직을 추가할 수 있습니다.
# 예를 들어, 현재 비밀번호가 "1234"라고 가정
if current_password != global_var.password:
messagebox.showerror("Error", "현재 비밀번호가 잘못됨 ")
return
if new_password != confirm_password:
messagebox.showerror("Error", "새 비밀번호가 일치하지 않음 ")
return
if not new_password:
messagebox.showerror("Error", "비밀번호가 비어서는 안됨")
return
if not is_valid_password(new_password): #비밀번호 유효성검사
messagebox.showerror("Error", "비밀번호는 8자리 이상이 되어야 하고, 특수문자가 포함되어야 합니다.")
return
# 비밀번호 변경 로직을 여기에 추가
# 예를 들어, 파일에 저장하거나 데이터베이스에 저장
messagebox.showinfo("비밀번호 변경성공", "비밀번호 변경이 완료되었습니다.")
global_var.password = new_password
관리자 비밀번호 이벤트 처리를 담당하기 위한 파일을 구성해 보았다. is_valid_password 메서드를 통해서 비밀번호의 길이 확인과 특수문자 포함 여부를 확인해 비밀번호의 유효성을 확인해 True, False를 리턴했다. 또한 비밀번호에 대해서 현재 비밀번호가 잘못된 상황, 새 비밀번호가 일치하지 않는 상황, 비밀번호가 빈상황에 대한 이벤트 처리를 진행했다. 비밀번호가 모두 유효하다고 할 때 최종적으로 전역변수로 선언된 비밀번호가 변경된다.
비밀번호 변경 기능확인하기



글이 넘 길어서 좀 나눠서 진행하도록 해보겠습니다.,,,
'Development Project > Python tkinter 자판기 Network Project' 카테고리의 다른 글
| 자판기 네트워크 파트 구현 (0) | 2024.06.14 |
|---|---|
| 자판기 네트워크 프로그래밍 프로젝트 2(2) - UI 구현 및 화면 이동 구현(tkinter, python) (0) | 2024.06.11 |
| UI 프로그래밍 하기 tkinter & python 1 메인 화면 구성하기 (0) | 2024.06.04 |
| UI디자인 및 요구 사항 정의 (0) | 2024.06.01 |