使用Python開發(fā)一個(gè)現(xiàn)代化屏幕取色器
一、項(xiàng)目概述
在UI設(shè)計(jì)、網(wǎng)頁(yè)開發(fā)等場(chǎng)景中,顏色拾取是高頻需求。本文介紹的Modern Color Picker具有以下特點(diǎn):
- 現(xiàn)代化UI設(shè)計(jì) - 采用Fluent Design風(fēng)格
- 高性能取色 - 多線程實(shí)現(xiàn)毫秒級(jí)響應(yīng)
- 智能反饋 - 可視化顏色預(yù)覽+復(fù)制提示
- 跨平臺(tái)兼容 - 核心邏輯支持Windows/macOS
二、核心功能解析
2.1 實(shí)時(shí)顏色追蹤
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)
獨(dú)立線程處理鼠標(biāo)坐標(biāo)獲取
通過信號(hào)槽機(jī)制更新UI
33fps的采樣頻率平衡性能與流暢度
2.2 智能顏色顯示
# 根據(jù)亮度自動(dòng)調(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")
使用光度公式實(shí)現(xiàn)智能反色,確保文字始終可見
三、效果展示


四、實(shí)現(xiàn)步驟詳解
4.1 環(huán)境配置
pip install pyqt5 pyautogui pillow pywin32
4.2 關(guān)鍵實(shí)現(xiàn)步驟
創(chuàng)建無邊框窗口
self.setWindowFlags(Qt.FramelessWindowHint | Qt.WindowStaysOnTopHint) self.setAttribute(Qt.WA_TranslucentBackground)
設(shè)計(jì)顏色預(yù)覽組件
path.addRoundedRect(0, 0, self.width(), self.height(), 12, 12) painter.fillPath(path, QColor(self.color))
實(shí)現(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 架構(gòu)設(shè)計(jì)

5.2 性能優(yōu)化點(diǎn)
- 雙緩沖繪圖:
QPainter.setRenderHint(QPainter.Antialiasing) - 資源復(fù)用:重復(fù)使用QPixmap緩存
- 線程安全:通過信號(hào)槽跨線程通信
六、完整源碼
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) # 顏色變化信號(hào)
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
# 啟動(dòng)顏色跟蹤線程
self.track_thread = threading.Thread(target=self.track_mouse_color)
self.track_thread.daemon = True
self.track_thread.start()
# 連接信號(hào)
self.color_changed.connect(self.update_current_color)
def setup_window(self):
"""設(shè)置窗口屬性"""
self.setWindowTitle("?? Modern Color Picker")
self.setWindowFlags(Qt.FramelessWindowHint | Qt.WindowStaysOnTopHint)
self.setAttribute(Qt.WA_TranslucentBackground)
# 設(shè)置窗口大小和位置
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):
"""設(shè)置用戶界面"""
# 主窗口
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)
# 標(biāo)題欄
self.setup_title_bar(main_layout)
# 顏色預(yù)覽區(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):
"""設(shè)置自定義標(biāo)題欄"""
title_bar = QWidget()
title_bar.setFixedHeight(40)
title_bar.setStyleSheet("background-color: #2c2c2e; border-radius: 10px 10px 0 0;")
# 標(biāo)題欄布局
title_layout = QHBoxLayout(title_bar)
title_layout.setContentsMargins(15, 0, 15, 0)
title_layout.setSpacing(0)
# 標(biāo)題
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)
# 關(guān)閉按鈕
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):
"""設(shè)置顏色預(yù)覽區(qū)域"""
preview_widget = QWidget()
preview_layout = QHBoxLayout(preview_widget)
preview_layout.setContentsMargins(0, 10, 0, 20)
preview_layout.setSpacing(40)
# 當(dāng)前顏色預(yù)覽
current_frame = QVBoxLayout()
current_frame.setSpacing(8)
current_label = QLabel("?? 當(dāng)前顏色")
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)
# 確認(rèn)顏色預(yù)覽
confirm_frame = QVBoxLayout()
confirm_frame.setSpacing(8)
confirm_label = QLabel("? 確認(rèn)顏色")
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):
"""設(shè)置顏色信息顯示區(qū)域"""
# 創(chuàng)建一個(gè)垂直布局容器來包裹兩個(gè)信息框
color_info_container = QVBoxLayout()
color_info_container.setContentsMargins(0, 0, 0, 0)
color_info_container.setSpacing(10) # 這里設(shè)置兩個(gè)信息框之間的間距
# 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):
"""設(shè)置操作按鈕"""
btn_widget = QWidget()
btn_layout = QHBoxLayout(btn_widget)
btn_layout.setContentsMargins(0, 10, 0, 20)
btn_layout.setSpacing(10)
# 復(fù)制RGB按鈕
self.copy_rgb_btn = ModernButton("?? 復(fù)制 RGB")
self.copy_rgb_btn.setEnabled(False)
self.copy_rgb_btn.clicked.connect(self.copy_rgb)
# 復(fù)制HEX按鈕
self.copy_hex_btn = ModernButton("?? 復(fù)制 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):
"""設(shè)置取色區(qū)域"""
self.pick_area = QLabel("??? 按住鼠標(biāo)右鍵不放\n\n?? 拖動(dòng)到目標(biāo)位置\n\n? 松開以確認(rè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)
# 鼠標(biāo)事件
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):
"""鼠標(biāo)按下事件(用于窗口拖動(dòng))"""
if event.button() == Qt.LeftButton:
self.drag_start_pos = event.globalPos() - self.pos()
event.accept()
def mouseMoveEvent(self, event):
"""鼠標(biāo)移動(dòng)事件(用于窗口拖動(dòng))"""
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ū)域鼠標(biāo)按下事件"""
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("?? 拖動(dòng)到目標(biāo)位置\n\n? 松開以確認(rèn)顏色")
def on_mouse_up(self, event):
"""取色區(qū)域鼠標(biāo)釋放事件"""
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)
# 保存當(dāng)前顏色
self.current_rgb = f"{rgb[0]}, {rgb[1]}, {rgb[2]}"
self.current_hex = hex_color.upper()
# 更新確認(rèn)顏色預(yù)覽
self.confirm_color_preview.set_color(hex_color)
# 自動(dòng)復(fù)制HEX到剪貼板
self.copy_to_clipboard(hex_color.upper())
self.show_status(f"?? 顏色 {hex_color.upper()} 已復(fù)制到剪貼板", 3000)
# 恢復(fù)取色區(qū)域提示
self.pick_area.setText("??? 按住鼠標(biāo)左鍵不放\n\n?? 拖動(dòng)到目標(biāo)位置\n\n? 松開以確認(rè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):
"""復(fù)制文本到剪貼板"""
win32clipboard.OpenClipboard()
win32clipboard.EmptyClipboard()
win32clipboard.SetClipboardText(text)
win32clipboard.CloseClipboard()
def update_current_color(self, rgb):
"""更新當(dāng)前顏色預(yù)覽"""
hex_color = '#%02x%02x%02x' % rgb
self.current_color_preview.set_color(hex_color)
def track_mouse_color(self):
"""跟蹤鼠標(biāo)位置的顏色"""
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):
"""復(fù)制RGB值"""
if self.current_rgb:
self.copy_to_clipboard(self.current_rgb)
self.flash_button(self.copy_rgb_btn, "? 已復(fù)制")
def copy_hex(self):
"""復(fù)制HEX值"""
if self.current_hex:
self.copy_to_clipboard(self.current_hex)
self.flash_button(self.copy_hex_btn, "? 已復(fù)制")
def flash_button(self, button, temp_text):
"""按鈕點(diǎn)擊反饋效果"""
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):
"""退出應(yīng)用程序"""
self.stop_thread = True
self.close()
# ========== 自定義控件 ==========
class ColorPreviewWidget(QWidget):
"""顏色預(yù)覽控件"""
def __init__(self, parent=None):
super().__init__(parent)
self.color = "#ffffff"
self.setMinimumSize(80, 80)
def set_color(self, color):
"""設(shè)置顏色"""
self.color = color
self.update()
def paintEvent(self, event):
"""繪制圓角顏色預(yù)覽"""
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)
# 標(biāo)題
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)
# 設(shè)置應(yīng)用程序字體
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_())
關(guān)鍵文件結(jié)構(gòu):
/ModernColorPicker
│── main.py # 程序入口
│── components.py # 自定義組件
│── requirements.txt # 依賴庫(kù)
七、總結(jié)與展望
7.1 技術(shù)總結(jié)
實(shí)現(xiàn)了0延遲的顏色采樣
開發(fā)了符合現(xiàn)代審美的UI組件
解決了高DPI屏幕下的顯示問題
7.2 優(yōu)化方向
增加歷史顏色記錄
支持顏色格式轉(zhuǎn)換
添加插件系統(tǒng)
到此這篇關(guān)于使用Python開發(fā)一個(gè)現(xiàn)代化屏幕取色器的文章就介紹到這了,更多相關(guān)Python屏幕取色器內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
wxPython學(xué)習(xí)之主框架實(shí)例
這篇文章主要介紹了wxPython學(xué)習(xí)之主框架應(yīng)用實(shí)例,以一個(gè)基礎(chǔ)的彈出窗體實(shí)例講述了wxPython主框架應(yīng)用程序的實(shí)現(xiàn)方法,需要的朋友可以參考下2014-09-09
手把手教你用Python中的Linting提高代碼質(zhì)量
Python是一種不斷發(fā)展的語(yǔ)言,隨著它的演化和擴(kuò)展,可用工具和開發(fā)策略的數(shù)量也在增加,近來流行的一個(gè)過程是linting—檢查代碼的潛在問題,下面這篇文章主要給大家介紹了關(guān)于用Python中Linting提高代碼質(zhì)量的相關(guān)資料,需要的朋友可以參考下2023-01-01
python實(shí)現(xiàn)帶錯(cuò)誤處理功能的遠(yuǎn)程文件讀取方法
這篇文章主要介紹了python實(shí)現(xiàn)帶錯(cuò)誤處理功能的遠(yuǎn)程文件讀取方法,涉及Python使用socket操作遠(yuǎn)程文件的相關(guān)技巧,非常具有實(shí)用價(jià)值,需要的朋友可以參考下2015-04-04
Python+matplotlib實(shí)現(xiàn)華麗的文本框演示代碼
這篇文章主要介紹了Python+matplotlib實(shí)現(xiàn)華麗的文本框演示代碼,分享了相關(guān)代碼示例,小編覺得還是挺不錯(cuò)的,具有一定借鑒價(jià)值,需要的朋友可以參考下2018-01-01
python torch.utils.data.DataLoader使用方法
這篇文章主要介紹了python torch.utils.data.DataLoader使用方法,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-04-04
Pytorch中關(guān)于F.normalize計(jì)算理解
這篇文章主要介紹了Pytorch中關(guān)于F.normalize計(jì)算理解,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-02-02
Python實(shí)現(xiàn)的對(duì)本地host127.0.0.1主機(jī)進(jìn)行掃描端口功能示例
這篇文章主要介紹了Python實(shí)現(xiàn)的對(duì)本地host127.0.0.1主機(jī)進(jìn)行掃描端口功能,可實(shí)現(xiàn)掃描本機(jī)開放端口的功能,涉及Python socket模塊與Thread多線程模塊相關(guān)使用技巧,需要的朋友可以參考下2019-02-02
對(duì)python requests發(fā)送json格式數(shù)據(jù)的實(shí)例詳解
今天小編就為大家分享一篇對(duì)python requests發(fā)送json格式數(shù)據(jù)的實(shí)例詳解,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2018-12-12

