728x90
반응형

hyunju.png
0.08MB
miko.png
0.52MB

오늘은 GUI, Tkinter의 Window, Label, Button, Canvas에 대해 알아보고 간단한 제비뽑기 프로그램을 만들어봤다.

1 GUI란?

GUI: 소프트웨어의 조작 방법을 직관적으로 알 수 있게 해주는 인터페이스

1.1 윈도우 표시하기

import tkinter

root = tkinter.Tk() # 윈도우 요소(객체) 생성
root.mainloop() # 윈도우 표시

tkinter.Tk() 명령으로 윈도우 요소(객체)를 생성할 수 있다.

mainloop 명령으로 화면에 윈도우를 표시할 수 있다.

1.2 제목과 크기 지정하기

import tkinter

root = tkinter.Tk() 
root.title("첫 번째 윈도우") # 윈도우 제목 지정
root.geometry("800x600") # 윈도우 크기 지정
root.mainloop() 

제목은 title, 크기는 geometry 명령으로 지정할 수 있다.

 

2 라벨 배치하기

2.1 라벨 배치

import tkinter

root = tkinter.Tk() 
root.title("첫 번째 윈도우") 
root.geometry("800x600") 
# 라벨 컴포넌트 생성
label = tkinter.Label(root, text="라벨 문자열", font=("Times New Roman", 24)) 
# 윈도우에 라벨 배치
label.place(x=200, y=100)
root.mainloop() 

라벨은 Label 명령으로 만들고 place 명령으로 배치한다.

아래 같이 라벨이 지정한 위치에 생성된다.

2.2 사용 가능한 폰트 확인

import tkinter
import tkinter.font

root = tkinter.Tk()
fonts = tkinter.font.families()
print(len(fonts))

무려 553개나 사용할 수 있다! 하지만 계속 Times New Roman을 사용할 예정이다.

 

3 버튼 배치하기

3.1 버튼 배치

import tkinter

root = tkinter.Tk() 
root.title("첫 번째 버튼") 
root.geometry("800x600") 
# 버튼 컴포넌트 생성
button = tkinter.Button(root, text="버튼 문자열", font=("Times New Roman", 24))
# 윈도우에 버튼 배치
button.place(x=200, y=100) 
root.mainloop() 

라벨과 마찬가지로 버튼은 Button 명령으로 만든 뒤 place 명령으로 배치한다.

아직은 버튼을 눌러도 아무 반응이 없다.

3.2 버튼 클릭 시 반응

import tkinter

# 버튼 문자열 변경 함수
def click_btn():
    button["text"] = "클릭했습니다"

root = tkinter.Tk() 
root.title("첫 번째 버튼") 
root.geometry("800x600") 
# 버튼 컴포넌트 생성 -> command=로 클릭 시 동작할 함수 지정
button = tkinter.Button(root, text="버튼 문자열",
                        font=("Times New Roman", 24), command=click_btn)
button.place(x=200, y=100) 
root.mainloop() 

버튼을 클릭했을 때의 처리를 함수로 정의해놓고, 버튼을 생성하는 식 안에 'command=함수'를 입력하면

버튼을 클릭했을 때 해당 함수를 실행하고 이를 확인할 수 있다.

이제 버튼을 누르면 "버튼 문자열"이 "클릭했습니다"로 바뀐다.

 

4 캔버스 사용하기

캔버스: 이미지나 도형을 그리는 GUI

4.1 캔버스 배치

import tkinter

root = tkinter.Tk() 
root.title("첫 번째 캔버스") 
# 캔버스 컴포넌트 생성
canvas = tkinter.Canvas(root, width=400, height=600,
                       bg="skyblue")
# 윈도우에 캔버스 배치
canvas.pack()
root.mainloop() 

캔버스는 Canvas 명령으로 생성하고 pack, place 명령으로 배치한다.

pack() 명령으로 배치하면 캔버스 크기에 맞춰 윈도우 크기가 결정된다.  
윈도우에 캔버스만을 배치하는 경우에는 아래 코드와 같이 root.geometry()를 생략할 수 있다.

4.2 캔버스에 이미지 표시하기

import tkinter

root = tkinter.Tk() 
root.title("첫 번째 캔버스") 
canvas = tkinter.Canvas(root, width=400, height=600)
canvas.pack()
# gazou에 이미지 파일 로딩
gazou = tkinter.PhotoImage(file="hyunju.png")
# 캔버스에 이미지 그리기
canvas.create_image(200, 300, image=gazou)
root.mainloop() 

