반응형
반응형

 

 ./input/ 폴더 안에 있는 엑셀파일을 찾아서 데이터 있는 셀의 앞뒤 공백을 삭제

 

# pip install pandas openpyxl


"""
    ./input/ 폴더 안에 있는 엑셀파일을 찾아서 데이터 있는 셀의 앞뒤 공백을 삭제

"""

import pandas as pd
import os

# 엑셀 파일이 있는 폴더 경로
input_folder = "./input/"

# 공백이 제거된 파일을 저장할 폴더 (원본 폴더에 저장)
output_folder = "./output/"

# 출력 폴더가 없으면 생성
if not os.path.isdir(output_folder):
    os.makedirs(output_folder)

# input 폴더 내의 모든 파일 목록 가져오기
files = os.listdir(input_folder)

print(f"'{input_folder}' 폴더에서 엑셀 파일을 찾고 있습니다...")

# 파일 목록을 순회
for filename in files:
    # 파일 확장자가 .xlsx 또는 .xls인지 확인
    if filename.endswith(".xlsx") or filename.endswith(".xls"):
        print(f"\n파일 '{filename}' 처리 중...")
        
        # 전체 파일 경로 설정
        file_path = os.path.join(input_folder, filename)
        
        try:
            # 엑셀 파일을 데이터프레임으로 읽어오기
            # 모든 시트를 읽어오기 위해 sheet_name=None 옵션 사용
            #excel_data = pd.read_excel(file_path, sheet_name=None) # 헤더 제외
            excel_data = pd.read_excel(file_path, sheet_name=None, header=0, dtype=str) # 모든 데이터를 문자열로 읽기
            
            # 수정한 내용을 저장할 새로운 엑셀 파일 객체 생성
            output_filepath = os.path.join(output_folder, f"{os.path.splitext(filename)[0]}_cleaned{os.path.splitext(filename)[1]}")
            
            with pd.ExcelWriter(output_filepath, engine='openpyxl') as writer:
                # 각 시트(Sheet)를 순회하며 작업
                for sheet_name, df in excel_data.items():
                    print(f"  - 시트 '{sheet_name}' 공백 삭제 중...")
                    
                    # 문자열 타입의 열만 선택하여 공백 제거
                    for col in df.columns:
                        if df[col].dtype == 'object':
                            # .str.strip() 메서드로 앞뒤 공백 제거
                            #df[col] = df[col].astype(str).str.strip()                            
                            df[col] = df[col].fillna('').astype(str).str.strip() # NaN 값이 있을 때 오류 방지
                            print(df[col])
                    
                    # 수정된 데이터프레임을 새로운 엑셀 파일의 해당 시트에 저장
                    df.to_excel(writer, sheet_name=sheet_name, index=False)
            
            print(f"'{filename}' 파일 처리가 완료되었습니다. '{output_filepath}'에 저장됨.")

        except Exception as e:
            print(f"  - 오류 발생: '{filename}' 파일을 처리할 수 없습니다. 오류: {e}")

print("\n모든 엑셀 파일 처리가 완료되었습니다.")
반응형
반응형

[python] python, rust 의 관계

 

파이썬(Python)과 러스트(Rust)는 서로 경쟁 관계라기보다는 상호 보완적인 관계에 가깝습니다. 두 언어는 설계 철학부터 특징까지 매우 다르며, 각각의 강점을 활용해 시너지를 낼 수 있습니다.

 

 

두 언어의 근본적인 차이점

특징 파이썬(Python) 러스트(Rust)
언어 레벨 고수준 언어 (High-level) 저수준 언어 (Low-level)
컴파일 방식 인터프리터 방식 (실행 시 해석) 컴파일 방식 (실행 전 기계어로 변환)
타입 시스템 동적 타입 (Dynamic) 정적 타입 (Static)
메모리 관리 자동 (가비지 컬렉터) 수동 (소유권 시스템)
주요 강점 생산성, 쉬운 문법, 방대한 라이브러리 성능, 안정성, 메모리 안전성
주요 단점 느린 속도, 높은 메모리 사용량 어려운 학습 곡선, 긴 개발 시간

 

상호 보완적인 관계: 왜 함께 사용하는가?

파이썬은 개발 속도가 빠르고 배우기 쉬워 전체 애플리케이션의 뼈대를 만드는 데 탁월합니다. 하지만 속도가 중요하거나 복잡한 계산을 처리해야 하는 작업에서는 성능의 한계가 명확합니다.

