Python for Beginners
Flet 날씨 App
Andrew's Akashic Records
2024. 11. 5. 10:51
728x90
이 책에 포함된 날씨 어플리케이션은 flet를 이용해 UI를 구성하고, python_weather 라이브러리로 날씨 데이터를 가져옵니다. 이 앱은 사용자가 특정 도시를 입력하면 해당 도시의 날씨 상태(아이콘, 온도, 습도 등)를 표시합니다.
비동기 처리를 통해 get_weather 함수가 날씨 데이터를 효율적으로 가져올 수 있도록 설계되었으며, flet의 다양한 UI 컴포넌트를 사용하여 간단하면서도 유용한 GUI를 구성하고 있습니다.
import flet as ft
import python_weather
# Functions
def get_weather_icon(condition):
if condition == "Thunderstorm":
return '🌩️'
elif condition == "Drizzle":
return '🌧️'
elif condition == "Rainy":
return '☔'
elif condition == "Snowy":
return '☃️'
elif condition == "Windy":
return '🌀'
elif condition == "Sunny":
return '☀️'
elif condition == "Cloudy":
return '☁️'
elif condition == "Clear":
return '🌝'
else:
return '😁'
def to_celsius(fa):
changeNumber = (int(fa) - 32) * 5.0 / 9.0
return round(changeNumber, 1)
def main(page: ft.Page):
async def get_weather(e):
async with python_weather.Client(unit=python_weather.IMPERIAL) as client:
weater = await client.get(inputBox.value)
temperText.value = str(to_celsius(weater.temperature)) + ' ° '
humidityPercent.value = '💧 ' + str(weater.humidity) + '%'
cityName.value = inputBox.value
iconImage.value = get_weather_icon((weater.description))
weather_text.value = weater.description
page.update()
page.title = "Weather Layout"
page.window.width = 300
page.window.height = 350
page.horizontal_alignment = page.vertical_alignment = "center"
cityName = ft.ElevatedButton('seoul')
humidityPercent = ft.Text('💧 22%',
color="blueaccent",
size=20,
weight=ft.FontWeight.BOLD)
weather_city = ft.Container(content=ft.Row([
cityName,
humidityPercent],
alignment=ft.MainAxisAlignment.SPACE_BETWEEN),
margin=10)
iconImage = ft.Text('☀️', size=70)
temperText = ft.Text('21', size=60,
color="black",
weight=ft.FontWeight.BOLD)
weather_imogi = ft.Row([iconImage, temperText],
alignment=ft.MainAxisAlignment.CENTER)
weather_text = ft.Text('Sunny',
weight=ft.FontWeight.BOLD, size=35, color="white")
inputBox = ft.TextField(hint_text="search", border_width=5, bgcolor="black")
weather_search = ft.Row([ft.IconButton(
icon=ft.icons.REPLY,
icon_size=30,
on_click=get_weather),
ft.Container(content=inputBox, width=150),
ft.IconButton(icon=ft.icons.SEARCH, icon_size=30,
on_click=get_weather)],
alignment=ft.MainAxisAlignment.CENTER
)
weatherInfo = ft.Column([weather_city,
weather_imogi,
weather_text,
weather_search],
alignment=ft.MainAxisAlignment.SPACE_BETWEEN,
horizontal_alignment=ft.CrossAxisAlignment.CENTER)
body = ft.Container(width=300,
image=ft.DecorationImage("https://picsum.photos/300/300?1", fit="fill"),
border_radius=ft.border_radius.all(30),
expand=1,
content=weatherInfo)
page.add(body)
ft.app(main)
주요 라이브러리
- flet: Python으로 GUI 앱을 작성할 수 있는 라이브러리입니다. 플러터(Flutter)와 유사한 구조를 통해 앱을 쉽고 빠르게 제작할 수 있습니다.
- python_weather: 날씨 정보를 가져오기 위한 Python API 클라이언트입니다. 기본적으로 날씨 데이터를 검색할 수 있는 비동기 클라이언트를 제공합니다.
주요 함수들
get_weather_icon(condition)
condition
변수에 따라 적절한 이모지 형태의 날씨 아이콘을 반환합니다.- 예를 들어 "Thunderstorm"이면 '🌩️'을 반환하고, "Sunny"면 '☀️'을 반환합니다.
- 조건에 따라 다양한 날씨 상태를 시각적으로 표현할 수 있도록 돕는 유틸리티 함수입니다.
to_celsius(fa)
fa
에 입력된 화씨(°F) 온도를 섭씨(°C)로 변환하는 함수입니다.- 계산식은
(fa - 32) * 5.0 / 9.0
이며, 소수점 첫째 자리에서 반올림한 값을 반환합니다.
메인 함수 (main(page: ft.Page)
)
이 함수는 전체 UI와 동작을 설정합니다.
get_weather(e)
- 날씨를 검색하는 비동기 함수입니다.
- 사용자가 입력한 도시명(
inputBox.value
)을 사용해 날씨를 가져오고, 그 정보를 UI 요소들에 업데이트합니다. python_weather.Client
를 사용하여 날씨 데이터를 받아오며,unit=python_weather.IMPERIAL
로 설정했기 때문에 화씨 단위를 사용하고 있습니다.- 데이터 업데이트가 완료되면
page.update()
를 호출하여 UI가 새로 고쳐집니다.
- 페이지 설정
page.title
: 페이지의 제목을 "Weather Layout"으로 설정합니다.page.window.width
및page.window.height
: 창의 너비와 높이를 각각 300과 350으로 설정합니다.page.horizontal_alignment
,page.vertical_alignment
: 페이지의 수평 및 수직 정렬을 "center"로 설정하여 모든 요소가 중앙에 위치하도록 합니다.
- UI 요소 구성
cityName
: 현재 도시명을 표시하는ElevatedButton
입니다. 기본값은 'seoul'입니다.humidityPercent
: 습도를 표시하는 텍스트 요소입니다. 기본값은 '💧 22%'입니다. 이 텍스트는 파란색("blueaccent"
)이며, 굵게(ft.FontWeight.BOLD
) 설정되어 있습니다.weather_city
:cityName
과humidityPercent
를 포함하는Container
입니다.Row
레이아웃을 사용하여 두 요소를 가로로 배치하며,alignment
속성으로 요소 간의 간격을 띄워 정렬합니다.iconImage
: 날씨 상태에 맞는 이모지를 표시하는 텍스트 요소로 기본값은 '☀️'입니다.temperText
: 온도를 표시하는 텍스트로 기본값은 '21'입니다.weather_imogi
: 날씨 이모지(iconImage
)와 온도(temperText
)를 가로로 배치한Row
입니다.weather_text
: 날씨 상태를 텍스트로 표현하는 요소로, 기본값은 'Sunny'입니다.inputBox
: 사용자가 검색할 도시명을 입력하는TextField
입니다. 배경색은 검정("black"
)이며, 검색을 위한 입력 필드입니다.weather_search
: 검색과 관련된 UI 요소로, 검색 아이콘 버튼과inputBox
를 가로로 배치한Row
입니다.on_click
이벤트로get_weather
함수를 호출하여 검색 기능을 동작하도록 구성되어 있습니다.weatherInfo
: 날씨와 관련된 모든 정보를 담고 있는Column
입니다. 이Column
에는weather_city
,weather_imogi
,weather_text
,weather_search
가 포함되어 있습니다. 전체적으로 수직으로 정렬되며, 가로 정렬은 중앙(ft.CrossAxisAlignment.CENTER
)에 맞춰져 있습니다.
body
- 날씨 정보 전체를 감싸는 컨테이너로, 배경 이미지를 설정하여 앱의 비주얼을 개선합니다.
DecorationImage
을 사용해 이미지(https://picsum.photos/300/300?1
- 랜덤 이미지)와 컨테이너를 꾸며줍니다.border_radius
로 컨테이너의 모서리를 둥글게 만들어 좀 더 부드러운 느낌을 줍니다.weatherInfo
는 이body
컨테이너의 내용(content
)으로 설정되어 있습니다.
page.add(body)
- 모든 UI 요소들이 포함된
body
컨테이너를 페이지에 추가합니다.
ft.app(main)
flet
앱의 진입점으로,main
함수가 앱의 실행을 담당합니다.
개선된 날씨 앱
import flet as ft
import python_weather
from flet_toast import flet_toast
# Functions
def get_weather_icon(condition):
weather_icons = {
"Thunderstorm": '🌩️',
"Drizzle": '🌧️',
"Rainy": '☔',
"Snowy": '☃️',
"Windy": '🌀',
"Sunny": '☀️',
"Cloudy": '☁️',
"Clear": '🌝'
}
return weather_icons.get(condition, '😁')
def to_celsius(fahrenheit):
return round((int(fahrenheit) - 32) * 5.0 / 9.0, 1)
async def fetch_weather(location):
async with python_weather.Client(unit=python_weather.IMPERIAL) as client:
return await client.get(location)
def main(page: ft.Page):
async def get_weather(e):
if not input_box.value:
weather_text.value = "Please enter a city name."
page.update()
return
try:
weather = await fetch_weather(input_box.value)
temperature = to_celsius(weather.temperature)
# Update UI elements
temperature_text.value = f"{temperature} °C"
humidity_text.value = f"💧 {weather.humidity}%"
city_name.value = input_box.value.capitalize()
icon_text.value = get_weather_icon(weather.description)
weather_description.value = weather.description.capitalize()
weather_text.value = ""
except Exception as ex:
flet_toast.warning(
page=page,
message='Could not retrieve weather information. Please check the city name.',
position=flet_toast.Position.BOTTOM_RIGHT,
duration=5
)
page.update()
# Page settings
page.title = "Weather App"
page.window.width = 300
page.window.height = 400
page.horizontal_alignment = page.vertical_alignment = "center"
# UI elements
city_name = ft.Text('',
weight=ft.FontWeight.BOLD,
size=20)
humidity_text = ft.Text('',
color="blueaccent",
size=20,
weight=ft.FontWeight.BOLD)
weather_header = ft.Container(
content=ft.Row([
city_name,
humidity_text
], alignment=ft.MainAxisAlignment.SPACE_BETWEEN),
margin=10
)
icon_text = ft.Text('☀️',
size=70)
temperature_text = ft.Text('21',
size=60,
color="black",
weight=ft.FontWeight.BOLD)
weather_icon_row = ft.Row([
icon_text,
temperature_text],
alignment=ft.MainAxisAlignment.CENTER)
weather_description = ft.Text('Sunny',
weight=ft.FontWeight.BOLD,
size=35,
color="white")
weather_text = ft.Text('',
color="red",
size=16)
input_box = ft.TextField(hint_text="Enter city",
border_width=2,
bgcolor="white",
color="black",
on_submit=get_weather)
search_button = ft.IconButton(icon=ft.icons.SEARCH,
icon_size=30,
on_click=get_weather)
weather_search_row = ft.Row([
search_button,
ft.Container(content=input_box,
width=150)
], alignment=ft.MainAxisAlignment.CENTER)
# Weather info layout
weather_info = ft.Column([
weather_header,
weather_icon_row,
weather_description,
weather_search_row,
weather_text
],
alignment=ft.MainAxisAlignment.SPACE_BETWEEN,
horizontal_alignment=ft.CrossAxisAlignment.CENTER)
# Background container
body = ft.Container(
width=300,
image=ft.DecorationImage("https://picsum.photos/300/300?1", fit="fill"),
border_radius=ft.border_radius.all(30),
expand=1,
content=weather_info
)
page.add(body)
ft.app(main)
아래는 코드 개선을 통해 더 효율적이고 가독성이 높은 날씨 어플리케이션을 만들어 보았습니다. 개선 사항에는 함수의 최적화, UI 요소의 재사용, 코드 중복 제거 등이 포함되어 있습니다.
개선된 사항 설명
- 함수 최적화
get_weather_icon(condition)
함수에서 조건문을 딕셔너리(weather_icons
)로 변환하여 가독성을 높이고 유지보수를 쉽게 했습니다.
- 비동기 코드 개선
fetch_weather(location)
함수를 따로 만들어서 코드의 재사용성을 높였습니다.
- 오류 처리
- 사용자가 잘못된 도시명을 입력하거나 인터넷 연결 문제로 인해 날씨 정보를 가져오지 못하는 경우를 대비해 예외 처리(
try-except
)를 추가했습니다.
- 코드 간소화 및 가독성 향상
- 중복되거나 불필요한 변수를 제거하고 코드를 간결하게 정리했습니다.
- 날씨 상태 업데이트 관련 코드를 그룹화하여 각 UI 업데이트가 한 번에 일어나도록 했습니다.
- 사용자 경험 향상
- 도시명을 입력하지 않았을 경우 사용자에게 알림 메시지를 표시하도록 하여 더 직관적인 경험을 제공합니다.
- 도시명은
capitalize()
메서드를 사용해 첫 글자만 대문자로 표기하여 가독성을 높였습니다.
이제 이 개선된 코드로 더 효율적이고 유지보수하기 쉬운 날씨 어플리케이션을 사용할 수 있을 것입니다. 추가적인 개선이나 기능 추가에 대해 궁금한 점이 있으면 말씀해 주세요!
728x90