캔버스에 이미지를 표시할 때는 PhotoImage 매서드로 이미지를 로딩하고 

create_image 매서드로 이미지를 그린다.

create_image() 명령에 지정한 X, Y 좌표는 이미지의 중점이다. 다른 값을 넣으면 이미지가 잘린다. 

 

5 제비뽑기 프로그램 만들기

앞에서 봤던 라벨, 버튼, 캔버스를 사용해서 간단한 제비뽑기 프로그램을 만들어 보자!

5.1 이미지 표시

import tkinter

root = tkinter.Tk() 
root.title("제비봅기 프로그램")
# 윈도우 크기 고정
root.resizable(False, False) 
canvas = tkinter.Canvas(root, width=800, height=600)
canvas.pack()
gazou = tkinter.PhotoImage(file="miko.png")
canvas.create_image(400, 300, image=gazou)
root.mainloop() 

우선 캔버스를 배치하고 이미지를 표시한다. 

resizable() 명령으로 윈도우 크기를 변경하지 못하게 한다.  
첫 번째 인수는 가로 방향 크기 변경 여부, 두 번째 인수는 세로 방향 크기 변경 여부를 의미한다.

5.2 GUI 배치

import tkinter

root = tkinter.Tk() 
root.title("제비봅기 프로그램")
root.resizable(False, False) 
canvas = tkinter.Canvas(root, width=800, height=600)
canvas.pack()
gazou = tkinter.PhotoImage(file="miko.png")
canvas.create_image(400, 300, image=gazou)

# 라벨 추가
label = tkinter.Label(root, text="??", font=("Times New Roman", 120),
                        bg="white")
label.place(x=380, y=60)
# 버튼 추가
button = tkinter.Button(root, text="제비뽑기", font=("Times New Roman", 36),
                        fg="skyblue")
button.place(x=360, y=400)

root.mainloop() 

두 번째로 캔버스 상에 라벨과 버튼을 배치했다. 

5.3 버튼 클릭 시 반응하기

import tkinter

# 버튼 클릭시 실행될 함수 정의 -> 라벨 문자열 무작위로 변경
def click_btn():
    label["text"] = random.choice(["대길", "중길", "소길", "흉"])
    label.update()

root = tkinter.Tk() 
root.title("제비봅기 프로그램")
root.resizable(False, False) 
canvas = tkinter.Canvas(root, width=800, height=600)
canvas.pack()
gazou = tkinter.PhotoImage(file="miko.png")
canvas.create_image(400, 300, image=gazou)

label = tkinter.Label(root, text="??", font=("Times New Roman", 120),
                        bg="white")
label.place(x=380, y=60)
# 버튼 생성, command 인자로 클릭 시 호출할 함수 지정
button = tkinter.Button(root, text="제비뽑기", font=("Times New Roman", 36),
                        fg="skyblue", command=click_btn)
button.place(x=360, y=400)

root.mainloop() 

버튼을 클릭할 때 제비뽑기 결과가 표시되도록 했다. 

버튼을 누르면 대길. 중길, 소길, 흉 중 랜덤으로 하나가 표시된다!

 

6 캔버스에 도형 그리기

import tkinter

root = tkinter.Tk()
root.title("캔버스에 도형 그리기")
root.geometry("500x400")
cvs = tkinter.Canvas(root, width=500, height=400, bg="white")
cvs.pack()
cvs.create_text(250, 25, text="문자열", fill="green", font=("Times New Roman", 24))
cvs.create_line(30, 30, 70, 80, fill="navy", width=5)
cvs.create_line(120, 20, 80, 50, 200, 80, 140, 120, fill="blue", smooth=True)
cvs.create_rectangle(40, 140, 160, 200, fill="lime")
cvs.create_rectangle(60, 240, 120, 360, fill="pink", outline="red", width=5)
cvs.create_oval(250 - 40, 100 - 40, 250 + 40, 100 + 40, fill="silver", outline="purple")
cvs.create_oval(250 - 80, 200 - 40, 250 + 80, 200 + 40, fill="cyan", width=0)
cvs.create_polygon(250, 250, 150, 350, 350, 350, fill="magenta", width=0)
cvs.create_arc(400 - 50, 100 - 50, 400 + 50, 100 + 50, fill="yellow", start=30, extent=300)
cvs.create_arc(400 - 50, 250 - 50, 400 + 50, 250 + 50, fill="gold", start=0, extent=120, style=tkinter.CHORD)
cvs.create_arc(400 - 50, 350 - 50, 400 + 50, 350 + 50, outline="orange", start=0, extent=120, style=tkinter.ARC)
cvs.mainloop()