바로 이 지점에서 러스트가 파이썬의 단점을 완벽하게 보완해 줍니다. 러스트는 뛰어난 성능과 메모리 효율성을 자랑하므로, 파이썬으로 만든 애플리케이션의 '병목 현상(bottleneck)'을 해결하는 데 이상적입니다.

예시: 파이썬으로 웹 서버를 구축했다고 가정해 봅시다. 웹 서버의 전체적인 로직은 파이썬으로 빠르게 개발할 수 있습니다. 그러나 특정 요청을 처리하는 과정에서 데이터 분석이나 복잡한 이미지 처리와 같은 고성능 작업이 필요할 수 있습니다.

이 경우, 해당 고성능 작업 부분만을 러스트로 작성합니다. 러스트는 이 작업을 매우 빠르게 처리하고, 그 결과를 다시 파이썬으로 전달해 줍니다. 이렇게 하면 파이썬의 빠른 개발 생산성러스트의 탁월한 실행 속도를 모두 얻을 수 있습니다.

실제 협업 방식

러스트로 작성된 코드는 '파이썬 모듈' 형태로 컴파일될 수 있습니다. **PyO3**나 **rust-cpython**과 같은 라이브러리를 사용하면, 러스트의 함수나 클래스를 마치 파이썬 함수처럼 호출할 수 있는 모듈을 쉽게 만들 수 있습니다.

즉, 러스트는 파이썬의 '성능을 위한 보조 도구' 역할을 하며, 파이썬 생태계에 새로운 가능성을 불어넣고 있습니다.

 

 

 

러스트 재단에서 개발되고 있는 메모리 안전성과 성능 및 편의성에 중점을 둔 프로그래밍 언어이다. 가비지 컬렉터 없이 메모리 안전성을 제공하는 대표적인 언어다. C++의 대체재로서 등장했다.

https://youtu.be/5C_HPTJg5ek

 

반응형
반응형

[python] pyx - The next step inPython packaging

 

https://astral.sh/pyx

 

pyx | Astral

A Python-native package registry from the creators of uv.

astral.sh

 

https://news.hada.io/topic?id=22514

 

  • pyx는 uv 개발팀이 만든 Python 네이티브 패키지 레지스트리로, PyPI·PyTorch·사설 소스 설치 속도를 최대 10배 향상
  • 기존 패키지 레지스트리 범위를 넘어, 속도·보안·GPU 인식 기능을 제공하며, 내부 패키지와 PyPI·PyTorch 같은 공개 소스 모두 지원
  • 패키지 인기, 생성 시기, 취약점 여부 등 기준으로 필터링 가능한 전용 인덱스 URL을 제공해 보안성과 컴플라이언스를 강화
  • Python에 특화된 최신 표준 지원과 uv와의 직접 통합을 통해 설정 없이 인증과 사용이 가능함
  • 팀 내 중복 빌드, PyTorch·CUDA 설치 난이도, 빌드 깨짐, 인증 불편 등 엔터프라이즈 환경의 주요 문제를 서버-클라이언트 통합으로 해결
  • GPU 인식 기능으로 하드웨어에 맞는 PyTorch, vLLM, FlashAttention, DeepSpeed 등의 사전 빌드 버전을 일관된 메타데이터와 최적 구성으로 제공함
  • 최적화된 아티팩트와 uv 네이티브 메타데이터 API를 통해 다른 사설 레지스트리 대비 월등한 성능을 제공

Astral의 비전과 배경

  • Astral은 Python 생태계를 위한 고성능 개발 도구를 만드는 회사로, Ruff(린터·포매터)와 uv(패키지 매니저)로 잘 알려짐
  • 창업 배경은 Python이 세계에서 가장 인기 있는 프로그래밍 언어임에도 불구하고 툴링 측면에서 충분히 지원받지 못하고 있음을 느꼈기 때문임
  • 현재 Astral 도구 체인은 월 1억 건 이상 설치, uv는 하루 5억 건 이상의 요청을 처리하며 폭발적으로 성장 중임
  • 목표는 Python을 가장 생산적인 프로그래밍 생태계로 만드는 것이며, 이를 위해 클라이언트 도구를 넘어 Python 클라우드를 구축하려 함

