試験運用中なLinux備忘録・旧記事

はてなダイアリーで公開していた2007年5月-2015年3月の記事を保存しています。

端末の256色パレットをGUI上で確認できるツールを更新(ボタンを改善・クリップボードに色の情報を記憶)

以前、「端末の256色パレットをGTK+のGUI上で表示できるツールを作成」で端末の256色パレットをGTK+GUI上で確認できるTermColorPalette256というツールを作成したが、当時は色を表示するためのGUI部品にGTK+の色選択ボタンを用いており、別の色を選択したときに表示色が変わってしまったり、色のコードを画面上で確認しなくてはならなかったりして、使い勝手はあまり良くなかった。
今回、これを改善し、ボタンはgtk.Buttonクラスを継承したクラスのメンバかつ子ウィジェット*1としてGTK+のDrawingAreaウィジェット(関連記事:前半 後半)を含めるという形をとることで、画面上の表示色を付けられつつ、色コード(番号)や色の文字列表記に関する情報を中に含むことができるようになった。これにより、各ボタンのツールチップとして色コードと色の文字列表記を表示できたり、ボタンクリックで色の256色パレット上のコードか16進表記の文字列をクリップボードに記憶したりできるようになった。

スクリーンショット


196-231番のタブでツールチップ表示したところ

グレイスケール色のタブ

コード

動作にはPyGTKとPyGObjectが必要。
[任意]ファイル名: termcolorpalette256-1.0.py ライセンス: GPL-3 (or lator)

#! /usr/bin/python
# -*- coding: utf-8 -*-

# TermColorPalette256 (C) 2009-2010 kakurasan
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, see <http://www.gnu.org/licenses/>.


import sys
try:
  import pygtk
  pygtk.require('2.0')
except:
  pass
try:
  import gobject
  import gtk
except:
  print >> sys.stderr, 'Error: PyGTK is not installed'
  sys.exit(1)


class ColorDrawingButton(gtk.Button):
  """
  button with DrawingArea for color displaying
  """
  def __init__(self, code, color):
    gtk.Button.__init__(self)
    (self.__code, self.__color) = (code, color)
    self.__width = self.__height = 0
    self.__da = gtk.DrawingArea()
    self.add(self.__da)
    self.__da.connect('size-allocate', self.__on_da_size_allocate)
    self.__da.connect('expose-event', self.__on_da_expose_event)
    self.connect('clicked', self.__on_self_clicked)
    self.props.relief = gtk.RELIEF_NONE
    self.props.tooltip_text = 'code:%d color:%s' % (self.__code, self.__color)
  def __on_da_size_allocate(self, widget, allocation):
    """
    size changed
    """
    self.__width = allocation.width
    self.__height = allocation.height
  def __on_da_expose_event(self, widget, event):
    """
    draw/fill rectangle (using Cairo)
    """
    ctx = widget.window.cairo_create()
    ctx.set_source_color(gtk.gdk.Color(self.__color))
    ctx.rectangle(0, 0, self.__width, self.__height)
    ctx.fill()
  def __on_self_clicked(self, widget):
    """
    copy color code/string to clipboard
    """
    copy_type = widget.parent.parent.parent.parent.props.copy_type
    if copy_type == 'code':
      TermColorPalette256.clipboard.set_text(str(self.__code))
    else:
      color = self.__color
      if widget.parent.parent.parent.parent.props.no_numbersign:
        color = color[1:]
      if copy_type == 'upper':
        color = color.upper()
      TermColorPalette256.clipboard.set_text(color)

class ColorCubeColorTab(gtk.Table):
  """
  tab for colorcube color
  """
  table = [0x00, 0x5f, 0x87, 0xaf, 0xd7, 0xff]
  def __init__(self, offset):
    gtk.Table.__init__(self, 7, 7)
    for i in range(6):
      # 0 1 2 3 4 5
      self.attach(gtk.Label(str(i)), i + 1, (i + 1) + 1, 0, 1)
      # offset
      self.attach(gtk.Label('%03d' % (16 + 36 * offset + 6 * i)), 0, 1, i + 1, (i + 1) + 1)
    for i in range(6):
      for j in range(6):
        col = '#%02x%02x%02x' % (ColorCubeColorTab.table[offset], ColorCubeColorTab.table[i], ColorCubeColorTab.table[j])
        button = ColorDrawingButton(16 + 36 * offset + 6 * i + j, col)
        self.attach(button, j + 1, (j + 1) + 1, i + 1, (i + 1) + 1)

