본문 바로가기
기타/Vibe Coding

[Merry Christmas] 트리 위젯으로 컴퓨터 꾸미기

by ㅇ달빛천사ㅇ 2025. 12. 31.
728x90

개발 동기

네이버 블로그에 'To, 블로그씨' 라고 블로그에서 매일 글 쓸 주제를 주는데

그곳에서 트리 만들기에 대한 질문을 해줘서

집에 트리도 없고 해서 컴퓨터에 트리를 하나 꾸며볼까? 하고 만들게 되었습니다.

 

↓↓ 아래 링크를 클릭하시면 네이버 블로그의 To, 블로그씨 글을 보실 수 있습니다.

 

나 + ing : 네이버 블로그

여러가지 모험과 도전, 실험, 탐구를 좋아하는 ㅇ달빛천사ㅇ의 블로그입니다.

blog.naver.com

 


주요 기능

  • 컴퓨터 화면에 내가 업로드한 이미지 띄우기(배경이 없을 수록 더 예뻐요.)
  • 이미지와 함께 LED 전광판에 설정한 메세지 띄우기
  • 위젯이 항상 화면 맨 위에 올라와 있어서 다른 작업을 하더라도 가리지 않음.

새해 동기 부여 이미지와 올해 다짐, 목표 메세지등을 설정하여 다양하게 활용할 수 있습니다.

 


업그레이드 한다면 추가하고 싶은 기능

  • 실행창을 작업 표시줄의 숨김 아이콘으로 만들기
  • 위젯, 텍스트 크기 조정 기능
  • git, video 혹은 여러 이미지 설정 기능

사용 방법 안내 영상

 


파이썬 코드

import sys
from PyQt5.QtWidgets import (QApplication, QWidget, QLabel, QVBoxLayout, 
                             QLineEdit, QPushButton, QFileDialog, QFrame, QHBoxLayout)
from PyQt5.QtCore import Qt, QTimer, QPoint
from PyQt5.QtGui import QPixmap, QFont, QColor, QPalette

class MarqueeLabel(QLabel):
    """글자가 흐르는 LED 효과를 주는 라벨 위젯"""
    def __init__(self, parent=None):
        super().__init__(parent)
        self.px = 0
        self.py = 0
        self.text_content = ""
        
        # 타이머 설정 (글자 움직임 속도 조절)
        self.timer = QTimer(self)
        self.timer.timeout.connect(self.update_position)
        self.timer.start(30)  # 30ms마다 갱신 (숫자가 작을수록 빠름)
        
        # 스타일 설정 (전광판 느낌: 검은 배경에 형광색 글씨)
        self.setStyleSheet("color: #00FF00; background-color: black; font-weight: bold; font-size: 14px; border-radius: 5px;")
        self.setAlignment(Qt.AlignVCenter)
        self.setFixedHeight(30)

    def set_marquee_text(self, text):
        self.text_content = text + "   "  # 반복시 간격 
        self.setText(self.text_content)
        self.px = self.width()  # 시작 위치 (오른쪽 끝)
        self.update()

    def update_position(self):
        if not self.text_content:
            return
            
        # 텍스트 너비 계산
        font_metrics = self.fontMetrics()
        text_width = font_metrics.width(self.text_content)
        
        # 위치 이동
        self.px -= 2  # 이동 속도 (픽셀 단위)
        
        # 글자가 왼쪽으로 완전히 사라지면 다시 오른쪽 끝으로
        if self.px < -text_width:
            self.px = self.width()
            
        self.move(self.px, 0)
        
    # 창 크기가 변할 때(초기화 등) 너비 재설정
    def resizeEvent(self, event):
        self.setFixedWidth(event.size().width())
        super().resizeEvent(event)