pyx 소개

  • pyx는 uv의 최적화된 백엔드로 설계된 Python 네이티브 패키지 레지스트리
    • 내부 패키지 호스팅 가능
    • PyPI, PyTorch 인덱스 같은 공개 소스에 대한 가속·설정 가능 프런트엔드 역할
  • 주요 특징
    • 빠른 설치 속도 : 패키지 설치 및 빌드 최적화
      • PyPI, PyTorch, 내부 프라이빗 소스에서 패키지 설치 시 최적화된 아티팩트와 uv 네이티브 메타데이터 API 활용
      • 타 사설 레지스트리 대비 최대 10배 빠른 속도 제공
    • 보안 및 규정 준수 강화 : 의존성·공급망 이해를 통한 위험 최소화
      • 패키지 필터링을 위한 전용 인덱스 URL 생성 가능
      • 인기, 배포 연령, 취약점 상태 등의 기준으로 패키지 접근 제어
      • 서버 측에서 재현 가능한 빌드 보장
    • 최신 표준 지원
      • Python에 특화된 최신 패키징 표준과 워크플로를 지원
      • uv와 직접 통합돼 별도 설정 없이 원활한 인증 및 사용 가능
    • GPU 인식 패키지 배포 : CUDA·PyTorch 관련 빌드 및 배포 단순화
      • PyTorch, vLLM, FlashAttention, DeepSpeed 등 GPU 관련 라이브러리의 맞춤형 사전 빌드 제공
      • 하드웨어 기반 최적 구성과 일관된 메타데이터 유지

해결하려는 문제

  • PyTorch·CUDA·FlashAttention·DeepSpeed 등 GPU 관련 라이브러리 설치의 어려움
  • 팀 내 동일 패키지의 반복 빌드로 인한 리소스 낭비
  • setuptools 업데이트로 인한 빌드 오류
  • 내부 레지스트리 인증 과정의 불편함

서버-클라이언트 통합 전략

  • uv(클라이언트)  pyx(서버) 의 수직 통합으로 위 문제들을 직접 해결
  • pyx 없이 uv만, 또는 uv 없이 pyx만 사용 가능하지만 함께 사용할 때 최고의 경험 제공
  • 오픈소스 도구와의 깊은 통합으로 기존에는 불가능했던 개발 경험 구현 가능

비즈니스 모델

  • uv, Ruff, ty 등 Astral 도구는 영원히 무료·오픈소스·퍼미시브 라이선스 유지
  • 대신 pyx와 같은 유료 호스팅 서비스를 제공해 “다음 단계” 인프라 수요 충족

 

 

반응형
반응형

Rust, Python, TypeScript: 새로운 프로그래밍 3대장(Trifecta)

 

 

  • Rust, Python, TypeScript가 앞으로 소프트웨어 개발의 중심 언어로 부상할 것으로 예상됨
  • AI 코딩 도구의 보편화로 인해 개발자의 언어 충성도가 약화되고, 실용적이고 생태계가 강한 언어가 선택받게 됨
  • 아이디어 중심 프로그래밍(idea-oriented programming) 패러다임이 등장, 개발자는 설계와 검토에 집중하고 구현은 AI에게 맡기는 구조로 변화함
  • 이 세 언어는 고급 타입 시스템 강력한 패키지 매니저 생태계를 모두 갖추고 있어, AI 활용과 협업, 오류 방지에 유리함
  • 에러 메시지 품질, 생태계, 플랫폼 연계성 등도 점점 더 중요한 기준이 되고 있음

Rust, Python, TypeScript가 떠오르는 이유

  • Rust는 시스템 소프트웨어, 고성능이 필요한 영역, 타입 안전성과 메모리 효율성이 탁월함
  • Python은 실험·프로토타이핑·수학/과학 라이브러리 활용에 최적, 빠르게 아이디어를 검증 가능
  • TypeScript는 웹, 브라우저, 다양한 플랫폼에 바로 적용 가능하며, 강력한 타입 시스템과 npm 생태계로 확장성 확보

AI가 바꾸는 개발 패러다임: 아이디어 중심 프로그래밍

  • AI 코딩 도구(Large Language Model) 보편화로 프로그래밍이 '아이디어 구상→AI가 구현→사람이 설계/검토'로 변화 중
  • Vibe coding은 즉흥적·모호한 명령에 가깝지만, Idea-oriented programming은 설계와 구조, 지속 가능한 원칙에 집중
  • 개발자는 주요 설계/방향 제시, AI는 반복적/구현적 작업 담당, 아키텍트와 견습생의 관계에 비유