class GrayScaleColorTab(gtk.Table):
  """
  tab for grayscale color
  """
  def __init__(self):
    gtk.Table.__init__(self, 7, 7)
    for i in range(6):
      # 0 1 2 3 4 5
      self.attach(gtk.Label(str(i)), i + 1, (i + 1) + 1, 0, 1)
      # offset
      self.attach(gtk.Label('%03d' % (16 + 43 * i)), i + 1, (i + 1) + 1, 5, 6)
    for i in range(4):
      # each color code (16, 59, 102, 145, 188, 231)
      self.attach(gtk.Label('%03d' % (232 + 6 * i)), 0, 1, i + 1, (i + 1) + 1)
    self.attach(gtk.Label('c'), 0, 1, 6, 7)
    col = 0x08
    for i in range(4):
      for j in range(6):
        colstr = '#' + ('%02x' % col) * 3
        button = ColorDrawingButton(232 + 6 * i + j, colstr)
        self.attach(button, j + 1, (j + 1) + 1, i + 1, (i + 1) + 1)
        col += 10
    for i in range(6):
      col = ColorCubeColorTab.table[i]
      colstr = '#' + ('%02x' % col) * 3
      button = ColorDrawingButton(16 + 43 * i, colstr)
      self.attach(button, i + 1, (i + 1) + 1, 6, 7)
      col += 43

