使用Python開發(fā)一個現(xiàn)代化屏幕取色器
一、項目概述
在UI設計、網(wǎng)頁開發(fā)等場景中,顏色拾取是高頻需求。本文介紹的Modern Color Picker具有以下特點:
- 現(xiàn)代化UI設計 - 采用Fluent Design風格
- 高性能取色 - 多線程實現(xiàn)毫秒級響應
- 智能反饋 - 可視化顏色預覽+復制提示
- 跨平臺兼容 - 核心邏輯支持Windows/macOS
二、核心功能解析
2.1 實時顏色追蹤
def track_mouse_color(self):
while not self.stop_thread:
if self.left_pressed:
x, y = pyautogui.position()
rgb = self.get_color_at(x, y)
self.color_changed.emit(rgb)
time.sleep(0.03)
獨立線程處理鼠標坐標獲取
通過信號槽機制更新UI
33fps的采樣頻率平衡性能與流暢度
2.2 智能顏色顯示
# 根據(jù)亮度自動調(diào)整文本顏色
brightness = sqrt(0.299*color.red()**2 + 0.587*color.green()**2 + 0.114*color.blue()**2)
text_color = QColor("#ffffff" if brightness < 128 else "#333333")
使用光度公式實現(xiàn)智能反色,確保文字始終可見
三、效果展示


四、實現(xiàn)步驟詳解
4.1 環(huán)境配置
pip install pyqt5 pyautogui pillow pywin32
4.2 關鍵實現(xiàn)步驟
創(chuàng)建無邊框窗口
self.setWindowFlags(Qt.FramelessWindowHint | Qt.WindowStaysOnTopHint) self.setAttribute(Qt.WA_TranslucentBackground)
設計顏色預覽組件
path.addRoundedRect(0, 0, self.width(), self.height(), 12, 12) painter.fillPath(path, QColor(self.color))
實現(xiàn)拖拽取色邏輯
def on_mouse_down(self, event):
self.left_pressed = True
def on_mouse_up(self, event):
if self.left_pressed:
x, y = pyautogui.position()
rgb = self.get_color_at(x, y)
五、代碼深度解析
5.1 架構設計