언어 선택 기준의 변화

  • 예전에는 자신이 잘 아는 언어(예: Rust)로 빠르게 작업했으나, AI와 함께 일할 땐 필요한 라이브러리, 성능, 플랫폼 연계성이 더 중요해짐
  • 프로젝트에 따라 Python(머신러닝), TypeScript(웹·VSCode Extension), Rust(기본 선택) 등 상황별 최적 언어를 선택

타입 시스템의 중요성

  • AI 코딩 보조 환경에서는 고급 타입 시스템이 더 중요해짐
  • Rust와 TypeScript는 상태와 데이터 유효성, 실수 방지를 코드 레벨에서 강제, AI가 실수할 확률도 줄어듦
  • Python 역시 mypy, pydantic 등 설정을 통해 타입 안정성을 확보 가능

생태계와 패키지 매니저의 가치 상승

  • AI와 함께 개발할 때는 대형 라이브러리 활용이 쉬워져서, 생태계의 크기와 패키지 매니저의 품질이 더욱 중요해짐
  • Rust(cargo), TypeScript(npm), Python(uv 등) 모두 현대적인 패키지 매니저를 갖춤

사소한 문법과 워크어라운드는 덜 중요해지지만, 에러 메시지와 가이드 품질은 여전히 중요

  • LLM(AI)은 반복적 문법 실수는 빠르게 고치지만, 에러 메시지/가이드가 명확해야 효과적으로 보정 가능
  • Rust 커뮤니티의 언어 친화성(ergonomics) 노력처럼, AI도 좋은 에러 메시지를 활용해 더 나은 코드를 생산

결론: LLM이 강력한 개발 도구를 모두에게 열어줌

  • 아이디어 중심 프로그래밍 시대에는 주니어도 Principal Engineer급 설계/지휘 경험 가능
  • 코딩 자체의 ‘플로우’가 줄어든다는 우려도 있으나, 설계와 아이디어 중심의 개발 경험이 더 가치 있어질 전망

► AI와 함께하는 시대, Rust, Python, TypeScript가 강력한 타입, 생태계, 플랫폼 지원으로 개발 현장의 표준으로 자리 잡고 있음

반응형
반응형

[python] 대상 주식을 시계와 함께 실행하는 .exe 파일 만들기

 

 

.EXE로 배포하기. 

pyinstaller --onefile --windowed miniClockApp_finance_003.py

 

 

miniClockApp_finance_003.py

# pyinstaller --onefile --windowed miniClockApp_finance_003.py
# 시간과 슬라이드 바 사이에 공백이 없이 100% fill 로 처리하는데 시계는 center에 위치하고 아래의 버튼과도 100% fill.
# 공백을 완전히 없애고 100% 채움(fill) 처리하며, 시계는 중앙에 위치하도록 수정
"""
초미니 시계가 나타나고, 상단에는 주식의 이름, 현재가, 목표가, 그리고 차이가 함께 표시되며 주기적으로 순환합니다.
'Full Screen' 버튼을 눌러 전체 화면으로 전환하면 전체 배경이 불투명 검은색으로 변하고, 주식 및 시계 정보가 더 크게 표시됩니다. 
바탕화면을 클릭하면 다시 원래 크기로 돌아옵니다.
"""

import sys
from PyQt5.QtWidgets import QApplication, QWidget, QVBoxLayout, QHBoxLayout, QLabel, QPushButton, QSizePolicy
from PyQt5.QtCore import QTimer, QTime, Qt, QPoint, QEvent
from PyQt5.QtGui import QFont, QColor
import yfinance as yf # 주식 데이터 가져오기 위한 라이브러리