class MainWindow(gtk.Window):
  """
  main window
  """
  __gproperties__ = \
  {
    'copy-type' : (gobject.TYPE_STRING,
                   'copy type',
                   'copy type (code, lower, or upper)',
                   '',
                   gobject.PARAM_READABLE),
    'no-numbersign' : (gobject.TYPE_BOOLEAN,
                       'no numsign',
                       'if True, do not copy "#"',
                       False,
                       gobject.PARAM_READABLE),
  }
  def __init__(self, *args, **kwargs):
    gtk.Window.__init__(self, *args, **kwargs)
    self.props.title = 'TermColorPalette256'
    self.set_size_request(450, 250)
    self.resize(450, 260)
    self.__accelgroup = gtk.AccelGroup()
    self.add_accel_group(self.__accelgroup)
    self.__item_quit = gtk.ImageMenuItem(gtk.STOCK_QUIT, self.__accelgroup)
    self.__item_about = gtk.ImageMenuItem(gtk.STOCK_ABOUT, self.__accelgroup)
    self.__menu_file = gtk.Menu()
    self.__menu_file.add(self.__item_quit)
    self.__menu_help = gtk.Menu()
    self.__menu_help.add(self.__item_about)
    self.__item_file = gtk.MenuItem('_File')
    self.__item_help = gtk.MenuItem('_Help')
    self.__item_file.props.submenu = self.__menu_file
    self.__item_help.props.submenu = self.__menu_help
    self.__menubar = gtk.MenuBar()
    self.__menubar.append(self.__item_file)
    self.__menubar.append(self.__item_help)
    self.__radio_copy_colorcode = gtk.RadioButton(label='_code')
    self.__radio_copy_colorstr_lower = gtk.RadioButton(group=self.__radio_copy_colorcode, label='string/_lowercase')
    self.__radio_copy_colorstr_upper = gtk.RadioButton(group=self.__radio_copy_colorcode, label='string/_uppercase')
    self.__check_copy_no_numsign = gtk.CheckButton('no _numbersign(#)')
    self.__check_copy_no_numsign.props.sensitive = False
    # containers
    self.__notebook = gtk.Notebook()
    self.__notebook.props.tab_pos = gtk.POS_LEFT
    for i in range(6):
      child = ColorCubeColorTab(i)
      self.__notebook.append_page(child)
      self.__notebook.set_tab_label_text(child, '%03d-%03d(R=0x%02x)' % ((16 + 36 * i), (16 + 36 * (i + 1) - 1), ColorCubeColorTab.table[i]))
    child = GrayScaleColorTab()
    self.__notebook.append_page(child)
    self.__notebook.set_tab_label_text(child, 'grayscale')
    self.__hbox_copy = gtk.HBox()
    self.__hbox_copy.pack_start(self.__radio_copy_colorcode, expand=False, fill=False)
    self.__hbox_copy.pack_start(self.__radio_copy_colorstr_lower, expand=False, fill=False)
    self.__hbox_copy.pack_start(self.__radio_copy_colorstr_upper, expand=False, fill=False)
    self.__hbox_copy.pack_start(self.__check_copy_no_numsign, expand=False, fill=False)
    self.__vbox = gtk.VBox()
    self.__vbox.pack_start(self.__menubar, expand=False, fill=False)
    self.__vbox.pack_start(self.__notebook)
    self.__vbox.pack_start(self.__hbox_copy)
    self.add(self.__vbox)
    # signals
    self.__item_quit.connect('activate', gtk.main_quit)
    self.__item_about.connect('activate', self.__on_item_about_activate)
    self.__radio_copy_colorcode.connect('toggled', lambda widget: self.__check_copy_no_numsign.set_sensitive(not widget.props.active))
    self.connect('delete_event', gtk.main_quit)
    try:
      self.__icon = gtk.gdk.pixbuf_new_from_file('/usr/share/pixmaps/gnome-color-xterm.png')
      self.props.icon = self.__icon
    except:
      self.__icon = None
  def __on_item_about_activate(self, widget):
    """
    run about dialog
    """
    aboutdlg = gtk.AboutDialog()
    aboutdlg.props.authors = ['kakurasan <kakurasan AT gmail DOT com>',]
    aboutdlg.props.copyright = '〓 2009-2010 kakurasan'
    aboutdlg.props.license = 'This program is licensed under GPL-3 or lator.'
    aboutdlg.props.version = '1.0'
    aboutdlg.props.comments = 'terminal 256 color palette/selector'
    aboutdlg.props.logo = aboutdlg.props.icon = self.__icon
    aboutdlg.set_program_name('TermColorPalette256')
    aboutdlg.run()
    aboutdlg.destroy()
  def do_get_property(self, property):
    """
    get property
    """
    if property.name == 'copy-type':
      if self.__radio_copy_colorcode.props.active:
        return 'code'
      elif self.__radio_copy_colorstr_lower.props.active:
        return 'lower'
      else:
        return 'upper'
    elif property.name == 'no-numbersign':
      return self.__check_copy_no_numsign.props.active

class TermColorPalette256:
  """
  terminal 256 color palette/selector
  """
  clipboard = gtk.Clipboard()
  def main(self):
    """
    main
    """
    win = MainWindow()
    win.show_all()
    gtk.main()


if __name__ == '__main__':
  app = TermColorPalette256()
  app.main()

使い方

はじめに256色パレットの一部(16-51番)が表示され、左に並ぶタブでページを切り替えることができる。
色の部分はボタンになっており、クリックすることでその色の情報をクリップボードにコピーできる。また、ツールチップとして色コードと文字列表記を確認することもできる。
コピーされる内容は

  • 256色パレット上の色コード(番号)
  • 文字列(16進のA-Fは大文字表記)
  • 文字列(16進のA-Fは小文字表記)

のいずれかで、下部のラジオボタンで選択する。文字列表記の場合には「#」(numbersign)記号を先頭に付けるかどうかもチェックで設定できる。この設定を変更しても現在のクリップボードの内容は変わらず、次のボタンクリック時から有効になる。

関連記事:

使用したバージョン:

  • Python 2.6.4
  • PyGTK 2.16.0
  • pycairo 1.8.6

*1:GTK+のボタン(gtk.Buttonクラス)は(gtk.Binクラスの子クラスで)別のGUI部品を1つだけ中に入れることができる