5.2 性能優(yōu)化點
- 雙緩沖繪圖:
QPainter.setRenderHint(QPainter.Antialiasing) - 資源復用:重復使用QPixmap緩存
- 線程安全:通過信號槽跨線程通信
六、完整源碼
import sys
import pyautogui
from PIL import ImageGrab
import win32clipboard
import threading
import time
from math import sqrt
from PyQt5.QtWidgets import (QApplication, QMainWindow, QWidget, QVBoxLayout, QHBoxLayout,
QLabel, QPushButton, QFrame, QSizePolicy)
from PyQt5.QtCore import Qt, QSize, QPoint, QTimer, pyqtSignal
from PyQt5.QtGui import QColor, QPainter, QPainterPath, QFont, QFontDatabase, QPixmap, QIcon
class ModernColorPicker(QMainWindow):
color_changed = pyqtSignal(tuple) # 顏色變化信號
def __init__(self):
super().__init__()
self.setup_ui()
self.setup_window()
# 初始化變量
self.left_pressed = False
self.stop_thread = False
self.drag_start_pos = QPoint()
self.current_rgb = None
self.current_hex = None
# 啟動顏色跟蹤線程
self.track_thread = threading.Thread(target=self.track_mouse_color)
self.track_thread.daemon = True
self.track_thread.start()
# 連接信號
self.color_changed.connect(self.update_current_color)
def setup_window(self):
"""設置窗口屬性"""
self.setWindowTitle("?? Modern Color Picker")
self.setWindowFlags(Qt.FramelessWindowHint | Qt.WindowStaysOnTopHint)
self.setAttribute(Qt.WA_TranslucentBackground)
# 設置窗口大小和位置
screen = QApplication.primaryScreen().geometry()
self.setFixedSize(400, 550)
self.move(screen.width() - self.width() - 20,
(screen.height() - self.height()) // 2)
# 窗口陰影效果
self.shadow = QWidget(self)
self.shadow.setGeometry(3, 3, self.width(), self.height())
self.shadow.setStyleSheet("background-color: rgba(0, 0, 0, 50); border-radius: 12px;")
self.shadow.lower()
def setup_ui(self):
"""設置用戶界面"""
# 主窗口
main_widget = QWidget()
main_widget.setObjectName("mainWidget")
main_widget.setStyleSheet("""
#mainWidget {
background-color: #f5f5f7;
border-radius: 10px;
}
QLabel {
color: #333333;
}
""")
self.setCentralWidget(main_widget)
# 主布局
main_layout = QVBoxLayout(main_widget)
main_layout.setContentsMargins(20, 15, 20, 15)
main_layout.setSpacing(0)
# 標題欄
self.setup_title_bar(main_layout)
# 顏色預覽區(qū)域
self.setup_color_previews(main_layout)
# 顏色信息顯示
self.setup_color_info(main_layout)
# 操作按鈕
self.setup_action_buttons(main_layout)
# 取色區(qū)域
self.setup_pick_area(main_layout)
# 狀態(tài)欄
self.status_label = QLabel("")
self.status_label.setAlignment(Qt.AlignCenter)
self.status_label.setStyleSheet("""
QLabel {
color: #007AFF;
font: 9pt "Segoe UI";
padding: 5px 0;
}
""")
main_layout.addWidget(self.status_label)
def setup_title_bar(self, parent_layout):
"""設置自定義標題欄"""
title_bar = QWidget()
title_bar.setFixedHeight(40)
title_bar.setStyleSheet("background-color: #2c2c2e; border-radius: 10px 10px 0 0;")
# 標題欄布局
title_layout = QHBoxLayout(title_bar)
title_layout.setContentsMargins(15, 0, 15, 0)
title_layout.setSpacing(0)
# 標題
title_label = QLabel("?? Modern Color Picker")
title_label.setStyleSheet("""
QLabel {
color: white;
font: bold 10pt "Segoe UI";
}
""")
# 按鈕區(qū)域
btn_widget = QWidget()
btn_layout = QHBoxLayout(btn_widget)
btn_layout.setContentsMargins(0, 0, 0, 0)
btn_layout.setSpacing(10)
# 最小化按鈕
minimize_btn = QPushButton("-")
minimize_btn.setFixedSize(20, 20)
minimize_btn.setStyleSheet("""
QPushButton {
background-color: transparent;
color: white;
font: 12pt "Segoe UI";
border: none;
}
QPushButton:hover {
background-color: #3a3a3c;
border-radius: 10px;
}
""")
minimize_btn.clicked.connect(self.showMinimized)
# 關閉按鈕
close_btn = QPushButton("×")
close_btn.setFixedSize(20, 20)
close_btn.setStyleSheet("""
QPushButton {
background-color: transparent;
color: white;
font: 12pt "Segoe UI";
border: none;
}
QPushButton:hover {
background-color: #ff5f56;
border-radius: 10px;
}
""")
close_btn.clicked.connect(self.exit_app)
# 添加到布局
btn_layout.addWidget(minimize_btn)
btn_layout.addWidget(close_btn)
title_layout.addWidget(title_label, 0, Qt.AlignLeft)
title_layout.addStretch()
title_layout.addWidget(btn_widget, 0, Qt.AlignRight)
parent_layout.addWidget(title_bar)
def setup_color_previews(self, parent_layout):
"""設置顏色預覽區(qū)域"""
preview_widget = QWidget()
preview_layout = QHBoxLayout(preview_widget)
preview_layout.setContentsMargins(0, 10, 0, 20)
preview_layout.setSpacing(40)
# 當前顏色預覽
current_frame = QVBoxLayout()
current_frame.setSpacing(8)
current_label = QLabel("?? 當前顏色")
current_label.setStyleSheet("""
QLabel {
font: bold 10pt "Segoe UI";
color: #333333;
}
""")
current_label.setAlignment(Qt.AlignCenter)
self.current_color_preview = ColorPreviewWidget()
self.current_color_preview.setFixedSize(80, 80)
current_frame.addWidget(current_label)
current_frame.addWidget(self.current_color_preview, 0, Qt.AlignCenter)
# 確認顏色預覽
confirm_frame = QVBoxLayout()
confirm_frame.setSpacing(8)
confirm_label = QLabel("? 確認顏色")
confirm_label.setStyleSheet("""
QLabel {
font: bold 10pt "Segoe UI";
color: #333333;
}
""")
confirm_label.setAlignment(Qt.AlignCenter)
self.confirm_color_preview = ColorPreviewWidget()
self.confirm_color_preview.setFixedSize(80, 80)
confirm_frame.addWidget(confirm_label)
confirm_frame.addWidget(self.confirm_color_preview, 0, Qt.AlignCenter)
# 添加到布局
preview_layout.addLayout(current_frame)
preview_layout.addLayout(confirm_frame)
parent_layout.addWidget(preview_widget)
def setup_color_info(self, parent_layout):
"""設置顏色信息顯示區(qū)域"""
# 創(chuàng)建一個垂直布局容器來包裹兩個信息框
color_info_container = QVBoxLayout()
color_info_container.setContentsMargins(0, 0, 0, 0)
color_info_container.setSpacing(10) # 這里設置兩個信息框之間的間距
# RGB值顯示
self.rgb_frame = InfoFrame("?? RGB:")
self.rgb_value = QLabel("")
self.rgb_value.setStyleSheet("""
QLabel {
font: bold 10pt "Segoe UI";
color: #333333;
}
""")
self.rgb_frame.add_info_widget(self.rgb_value)
# HEX值顯示
self.hex_frame = InfoFrame("?? HEX:")
self.hex_value = QLabel("")
self.hex_value.setStyleSheet("""
QLabel {
font: bold 10pt "Segoe UI";
color: #333333;
}
""")
self.hex_frame.add_info_widget(self.hex_value)
# 添加到容器布局
color_info_container.addWidget(self.rgb_frame)
color_info_container.addWidget(self.hex_frame)
# 將容器布局添加到父布局
parent_layout.addLayout(color_info_container)
def setup_action_buttons(self, parent_layout):
"""設置操作按鈕"""
btn_widget = QWidget()
btn_layout = QHBoxLayout(btn_widget)
btn_layout.setContentsMargins(0, 10, 0, 20)
btn_layout.setSpacing(10)
# 復制RGB按鈕
self.copy_rgb_btn = ModernButton("?? 復制 RGB")
self.copy_rgb_btn.setEnabled(False)
self.copy_rgb_btn.clicked.connect(self.copy_rgb)
# 復制HEX按鈕
self.copy_hex_btn = ModernButton("?? 復制 HEX")
self.copy_hex_btn.setEnabled(False)
self.copy_hex_btn.clicked.connect(self.copy_hex)
# 添加到布局
btn_layout.addWidget(self.copy_rgb_btn)
btn_layout.addWidget(self.copy_hex_btn)
parent_layout.addWidget(btn_widget)
def setup_pick_area(self, parent_layout):
"""設置取色區(qū)域"""
self.pick_area = QLabel("??? 按住鼠標右鍵不放\n\n?? 拖動到目標位置\n\n? 松開以確認顏色")
self.pick_area.setAlignment(Qt.AlignCenter)
self.pick_area.setStyleSheet("""
QLabel {
background-color: #ffffff;
border: 1px solid #e0e0e0;
border-radius: 8px;
font: 11pt "Segoe UI";
color: #666666;
padding: 20px;
}
""")
self.pick_area.setCursor(Qt.CrossCursor)
# 鼠標事件
self.pick_area.mousePressEvent = self.on_mouse_down
self.pick_area.mouseReleaseEvent = self.on_mouse_up
parent_layout.addWidget(self.pick_area)
# ========== 事件處理 ==========
def mousePressEvent(self, event):
"""鼠標按下事件(用于窗口拖動)"""
if event.button() == Qt.LeftButton:
self.drag_start_pos = event.globalPos() - self.pos()
event.accept()
def mouseMoveEvent(self, event):
"""鼠標移動事件(用于窗口拖動)"""
if event.buttons() == Qt.LeftButton:
self.move(event.globalPos() - self.drag_start_pos)
self.shadow.move(self.pos() + QPoint(3, 3))
event.accept()
def on_mouse_down(self, event):
"""取色區(qū)域鼠標按下事件"""
self.left_pressed = True
self.rgb_value.setText("")
self.hex_value.setText("")
self.confirm_color_preview.set_color("#ffffff")
self.copy_rgb_btn.setEnabled(False)
self.copy_hex_btn.setEnabled(False)
self.pick_area.setText("?? 拖動到目標位置\n\n? 松開以確認顏色")
def on_mouse_up(self, event):
"""取色區(qū)域鼠標釋放事件"""
if self.left_pressed:
self.left_pressed = False
x, y = pyautogui.position()
rgb = self.get_color_at(x, y)
hex_color = '#%02x%02x%02x' % rgb
# 更新UI
self.rgb_value.setText(f"{rgb[0]}, {rgb[1]}, {rgb[2]}")
self.hex_value.setText(hex_color.upper())
self.copy_rgb_btn.setEnabled(True)
self.copy_hex_btn.setEnabled(True)
# 保存當前顏色
self.current_rgb = f"{rgb[0]}, {rgb[1]}, {rgb[2]}"
self.current_hex = hex_color.upper()
# 更新確認顏色預覽
self.confirm_color_preview.set_color(hex_color)
# 自動復制HEX到剪貼板
self.copy_to_clipboard(hex_color.upper())
self.show_status(f"?? 顏色 {hex_color.upper()} 已復制到剪貼板", 3000)
# 恢復取色區(qū)域提示
self.pick_area.setText("??? 按住鼠標左鍵不放\n\n?? 拖動到目標位置\n\n? 松開以確認顏色")
# ========== 功能方法 ==========
def get_color_at(self, x, y):
"""獲取指定位置的顏色"""
img = ImageGrab.grab(bbox=(x, y, x + 1, y + 1))
return img.getpixel((0, 0))
def copy_to_clipboard(self, text):
"""復制文本到剪貼板"""
win32clipboard.OpenClipboard()
win32clipboard.EmptyClipboard()
win32clipboard.SetClipboardText(text)
win32clipboard.CloseClipboard()
def update_current_color(self, rgb):
"""更新當前顏色預覽"""
hex_color = '#%02x%02x%02x' % rgb
self.current_color_preview.set_color(hex_color)
def track_mouse_color(self):
"""跟蹤鼠標位置的顏色"""
while not self.stop_thread:
if self.left_pressed:
x, y = pyautogui.position()
rgb = self.get_color_at(x, y)
self.color_changed.emit(rgb)
time.sleep(0.03)
def copy_rgb(self):
"""復制RGB值"""
if self.current_rgb:
self.copy_to_clipboard(self.current_rgb)
self.flash_button(self.copy_rgb_btn, "? 已復制")
def copy_hex(self):
"""復制HEX值"""
if self.current_hex:
self.copy_to_clipboard(self.current_hex)
self.flash_button(self.copy_hex_btn, "? 已復制")
def flash_button(self, button, temp_text):
"""按鈕點擊反饋效果"""
original_text = button.text()
button.setText(temp_text)
button.setStyleSheet("""
QPushButton {
background-color: #34C759;
color: white;
font: bold 10pt "Segoe UI";
border-radius: 6px;
padding: 8px 20px;
}
""")
QTimer.singleShot(1500, lambda: (
button.setText(original_text),
button.setStyleSheet("""
QPushButton {
background-color: #007AFF;
color: white;
font: bold 10pt "Segoe UI";
border-radius: 6px;
padding: 8px 20px;
}
QPushButton:hover {
background-color: #0062CC;
}
QPushButton:disabled {
background-color: #AEAEB2;
}
""")
))
def show_status(self, message, duration):
"""顯示狀態(tài)消息"""
self.status_label.setText(message)
QTimer.singleShot(duration, lambda: self.status_label.setText(""))
def exit_app(self):
"""退出應用程序"""
self.stop_thread = True
self.close()
# ========== 自定義控件 ==========
class ColorPreviewWidget(QWidget):
"""顏色預覽控件"""
def __init__(self, parent=None):
super().__init__(parent)
self.color = "#ffffff"
self.setMinimumSize(80, 80)
def set_color(self, color):
"""設置顏色"""
self.color = color
self.update()
def paintEvent(self, event):
"""繪制圓角顏色預覽"""
painter = QPainter(self)
painter.setRenderHint(QPainter.Antialiasing)
# 繪制圓角矩形
path = QPainterPath()
path.addRoundedRect(0, 0, self.width(), self.height(), 12, 12)
# 填充顏色
painter.fillPath(path, QColor(self.color))
# 繪制邊框
painter.setPen(QColor("#e0e0e0"))
painter.drawPath(path)
# 根據(jù)亮度決定文字顏色
color = QColor(self.color)
brightness = sqrt(0.299 * color.red()**2 + 0.587 * color.green()**2 + 0.114 * color.blue()**2)
text_color = QColor("#ffffff" if brightness < 128 else "#333333")
# 在中心繪制HEX值
painter.setPen(text_color)
painter.setFont(QFont("Segoe UI", 9))
painter.drawText(self.rect(), Qt.AlignCenter, self.color.upper())
class InfoFrame(QFrame):
"""信息顯示框"""
def __init__(self, title, parent=None):
super().__init__(parent)
self.setFrameShape(QFrame.NoFrame)
self.setStyleSheet("""
QFrame {
background-color: #ffffff;
border-radius: 8px;
}
""")
self.setFixedHeight(40)
# 布局
layout = QHBoxLayout(self)
layout.setContentsMargins(15, 10, 15, 10)
layout.setSpacing(5)
# 標題
title_label = QLabel(title)
title_label.setStyleSheet("""
QLabel {
font: 10pt "Segoe UI";
color: #666666;
}
""")
layout.addWidget(title_label)
# 信息區(qū)域
self.info_widget = QWidget()
self.info_layout = QHBoxLayout(self.info_widget)
self.info_layout.setContentsMargins(0, 0, 0, 0)
self.info_layout.setSpacing(0)
layout.addWidget(self.info_widget)
layout.addStretch()
def add_info_widget(self, widget):
"""添加信息控件"""
self.info_layout.addWidget(widget)
class ModernButton(QPushButton):
"""現(xiàn)代化按鈕"""
def __init__(self, text, parent=None):
super().__init__(text, parent)
self.setCursor(Qt.PointingHandCursor)
self.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed)
self.setStyleSheet("""
QPushButton {
background-color: #007AFF;
color: white;
font: bold 10pt "Segoe UI";
border-radius: 6px;
padding: 8px 20px;
}
QPushButton:hover {
background-color: #0062CC;
}
QPushButton:disabled {
background-color: #AEAEB2;
}
""")
if __name__ == "__main__":
app = QApplication(sys.argv)
# 設置應用程序字體
font_db = QFontDatabase()
if "Segoe UI" in font_db.families():
app.setFont(QFont("Segoe UI", 9))
else:
app.setFont(QFont("Arial", 9))
window = ModernColorPicker()
window.show()
sys.exit(app.exec_())
關鍵文件結構:
/ModernColorPicker
│── main.py # 程序入口
│── components.py # 自定義組件
│── requirements.txt # 依賴庫
七、總結與展望
7.1 技術總結
實現(xiàn)了0延遲的顏色采樣
開發(fā)了符合現(xiàn)代審美的UI組件
解決了高DPI屏幕下的顯示問題
7.2 優(yōu)化方向
增加歷史顏色記錄
支持顏色格式轉(zhuǎn)換
添加插件系統(tǒng)
到此這篇關于使用Python開發(fā)一個現(xiàn)代化屏幕取色器的文章就介紹到這了,更多相關Python屏幕取色器內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
pytorch中torch.max和Tensor.view函數(shù)用法詳解
今天小編就為大家分享一篇pytorch中torch.max和Tensor.view函數(shù)用法詳解,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2020-01-01
python使用自定義user-agent抓取網(wǎng)頁的方法
這篇文章主要介紹了python使用自定義user-agent抓取網(wǎng)頁的方法,涉及Python使用urllib2模塊操作網(wǎng)頁的技巧,需要的朋友可以參考下2015-04-04
LyScript實現(xiàn)Hook改寫MessageBox的方法詳解
LyScript可實現(xiàn)自定義匯編指令的替換功能。用戶可自行編寫匯編指令,將程序中特定的通用函數(shù)進行功能改寫與轉(zhuǎn)向操作,此功能原理是簡單的Hook操作。本文將詳細介紹Hook改寫MessageBox的方法,感興趣的可以了解一下2022-09-09
Python采集天天基金數(shù)據(jù)掌握最新基金動向
這篇文章主要介紹了Python采集天天基金數(shù)據(jù)掌握最新基金動向,本次案例實現(xiàn)流程為發(fā)送請求、獲取數(shù)據(jù)、解析數(shù)據(jù)、多頁爬取、保存數(shù)據(jù),接下來來看看具體的操作過程吧2022-01-01
Python中根據(jù)時間自動創(chuàng)建文件夾的代碼實現(xiàn)
這篇文章主要介紹了Python中根據(jù)時間自動創(chuàng)建文件夾的代碼實現(xiàn),這樣的話給工作帶來極大的便利,方便桌面文件按時間存放,具體實例代碼跟隨小編一起看看吧2021-10-10