class MiniClockApp(QWidget):
    def __init__(self):
        super().__init__()
        self.is_fullscreen = False  # 전체 화면 상태 추적 변수
        self.old_pos = None         # 창 이동을 위한 이전 마우스 위치 저장
        
        # 모니터링할 주식 목록과 목표 가격 설정
        self.stocks_to_monitor = {
            '007310.KS': ('오뚜기', 396167),
            '215200.KQ': ('메가스터디교육', 36900),
            '005930.KS': ('삼성전자', 49900),
            '005387.KS': ('현대차2우B', 133300),
            '138910.KS': ('KODEX 구리 선물 ETF', 7190),
            'META': ('메타', 450.80),
            'AMZN': ('아마존', 151.61)
        }
        self.formatted_stock_data = [] # 주식 전체 정보를 저장할 리스트
        self.current_stock_index = 0 # 현재 표시 중인 주식 정보 인덱스

        self.initUI()
        
        # 창 플래그 설정: 프레임 없이, 항상 위에, 배경 투명 (초미니 시계에 적합)
        self.setWindowFlags(Qt.FramelessWindowHint | Qt.WindowStaysOnTopHint) 
        self.setAttribute(Qt.WA_TranslucentBackground) 

        # 전체화면 시 바탕화면 클릭 감지를 위한 이벤트 필터 설치
        QApplication.instance().installEventFilter(self)

        # 1초마다 시간 업데이트 타이머
        self.time_timer = QTimer(self)
        self.time_timer.timeout.connect(self.update_time) 
        self.time_timer.start(1000) 

        # 30초마다 주식 가격 데이터 가져오기 타이머
        self.stock_fetch_timer = QTimer(self)
        self.stock_fetch_timer.timeout.connect(self.fetch_and_process_stock_prices)
        self.stock_fetch_timer.start(30000) # 30초마다 업데이트

        # 3초마다 주식 정보 순환 표시 타이머
        self.stock_display_timer = QTimer(self)
        self.stock_display_timer.timeout.connect(self.display_next_stock_info)
        self.stock_display_timer.start(3000) # 3초마다 순환

        # 초기 업데이트
        self.update_time()
        self.fetch_and_process_stock_prices() # 앱 시작 시 바로 주식 데이터 가져오기

    def initUI(self):
        # 윈도우 설정
        self.setWindowTitle('초미니 시계 & 주식 모니터')
        self.setGeometry(100, 100, 400, 400) # 초기 위치 및 크기
        self.setMinimumSize(400, 400) # 최소 크기 400x400으로 고정

        # 메인 레이아웃 (세로 방향)
        main_layout = QVBoxLayout()
        self.setLayout(main_layout)
        # 전체 레이아웃의 안쪽 여백과 위젯 간 간격을 모두 0으로 설정하여 꽉 차게 만듦
        main_layout.setContentsMargins(0, 0, 0, 0) 
        main_layout.setSpacing(0) 

        # 주식 정보 표시 레이블
        self.stock_info_label = QLabel(self)
        self.stock_info_label.setAlignment(Qt.AlignCenter) # 중앙 정렬
        self.stock_info_label.setFont(QFont('Arial', 12)) # 폰트 크기 12px
        self.stock_info_label.setStyleSheet("color: #FFFFFF; background-color: rgba(0,0,0,180); padding: 0px;") 
        # 수직 방향으로 확장 가능하도록 설정 (Vertical, Expanding)
        self.stock_info_label.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Expanding)
        main_layout.addWidget(self.stock_info_label)

        # 시간 표시 레이블
        self.time_label = QLabel(self)
        self.time_label.setAlignment(Qt.AlignCenter) # 중앙 정렬
        self.time_label.setFont(QFont('Arial', 40)) # 폰트 및 크기 설정
        self.time_label.setStyleSheet("color: white; background-color: rgba(0,0,0,150); padding: 0px;") 
        # 수직 방향으로 확장 가능하도록 설정 (Vertical, Expanding)
        self.time_label.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Expanding)
        main_layout.addWidget(self.time_label)

        # 버튼들을 위한 수평 레이아웃
        button_layout = QHBoxLayout()
        # 버튼 레이아웃은 수직으로 고정 (Fixed) 또는 선호(Preferred)로 설정하여
        # 남은 공간을 주식 정보와 시계가 나눠 갖도록 함
        button_layout.setSizeConstraint(QHBoxLayout.SetFixedSize) # 버튼 레이아웃이 자체 크기를 유지하도록 설정
        main_layout.addLayout(button_layout) 

        # 풀스크린 버튼
        self.fullscreen_button = QPushButton('Full Screen', self)
        self.fullscreen_button.clicked.connect(self.toggle_fullscreen) 
        self.fullscreen_button.setStyleSheet("""
            QPushButton {
                background-color: #4CAF50;
                color: white;
                border-radius: 0px; 
                padding: 10px; 
                height: 50px; /* 버튼 높이 고정 */
            }
            QPushButton:hover {
                background-color: #45a049;
            }
        """)
        button_layout.addWidget(self.fullscreen_button)

        # 종료 버튼 추가
        self.exit_button = QPushButton('Exit', self)
        self.exit_button.clicked.connect(QApplication.instance().quit) 
        self.exit_button.setStyleSheet("""
            QPushButton {
                background-color: #f44336;
                color: white;
                border-radius: 0px; 
                padding: 10px; 
                height: 50px; /* 버튼 높이 고정 */
            }
            QPushButton:hover {
                background-color: #da190b;
            }
        """)
        button_layout.addWidget(self.exit_button)

    def update_time(self):
        current_time = QTime.currentTime()
        display_text = current_time.toString('hh:mm:ss') 
        self.time_label.setText(display_text)

    def fetch_and_process_stock_prices(self):
        temp_stock_data = []
        for ticker, (name, target_price) in self.stocks_to_monitor.items():
            try:
                stock_info = yf.Ticker(ticker).history(period='1d')
                if not stock_info.empty:
                    current_price = stock_info['Close'].iloc[-1]
                    price_diff = current_price - target_price

                    diff_color_code = "#AAAAAA" 
                    if price_diff > 0:
                        diff_text = f'+{price_diff:,.2f}'
                        diff_color_code = "#FF0000" 
                    elif price_diff < 0:
                        diff_text = f'{price_diff:,.2f}' 
                        diff_color_code = "#0000FF" 
                    else:
                        diff_text = f'0.00'
                    
                    formatted_text = (
                        f'<span style="font-size:14px; font-weight:bold;">{name}</span><br>'
                        f'<span style="font-size:12px;">현재가: {current_price:,.2f} | '
                        f'목표가: {target_price:,.2f} | '
                        f'<span style="color:{diff_color_code};">차이: {diff_text}</span></span>'
                    )
                    temp_stock_data.append((formatted_text))

                else:
                    temp_stock_data.append(f'{name}: 데이터 없음')
            except Exception as e:
                temp_stock_data.append(f'{name}: 오류 발생')
                print(f"Error fetching {ticker}: {e}")
        
        self.formatted_stock_data = temp_stock_data
        self.current_stock_index = 0
        self.display_next_stock_info() 

    def display_next_stock_info(self):
        if not self.formatted_stock_data:
            self.stock_info_label.setText("주식 정보 없음")
            self.stock_info_label.setStyleSheet("color: #AAAAAA; background-color: rgba(0,0,0,180); padding: 0px;")
            return

        text = self.formatted_stock_data[self.current_stock_index]
        self.stock_info_label.setText(text)
        
        self.current_stock_index = (self.current_stock_index + 1) % len(self.formatted_stock_data)

    def toggle_fullscreen(self):
        if not self.is_fullscreen:
            self.showFullScreen()
            # 전체 화면 시 폰트 크기 및 배경 불투명도 조절
            self.time_label.setFont(QFont('Arial', 200)) 
            self.time_label.setStyleSheet("color: white; background-color: rgba(0,0,0,220); padding: 0px;")
            
            # 전체 화면 시 주식 정보 폰트 크기 키우기 및 배경 색상 변경
            self.stock_info_label.setFont(QFont('Arial', 40)) 
            self.stock_info_label.setStyleSheet("color: #FFFFFF; background-color: rgba(0,0,0,220); padding: 0px;")
            
            # 전체 앱의 배경을 불투명 검은색으로 변경
            QApplication.instance().setStyleSheet("QWidget { background-color: rgba(0,0,0,220); }")

            self.fullscreen_button.setText('Exit Full Screen')
            self.exit_button.setVisible(False) 
            self.is_fullscreen = True
        else:
            self.showNormal()
            self.setGeometry(100, 100, 400, 400) 
            
            # 원래 폰트 크기 및 배경 불투명도 복원
            self.time_label.setFont(QFont('Arial', 40))
            self.time_label.setStyleSheet("color: white; background-color: rgba(0,0,0,150); padding: 0px;")
            
            # 주식 정보 폰트 크기 원래대로 및 배경색 복원
            self.stock_info_label.setFont(QFont('Arial', 12)) 
            self.stock_info_label.setStyleSheet("color: #FFFFFF; background-color: rgba(0,0,0,180); padding: 0px;")
            
            # 전체 앱의 배경을 원래대로 복원 (투명)
            QApplication.instance().setStyleSheet("QWidget { background-color: transparent; }")

            self.fullscreen_button.setText('Full Screen')
            self.exit_button.setVisible(True) 
            self.is_fullscreen = False
            
    # --- 마우스 드래그로 창 이동 기능 ---
    def mousePressEvent(self, event):
        if event.button() == Qt.LeftButton and not self.is_fullscreen:
            self.old_pos = event.globalPos() - self.frameGeometry().topLeft()
            event.accept()

    def mouseMoveEvent(self, event):
        if event.buttons() == Qt.LeftButton and not self.is_fullscreen:
            if self.old_pos is not None:
                self.move(event.globalPos() - self.old_pos)
                event.accept()

    def mouseReleaseEvent(self, event):
        if event.button() == Qt.LeftButton:
            self.old_pos = None
            event.accept()

    # --- 이벤트 필터: 전체화면일 때 외부 클릭 감지 ---
    def eventFilter(self, obj, event):
        if self.is_fullscreen and event.type() == QEvent.MouseButtonPress:
            if not self.rect().contains(self.mapFromGlobal(event.globalPos())):
                self.toggle_fullscreen() 
                return True 
        return super().eventFilter(obj, event) 

