escape()와 encodeURIComponent()는 모두 문자열을 인코딩하는 JavaScript 함수이지만, 사용 목적과 인코딩 방식에서 중요한 차이가 있습니다. 웹 환경에서 데이터를 안전하게 전송하기 위해 주로 encodeURIComponent()를 사용하고, escape()는 더 이상 권장되지 않습니다.
VS Code에서 Copilot이 내 정보를 학습에 사용하지 않도록 설정하려면, GitHub Copilot 설정과 VS Code 자체의 원격 분석(telemetry) 설정을 변경해야 합니다. 이는 개인 정보 보호를 강화하는 데 도움이 됩니다.
{
//VS Code 원격 분석(Telemetry) 설정 변경
"telemetry.telemetryLevel": "off",
"telemetry.feedback.enabled": false,
"workbench.enableExperiments": false,
// 특정 파일 유형에서 Copilot 비활성화
//.env, .ini, .json, .yaml 등과 같이 민감한 정보가 포함될 수 있는 특정 파일 유형에 대해 Copilot이 내용을 처리하거나 제안하는 것을 막을 수 있습니다.
"github.copilot.enable": {
"*": true, // 모든 파일 형식에 대해 기본적으로 활성화 (또는 false)
"plaintext": false, // 예시: 일반 텍스트 파일에서 비활성화
"dotenv": false, // .env 파일에서 비활성화
"ini": false, // .ini 파일에서 비활성화
"json": false, // .json 파일에서 비활성화
"yaml": false // .yaml 파일에서 비활성화
}
}
# img_to_sketch_001.py
# 이미지 읽어서 스케치 형식으로 변환
# 파이썬 컴파일 경로가 달라서 현재 폴더의 이미지를 호출하지 못할때 작업디렉토리를 변경한다.
import os
from pathlib import Path
# src 상위 폴더를 실행폴더로 지정하려고 한다.
###real_path = Path(__file__).parent.parent
real_path = Path(__file__).parent
print(real_path)
#작업 디렉토리 변경
os.chdir(real_path)
import cv2
import numpy as np
import os
def convert_to_sketch(image_path, output_path="sketch_output.jpg"):
"""
사진을 읽어들여 스케치 형식으로 변환하고 저장합니다.
"""
try:
# 1. 이미지 로드
img = cv2.imread(image_path)
if img is None:
print(f"오류: 이미지를 로드할 수 없습니다. 경로를 확인하세요: {image_path}")
return
# 2. 이미지를 회색조로 변환
gray_img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# 3. 이미지 반전 (어두운 부분은 밝게, 밝은 부분은 어둡게)
# 펜으로 그린 선처럼 보이게 합니다.
inverted_img = 255 - gray_img
# 4. 가우시안 블러 적용
# 이미지를 부드럽게 만들고, 반전된 이미지와 블렌딩할 때 경계를 부드럽게 합니다.
# (21, 21)은 커널 크기, 0은 시그마 값 (자동 계산)
blurred_img = cv2.GaussianBlur(inverted_img, (21, 21), 0)
# 5. 컬러 닷지 블렌드 모드 적용 (스케치 효과의 핵심)
# 원본 회색조 이미지와 블러 처리된 반전 이미지를 혼합합니다.
# "닷지(Dodge)" 모드는 밝은 영역을 더 밝게 만들고, 어두운 영역의 디테일을 유지합니다.
# OpenCV에서는 직접적인 닷지 블렌드 모드 함수를 제공하지 않으므로, 수학적 연산을 사용합니다.
# result = (gray_img * 256) / (255 - blurred_img + epsilon)
# 여기서 255 - blurred_img가 0이 되는 것을 방지하기 위해 작은 값(epsilon)을 더합니다.
epsilon = 10 # 0으로 나누는 것을 방지하기 위한 작은 상수
sketch_img = cv2.divide(gray_img, 255 - blurred_img + epsilon, scale=256)
# 6. 결과 이미지 저장
cv2.imwrite(output_path, sketch_img)
print(f"스케치 이미지가 '{output_path}'로 성공적으로 저장되었습니다.")
except Exception as e:
print(f"스케치 변환 중 오류 발생: {e}")
# --- 사용 예시 ---
# 1. 'input_image.jpg' 라는 이름의 사진 파일을 이 파이썬 스크립트와 같은 폴더에 놓으세요.
# 2. 또는 정확한 이미지 파일 경로를 지정하세요.
input_image_path = "person_01.jpg"
# 파일명과 확장자 분리 및 새 파일명 생성
file_name_without_extension, file_extension = os.path.splitext(input_image_path)
output_sketch_path = f"{file_name_without_extension}_sketch{file_extension}"
print(f"원본 파일명: {input_image_path}")
print(f"확장자 없는 파일명: {file_name_without_extension}")
print(f"확장자: {file_extension}")
#output_caricature_path = file_name_without_extension + "_caricature_result." + file_extension
# F-string을 사용하여 문자열 내부에 변수를 직접 삽입합니다.
output_sketch_path = f"{file_name_without_extension}_caricature_result{file_extension}"
convert_to_sketch(input_image_path, output_sketch_path)
# 결과 이미지 확인 (선택 사항)
# try:
# # OpenCV로 이미지 표시 (OpenCV 창을 사용)
# result_img = cv2.imread(output_sketch_path)
# if result_img is not None:
# cv2.imshow("Sketch Image", result_img)
# cv2.waitKey(0) # 아무 키나 누를 때까지 대기
# cv2.destroyAllWindows() # 모든 OpenCV 창 닫기
# else:
# print("결과 이미지를 읽을 수 없습니다.")
# except FileNotFoundError:
# print("결과 이미지를 찾을 수 없어 미리보기를 실행할 수 없습니다.")
# except Exception as e:
# print(f"결과 이미지 미리보기 중 오류 발생: {e}")
# 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_())