使用Python開(kāi)發(fā)一個(gè)現(xiàn)代化屏幕取色器
一、項(xiàng)目概述
在UI設(shè)計(jì)、網(wǎng)頁(yè)開(kāi)發(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)獲取
通過(guò)信號(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)智能反色,確保文字始終可見(jiàn)
三、效果展示
四、實(shí)現(xiàn)步驟詳解
4.1 環(huán)境配置
pip install pyqt5 pyautogui pillow pywin32
4.2 關(guān)鍵實(shí)現(xiàn)步驟
創(chuàng)建無(wú)邊框窗口
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緩存
- 線程安全:通過(guò)信號(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è)垂直布局容器來(lái)包裹兩個(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? 松開(kāi)以確認(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? 松開(kāi)以確認(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? 松開(kāi)以確認(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延遲的顏色采樣
開(kāi)發(fā)了符合現(xiàn)代審美的UI組件
解決了高DPI屏幕下的顯示問(wèn)題
7.2 優(yōu)化方向
增加歷史顏色記錄
支持顏色格式轉(zhuǎn)換
添加插件系統(tǒng)
到此這篇關(guān)于使用Python開(kāi)發(fā)一個(gè)現(xiàn)代化屏幕取色器的文章就介紹到這了,更多相關(guān)Python屏幕取色器內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
pytorch中torch.max和Tensor.view函數(shù)用法詳解
今天小編就為大家分享一篇pytorch中torch.max和Tensor.view函數(shù)用法詳解,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2020-01-01python使用自定義user-agent抓取網(wǎng)頁(yè)的方法
這篇文章主要介紹了python使用自定義user-agent抓取網(wǎng)頁(yè)的方法,涉及Python使用urllib2模塊操作網(wǎng)頁(yè)的技巧,需要的朋友可以參考下2015-04-04python實(shí)現(xiàn)簡(jiǎn)單淘寶秒殺功能
這篇文章主要為大家詳細(xì)介紹了python實(shí)現(xiàn)簡(jiǎn)單淘寶秒殺功能,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-05-05在pycharm中輸入import torch報(bào)錯(cuò)如何解決
這篇文章主要介紹了在pycharm中輸入import torch報(bào)錯(cuò)如何解決問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-01-01LyScript實(shí)現(xiàn)Hook改寫(xiě)MessageBox的方法詳解
LyScript可實(shí)現(xiàn)自定義匯編指令的替換功能。用戶可自行編寫(xiě)匯編指令,將程序中特定的通用函數(shù)進(jìn)行功能改寫(xiě)與轉(zhuǎn)向操作,此功能原理是簡(jiǎn)單的Hook操作。本文將詳細(xì)介紹Hook改寫(xiě)MessageBox的方法,感興趣的可以了解一下2022-09-09Python采集天天基金數(shù)據(jù)掌握最新基金動(dòng)向
這篇文章主要介紹了Python采集天天基金數(shù)據(jù)掌握最新基金動(dòng)向,本次案例實(shí)現(xiàn)流程為發(fā)送請(qǐng)求、獲取數(shù)據(jù)、解析數(shù)據(jù)、多頁(yè)爬取、保存數(shù)據(jù),接下來(lái)來(lái)看看具體的操作過(guò)程吧2022-01-01Pytorch 多維數(shù)組運(yùn)算過(guò)程的索引處理方式
今天小編就為大家分享一篇Pytorch 多維數(shù)組運(yùn)算過(guò)程的索引處理方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2019-12-12Python中根據(jù)時(shí)間自動(dòng)創(chuàng)建文件夾的代碼實(shí)現(xiàn)
這篇文章主要介紹了Python中根據(jù)時(shí)間自動(dòng)創(chuàng)建文件夾的代碼實(shí)現(xiàn),這樣的話給工作帶來(lái)極大的便利,方便桌面文件按時(shí)間存放,具體實(shí)例代碼跟隨小編一起看看吧2021-10-10