# 애플리케이션 실행
if __name__ == '__main__':
    app = QApplication(sys.argv)
    
    # 애플리케이션 전체 기본 스타일 시트 (창 배경은 기본 투명)
    app.setStyleSheet("""
        QWidget {
            background-color: transparent; /* 기본 창 배경 투명 */
            color: #EEEEEE; /* 기본 글자색 밝게 */
            font-family: Arial;
        }
        QPushButton {
            font-size: 14px;
        }
    """)
    
    clock_app = MiniClockApp()
    clock_app.show()
    sys.exit(app.exec_())
반응형
반응형

[pytho] FFmpeg 설치하기 

 

 

1. Windows에 FFmpeg 설치하기 (권장 방법)

Windows에서는 패키지 관리자를 사용하거나, 수동으로 바이너리 파일을 다운로드하여 설치할 수 있습니다. 수동 설치 후 **환경 변수(PATH)**를 설정하는 것이 중요합니다.

1-1. 수동 설치 (일반적이고 권장되는 방법)

  1. FFmpeg 다운로드:
    • FFmpeg 공식 다운로드 페이지로 이동합니다.
    • "Windows builds" 섹션 아래에서 권장되는 빌드 중 하나를 선택합니다. (예: gyan.dev 또는 BtbN)
    • gyan.dev로 접속했다면, "release builds" 섹션에서 ffmpeg-release-full.7z (전체 기능 포함) 또는 ffmpeg-release-essentials.7z (필수 기능만 포함) 중 하나를 다운로드합니다. 보통 full 버전을 권장합니다.
  2. 압축 해제: 다운로드한 .7z 파일의 압축을 해제합니다. (7-Zip 같은 압축 해제 프로그램이 필요할 수 있습니다.) 압축을 해제한 폴더를 C:\ 또는 D:\와 같이 이동하거나 삭제하지 않을 안정적인 위치에 놓습니다. (예: C:\ffmpeg)
  3. 환경 변수(PATH) 설정: FFmpeg 실행 파일이 있는 bin 폴더의 경로를 시스템 PATH 환경 변수에 추가해야 합니다. 그래야 명령 프롬프트의 어느 위치에서든 ffmpeg 명령어를 사용할 수 있습니다.
    • "시작" 메뉴 검색창에 "환경 변수"를 입력하고 **"시스템 환경 변수 편집"**을 엽니다.
    • "고급" 탭에서 "환경 변수(N)..." 버튼을 클릭합니다.
    • "사용자 변수" 또는 "시스템 변수" 섹션에서 Path를 찾아 선택하고 "편집(I)..." 버튼을 클릭합니다.
    • **"새로 만들기(N)"**를 클릭하고, 압축을 해제한 FFmpeg 폴더 안의 bin 폴더 경로를 붙여넣습니다. (예: C:\ffmpeg\bin 또는 C:\Users\사용자명\Downloads\ffmpeg-xxxxx-full_build\bin)
    • 모든 창에서 **"확인"**을 클릭하여 변경 사항을 저장합니다.
  4. 설치 확인:
    • 새로운 명령 프롬프트(CMD) 또는 PowerShell 창을 엽니다. (환경 변수 변경 사항을 적용하려면 기존 창은 닫고 새로 열어야 합니다.)
    • ffmpeg -version을 입력하여 FFmpeg 버전 정보가 올바르게 출력되는지 확인합니다.

 

 

반응형

+ Recent posts