class DesktopWidget(QWidget):
    """화면 맨 위에 떠 있는 이미지+전광판 위젯"""
    def __init__(self):
        super().__init__()
        
        # 1. 윈도우 설정 (투명 배경, 항상 위, 테두리 없음)
        self.setWindowFlags(Qt.WindowStaysOnTopHint | Qt.FramelessWindowHint | Qt.Tool)
        self.setAttribute(Qt.WA_TranslucentBackground)
        
        # 2. 레이아웃 구성
        self.layout = QVBoxLayout()
        self.layout.setContentsMargins(0, 0, 0, 0)
        self.layout.setSpacing(0)
        self.setLayout(self.layout)
        
        # 3. 이미지 영역 (상단)
        self.image_label = QLabel()
        self.image_label.setAlignment(Qt.AlignCenter)
        self.layout.addWidget(self.image_label)
        
        # 4. 전광판 영역 (하단) - 움직이는 라벨을 담을 컨테이너
        self.marquee_container = QWidget()
        self.marquee_container.setStyleSheet("background-color: black; border-radius: 5px;")
        self.marquee_container.setFixedHeight(30)
        self.marquee_container.setFixedWidth(200) # 기본 너비
        
        self.marquee_label = MarqueeLabel(self.marquee_container)
        
        # 컨테이너를 메인 레이아웃에 추가
        self.layout.addWidget(self.marquee_container, alignment=Qt.AlignCenter)
        
        # 드래그 이동을 위한 변수
        self.old_pos = None

    def update_content(self, image_path, text):
        # 이미지 로드
        if image_path:
            pixmap = QPixmap(image_path)
            # 이미지 크기 조절 (너무 크면 줄임)
            pixmap = pixmap.scaled(200, 200, Qt.KeepAspectRatio, Qt.SmoothTransformation)
            self.image_label.setPixmap(pixmap)
            self.marquee_container.setFixedWidth(pixmap.width()) # 전광판 너비를 이미지에 맞춤
            self.marquee_label.setFixedWidth(pixmap.width())
            
        # 텍스트 설정
        self.marquee_label.set_marquee_text(text)
        self.show()

    # --- 마우스로 창 드래그해서 옮기는 기능 ---
    def mousePressEvent(self, event):
        if event.button() == Qt.LeftButton:
            self.old_pos = event.globalPos()

    def mouseMoveEvent(self, event):
        if self.old_pos:
            delta = QPoint(event.globalPos() - self.old_pos)
            self.move(self.x() + delta.x(), self.y() + delta.y())
            self.old_pos = event.globalPos()

    def mouseReleaseEvent(self, event):
        self.old_pos = None


class ControlPanel(QWidget):
    """설정을 위한 GUI 창"""
    def __init__(self):
        super().__init__()
        self.setWindowTitle("위젯 설정")
        self.setGeometry(100, 100, 300, 150)
        self.widget_instance = DesktopWidget() # 위젯 인스턴스 생성
        self.image_path = None

        layout = QVBoxLayout()

        # 이미지 선택 버튼
        self.btn_image = QPushButton("1. 이미지 선택 (배경 없는 PNG 추천)")
        self.btn_image.clicked.connect(self.select_image)
        layout.addWidget(self.btn_image)
        
        self.lbl_path = QLabel("선택된 파일 없음")
        layout.addWidget(self.lbl_path)

        # 텍스트 입력
        self.input_text = QLineEdit()
        self.input_text.setPlaceholderText("2. 전광판에 띄울 문구를 입력하세요")
        layout.addWidget(self.input_text)

        # 실행 버튼
        self.btn_run = QPushButton("3. 위젯 띄우기 / 업데이트")
        self.btn_run.clicked.connect(self.run_widget)
        self.btn_run.setStyleSheet("background-color: #4CAF50; color: white; font-weight: bold;")
        layout.addWidget(self.btn_run)
        
        # 종료 안내
        layout.addWidget(QLabel("※ 위젯을 끄려면 이 설정창을 닫으세요."))

        self.setLayout(layout)

    def select_image(self):
        file_name, _ = QFileDialog.getOpenFileName(self, "이미지 열기", "", "Images (*.png *.jpg *.jpeg)")
        if file_name:
            self.image_path = file_name
            self.lbl_path.setText(file_name.split('/')[-1]) # 파일명만 표시

    def run_widget(self):
        text = self.input_text.text()
        if not self.image_path:
            self.lbl_path.setText("이미지를 먼저 선택해주세요!")
            return
        
        self.widget_instance.update_content(self.image_path, text)

if __name__ == '__main__':
    app = QApplication(sys.argv)
    control_panel = ControlPanel()
    control_panel.show()
    sys.exit(app.exec_())

실행 파일

onefile 배포 파일과 onedir 배포 파일이 있습니다.
onefile 배포 파일을 다운받으시려면 .exe파일만 다운받으시면 되고
onedir 배포 파일을 다운받으시려면 .zip파일만 다운받으신 후에 압축 풀기하시고 실행 파일(.exe) 실행하시면 됩니다.
onefile 배포 파일과 onedir 배포 파일의 차이는 
다운로드의 편리성 면에서 onefile 배포 파일이 더 편하고
프로그램 크기에 따라 다를 수 있지만 실행 속도의 측면에서는 onedir 배포 파일이 더 빠를 수 있습니다.

 

CodingWithGemini/MerryChristmas/dist at 58bdcaaf03e0161ce2cd0937ec75521b48b6caf2 · MinjuKang727/CodingWithGemini

Contribute to MinjuKang727/CodingWithGemini development by creating an account on GitHub.

github.com

예시용으로 사용했던 트리 이미지를 사용해 보고 싶으시면 'Christmas Tree.png' 파일을 다운받아 사용하시면 됩니다.

원하는 파일 클릭하셔서 들어가신 후에
오른쪽 상단의 'Download raw file' 버튼 클릭하시면 다운받아집니다.

다운로드 방법은 사진 참고하셔서 원하시는 파일 클릭 후에 바뀐 화면 우측 상단의 다운로드 버튼을 클릭하시면 됩니다.

다운로드 버튼 위로 마우스를 옮기면 'Download raw file'이라고 메세지가 뜹니다.

 

728x90


Top