직선: create_line(x1, y1, x2, y2, fill=?, width=?)

사각형: create_rectangle(x1, y1, x2, y2, fill=?, outline=?, width=?)

타원형: create_oval(x1, y1, x2, y2, fill=?, outline=?, width=?)

다각형: create_polygon(x1, y1, x2, y2, x3, y3, ..., fill=?, outline=?, width=?)

원호: create_arc(x1, y1, x2, y2, x3, y3, ..., fill=?, outline=?, start=?, extent=?, style=?)

끝!

여기까진 책에서 본 그래도 따라한 거니까, 이번엔 제비뽑기 말고 다른 걸로 직접 프로그램을 만들어봐야겠다.

 

코드 github: https://github.com/kimjinho1/Python-Game/blob/main/GUI%20%EA%B8%B0%EC%B4%88%201(tkinter)/GUI%20%EA%B8%B0%EC%B4%88%201.ipynb

 

kimjinho1/Python-Game

Contribute to kimjinho1/Python-Game development by creating an account on GitHub.

github.com

728x90
반응형
728x90
반응형

아두이노 LED 색깔 조절하기: https://jinho-study.tistory.com/241

 

아두이노 LED 색깔 조절하기(NeoPixel)

NeoPixel을 사용해서 빨간색, 초록색, 파란색, 노란색, 흰색을 켜보았다. (아두이노 extension board와 NeoPixel이 필요하고, Adafruit_NeoPixel 라이브러리를 설치해야 한다) #include //..

jinho-study.tistory.com

저번에는 NeoPixel로 빨강, 초록, 파랑, 노랑, 흰색을 켰었는데 똑같은 코드를 5번이나 넣는 식으로 구현을 했었다.

개인적으로 이런 코드는 비효율적이라고 생각하기에, 배열을 사용해서 코드를 짧게 구현해 보려고 한다.

그리고 버튼을 누르고 있어야 NeoPixel이 켜지는 기능과 색을 조금 더 추가하려고 한다.

우선 단순하게 코드를 짜면 아래와 같다. 

#include <Adafruit_NeoPixel.h>
#define LED 11 // LED Pin
Adafruit_NeoPixel pixels = Adafruit_NeoPixel(1, LED, NEO_GRB + NEO_KHZ800);

const int SW = 10; // Button Pin
int led = 0; // LED의 상태를 저장해주는 변수

void setup() {
  pixels.begin();
  pixels.show();
  pinMode(SW, INPUT);
}

void loop() {  
  // 버튼을 누르고 있을 때 Led를 켬
  if (digitalRead(SW) == LOW) {
    if (led == 0) {
      pixels.setPixelColor(0, 100, 0, 0); // 빨강
      pixels.show(); 
      delay(1000);
    }    
    if (led == 1) {
      pixels.setPixelColor(0, 0, 100, 0); // 파랑
      pixels.show(); 
      delay(1000);
    }
    if (led == 2) {
      pixels.setPixelColor(0, 0, 0, 100); // 초록
      pixels.show(); 
      delay(50);
    }
    if (led == 3) {
      pixels.setPixelColor(0, 100, 100, 0); // 노랑
      pixels.show(); 
      delay(1000);
    }
    if (led == 4) {
      pixels.setPixelColor(0, 100, 0, 100); //  보라
      pixels.show(); 
      delay(1000);
    }
    if (led == 5) {
      pixels.setPixelColor(0, 0, 100, 100); // 하늘
      pixels.show(); 
      delay(1000);
    }
    if (led == 6) {
      pixels.setPixelColor(0, 100, 100, 100); // 하양
      pixels.show(); 
      delay(1000);
      led = -1;
    }
    led += 1;
  }
  // 버튼을 누르지 않았을 때는 정전 상태
  else {
    pixels.setPixelColor(0, 0, 0, 0);
    pixels.show();     
  }
}

버튼을 누르고 있을 때는 LED가 켜지면서 색깔이 계속 바뀌고, 누르지 않았을 때는 정전 상태이다.

