fletでカラーピッカーが作れたので共有します。
見た目はこんな感じ↓
(2023/09/12 追記)
ドラッグするタイプのカラーピッカーも作れたのでそのうち記事にします
fletでの色の指定とグラデーションについて
fletではft.colors.RED
など用意されている色のほかに、16進数のカラーコードでの色指定ができます。
また、fletのグラデーションは三種類あります。
ft.LinearGradient
とft.RadialGradient
では3つ以上の色をグラデーションにできますが、ft.SweepGradient
だけは2色までしか設定できません。
コントロールの設定
今回のコードでは、スライダーでHSVの各値、もしくはテキスト入力でカラーコードを取得し、プログラムでカラーコードに変換して表示しています。
スライダー自体の色は変更できないので、スライダーをコンテナに格納し、コンテナの背景にグラデーションを設定してカラーそのものを選択しているように見せています。
コード
今回は、画面に表示するものを書くファイルと色空間の変換を行うファイルに分けて作成しています。
色変換
colorpicker_detail_process.py
import math
import colorsys
def hsv_to_hex(h,s,v):
# hsv to rgb
r,g,b = colorsys.hsv_to_rgb(h,s,v)
r,g,b = r*255, g*255, b*255
# rgb to hex
return "#%02X%02X%02X" % (int(r), int(g), int(b))
def hex_to_hsv(hex_str):
try:
hex_str = hex_str.lstrip("#")
if len(hex_str) != 6:
return "error!"
r,g,b = tuple(int(hex_str[i:i+2], 16) for i in (0, 2, 4))
h,s,v = colorsys.rgb_to_hsv(r,g,b)
v = v/255
return h,s,v
except:
return "error!"
表示側
main.py
import flet as ft
# 色変換のファイルをインポート
import colorpicker_detail_process as dp
# スライダーとコンテナを組み合わせるクラス
class AreaTemplate(ft.UserControl):
def __init__(self, value, color_list, change_method):
super().__init__()
self.value = value
self.color_list = color_list
self.change_method = change_method
def build(self):
return ft.Container(
height=10,
padding=0,
content=ft.Slider(
min=0,
max=1,
value=self.value,
divisions=1000,
on_change=self.change_method,
active_color=ft.colors.TRANSPARENT, # スライダーの色を透明にする
inactive_color=ft.colors.TRANSPARENT, # スライダーの色を透明にする
thumb_color=ft.colors.WHITE, # スライダーのボタンの色を白にする
),
# 背景グラデーションの設定
gradient=ft.LinearGradient(
begin=ft.alignment.center_left,
end=ft.alignment.center_right,
colors=self.color_list,
)
)
# カラーピッカーの見た目と処理が書いてあるクラス
class ColorPicker(ft.UserControl):
def __init__(self):
super().__init__()
def build(self):
# 色を表示する円の大きさ
self.color_size = 100
# グラデーションを構成する色の数
color_count = 100
# HSVの初期値
self.h_value, self.s_value, self.v_value = 0.5, 0.5, 0.5
# グラデーションにする色のリスト
self.color_division = [n/color_count for n in range(color_count)] # 等差数列を作成
self.h_list = [dp.hsv_to_hex(n,1,self.v_value) for n in self.color_division] # 等差数列を利用して、HSVの各値を0から1まで変化させてグラデーションに使う色のリストを作成
self.s_list = [dp.hsv_to_hex(self.h_value,n,1) for n in self.color_division]
v_list = [dp.hsv_to_hex(0,0,n) for n in self.color_division]
# HSVのスライダーのインスタンスの作成
self.h_area = AreaTemplate(self.h_value,self.h_list, self.h_change)
self.s_area = AreaTemplate(self.s_value,self.s_list, self.s_change)
self.v_area = AreaTemplate(self.v_value,v_list, self.v_change)
# カラーコードを表示するTextFieldの作成
self.display_color_code = ft.TextField(
value=dp.hsv_to_hex(self.h_value, self.s_value, self.v_value),
border="underline",
on_submit=self.color_code_change
)
# 色を表示する円の作成
self.display_color = ft.Container(
width=self.color_size,
height=self.color_size,
border_radius=self.color_size,
bgcolor=dp.hsv_to_hex(self.h_value, self.s_value, self.v_value)
# 表示の作成
return ft.Column(
alignment="center",
controls=[
self.display_color,
self.display_color_code,
self.h_area,
self.s_area,
self.v_area
]
)
# h,s,vのスライダーが変更されたときの処理
def h_change(self, e):
# スライダーの値を取得
self.h_value = e.control.value
# グラデーションにする色のリストを更新
self.s_list = [dp.hsv_to_hex(self.h_value,n,1) for n in self.color_division]
self.s_area.controls[0].gradient.colors = self.s_list
# カラーコードと表示色の更新
self.display_color_code.value=dp.hsv_to_hex(self.h_value, self.s_value, self.v_value)
self.display_color.shapes[0].paint.color = dp.hsv_to_hex(self.h_value, self.s_value, self.v_value)
# 表示の更新
self.s_area.update()
self.update()
def s_change(self, e):
self.s_value = e.control.value
self.display_color_code.value=dp.hsv_to_hex(self.h_value, self.s_value, self.v_value)
self.display_color.shapes[0].paint.color = dp.hsv_to_hex(self.h_value, self.s_value, self.v_value)
self.update()
def v_change(self, e):
self.v_value = e.control.value
self.h_list = [dp.hsv_to_hex(n,1,self.v_value) for n in self.color_division]
self.h_area.controls[0].gradient.colors = self.h_list
self.display_color_code.value=dp.hsv_to_hex(self.h_value, self.s_value, self.v_value)
self.display_color.shapes[0].paint.color = dp.hsv_to_hex(self.h_value, self.s_value, self.v_value)
self.h_area.update()
self.update()
# テキストが入力されたときの処理
def color_code_change(self, e):
# 入力された値を取得
hex_str = e.control.value
# 入力された値がカラーコードかどうかの判定と処理
if dp.hex_to_hsv(hex_str) == "error!":
self.display_color_code.value = dp.hsv_to_hex(self.h_value, self.s_value, self.v_value) # TextFieldの表示を元に戻す
self.display_color_code.error_text = "16進数のカラーコードを入力してください"
self.update()
return "error!"
else:
self.display_color_code.error_text = None # エラーテキストをリセット
self.update()
# 入力されたカラーコードをHSVに変換して取得
self.h_value, self.s_value, self.v_value = dp.hex_to_hsv(hex_str)
# 各スライダーの更新
self.h_area.controls[0].content.value = self.h_value
self.s_area.controls[0].content.value = self.s_value
self.v_area.controls[0].content.value = self.v_value
self.s_list = [dp.hsv_to_hex(self.h_value,n,1) for n in self.color_division]
self.s_area.controls[0].gradient.colors = self.s_list
self.h_list = [dp.hsv_to_hex(n,1,self.v_value) for n in self.color_division]
self.h_area.controls[0].gradient.colors = self.h_list
self.h_area.update()
self.s_area.update()
self.v_area.update()
# 表示色の更新
self.display_color.shapes[0].paint.color = dp.hsv_to_hex(self.h_value, self.s_value, self.v_value)
self.display_color.update()
def main(page: ft.Page):
page.add(ColorPicker())
if __name__ == "__main__":
ft.app(target=main)