0. きっかけ
PowerShellで作っていたツールをLinux環境で使いたくなったから。
1. ツールの詳細
RGB値を入力して色を確認できる簡易ツール。
Windows用としてPowerShell+WinForms、
Linux用としてPython+Tkinterで作成。
可能な限り同じ操作感・同じ機能になるよう実装した。
2. 完成画面
PowerShell版
Python版
3. 影付き文字の実装差異
3-1. PowerShell+WinForms側
Labelを2つズラして重ねることで実装。
影用Labelを親にして、その上に文字用Labelを重ねることで、
比較的シンプルに影付き文字を表現できた。
#▼ラベル(影)
$lbs = New-Object System.Windows.Forms.Label
$lbs.Text = $text + ":"
$lbs.Top = $y + 1
$lbs.Left = 1 + 1
$lbs.Width = 23
$lbs.Height = 20
$lbs.TextAlign = [System.Drawing.ContentAlignment]::MiddleRight
$lbs.ForeColor = [System.Drawing.Color]::Silver
$mainForm.Controls.Add($lbs)
#▼ラベル
$lb = New-Object System.Windows.Forms.Label
$lb.Parent = $lbs
$lb.Text = $lbs.Text
$lb.Top = - 1
$lb.Left = - 1
$lb.Width = $lbs.Width
$lb.Height = $lbs.Height
$lb.TextAlign = [System.Drawing.ContentAlignment]::MiddleRight
$lb.ForeColor = [System.Drawing.Color]::Black
$lb.BackColor = [System.Drawing.Color]::FromARGB(0, 255, 255, 255)
$lbs.Controls.Add($lb)
3-2. Python+Tkinter側
TkinterのLabelは背景色が透過できないため、
Canvasの上に文字列2つをズラして作成した。
# 影作成
text = self.canvas.create_text(
x + 1, y + 1,
text = label_text,
fill = "silver"
)
# 文字作成
text = self.canvas.create_text(
x, y,
text = label_text,
fill = "black"
)
影付き文字そのものはcreate_text()を2回呼び出すだけ。
しかし、描画位置を決定するために、
- フォント取得
- metrics取得
- 文字列幅取得
- Canvasサイズ計算
といった下準備が必要となり、実際のコード量は大きく増えた。
また、Canvasのサイズ計算にScaleおよびEntryのサイズ情報を利用しているため、
Canvasの生成順序も画面上の配置順とは一致していない。
4. 入力補正の実装差異
4-1. PowerShell+WinForms側
TextChangedイベントで補正処理を実施。
#▼ScrollBarとTextBoxの値連携
$sb.Add_ValueChanged({
$tb.Text = $sb.Value
}.GetNewClosure())
$tb.Add_TextChanged({
RangeRGB $tb
$sb.Value = $tb.Text
ChangeColor # ←補正処理用関数
}.GetNewClosure())
4-2. Python+Tkinter側
StringVar.trace_add()とEntryのPasteイベントで補正処理を実施。
PowerShell版では TextChanged イベントを利用することで、
入力補正と色変更処理をほぼ1か所に集約できた。
一方Tkinter版では、StringVar.trace_add()で入力変更を拾えば良いと思っていたが、
貼り付け操作では期待したように処理できなかったため、
Pasteイベントを追加で処理することにした。
その結果、入力補正に関するコードは複数のイベントハンドラへ分散する形となった。
特に貼り付け処理はWinFormsより実装量が大きく、
選択範囲の置換とカーソル位置補正を自前で行う必要があった。
▼イベント登録
#▼Entry(column=2)
self.textvar = tk.StringVar(value = 127) # 初期値
self.textvar.trace_add("write", self.on_entry_change)
self.entry = tk.Entry(
self,
width = 3,
justify = "center",
bd = 1,
highlightthickness = 0,
textvariable = self.textvar
)
self.on_entry_change()
self.entry.bind("<KeyPress-Up>", self.increment)
self.entry.bind("<KeyPress-Down>", self.decrement)
self.entry.bind("<<Paste>>", self.on_paste)
self.entry.grid(row=0, column=2, sticky="e")
▼呼び出し関数
def on_scale_change(self, value):
self.textvar.set(value)
ChangeColor()
def on_entry_change(self, *args):
self.textvar.set(
self.RangeRGB(self.entry.get())
)
self.scale.set(
int(self.textvar.get())
)
def on_paste(self, event):
# クリップボードから値取得
try:
paste_txt = event.widget.clipboard_get()
txt = event.widget.get()
except tk.TclError:
return "break"
# 選択範囲の文字列を削除
if event.widget.selection_present():
select_start = min(
event.widget.index(tk.ANCHOR),
event.widget.index(tk.INSERT)
)
select_end = max(
event.widget.index(tk.ANCHOR),
event.widget.index(tk.INSERT)
)
event.widget.delete(select_start, select_end)
pos = select_start
else:
pos = event.widget.index(tk.INSERT)
# クリップボードの文字を貼り付け
event.widget.insert(pos, paste_txt)
event.widget.icursor(pos + len(paste_txt))
return "break"
イベントが複数に分散しているのは、 入力経路ごとに発生条件が異なるため。
5. 作ってみた感想
同じツールでも、GUIライブラリや言語が変わると実装方法はかなり異なる。
PowerShell+WinFormsでは比較的素直に実装できる部分でも、
Python+Tkinterでは別のアプローチが必要になる場面があった。
特にTkinterはイベント処理周りの考え方がWinFormsと大きく異なり、
実装中に何度も悩まされた。
ただし最終的には、ほぼ同じ操作感で実現できた。
Tkinterのイベント、まぢパねぇ ![]()

