nvidia,fglrx以外のドライバ向けのX Window Systemの明るさとガンマのGUI調整ツールを作成
「X Window Systemでnvidia,fglrx以外のドライバを用いた環境における明るさとガンマの調整について(ページ2/3)」「X Window Systemでnvidia,fglrx以外のドライバを用いた環境における明るさとガンマの調整について(ページ3/3)」で作成した、nvidia,fglrx以外のドライバ向けの明るさとガンマの調整ツールのGUI版を作成した。
左側に色曲線,右側に操作のGUI部品を配置しており、いずれかの値を変更するとその場で画面上に反映され、同時に設定の自動キャンセルのカウントダウン(10秒)が開始される。
カウントダウンが始まると「適用」ボタンが押せるようになり、カウントが0になるまでに「適用」を押さないとカウントダウン終了のタイミングで自動的に変更前の値に戻る。
コードはPython(GUIにはPyGTK)を使用しており、動作には
- バージョン2.6/2.7系のいずれかのPython
- PyGTK(PyCairo含む)のバージョン2系
- PyGObject
が必要。
[任意]ファイル名: xbrightness-rgbgamma-gtk.py ライセンス: MIT
#! /usr/bin/python # # X11 brightness / gamma adjustment tool (PyGTK GUI) # version 20110505 # (C) 2011 kakurasan # Licensed under MIT # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal # in the Software without restriction, including without limitation the rights # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell # copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # THE SOFTWARE. # # THANKS: # xbrightness by Asher Blum and Jean-Daniel Pauget import ctypes import math import sys try: import pygtk pygtk.require ('2.0') except: pass try: import cairo import glib import gtk except: sys.exit (1) def run_aboutdialog (window): """ Run "about" dialog """ dialog = gtk.AboutDialog () dialog.props.authors = ['kakurasan',] dialog.props.comments = 'Brightness and gamma adjustment tool' dialog.props.copyright = '(C) 2011 kakurasan' dialog.props.license = 'MIT' dialog.props.program_name = 'xbrightness-rgbgamma-gtk' dialog.props.version = '20110429' dialog.run () dialog.destroy () class GetGammaRampSizeError (Exception): pass class GetGammaRampError (Exception): pass class SetGammaRampError (Exception): pass class Libxxf86vm: __lib = ctypes.CDLL ('libXxf86vm.so.1') __ramp_size = ctypes.c_int () def __init__ (self): # XOpenDisplay(): Display * self.__display = self.__lib.XOpenDisplay (None) # envar "DISPLAY" # XDefaultScreen: Display * self.__screen = self.__lib.XDefaultScreen (self.__display) # XF86VidModeGetGammaRampSize(): Display *, int, int * ret = self.__lib.XF86VidModeGetGammaRampSize (self.__display, self.__screen, ctypes.byref (self.__ramp_size)) if not ret: raise GetGammaRampSizeError () Ramp = ctypes.c_ushort * self.__ramp_size.value # type (self.__ramp_red, self.__ramp_green, self.__ramp_blue) = (Ramp (), Ramp (), Ramp ()) # XF86VidModeGetGammaRamp(): Display *, int, int, # unsigned short *, unsigned short *, unsigned short * ret = self.__lib.XF86VidModeGetGammaRamp (self.__display, self.__screen, self.__ramp_size, self.__ramp_red, self.__ramp_green, self.__ramp_blue) if not ret: raise GetGammaRampError () def __del__ (self): if self.__display: # XCloseDisplay(): Display * self.__lib.XCloseDisplay (self.__display) def set_gamma_and_brightness (self, brightness, gamma_red, gamma_green, gamma_blue): for i in range (self.__ramp_size.value): p = i / float (self.__ramp_size.value - 1) self.__ramp_red[i] = ctypes.c_ushort (int (brightness * pow (p, 1 / gamma_red))) self.__ramp_green[i] = ctypes.c_ushort (int (brightness * pow (p, 1 / gamma_green))) self.__ramp_blue[i] = ctypes.c_ushort (int (brightness * pow (p, 1 / gamma_blue))) # XF86VidModeSetGammaRamp(): Display *, int, int, # unsigned short *, unsigned short *, unsigned short * ret = self.__lib.XF86VidModeSetGammaRamp (self.__display, self.__screen, self.__ramp_size, self.__ramp_red, self.__ramp_green, self.__ramp_blue) if not ret: raise SetGammaRampError () @property def ramp_red (self): return self.__ramp_red @property def ramp_green (self): return self.__ramp_green @property def ramp_blue (self): return self.__ramp_blue @property def ramp_size (self): return self.__ramp_size.value class GraphDrawingArea (gtk.DrawingArea): """ DrawingArea for gamma curve """ def __init__ (self, lib): gtk.DrawingArea.__init__ (self) self.connect ('size-allocate', self.__on_self_size_allocate) self.connect ('expose-event', self.__on_self_expose_event) self.__lib = lib self.__ramp_red = lib.ramp_red self.__ramp_green = lib.ramp_green self.__ramp_blue = lib.ramp_blue self.__ramp_size = lib.ramp_size def __on_self_size_allocate (self, widget, allocation): """ 'size-allocate' handler for this DrawingArea """ self.__width = allocation.width self.__height = allocation.height def __on_self_expose_event (self, widget, event): """ Draw gamma curve """ x_step = float (self.__width) / float (self.__ramp_size - 1) y_step = self.__height / 256.0 ctx_bg = widget.window.cairo_create () ctx_bg.set_source_rgb (1.0, 1.0, 1.0) ctx_bg.rectangle (0, 0, self.__width, self.__height) ctx_bg.fill () ctx_r = widget.window.cairo_create () ctx_r.set_source_rgba (0.8, 0.0, 0.0, 0.5) ctx_r.new_path () ctx_g = widget.window.cairo_create () ctx_g.set_source_rgba (0.0, 0.8, 0.0, 0.5) ctx_g.new_path () ctx_b = widget.window.cairo_create () ctx_b.set_source_rgba (0.0, 0.0, 0.8, 0.5) ctx_b.new_path () for i in range (self.__ramp_size): ctx_r.line_to (x_step * i, y_step * (65535 - self.__ramp_red[i]) / 256.0) ctx_g.line_to (x_step * i, y_step * (65535 - self.__ramp_green[i]) / 256.0) ctx_b.line_to (x_step * i, y_step * (65535 - self.__ramp_blue[i]) / 256.0) ctx_r.stroke () ctx_g.stroke () ctx_b.stroke () def redraw (self): """ Force redraw """ self.__ramp_red = self.__lib.ramp_red self.__ramp_green = self.__lib.ramp_green self.__ramp_blue = self.__lib.ramp_blue self.emit ('expose-event', None) class MainWindow (gtk.Window): """ Application main window """ def __init__ (self, *args, **kwargs): gtk.Window.__init__ (self, *args, **kwargs) self.__lib = Libxxf86vm () 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_file.props.submenu = self.__menu_file self.__item_help = gtk.MenuItem ('_Help') 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.__drawingarea = GraphDrawingArea (self.__lib) self.__frame_brightness = gtk.Frame ('Brightness') self.__frame_gamma = gtk.Frame ('Gamma correction') self.__frame_gamma_red = gtk.Frame ('Red') self.__frame_gamma_green = gtk.Frame ('Green') self.__frame_gamma_blue = gtk.Frame ('Blue') self.__frame_brightness.props.shadow_type = self.__frame_gamma.props.shadow_type = gtk.SHADOW_IN self.__hbox_brightness = gtk.HBox () self.__hbox_gamma_red = gtk.HBox () self.__hbox_gamma_green = gtk.HBox () self.__hbox_gamma_blue = gtk.HBox () # calculate current gamma (ramp_red, ramp_green, ramp_blue) = self.__lib.ramp_red, self.__lib.ramp_green, self.__lib.ramp_blue ramp_size = self.__lib.ramp_size self.__brightness = ramp_red[ramp_size - 1] self.__gamma_red = float ('{0:.2f}'.format (1.0 / math.log (ramp_red[ramp_size / 2] / float (self.__brightness), (ramp_size / 2) / float (ramp_size - 1)))) self.__gamma_green = float ('{0:.2f}'.format (1.0 / math.log (ramp_green[ramp_size / 2] / float (self.__brightness), (ramp_size / 2) / float (ramp_size - 1)))) self.__gamma_blue = float ('{0:.2f}'.format (1.0 / math.log (ramp_blue[ramp_size / 2] / float (self.__brightness), (ramp_size / 2) / float (ramp_size - 1)))) # values self.__adjustment_brightness = gtk.Adjustment (value=self.__brightness, lower=0, upper=65535, step_incr=1, page_incr=100) self.__adjustment_gamma_red = gtk.Adjustment (value=self.__gamma_red, lower=0.01, upper=3, step_incr=0.01, page_incr=0.05) self.__adjustment_gamma_green = gtk.Adjustment (value=self.__gamma_green, lower=0.01, upper=3, step_incr=0.01, page_incr=0.05) self.__adjustment_gamma_blue = gtk.Adjustment (value=self.__gamma_blue, lower=0.01, upper=3, step_incr=0.01, page_incr=0.05) self.__hscale_brightness = gtk.HScale (self.__adjustment_brightness) self.__hscale_gamma_red = gtk.HScale (self.__adjustment_gamma_red) self.__hscale_gamma_green = gtk.HScale (self.__adjustment_gamma_green) self.__hscale_gamma_blue = gtk.HScale (self.__adjustment_gamma_blue) self.__hscale_brightness.props.draw_value \ = self.__hscale_gamma_red.props.draw_value \ = self.__hscale_gamma_green.props.draw_value \ = self.__hscale_gamma_blue.props.draw_value = False self.__spbtn_brightness = gtk.SpinButton (self.__adjustment_brightness) self.__spbtn_gamma_red = gtk.SpinButton (self.__adjustment_gamma_red, digits=2) self.__spbtn_gamma_green = gtk.SpinButton (self.__adjustment_gamma_green, digits=2) self.__spbtn_gamma_blue = gtk.SpinButton (self.__adjustment_gamma_blue, digits=2) self.__button_apply = gtk.Button (stock=gtk.STOCK_APPLY) self.__button_apply.props.sensitive = False self.__statusbar = gtk.Statusbar () self.__ctxid = self.__statusbar.get_context_id ('status') self.__hbox_brightness = gtk.HBox () self.__hbox_brightness.pack_start (self.__hscale_brightness) self.__hbox_brightness.pack_start (self.__spbtn_brightness, expand=False, fill=False) self.__hbox_gamma_red = gtk.HBox () self.__hbox_gamma_red.pack_start (self.__hscale_gamma_red) self.__hbox_gamma_red.pack_start (self.__spbtn_gamma_red, expand=False, fill=False) self.__hbox_gamma_green = gtk.HBox () self.__hbox_gamma_green.pack_start (self.__hscale_gamma_green) self.__hbox_gamma_green.pack_start (self.__spbtn_gamma_green, expand=False, fill=False) self.__hbox_gamma_blue = gtk.HBox () self.__hbox_gamma_blue.pack_start (self.__hscale_gamma_blue) self.__hbox_gamma_blue.pack_start (self.__spbtn_gamma_blue, expand=False, fill=False) self.__frame_gamma_red.add (self.__hbox_gamma_red) self.__frame_gamma_green.add (self.__hbox_gamma_green) self.__frame_gamma_blue.add (self.__hbox_gamma_blue) self.__vbox_gamma = gtk.VBox () self.__vbox_gamma.pack_start (self.__frame_gamma_red) self.__vbox_gamma.pack_start (self.__frame_gamma_green) self.__vbox_gamma.pack_start (self.__frame_gamma_blue) self.__frame_brightness.add (self.__hbox_brightness) self.__frame_gamma.add (self.__vbox_gamma) self.__vbox_right = gtk.VBox () self.__vbox_right.props.width_request = 200 self.__vbox_right.pack_start (self.__frame_brightness) self.__vbox_right.pack_start (self.__frame_gamma) self.__vbox_right.pack_start (self.__button_apply, expand=False, fill=False) self.__aspectframe = gtk.AspectFrame (yalign=0) self.__aspectframe.add (self.__drawingarea) self.__hbox = gtk.HBox () self.__hbox.pack_start (self.__aspectframe) self.__hbox.pack_start (self.__vbox_right, expand=False, fill=False) self.__vbox = gtk.VBox () self.__vbox.pack_start (self.__menubar, expand=False, fill=False) self.__vbox.pack_start (self.__hbox) self.__vbox.pack_start (self.__statusbar, expand=False, fill=False) # signals self.__item_quit.connect ('activate', gtk.main_quit) self.__item_about.connect ('activate', run_aboutdialog) self.__adjustment_brightness.connect ('value-changed', self.__on_adjustment_value_changed) self.__adjustment_gamma_red.connect ('value-changed', self.__on_adjustment_value_changed) self.__adjustment_gamma_green.connect ('value-changed', self.__on_adjustment_value_changed) self.__adjustment_gamma_blue.connect ('value-changed', self.__on_adjustment_value_changed) self.__button_apply.connect ('clicked', self.__on_button_apply_clicked) self.connect ('delete_event', gtk.main_quit) # window self.props.title = 'xbrightness-rgbgamma-gtk' self.props.width_request = 450 self.add (self.__vbox) def __on_adjustment_value_changed (self, adjustment): """ 'value-changed' handler for brightness/gamma """ self.__lib.set_gamma_and_brightness (self.__adjustment_brightness.props.value, self.__adjustment_gamma_red.props.value, self.__adjustment_gamma_green.props.value, self.__adjustment_gamma_blue.props.value) self.__drawingarea.redraw () self.__statusbar.pop (self.__ctxid) if adjustment == self.__adjustment_brightness: self.__statusbar.push (self.__ctxid, 'Set brightness to {0:d}'.format (int (adjustment.props.value))) elif adjustment == self.__adjustment_gamma_red: self.__statusbar.push (self.__ctxid, 'Set gamma/red to {0:.2f}'.format (adjustment.props.value)) elif adjustment == self.__adjustment_gamma_green: self.__statusbar.push (self.__ctxid, 'Set gamma/green to {0:.2f}'.format (adjustment.props.value)) elif adjustment == self.__adjustment_gamma_blue: self.__statusbar.push (self.__ctxid, 'Set gamma/blue to {0:.2f}'.format (adjustment.props.value)) if not self.__button_apply.props.sensitive: self.__button_apply.props.sensitive = True self.__cnt = 10 self.__toid = glib.timeout_add_seconds (1, self.__to_restore_countdown) def __to_restore_countdown (self): """ Count down "cancel after N sec(s)" counter """ self.__cnt -= 1 self.__statusbar.pop (self.__ctxid) self.__statusbar.push (self.__ctxid, 'Cancel after {0} second(s)'.format (self.__cnt)) if self.__cnt > 0: return True # continue else: # restore: lib self.__lib.set_gamma_and_brightness (self.__brightness, self.__gamma_red, self.__gamma_green, self.__gamma_blue) self.__drawingarea.redraw () # restore: widgets self.__adjustment_brightness.props.value = self.__brightness self.__adjustment_gamma_red.props.value = self.__gamma_red self.__adjustment_gamma_green.props.value = self.__gamma_green self.__adjustment_gamma_blue.props.value = self.__gamma_blue self.__statusbar.pop (self.__ctxid) self.__statusbar.push (self.__ctxid, 'Restored values'.format (self.__cnt)) self.__button_apply.props.sensitive = False return False # stop def __on_button_apply_clicked (self, widget): """ Apply values """ glib.source_remove (self.__toid) self.__brightness = self.__adjustment_brightness.props.value self.__gamma_red = self.__adjustment_gamma_red.props.value self.__gamma_green = self.__adjustment_gamma_green.props.value self.__gamma_blue = self.__adjustment_gamma_blue.props.value self.__button_apply.props.sensitive = False self.__statusbar.pop (self.__ctxid) self.__statusbar.push (self.__ctxid, 'Applied values'.format (self.__cnt)) def main (): """ Main """ win = MainWindow () win.show_all () gtk.main () if __name__ == '__main__': main ()
(2011/5/5)ディスプレイが開けなかった場合の処理を修正
libXxf86vm上の処理が失敗すると例外を発生(送出)するようにしているが、今回はlibXxf86vm上の処理が失敗することは想定しておらず、GUIのコード側では捕捉するようにはしていない。
これをもとに、メッセージの国際化や内部処理の改善をしたものをパッケージとして近い内に公開する予定。
使用したバージョン:
- Python 2.6.6, 2.7.1
- PyGTK 2.21.0, 2.22.0