구현이 되긴 했지만 코드가 쓸데없이 길다. 1차원 배열과 %(나머지 연산)을 사용하면 아래와 같이 짧게 구현할 수 있다.

#include <Adafruit_NeoPixel.h>
#define LED 11 // LED Pin
Adafruit_NeoPixel pixels = Adafruit_NeoPixel(1, LED, NEO_GRB + NEO_KHZ800);

const int SW = 10; // Button Pin
int led = 0; // LED의 상태를 저장해주는 변수

int R[7] = {100, 0, 0, 100, 100, 0, 100}; // 빨간색 
int G[7] = {0, 100, 0, 100, 0, 100, 100}; // 초록색
int B[7] = {0, 0, 100, 0, 100, 100, 100}; // 파란색

void setup() {
  pixels.begin();
  pixels.show();
  pinMode(SW, INPUT);
}

void loop() {  
  // 버튼을 누르고 있을 때 Led를 켬
  if (digitalRead(SW) == LOW) {
    // 배열을 사용함으로써 코드를 엄청 짧게 만들수 있음
    pixels.setPixelColor(0, R[led%7], G[led%7], B[led%7]); 
    pixels.show(); 
    delay(1000); // 1초 주기
    led += 1;
  }
  // 버튼을 누르지 않았을 때는 정전 상태
  pixels.setPixelColor(0, 0, 0, 0); 
  pixels.show();     
}

위에 코드를 보면 R, G, B 3개의 배열에 빨강, 초록, 파랑, 노랑, 보라, 하늘, 하양의 RGB 값들이 순서대로 저장되어 있다.

(100, 0, 0) 같은 방식으로 여러 번 반복할 필요 없기 때문에 훨씬 코드가 짧아지게 된다. 

R[led%7], G[...], B[...]의 의미 -> 지금 led 변수는 led의 색깔(순서)을 가리키는데 예를 들면

led = 3 -> R[3%7] = R[3] = 100, 

led = 10 -> R[10%7] = R[3] = 100과 같은 방식이다.

%(나머지 연산)을 사용하지 않는다면 중간에 아래와 같이 조건문을 추가해야 되는데 위의 방법보다는 좋지 않은 방법 같다.

if (led == 6) { 
  led = -1; 
}

마지막으로 2차원 배열을 사용해보자.

#include <Adafruit_NeoPixel.h>
#define LED 11 // LED Pin
Adafruit_NeoPixel pixels = Adafruit_NeoPixel(1, LED, NEO_GRB + NEO_KHZ800);

const int SW = 10; // Button Pin
int led = 0; // LED의 상태를 저장해주는 변수

// RGB 값이 저장되어 있는 2차원 배열(빨강, 파랑, 초록, 노랑, 보라, 하늘, 하양)
int RGB[7][3] = {{100,0,0}, {0,100,0}, {0,0,100}, {100,100,0}, {100,0,100}, {0,100,100}, {100,100,100}};

void setup() {
  pixels.begin();
  pixels.show();
  pinMode(SW, INPUT);
}

void loop() {  
  // 버튼을 누르고 있을 때 Led를 켬
  if (digitalRead(SW) == LOW) {
    // 배열을 사용함으로써 코드를 엄청 짧게 만들수 있음
    pixels.setPixelColor(0, RGB[led%7][0], RGB[led%7][1], RGB[led%7][2]); 
    pixels.show(); 
    delay(1000); // 1초 주기
    led += 1;
  }
  // 버튼을 누르지 않았을 때는 정전 상태
  pixels.setPixelColor(0, 0, 0, 0); 
  pixels.show();     
}

맨 위에 있는 한눈에 다 안 보이던 코드가 이제는 한눈에 다 보인다.

RGB[7][3]은 7행 3열인 행렬이라고 생각하면 될 것 같다.

첫 번째 행에는 빨간색의 RGB 값, 두 번째 행에는 초록색의 RGB 값이 들어있는 식이다. 

아래와 같이 잘 작동된다.

쉬운 예제더라도 다양한 방식으로 코드를 짜보는 게 처음 공부할 때는 도움이 되는 것 같다.

남이 짠 코드를 사용하더라도 그걸 그냥 사용하기만 하는 게 아니라, 자신의 것으로 만들고 사용하도록 하자. 그렇지 않으면 남는 게 정말 하나도 없다.  

728x90
반응형

+ Recent posts