Poodletooth-iLand/panda/python/Lib/site-packages/wx/lib/agw/cubecolourdialog.py
2015-03-06 06:11:40 -06:00

3524 lines
140 KiB
Python

# --------------------------------------------------------------------------- #
# CUBECOLOURDIALOG Widget wxPython IMPLEMENTATION
#
# Python Code By:
#
# Andrea Gavana, @ 16 Aug 2007
# Latest Revision: 19 Dec 2012, 21.00 GMT
#
#
# TODO List
#
# 1. Find A Way To Reduce Flickering On The 2 ColourPanels;
#
# 2. See Why wx.GCDC Doesn't Work As I Thought (!). It Looks Slow As A Turtle,
# But Probably I Am Doing Something Wrong While Painting The Alpha Textures.
#
#
# For All Kind Of Problems, Requests Of Enhancements And Bug Reports, Please
# Write To Me At:
#
# andrea.gavana@gmail.com
# andrea.gavana@maerskoil.com
#
# Or, Obviously, To The wxPython Mailing List!!!
#
# Tags: phoenix-port, unittest, documented, py3-port
#
# End Of Comments
# --------------------------------------------------------------------------- #
"""
:class:`CubeColourDialog` is an alternative implementation of :class:`ColourDialog`.
Description
===========
The :class:`CubeColourDialog` is an alternative implementation of :class:`ColourDialog`, and it
offers different functionalities with respect to the default wxPython one. It
can be used as a replacement of :class:`ColourDialog` with exactly the same syntax and
methods.
Some features:
- RGB components may be controlled using spin controls or with mouse gestures
on a 3D RGB cube, with the 3 components laying on the X, Y, Z axes;
- HSB components may be controlled using spin controls or with mouse gestures
on a 2D colour wheel;
- Brightness has its own vertical slider to play with;
- The colour alpha channel can be controlled using another vertical slider, or
via spin control;
- The colour alpha channel controls can be completely hidden at startup or the
choice to use the alpha channel can be left to the user while playing with the
dialog, via a simple :class:`CheckBox`;
- The "old colour" and "new colour" are displayed in two small custom panel,
which support alpha transparency and texture;
- :class:`CubeColourDialog` displays also the HTML colour code in hexadecimal format;
- When available, a corresponding "Web Safe" colour is generated using a 500
web colours "database" (a dictionary inside the widget source code). Web Safe
colours are recognized by all the browsers;
- When available, a corresponding "HTML name" for the selected colour is displayed,
by using the same 500 web colours "database";
- When available, a corresponding "Microsoft Access Code" for the selected colour
is displayed, by using the same 500 web colours "database".
And much more.
Usage
=====
Usage example::
import wx
import wx.lib.agw.cubecolourdialog as CCD
# Our normal wxApp-derived class, as usual
app = wx.App(0)
colourData = wx.ColourData()
dlg = CCD.CubeColourDialog(None, colourData)
if dlg.ShowModal() == wx.ID_OK:
# If the user selected OK, then the dialog's wx.ColourData will
# contain valid information. Fetch the data ...
colourData = dlg.GetColourData()
h, s, v, a = dlg.GetHSVAColour()
# ... then do something with it. The actual colour data will be
# returned as a three-tuple (r, g, b) in this particular case.
colour = colourData.GetColour()
r, g, b, alpha = colour.Red(), colour.Green(), colour.Blue(), colour.Alpha()
print("You selected (RGBA): %d, %d, %d, %d"%(r, g, b, alpha))
print("You selected (HSVA): %d, %d, %d, %d"%(h, s, v, a))
# Once the dialog is destroyed, Mr. wx.ColourData is no longer your
# friend. Don't use it again!
dlg.Destroy()
app.MainLoop()
Window Styles
=============
This class supports the following window styles:
================== =========== ==================================================
Window Styles Hex Value Description
================== =========== ==================================================
``CCD_SHOW_ALPHA`` 0x1 Show the widget used to control colour alpha channels in :class:`CubeColourDialog`.
================== =========== ==================================================
Events Processing
=================
`No custom events are available for this class.`
License And Version
===================
:class:`CubeColourDialog` is distributed under the wxPython license.
Latest Revision: Andrea Gavana @ 19 Dec 2012, 21.00 GMT
Version 0.5.
"""
#----------------------------------------------------------------------
# Beginning Of CUBECOLOURDIALOG wxPython Code
#----------------------------------------------------------------------
import wx
import colorsys
from math import pi, sin, cos, sqrt, atan2
from wx.lib.embeddedimage import PyEmbeddedImage
# Define a translation string
_ = wx.GetTranslation
# Show the alpha control in the dialog
CCD_SHOW_ALPHA = 1
""" Show the widget used to control colour alpha channels in :class:`CubeColourDialog`. """
# Radius of the HSB colour wheel
RADIUS = 100
""" Radius of the HSB colour wheel. """
# Width of the mouse-controlled colour pointer
RECT_WIDTH = 5
""" Width of the mouse-controlled colour pointer. """
# Dictionary keys for the RGB colour cube
RED, GREEN, BLUE = 0, 1, 2
""" Dictionary keys for the RGB colour cube. """
Vertex = wx.Point(95, 109)
Top = wx.Point(95, 10)
Left = wx.Point(16, 148)
Right = wx.Point(174, 148)
colourAttributes = ["r", "g", "b", "h", "s", "v"]
colourMaxValues = [255, 255, 255, 359, 255, 255]
checkColour = wx.Colour(200, 200, 200)
HTMLCodes = {'#B0171F': ['Indian red', '2037680', ''],
'#DC143C': ['Crimson', '3937500', '#CC0033'],
'#FFB6C1': ['Lightpink', '12695295', '#FFCCCC'],
'#FFAEB9': ['Lightpink 1', '12168959', ''],
'#EEA2AD': ['Lightpink 2', '11379438', ''],
'#CD8C95': ['Lightpink 3', '9800909', ''],
'#8B5F65': ['Lightpink 4', '6643595', ''],
'#FFC0CB': ['Pink', '13353215', '#FFCCCC'],
'#FFB5C5': ['Pink 1', '12957183', ''],
'#EEA9B8': ['Pink 2', '12102126', ''],
'#CD919E': ['Pink 3', '10392013', ''],
'#8B636C': ['Pink 4', '7103371', ''],
'#DB7093': ['Palevioletred', '9662683', '#CC6699'],
'#FF82AB': ['Palevioletred 1', '11240191', ''],
'#EE799F': ['Palevioletred 2', '10451438', ''],
'#CD6889': ['Palevioletred 3', '9005261', ''],
'#8B475D': ['Palevioletred 4', '6113163', ''],
'#FFF0F5': ['Lavenderblush 1 (lavenderblush)', '16118015', '#FFFFFF'],
'#EEE0E5': ['Lavenderblush 2', '15065326', ''],
'#CDC1C5': ['Lavenderblush 3', '12960205', ''],
'#8B8386': ['Lavenderblush 4', '8815499', ''],
'#FF3E96': ['Violetred 1', '9846527', ''],
'#EE3A8C': ['Violetred 2', '9190126', ''],
'#CD3278': ['Violetred 3', '7877325', ''],
'#8B2252': ['Violetred 4', '5382795', ''],
'#FF69B4': ['Hotpink', '11823615', '#FF66CC'],
'#FF6EB4': ['Hotpink 1', '11824895', ''],
'#EE6AA7': ['Hotpink 2', '10971886', ''],
'#CD6090': ['Hotpink 3', '9461965', ''],
'#8B3A62': ['Hotpink 4', '6437515', ''],
'#872657': ['Raspberry', '5711495', ''],
'#FF1493': ['Deeppink 1 (deeppink)', '9639167', '#FF0099'],
'#EE1289': ['Deeppink 2', '8983278', ''],
'#CD1076': ['Deeppink 3', '7737549', ''],
'#8B0A50': ['Deeppink 4', '5245579', ''],
'#FF34B3': ['Maroon 1', '11744511', ''],
'#EE30A7': ['Maroon 2', '10957038', ''],
'#CD2990': ['Maroon 3', '9447885', ''],
'#8B1C62': ['Maroon 4', '6429835', ''],
'#C71585': ['Mediumvioletred', '8721863', '#CC0066'],
'#D02090': ['Violetred', '9445584', ''],
'#DA70D6': ['Orchid', '14053594', '#CC66CC'],
'#FF83FA': ['Orchid 1', '16417791', ''],
'#EE7AE9': ['Orchid 2', '15301358', ''],
'#CD69C9': ['Orchid 3', '13199821', ''],
'#8B4789': ['Orchid 4', '8996747', ''],
'#D8BFD8': ['Thistle', '14204888', '#CCCCCC'],
'#FFE1FF': ['Thistle 1', '16769535', ''],
'#EED2EE': ['Thistle 2', '15651566', ''],
'#CDB5CD': ['Thistle 3', '13481421', ''],
'#8B7B8B': ['Thistle 4', '9141131', ''],
'#FFBBFF': ['Plum 1', '16759807', ''],
'#EEAEEE': ['Plum 2', '15642350', ''],
'#CD96CD': ['Plum 3', '13473485', ''],
'#8B668B': ['Plum 4', '9135755', ''],
'#DDA0DD': ['Plum', '14524637', '#CC99CC'],
'#EE82EE': ['Violet', '15631086', '#FF99FF'],
'#FF00FF': ['Magenta (fuchsia)', '16711935', '#FF00FF'],
'#EE00EE': ['Magenta 2', '15597806', ''],
'#CD00CD': ['Magenta 3', '13435085', ''],
'#8B008B': ['Magenta 4 (darkmagenta)', '9109643', '#990099'],
'#800080': ['Purple', '8388736', '#990099'],
'#BA55D3': ['Mediumorchid', '13850042', '#CC66CC'],
'#E066FF': ['Mediumorchid 1', '16738016', ''],
'#D15FEE': ['Mediumorchid 2', '15622097', ''],
'#B452CD': ['Mediumorchid 3', '13456052', ''],
'#7A378B': ['Mediumorchid 4', '9123706', ''],
'#9400D3': ['Darkviolet', '13828244', '#9900CC'],
'#9932CC': ['Darkorchid', '13382297', '#9933CC'],
'#BF3EFF': ['Darkorchid 1', '16727743', ''],
'#B23AEE': ['Darkorchid 2', '15612594', ''],
'#9A32CD': ['Darkorchid 3', '13447834', ''],
'#68228B': ['Darkorchid 4', '9118312', ''],
'#4B0082': ['Indigo', '8519755', '#330099'],
'#8A2BE2': ['Blueviolet', '14822282', '#9933FF'],
'#9B30FF': ['Purple 1', '16724123', ''],
'#912CEE': ['Purple 2', '15608977', ''],
'#7D26CD': ['Purple 3', '13444733', ''],
'#551A8B': ['Purple 4', '9116245', ''],
'#9370DB': ['Mediumpurple', '14381203', '#9966CC'],
'#AB82FF': ['Mediumpurple 1', '16745131', ''],
'#9F79EE': ['Mediumpurple 2', '15628703', ''],
'#8968CD': ['Mediumpurple 3', '13461641', ''],
'#5D478B': ['Mediumpurple 4', '9127773', ''],
'#483D8B': ['Darkslateblue', '9125192', '#333399'],
'#8470FF': ['Lightslateblue', '16740484', ''],
'#7B68EE': ['Mediumslateblue', '15624315', '#6666FF'],
'#6A5ACD': ['Slateblue', '13458026', '#6666CC'],
'#836FFF': ['Slateblue 1', '16740227', ''],
'#7A67EE': ['Slateblue 2', '15624058', ''],
'#6959CD': ['Slateblue 3', '13457769', ''],
'#473C8B': ['Slateblue 4', '9124935', ''],
'#F8F8FF': ['Ghostwhite', '16775416', '#FFFFFF'],
'#E6E6FA': ['Lavender', '16443110', '#FFFFFF'],
'#0000FF': ['Blue', '16711680', '#0000FF'],
'#0000EE': ['Blue 2', '15597568', ''],
'#0000CD': ['Blue 3 (mediumblue)', '13434880', '#0000CC'],
'#00008B': ['Blue 4 (darkblue)', '9109504', '#000099'],
'#000080': ['Navy', '8388608', '#000099'],
'#191970': ['Midnightblue', '7346457', '#000066'],
'#3D59AB': ['Cobalt', '11229501', ''],
'#4169E1': ['Royalblue', '14772545', '#3366CC'],
'#4876FF': ['Royalblue 1', '16741960', ''],
'#436EEE': ['Royalblue 2', '15625795', ''],
'#3A5FCD': ['Royalblue 3', '13459258', ''],
'#27408B': ['Royalblue 4', '9125927', ''],
'#6495ED': ['Cornflowerblue', '15570276', '#6699FF'],
'#B0C4DE': ['Lightsteelblue', '14599344', '#99CCCC'],
'#CAE1FF': ['Lightsteelblue 1', '16769482', ''],
'#BCD2EE': ['Lightsteelblue 2', '15651516', ''],
'#A2B5CD': ['Lightsteelblue 3', '13481378', ''],
'#6E7B8B': ['Lightsteelblue 4', '9141102', ''],
'#778899': ['Lightslategray', '10061943', '#669999'],
'#708090': ['Slategray', '9470064', '#669999'],
'#C6E2FF': ['Slategray 1', '16769734', ''],
'#B9D3EE': ['Slategray 2', '15651769', ''],
'#9FB6CD': ['Slategray 3', '13481631', ''],
'#6C7B8B': ['Slategray 4', '9141100', ''],
'#1E90FF': ['Dodgerblue 1 (dodgerblue)', '16748574', '#3399FF'],
'#1C86EE': ['Dodgerblue 2', '15631900', ''],
'#1874CD': ['Dodgerblue 3', '13464600', ''],
'#104E8B': ['Dodgerblue 4', '9129488', ''],
'#F0F8FF': ['Aliceblue', '16775408', '#FFFFFF'],
'#4682B4': ['Steelblue', '11829830', '#3399CC'],
'#63B8FF': ['Steelblue 1', '16758883', ''],
'#5CACEE': ['Steelblue 2', '15641692', ''],
'#4F94CD': ['Steelblue 3', '13472847', ''],
'#36648B': ['Steelblue 4', '9135158', ''],
'#87CEFA': ['Lightskyblue', '16436871', '#99CCFF'],
'#B0E2FF': ['Lightskyblue 1', '16769712', ''],
'#A4D3EE': ['Lightskyblue 2', '15651748', ''],
'#8DB6CD': ['Lightskyblue 3', '13481613', ''],
'#607B8B': ['Lightskyblue 4', '9141088', ''],
'#87CEFF': ['Skyblue 1', '16764551', ''],
'#7EC0EE': ['Skyblue 2', '15646846', ''],
'#6CA6CD': ['Skyblue 3', '13477484', ''],
'#4A708B': ['Skyblue 4', '9138250', ''],
'#87CEEB': ['Skyblue', '15453831', '#99CCFF'],
'#00BFFF': ['Deepskyblue 1 (deepskyblue)', '16760576', '#00CCFF'],
'#00B2EE': ['Deepskyblue 2', '15643136', ''],
'#009ACD': ['Deepskyblue 3', '13474304', ''],
'#00688B': ['Deepskyblue 4', '9136128', ''],
'#33A1C9': ['Peacock', '13214003', ''],
'#ADD8E6': ['Lightblue', '15128749', '#99CCFF'],
'#BFEFFF': ['Lightblue 1', '16773055', ''],
'#B2DFEE': ['Lightblue 2', '15654834', ''],
'#9AC0CD': ['Lightblue 3', '13484186', ''],
'#68838B': ['Lightblue 4', '9143144', ''],
'#B0E0E6': ['Powderblue', '15130800', '#CCCCFF'],
'#98F5FF': ['Cadetblue 1', '16774552', ''],
'#8EE5EE': ['Cadetblue 2', '15656334', ''],
'#7AC5CD': ['Cadetblue 3', '13485434', ''],
'#53868B': ['Cadetblue 4', '9143891', ''],
'#00F5FF': ['Turquoise 1', '16774400', ''],
'#00E5EE': ['Turquoise 2', '15656192', ''],
'#00C5CD': ['Turquoise 3', '13485312', ''],
'#00868B': ['Turquoise 4', '9143808', ''],
'#5F9EA0': ['Cadetblue', '10526303', '#669999'],
'#00CED1': ['Darkturquoise', '13749760', '#00CCCC'],
'#F0FFFF': ['Azure 1 (azure)', '16777200', '#FFFFFF'],
'#E0EEEE': ['Azure 2', '15658720', ''],
'#C1CDCD': ['Azure 3', '13487553', ''],
'#838B8B': ['Azure 4', '9145219', ''],
'#E0FFFF': ['Lightcyan 1 (lightcyan)', '16777184', '#CCFFFF'],
'#D1EEEE': ['Lightcyan 2', '15658705', ''],
'#B4CDCD': ['Lightcyan 3', '13487540', ''],
'#7A8B8B': ['Lightcyan 4', '9145210', ''],
'#BBFFFF': ['Paleturquoise 1', '16777147', ''],
'#AEEEEE': ['Paleturquoise 2 (paleturquoise)', '15658670', ''],
'#96CDCD': ['Paleturquoise 3', '13487510', ''],
'#668B8B': ['Paleturquoise 4', '9145190', ''],
'#2F4F4F': ['Darkslategray', '5197615', '#336666'],
'#97FFFF': ['Darkslategray 1', '16777111', ''],
'#8DEEEE': ['Darkslategray 2', '15658637', ''],
'#79CDCD': ['Darkslategray 3', '13487481', ''],
'#528B8B': ['Darkslategray 4', '9145170', ''],
'#00FFFF': ['Cyan / aqua', '16776960', '#00FFFF'],
'#00EEEE': ['Cyan 2', '15658496', ''],
'#00CDCD': ['Cyan 3', '13487360', ''],
'#008B8B': ['Cyan 4 (darkcyan)', '9145088', '#009999'],
'#008080': ['Teal', '8421376', '#009999'],
'#48D1CC': ['Mediumturquoise', '13422920', '#33CCCC'],
'#20B2AA': ['Lightseagreen', '11186720', '#339999'],
'#03A89E': ['Manganeseblue', '10397699', ''],
'#40E0D0': ['Turquoise', '13688896', '#33CCCC'],
'#808A87': ['Coldgrey', '8882816', ''],
'#00C78C': ['Turquoiseblue', '9225984', ''],
'#7FFFD4': ['Aquamarine 1 (aquamarine)', '13959039', '#66FFCC'],
'#76EEC6': ['Aquamarine 2', '13037174', ''],
'#66CDAA': ['Aquamarine 3 (mediumaquamarine)', '11193702', '#66CC99'],
'#458B74': ['Aquamarine 4', '7637829', ''],
'#00FA9A': ['Mediumspringgreen', '10156544', '#00FF99'],
'#F5FFFA': ['Mintcream', '16449525', '#FFFFFF'],
'#00FF7F': ['Springgreen', '8388352', '#00FF66'],
'#00EE76': ['Springgreen 1', '7794176', ''],
'#00CD66': ['Springgreen 2', '6737152', ''],
'#008B45': ['Springgreen 3', '4557568', ''],
'#3CB371': ['Mediumseagreen', '7451452', '#33CC66'],
'#54FF9F': ['Seagreen 1', '10485588', ''],
'#4EEE94': ['Seagreen 2', '9760334', ''],
'#43CD80': ['Seagreen 3', '8441155', ''],
'#2E8B57': ['Seagreen 4 (seagreen)', '5737262', '#339966'],
'#00C957': ['Emeraldgreen', '5753088', ''],
'#BDFCC9': ['Mint', '13237437', ''],
'#3D9140': ['Cobaltgreen', '4231485', ''],
'#F0FFF0': ['Honeydew 1 (honeydew)', '15794160', '#FFFFFF'],
'#E0EEE0': ['Honeydew 2', '14741216', ''],
'#C1CDC1': ['Honeydew 3', '12701121', ''],
'#838B83': ['Honeydew 4', '8620931', ''],
'#8FBC8F': ['Darkseagreen', '9419919', '#99CC99'],
'#C1FFC1': ['Darkseagreen 1', '12713921', ''],
'#B4EEB4': ['Darkseagreen 2', '11857588', ''],
'#9BCD9B': ['Darkseagreen 3', '10210715', ''],
'#698B69': ['Darkseagreen 4', '6916969', ''],
'#98FB98': ['Palegreen', '10025880', '#99FF99'],
'#9AFF9A': ['Palegreen 1', '10157978', ''],
'#90EE90': ['Palegreen 2 (lightgreen)', '9498256', '#99FF99'],
'#7CCD7C': ['Palegreen 3', '8179068', ''],
'#548B54': ['Palegreen 4', '5540692', ''],
'#32CD32': ['Limegreen', '3329330', '#33CC33'],
'#228B22': ['Forestgreen', '2263842', '#339933'],
'#00FF00': ['Green 1 (lime)', '65280', '#00FF00'],
'#00EE00': ['Green 2', '60928', ''],
'#00CD00': ['Green 3', '52480', ''],
'#008B00': ['Green 4', '35584', ''],
'#008000': ['Green', '32768', '#009900'],
'#006400': ['Darkgreen', '25600', '#006600'],
'#308014': ['Sapgreen', '1343536', ''],
'#7CFC00': ['Lawngreen', '64636', '#66FF00'],
'#7FFF00': ['Chartreuse 1 (chartreuse)', '65407', '#66FF00'],
'#76EE00': ['Chartreuse 2', '61046', ''],
'#66CD00': ['Chartreuse 3', '52582', ''],
'#458B00': ['Chartreuse 4', '35653', ''],
'#ADFF2F': ['Greenyellow', '3145645', '#99FF33'],
'#CAFF70': ['Darkolivegreen 1', '7405514', ''],
'#BCEE68': ['Darkolivegreen 2', '6876860', ''],
'#A2CD5A': ['Darkolivegreen 3', '5950882', ''],
'#6E8B3D': ['Darkolivegreen 4', '4033390', ''],
'#556B2F': ['Darkolivegreen', '3107669', '#666633'],
'#6B8E23': ['Olivedrab', '2330219', '#669933'],
'#C0FF3E': ['Olivedrab 1', '4128704', ''],
'#B3EE3A': ['Olivedrab 2', '3862195', ''],
'#9ACD32': ['Olivedrab 3 (yellowgreen)', '3329434', '#99CC33'],
'#698B22': ['Olivedrab 4', '2263913', ''],
'#FFFFF0': ['Ivory 1 (ivory)', '15794175', '#FFFFFF'],
'#EEEEE0': ['Ivory 2', '14741230', ''],
'#CDCDC1': ['Ivory 3', '12701133', ''],
'#8B8B83': ['Ivory 4', '8620939', ''],
'#F5F5DC': ['Beige', '14480885', '#FFFFCC'],
'#FFFFE0': ['Lightyellow 1 (lightyellow)', '14745599', '#FFFFFF'],
'#EEEED1': ['Lightyellow 2', '13758190', ''],
'#CDCDB4': ['Lightyellow 3', '11849165', ''],
'#8B8B7A': ['Lightyellow 4', '8031115', ''],
'#FAFAD2': ['Lightgoldenrodyellow', '13826810', '#FFFFCC'],
'#FFFF00': ['Yellow 1 (yellow)', '65535', '#FFFF00'],
'#EEEE00': ['Yellow 2', '61166', ''],
'#CDCD00': ['Yellow 3', '52685', ''],
'#8B8B00': ['Yellow 4', '35723', ''],
'#808069': ['Warmgrey', '6914176', ''],
'#808000': ['Olive', '32896', '#999900'],
'#BDB76B': ['Darkkhaki', '7059389', '#CCCC66'],
'#FFF68F': ['Khaki 1', '9434879', ''],
'#EEE685': ['Khaki 2', '8775406', ''],
'#CDC673': ['Khaki 3', '7587533', ''],
'#8B864E': ['Khaki 4', '5146251', ''],
'#F0E68C': ['Khaki', '9234160', ''],
'#EEE8AA': ['Palegoldenrod', '11200750', '#FFFF99'],
'#FFFACD': ['Lemonchiffon 1 (lemonchiffon)', '13499135', '#FFFFCC'],
'#EEE9BF': ['Lemonchiffon 2', '12577262', ''],
'#CDC9A5': ['Lemonchiffon 3', '10865101', ''],
'#8B8970': ['Lemonchiffon 4', '7375243', ''],
'#FFEC8B': ['Lightgoldenrod 1', '9170175', ''],
'#EEDC82': ['Lightgoldenrod 2', '8576238', ''],
'#CDBE70': ['Lightgoldenrod 3', '7388877', ''],
'#8B814C': ['Lightgoldenrod 4', '5013899', ''],
'#E3CF57': ['Banana', '5754851', ''],
'#FFD700': ['Gold 1 (gold)', '55295', '#FFCC00'],
'#EEC900': ['Gold 2', '51694', ''],
'#CDAD00': ['Gold 3', '44493', ''],
'#8B7500': ['Gold 4', '30091', ''],
'#FFF8DC': ['Cornsilk 1 (cornsilk)', '14481663', '#FFFFCC'],
'#EEE8CD': ['Cornsilk 2', '13494510', ''],
'#CDC8B1': ['Cornsilk 3', '11651277', ''],
'#8B8878': ['Cornsilk 4', '7899275', ''],
'#DAA520': ['Goldenrod', '2139610', '#CC9933'],
'#FFC125': ['Goldenrod 1', '2474495', ''],
'#EEB422': ['Goldenrod 2', '2274542', ''],
'#CD9B1D': ['Goldenrod 3', '1940429', ''],
'#8B6914': ['Goldenrod 4', '1337739', ''],
'#B8860B': ['Darkgoldenrod', '755384', '#CC9900'],
'#FFB90F': ['Darkgoldenrod 1', '1030655', ''],
'#EEAD0E': ['Darkgoldenrod 2', '962030', ''],
'#CD950C': ['Darkgoldenrod 3', '824781', ''],
'#8B6508': ['Darkgoldenrod 4', '550283', ''],
'#FFA500': ['Orange 1 (orange)', '42495', '#FF9900'],
'#EE9A00': ['Orange 2', '39662', ''],
'#CD8500': ['Orange 3', '34253', ''],
'#8B5A00': ['Orange 4', '23179', ''],
'#FFFAF0': ['Floralwhite', '15792895', '#FFFFFF'],
'#FDF5E6': ['Oldlace', '15136253', '#FFFFFF'],
'#F5DEB3': ['Wheat', '11788021', '#FFCCCC'],
'#FFE7BA': ['Wheat 1', '12249087', ''],
'#EED8AE': ['Wheat 2', '11458798', ''],
'#CDBA96': ['Wheat 3', '9878221', ''],
'#8B7E66': ['Wheat 4', '6717067', ''],
'#FFE4B5': ['Moccasin', '11920639', '#FFCCCC'],
'#FFEFD5': ['Papayawhip', '14020607', '#FFFFCC'],
'#FFEBCD': ['Blanchedalmond', '13495295', '#FFFFCC'],
'#FFDEAD': ['Navajowhite 1 (navajowhite)', '11394815', '#FFCC99'],
'#EECFA1': ['Navajowhite 2', '10604526', ''],
'#CDB38B': ['Navajowhite 3', '9155533', ''],
'#8B795E': ['Navajowhite 4', '6191499', ''],
'#FCE6C9': ['Eggshell', '13231868', ''],
'#D2B48C': ['Tan', '9221330', '#CCCC99'],
'#9C661F': ['Brick', '2057884', ''],
'#FF9912': ['Cadmiumyellow', '1219071', ''],
'#FAEBD7': ['Antiquewhite', '14150650', '#FFFFCC'],
'#FFEFDB': ['Antiquewhite 1', '14413823', ''],
'#EEDFCC': ['Antiquewhite 2', '13426670', ''],
'#CDC0B0': ['Antiquewhite 3', '11583693', ''],
'#8B8378': ['Antiquewhite 4', '7897995', ''],
'#DEB887': ['Burlywood', '8894686', '#CCCC99'],
'#FFD39B': ['Burlywood 1', '10212351', ''],
'#EEC591': ['Burlywood 2', '9553390', ''],
'#CDAA7D': ['Burlywood 3', '8235725', ''],
'#8B7355': ['Burlywood 4', '5600139', ''],
'#FFE4C4': ['Bisque 1 (bisque)', '12903679', '#FFFFCC'],
'#EED5B7': ['Bisque 2', '12047854', ''],
'#CDB79E': ['Bisque 3', '10401741', ''],
'#8B7D6B': ['Bisque 4', '7044491', ''],
'#E3A869': ['Melon', '6924515', ''],
'#ED9121': ['Carrot', '2200045', ''],
'#FF8C00': ['Darkorange', '36095', '#FF9900'],
'#FF7F00': ['Darkorange 1', '32767', ''],
'#EE7600': ['Darkorange 2', '30446', ''],
'#CD6600': ['Darkorange 3', '26317', ''],
'#8B4500': ['Darkorange 4', '17803', ''],
'#FF8000': ['Orange', '33023', ''],
'#FFA54F': ['Tan 1', '5219839', ''],
'#EE9A49': ['Tan 2', '4823790', ''],
'#CD853F': ['Tan 3 (peru)', '4163021', '#CC9933'],
'#8B5A2B': ['Tan 4', '2841227', ''],
'#FAF0E6': ['Linen', '15134970', '#FFFFFF'],
'#FFDAB9': ['Peachpuff 1 (peachpuff)', '12180223', '#FFCCCC'],
'#EECBAD': ['Peachpuff 2', '11389934', ''],
'#CDAF95': ['Peachpuff 3', '9809869', ''],
'#8B7765': ['Peachpuff 4', '6649739', ''],
'#FFF5EE': ['Seashell 1 (seashell)', '15660543', '#FFFFFF'],
'#EEE5DE': ['Seashell 2', '14607854', ''],
'#CDC5BF': ['Seashell 3', '12568013', ''],
'#8B8682': ['Seashell 4', '8554123', ''],
'#F4A460': ['Sandybrown', '6333684', '#FF9966'],
'#C76114': ['Rawsienna', '1335751', ''],
'#D2691E': ['Chocolate', '1993170', '#CC6633'],
'#FF7F24': ['Chocolate 1', '2392063', ''],
'#EE7621': ['Chocolate 2', '2193134', ''],
'#CD661D': ['Chocolate 3', '1926861', ''],
'#8B4513': ['Chocolate 4 (saddlebrown)', '1262987', '#993300'],
'#292421': ['Ivoryblack', '2171945', ''],
'#FF7D40': ['Flesh', '4226559', ''],
'#FF6103': ['Cadmiumorange', '221695', ''],
'#8A360F': ['Burntsienna', '997002', ''],
'#A0522D': ['Sienna', '2970272', '#996633'],
'#FF8247': ['Sienna 1', '4686591', ''],
'#EE7942': ['Sienna 2', '4356590', ''],
'#CD6839': ['Sienna 3', '3762381', ''],
'#8B4726': ['Sienna 4', '2508683', ''],
'#FFA07A': ['Lightsalmon 1 (lightsalmon)', '8036607', '#FF9966'],
'#EE9572': ['Lightsalmon 2', '7509486', ''],
'#CD8162': ['Lightsalmon 3', '6455757', ''],
'#8B5742': ['Lightsalmon 4', '4347787', ''],
'#FF7F50': ['Coral', '5275647', '#FF6666'],
'#FF4500': ['Orangered 1 (orangered)', '17919', '#FF3300'],
'#EE4000': ['Orangered 2', '16622', ''],
'#CD3700': ['Orangered 3', '14285', ''],
'#8B2500': ['Orangered 4', '9611', ''],
'#5E2612': ['Sepia', '1189470', ''],
'#E9967A': ['Darksalmon', '8034025', '#FF9966'],
'#FF8C69': ['Salmon 1', '6917375', ''],
'#EE8262': ['Salmon 2', '6456046', ''],
'#CD7054': ['Salmon 3', '5533901', ''],
'#8B4C39': ['Salmon 4', '3755147', ''],
'#FF7256': ['Coral 1', '5665535', ''],
'#EE6A50': ['Coral 2', '5270254', ''],
'#CD5B45': ['Coral 3', '4545485', ''],
'#8B3E2F': ['Coral 4', '3096203', ''],
'#8A3324': ['Burntumber', '2372490', ''],
'#FF6347': ['Tomato 1 (tomato)', '4678655', '#FF6633'],
'#EE5C42': ['Tomato 2', '4349166', ''],
'#CD4F39': ['Tomato 3', '3755981', ''],
'#8B3626': ['Tomato 4', '2504331', ''],
'#FA8072': ['Salmon', '7504122', '#FF9966'],
'#FFE4E1': ['Mistyrose 1 (mistyrose)', '14804223', '#FFCCFF'],
'#EED5D2': ['Mistyrose 2', '13817326', ''],
'#CDB7B5': ['Mistyrose 3', '11909069', ''],
'#8B7D7B': ['Mistyrose 4', '8093067', ''],
'#FFFAFA': ['Snow 1 (snow)', '16448255', '#FFFFFF'],
'#EEE9E9': ['Snow 2', '15329774', ''],
'#CDC9C9': ['Snow 3', '13224397', ''],
'#8B8989': ['Snow 4', '9013643', ''],
'#BC8F8F': ['Rosybrown', '9408444', '#CC9999'],
'#FFC1C1': ['Rosybrown 1', '12698111', ''],
'#EEB4B4': ['Rosybrown 2', '11842798', ''],
'#CD9B9B': ['Rosybrown 3', '10197965', ''],
'#8B6969': ['Rosybrown 4', '6908299', ''],
'#F08080': ['Lightcoral', '8421616', '#FF9999'],
'#CD5C5C': ['Indianred', '6053069', '#CC6666'],
'#FF6A6A': ['Indianred 1', '6974207', ''],
'#EE6363': ['Indianred 2', '6513646', ''],
'#8B3A3A': ['Indianred 4', '3816075', ''],
'#CD5555': ['Indianred 3', '5592525', ''],
'#A52A2A': ['Brown', '2763429', '#993333'],
'#FF4040': ['Brown 1', '4210943', ''],
'#EE3B3B': ['Brown 2', '3881966', ''],
'#CD3333': ['Brown 3', '3355597', ''],
'#8B2323': ['Brown 4', '2302859', ''],
'#B22222': ['Firebrick', '2237106', '#993333'],
'#FF3030': ['Firebrick 1', '3158271', ''],
'#EE2C2C': ['Firebrick 2', '2895086', ''],
'#CD2626': ['Firebrick 3', '2500301', ''],
'#8B1A1A': ['Firebrick 4', '1710731', ''],
'#FF0000': ['Red 1 (red)', '255', '#FF0000'],
'#EE0000': ['Red 2', '238', ''],
'#CD0000': ['Red 3', '205', ''],
'#8B0000': ['Red 4 (darkred)', '139', '#990000'],
'#800000': ['Maroon', '128', '#990000'],
'#8E388E': ['Sgi beet', '9320590', ''],
'#7171C6': ['Sgi slateblue', '13005169', ''],
'#7D9EC0': ['Sgi lightblue', '12623485', ''],
'#388E8E': ['Sgi teal', '9342520', ''],
'#71C671': ['Sgi chartreuse', '7456369', ''],
'#8E8E38': ['Sgi olivedrab', '3706510', ''],
'#C5C1AA': ['Sgi brightgray', '11190725', ''],
'#C67171': ['Sgi salmon', '7434694', ''],
'#555555': ['Sgi darkgray', '5592405', ''],
'#1E1E1E': ['Sgi gray 12', '1973790', ''],
'#282828': ['Sgi gray 16', '2631720', ''],
'#515151': ['Sgi gray 32', '5329233', ''],
'#5B5B5B': ['Sgi gray 36', '5987163', ''],
'#848484': ['Sgi gray 52', '8684676', ''],
'#8E8E8E': ['Sgi gray 56', '9342606', ''],
'#AAAAAA': ['Sgi lightgray', '11184810', ''],
'#B7B7B7': ['Sgi gray 72', '12040119', ''],
'#C1C1C1': ['Sgi gray 76', '12698049', ''],
'#EAEAEA': ['Sgi gray 92', '15395562', ''],
'#F4F4F4': ['Sgi gray 96', '16053492', ''],
'#FFFFFF': ['White', '16777215', '#FFFFFF'],
'#F5F5F5': ['White smoke (gray)', '16119285', '#FFFFFF'],
'#DCDCDC': ['Gainsboro', '14474460', '#CCCCCC'],
'#D3D3D3': ['Lightgrey', '13882323', '#CCCCCC'],
'#C0C0C0': ['Silver', '12632256', '#CCCCCC'],
'#A9A9A9': ['Darkgray', '11119017', '#999999'],
'#808080': ['Gray', '8421504', ''],
'#696969': ['Dimgray (gray 42)', '6908265', '#666666'],
'#000000': ['Black', '0', '#000000'],
'#FCFCFC': ['Gray 99', '16579836', ''],
'#FAFAFA': ['Gray 98', '16448250', ''],
'#F7F7F7': ['Gray 97', '16250871', ''],
'#F2F2F2': ['Gray 95', '15921906', ''],
'#F0F0F0': ['Gray 94', '15790320', ''],
'#EDEDED': ['Gray 93', '15592941', ''],
'#EBEBEB': ['Gray 92', '15461355', ''],
'#E8E8E8': ['Gray 91', '15263976', ''],
'#E5E5E5': ['Gray 90', '15066597', ''],
'#E3E3E3': ['Gray 89', '14935011', ''],
'#E0E0E0': ['Gray 88', '14737632', ''],
'#DEDEDE': ['Gray 87', '14606046', ''],
'#DBDBDB': ['Gray 86', '14408667', ''],
'#D9D9D9': ['Gray 85', '14277081', ''],
'#D6D6D6': ['Gray 84', '14079702', ''],
'#D4D4D4': ['Gray 83', '13948116', ''],
'#D1D1D1': ['Gray 82', '13750737', ''],
'#CFCFCF': ['Gray 81', '13619151', ''],
'#CCCCCC': ['Gray 80', '13421772', ''],
'#C9C9C9': ['Gray 79', '13224393', ''],
'#C7C7C7': ['Gray 78', '13092807', ''],
'#C4C4C4': ['Gray 77', '12895428', ''],
'#C2C2C2': ['Gray 76', '12763842', ''],
'#BFBFBF': ['Gray 75', '12566463', ''],
'#BDBDBD': ['Gray 74', '12434877', ''],
'#BABABA': ['Gray 73', '12237498', ''],
'#B8B8B8': ['Gray 72', '12105912', ''],
'#B5B5B5': ['Gray 71', '11908533', ''],
'#B3B3B3': ['Gray 70', '11776947', ''],
'#B0B0B0': ['Gray 69', '11579568', ''],
'#ADADAD': ['Gray 68', '11382189', ''],
'#ABABAB': ['Gray 67', '11250603', ''],
'#A8A8A8': ['Gray 66', '11053224', ''],
'#A6A6A6': ['Gray 65', '10921638', ''],
'#A3A3A3': ['Gray 64', '10724259', ''],
'#A1A1A1': ['Gray 63', '10592673', ''],
'#9E9E9E': ['Gray 62', '10395294', ''],
'#9C9C9C': ['Gray 61', '10263708', ''],
'#999999': ['Gray 60', '10066329', ''],
'#969696': ['Gray 59', '9868950', ''],
'#949494': ['Gray 58', '9737364', ''],
'#919191': ['Gray 57', '9539985', ''],
'#8F8F8F': ['Gray 56', '9408399', ''],
'#8C8C8C': ['Gray 55', '9211020', ''],
'#8A8A8A': ['Gray 54', '9079434', ''],
'#878787': ['Gray 53', '8882055', ''],
'#858585': ['Gray 52', '8750469', ''],
'#828282': ['Gray 51', '8553090', ''],
'#7F7F7F': ['Gray 50', '8355711', ''],
'#7D7D7D': ['Gray 49', '8224125', ''],
'#7A7A7A': ['Gray 48', '8026746', ''],
'#787878': ['Gray 47', '7895160', ''],
'#757575': ['Gray 46', '7697781', ''],
'#737373': ['Gray 45', '7566195', ''],
'#707070': ['Gray 44', '7368816', ''],
'#6E6E6E': ['Gray 43', '7237230', ''],
'#6B6B6B': ['Gray 42', '7039851', ''],
'#696969': ['Dimgray (gray 42)', '6908265', '#666666'],
'#666666': ['Gray 40', '6710886', ''],
'#636363': ['Gray 39', '6513507', ''],
'#616161': ['Gray 38', '6381921', ''],
'#5E5E5E': ['Gray 37', '6184542', ''],
'#5C5C5C': ['Gray 36', '6052956', ''],
'#595959': ['Gray 35', '5855577', ''],
'#575757': ['Gray 34', '5723991', ''],
'#545454': ['Gray 33', '5526612', ''],
'#525252': ['Gray 32', '5395026', ''],
'#4F4F4F': ['Gray 31', '5197647', ''],
'#4D4D4D': ['Gray 30', '5066061', ''],
'#4A4A4A': ['Gray 29', '4868682', ''],
'#474747': ['Gray 28', '4671303', ''],
'#454545': ['Gray 27', '4539717', ''],
'#424242': ['Gray 26', '4342338', ''],
'#404040': ['Gray 25', '4210752', ''],
'#3D3D3D': ['Gray 24', '4013373', ''],
'#3B3B3B': ['Gray 23', '3881787', ''],
'#383838': ['Gray 22', '3684408', ''],
'#363636': ['Gray 21', '3552822', ''],
'#333333': ['Gray 20', '3355443', ''],
'#303030': ['Gray 19', '3158064', ''],
'#2E2E2E': ['Gray 18', '3026478', ''],
'#2B2B2B': ['Gray 17', '2829099', ''],
'#292929': ['Gray 16', '2697513', ''],
'#262626': ['Gray 15', '2500134', ''],
'#242424': ['Gray 14', '2368548', ''],
'#212121': ['Gray 13', '2171169', ''],
'#1F1F1F': ['Gray 12', '2039583', ''],
'#1C1C1C': ['Gray 11', '1842204', ''],
'#1A1A1A': ['Gray 10', '1710618', ''],
'#171717': ['Gray 9', '1513239', ''],
'#141414': ['Gray 8', '1315860', ''],
'#121212': ['Gray 7', '1184274', ''],
'#0F0F0F': ['Gray 6', '986895', ''],
'#0D0D0D': ['Gray 5', '855309', ''],
'#0A0A0A': ['Gray 4', '657930', ''],
'#080808': ['Gray 3', '526344', ''],
'#050505': ['Gray 2', '328965', ''],
'#030303': ['Gray 1', '197379', ''],
}
HSVWheelImage = PyEmbeddedImage(
"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"
"YII=")
#----------------------------------------------------------------------
RGBCubeImage = PyEmbeddedImage(
"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"
"RK5CYII=")
def rad2deg(x):
"""
Transforms radians into degrees.
:param `x`: a float representing an angle in radians.
"""
return 180.0*x/pi
def deg2rad(x):
"""
Transforms degrees into radians.
:param `x`: a float representing an angle in degrees.
"""
return x*pi/180.0
def toscale(x):
"""
Normalize a value as a function of the radius.
:param `x`: a float value to normalize
"""
return x*RADIUS/255.0
def scaletomax(x):
"""
Normalize a value as a function of the radius.
:param `x`: a float value to normalize
"""
return x*255.0/RADIUS
def rgb2html(colour):
"""
Transforms a RGB triplet into an html hex string.
:param `colour`: a tuple of red, green, blue integers.
"""
hexColour = "#%02x%02x%02x"%(colour.r, colour.g, colour.b)
return hexColour.upper()
def Slope(pt1, pt2):
"""
Calculates the slope of the line connecting 2 points.
:param `pt1`: an instance of :class:`Point`;
:param `pt2`: another instance of :class:`Point`.
"""
y = float(pt2.y - pt1.y)
x = float(pt2.x - pt1.x)
if x:
return y/x
else:
return None
def Intersection(line1, line2):
"""
Calculates the intersection point between 2 lines.
:param `line1`: an instance of :class:`LineDescription`;
:param `line2`: another instance of :class:`LineDescription`.
"""
if line1.slope == line2.slope:
# Parallel lines, no intersection
return wx.Point(0, 0)
elif line1.slope is None:
# First Line is vertical, eqn is x=0
# Put x = 0 in second line eqn to get y
x = line1.x
y = line2.slope*x + line2.c
elif line2.slope is None:
# second line is vertical Equation of line is x=0
# Put x = 0 in first line eqn to get y
x = line2.x
y = line1.slope*line2.x + line1.c
else:
y = ((line1.c*line2.slope) - (line2.c*line1.slope))/(line2.slope - line1.slope)
x = (y - line1.c)/line1.slope
return wx.Point(int(x), int(y))
def FindC(line):
""" Internal function. """
if line.slope is None:
c = line.y
else:
c = line.y - line.slope*line.x
return c
def PointOnLine(pt1, pt2, length, maxLen):
""" Internal function. """
a = float(length)
if pt2.x != pt1.x:
m = float((pt2.y - pt1.y))/(pt2.x - pt1.x)
m2 = m*m
a2 = a*a
c = pt1.y - m*pt1.x
c2 = c*c
A = 1.0
x = pt1.x
B = 2.0 * pt1.x
x *= x
C = x - a2/(m2 + 1)
x = (B + sqrt(B*B - (4.0*A*C)))/(2.0*A)
y = m*x + c
pt = wx.Point(int(x), int(y))
if Distance(pt, pt1) > maxLen or Distance(pt, pt2) > maxLen:
x = (B - sqrt(B*B - (4.0*A*C)))/(2.0*A)
y = m*x + c
pt = wx.Point(int(x), int(y))
else:
a2 = a*a
y = sqrt(a2)
x = 0.0
pt = wx.Point(int(x), int(y))
pt.x += pt1.x
pt.y += pt1.y
if Distance(pt, pt1) > maxLen or Distance(pt, pt2) > maxLen:
y = -1.0*y
pt = wx.Point(int(x), int(y))
pt.x += pt1.x
pt.y += pt1.y
return pt
def Distance(pt1, pt2):
"""
Returns the distance between 2 points.
:param `pt1`: an instance of :class:`Point`;
:param `pt2`: another instance of :class:`Point`.
"""
distance = sqrt((pt1.x - pt2.x)**2.0 + (pt1.y - pt2.y)**2.0)
return int(distance)
def AngleFromPoint(pt, center):
"""
Returns the angle between the x-axis and the line connecting the center and
the point `pt`.
:param `pt`: an instance of :class:`Point`;
:param `center`: a float value representing the center.
"""
y = -1*(pt.y - center.y)
x = pt.x - center.x
if x == 0 and y == 0:
return 0.0
else:
return atan2(y, x)
def PtFromAngle(angle, sat, center):
"""
Given the angle with respect to the x-axis, returns the point based on
the saturation value.
:param `angle`: a float representing an angle;
:param `sat`: a float representing the colour saturation value;
:param `center`: a float value representing the center.
"""
angle = deg2rad(angle)
sat = toscale(sat)
x = sat*cos(angle)
y = sat*sin(angle)
pt = wx.Point(int(x), -int(y))
pt.x += center.x
pt.y += center.y
return pt
def RestoreOldDC(dc, oldPen, oldBrush, oldMode):
"""
Restores the old settings for a :class:`DC`.
:param `dc`: an instance of :class:`DC`;
:param `oldPen`: an instance of :class:`Pen`;
:param `oldBrush`: an instance of :class:`Brush`;
:param `oldMode`: the :class:`DC` drawing mode bit.
"""
dc.SetPen(oldPen)
dc.SetBrush(oldBrush)
dc.SetLogicalFunction(oldMode)
def DrawCheckerBoard(dc, rect, checkColour, box=5):
"""
Draws a checkerboard on a :class:`DC`.
:param `dc`: an instance of :class:`DC`;
:param `rect`: the client rectangle on which to draw the checkerboard;
:param `checkColour`: the colour used for the dark checkerboards;
:param `box`: the checkerboards box sizes.
:note: Used for the Alpha channel control and the colour panels.
"""
y = rect.y
checkPen = wx.Pen(checkColour)
checkBrush = wx.Brush(checkColour)
dc.SetPen(checkPen)
dc.SetBrush(checkBrush)
dc.SetClippingRegion(rect)
while y < rect.height:
x = box*((y//box)%2) + 2
while x < rect.width:
dc.DrawRectangle(x, y, box, box)
x += box*2
y += box
class Colour(wx.Colour):
"""
This is a subclass of :class:`Colour`, which adds Hue, Saturation and Brightness
capability to the base class. It contains also methods to convert RGB triplets
into HSB triplets and vice-versa.
"""
def __init__(self, colour):
"""
Default class constructor.
:param `colour`: a standard :class:`Colour`.
"""
wx.Colour.__init__(self)
self.r = colour.Red()
self.g = colour.Green()
self.b = colour.Blue()
self._alpha = colour.Alpha()
self.ToHSV()
def ToRGB(self):
""" Converts a HSV triplet into a RGB triplet. """
maxVal = self.v
delta = (maxVal*self.s)/255.0
minVal = maxVal - delta
hue = float(self.h)
if self.h > 300 or self.h <= 60:
self.r = maxVal
if self.h > 300:
self.g = int(minVal)
hue = (hue - 360.0)/60.0
self.b = int(-(hue*delta - minVal))
else:
self.b = int(minVal)
hue = hue/60.0
self.g = int(hue*delta + minVal)
elif self.h > 60 and self.h < 180:
self.g = int(maxVal)
if self.h < 120:
self.b = int(minVal)
hue = (hue/60.0 - 2.0)*delta
self.r = int(minVal - hue)
else:
self.r = int(minVal)
hue = (hue/60.0 - 2.0)*delta
self.b = int(minVal + hue)
else:
self.b = int(maxVal)
if self.h < 240:
self.r = int(minVal)
hue = (hue/60.0 - 4.0)*delta
self.g = int(minVal - hue)
else:
self.g = int(minVal)
hue = (hue/60.0 - 4.0)*delta
self.r = int(minVal + hue)
def ToHSV(self):
""" Converts a RGB triplet into a HSV triplet. """
minVal = float(min(self.r, min(self.g, self.b)))
maxVal = float(max(self.r, max(self.g, self.b)))
delta = maxVal - minVal
self.v = int(maxVal)
if abs(delta) < 1e-6:
self.h = self.s = 0
else:
temp = delta/maxVal
self.s = int(temp*255.0)
if self.r == int(maxVal):
temp = float(self.g-self.b)/delta
elif self.g == int(maxVal):
temp = 2.0 + (float(self.b-self.r)/delta)
else:
temp = 4.0 + (float(self.r-self.g)/delta)
temp *= 60
if temp < 0:
temp += 360
elif temp >= 360.0:
temp = 0
self.h = int(temp)
def GetPyColour(self):
""" Returns the wxPython :class:`Colour` associated with this instance. """
return wx.Colour(self.r, self.g, self.b, self._alpha)
class LineDescription(object):
""" Simple class to store description and constants for a line in 2D space. """
def __init__(self, x=0, y=0, slope=None, c=None):
"""
Default class constructor.
Used internally. Do not call it in your code!
:param `x`: the x coordinate of the first point;
:param `y`: the y coordinate of the first point;
:param `slope`: the line's slope;
:param `c`: a floating point constant.
"""
self.x = x
self.y = y
self.slope = slope
self.c = c
class BasePyControl(wx.Control):
"""
Base class used to hold common code for the HSB colour wheel and the RGB
colour cube.
"""
def __init__(self, parent, bitmap=None):
"""
Default class constructor.
Used internally. Do not call it in your code!
:param `parent`: the control parent;
:param `bitmap`: the background bitmap for this custom control.
"""
wx.Control.__init__(self, parent, style=wx.NO_BORDER)
self.SetBackgroundStyle(wx.BG_STYLE_CUSTOM)
self._bitmap = bitmap
mask = wx.Mask(self._bitmap, wx.Colour(192, 192, 192))
self._bitmap.SetMask(mask)
self._mainDialog = wx.GetTopLevelParent(self)
self.Bind(wx.EVT_SIZE, self.OnSize)
self.Bind(wx.EVT_PAINT, self.OnPaint)
self.Bind(wx.EVT_ERASE_BACKGROUND, self.OnEraseBackground)
self.Bind(wx.EVT_LEFT_DOWN, self.OnLeftDown)
self.Bind(wx.EVT_LEFT_UP, self.OnLeftUp)
self.Bind(wx.EVT_MOTION, self.OnMotion)
def OnPaint(self, event):
"""
Handles the ``wx.EVT_PAINT`` for :class:`BasePyControl`.
:param `event`: a :class:`PaintEvent` event to be processed.
"""
dc = wx.AutoBufferedPaintDC(self)
dc.SetBackground(wx.Brush(self.GetParent().GetBackgroundColour()))
dc.Clear()
dc.DrawBitmap(self._bitmap, 0, 0, True)
if self._mainDialog._initOver:
self.DrawMarkers(dc)
def OnEraseBackground(self, event):
"""
Handles the ``wx.EVT_ERASE_BACKGROUND`` for :class:`BasePyControl`.
:param `event`: a :class:`EraseEvent` event to be processed.
:note: This is intentionally empty to reduce flicker.
"""
pass
def DrawMarkers(self, dc=None):
"""
Draws the markers on top of the background bitmap.
:param `dc`: an instance of :class:`DC`.
:note: This method must be overridden in derived classes.
"""
pass
def DrawLines(self, dc):
"""
Draws the lines connecting the markers on top of the background bitmap.
:param `dc`: an instance of :class:`DC`.
:note: This method must be overridden in derived classes.
"""
pass
def AcceptsFocusFromKeyboard(self):
"""
Can this window be given focus by keyboard navigation? If not, the
only way to give it focus (provided it accepts it at all) is to click
it.
:note: This method always returns ``False`` as we do not accept focus from
the keyboard.
:note: Overridden from :class:`Control`.
"""
return False
def AcceptsFocus(self):
"""
Can this window be given focus by mouse click?
:note: This method always returns ``False`` as we do not accept focus from
mouse click.
:note: Overridden from :class:`Control`.
"""
return False
def OnLeftDown(self, event):
"""
Handles the ``wx.EVT_LEFT_DOWN`` for :class:`BasePyControl`.
:param `event`: a :class:`MouseEvent` event to be processed.
:note: This method must be overridden in derived classes.
"""
pass
def OnLeftUp(self, event):
"""
Handles the ``wx.EVT_LEFT_UP`` for :class:`BasePyControl`.
:param `event`: a :class:`MouseEvent` event to be processed.
:note: This method must be overridden in derived classes.
"""
pass
def OnMotion(self, event):
"""
Handles the ``wx.EVT_MOTION`` for :class:`BasePyControl`.
:param `event`: a :class:`MouseEvent` event to be processed.
:note: This method must be overridden in derived classes.
"""
pass
def OnSize(self, event):
"""
Handles the ``wx.EVT_SIZE`` for :class:`BasePyControl`.
:param `event`: a :class:`SizeEvent` event to be processed.
"""
self.Refresh()
def DoGetBestSize(self):
"""
Overridden base class virtual. Determines the best size of the
control based on the bitmap size.
:note: Overridden from :class:`Control`.
"""
return wx.Size(self._bitmap.GetWidth(), self._bitmap.GetHeight())
class RGBCube(BasePyControl):
"""
Implements the drawing, mouse handling and sizing routines for the RGB
cube colour.
"""
def __init__(self, parent):
"""
Default class constructor.
Used internally. Do not call it in your code!
:param `parent`: the control parent window.
"""
BasePyControl.__init__(self, parent, bitmap=RGBCubeImage.GetBitmap())
self._index = -1
self._mouseIn = False
def DrawMarkers(self, dc=None):
"""
Draws the markers on top of the background bitmap.
:param `dc`: an instance of :class:`DC`.
"""
if dc is None:
dc = wx.ClientDC(self)
oldPen, oldBrush, oldMode = dc.GetPen(), dc.GetBrush(), dc.GetLogicalFunction()
dc.SetPen(wx.WHITE_PEN)
dc.SetBrush(wx.TRANSPARENT_BRUSH)
dc.SetLogicalFunction(wx.XOR)
rects = []
blueLen = self._mainDialog._blueLen
greenLen = self._mainDialog._greenLen
redLen = self._mainDialog._redLen
colour = self._mainDialog._colour
pt = [wx.Point() for i in range(3)]
pt[0] = PointOnLine(Vertex, Top, (colour.r*redLen)/255.0, redLen)
pt[1] = PointOnLine(Vertex, Left, (colour.g*greenLen)/255.0, greenLen)
pt[2] = PointOnLine(Vertex, Right, (colour.b*blueLen)/255.0, blueLen)
for i in range(3):
rect = wx.Rect(pt[i].x - RECT_WIDTH, pt[i].y - RECT_WIDTH, 2*RECT_WIDTH, 2*RECT_WIDTH)
rects.append(rect)
dc.DrawRectangle(rect)
self.DrawLines(dc)
RestoreOldDC(dc, oldPen, oldBrush, oldMode)
self._rects = rects
def DrawLines(self, dc):
"""
Draws the lines connecting the markers on top of the background bitmap.
:param `dc`: an instance of :class:`DC`.
"""
cuboid = self._mainDialog._cuboid
dc.DrawLine(cuboid[1], cuboid[2])
dc.DrawLine(cuboid[2], cuboid[3])
dc.DrawLine(cuboid[3], cuboid[4])
dc.DrawLine(cuboid[4], cuboid[5])
dc.DrawLine(cuboid[5], cuboid[2])
dc.DrawLine(cuboid[5], cuboid[6])
dc.DrawLine(cuboid[6], cuboid[7])
dc.DrawLine(cuboid[7], cuboid[4])
dc.DrawLine(cuboid[1], cuboid[6])
def OnLeftDown(self, event):
"""
Handles the ``wx.EVT_LEFT_DOWN`` for :class:`RGBCube`.
:param `event`: a :class:`MouseEvent` event to be processed.
"""
point = wx.Point(event.GetX(), event.GetY())
self._mouseIn = False
if self._rects[RED].Contains(point):
self.CaptureMouse()
self._mouseIn = True
self._index = RED
elif self._rects[GREEN].Contains(point):
self.CaptureMouse()
self._mouseIn = True
self._index = GREEN
elif self._rects[BLUE].Contains(point):
self.CaptureMouse()
self._mouseIn = True
self._index = BLUE
def OnLeftUp(self, event):
"""
Handles the ``wx.EVT_LEFT_UP`` for :class:`RGBCube`.
:param `event`: a :class:`MouseEvent` event to be processed.
"""
if self.GetCapture():
self.ReleaseMouse()
self._mouseIn = False
def OnMotion(self, event):
"""
Handles the ``wx.EVT_MOTION`` for :class:`RGBCube`.
:param `event`: a :class:`MouseEvent` event to be processed.
"""
point = wx.Point(event.GetX(), event.GetY())
if not (self.GetCapture() and self._mouseIn):
event.Skip()
return
bChange = False
mainDialog = self._mainDialog
colour = mainDialog._colour
redLen, greenLen, blueLen = mainDialog._redLen, mainDialog._greenLen, mainDialog._blueLen
dc = wx.ClientDC(self)
self.DrawMarkers(dc)
if self._index == RED:
if point.y > Vertex.y:
point.y = Vertex.y
point.x = Vertex.x
val = Distance(point, Vertex)
if val > redLen:
val = redLen
val = (float(val)/redLen)*255.0
colour.r = int(val)
pt = PointOnLine(Vertex, Top, (colour.r*redLen)/255.0, redLen)
self._rects[RED] = wx.Rect(pt.x - RECT_WIDTH, pt.y - RECT_WIDTH,
2*RECT_WIDTH, 2*RECT_WIDTH)
bChange = True
elif self._index == GREEN:
if point.x > Vertex.x:
point.x = Vertex.x
point.y = self._rects[GREEN].GetTop() + RECT_WIDTH
val = Distance(point, Vertex)
if val > greenLen:
val = greenLen
val = (float(val)/greenLen)*255.0
colour.g = int(val)
pt = PointOnLine(Vertex, Left, (colour.g*greenLen)/255.0, greenLen)
self._rects[GREEN] = wx.Rect(pt.x - RECT_WIDTH, pt.y - RECT_WIDTH,
2*RECT_WIDTH, 2*RECT_WIDTH)
bChange = True
elif self._index == BLUE:
if point.x < Vertex.x:
point.x = Vertex.x
point.y = self._rects[BLUE].GetTop() + RECT_WIDTH
val = Distance(point, Vertex)
if val > blueLen:
val = blueLen
val = (float(val)/blueLen)*255.0
colour.b = int(val)
pt = PointOnLine(Vertex, Right, (colour.b*blueLen)/255.0, blueLen)
self._rects[BLUE] = wx.Rect(pt.x - RECT_WIDTH, pt.y - RECT_WIDTH,
2*RECT_WIDTH, 2*RECT_WIDTH)
bChange = True
if bChange:
mainDialog.CalcCuboid()
self.DrawMarkers(dc)
colour.ToHSV()
mainDialog.SetSpinVals()
mainDialog.CalcRects()
mainDialog.DrawHSB()
mainDialog.DrawBright()
mainDialog.DrawAlpha()
class HSVWheel(BasePyControl):
"""
Implements the drawing, mouse handling and sizing routines for the HSV
colour wheel.
"""
def __init__(self, parent):
"""
Default class constructor.
Used internally. Do not call it in your code!
:param `parent`: the control parent window.
"""
BasePyControl.__init__(self, parent, bitmap=HSVWheelImage.GetBitmap())
self._mouseIn = False
def DrawMarkers(self, dc=None):
"""
Draws the markers on top of the background bitmap.
:param `dc`: an instance of :class:`DC`.
"""
if dc is None:
dc = wx.ClientDC(self)
oldPen, oldBrush, oldMode = dc.GetPen(), dc.GetBrush(), dc.GetLogicalFunction()
dc.SetPen(wx.WHITE_PEN)
dc.SetBrush(wx.TRANSPARENT_BRUSH)
dc.SetLogicalFunction(wx.XOR)
dc.DrawRectangle(self._mainDialog._currentRect)
RestoreOldDC(dc, oldPen, oldBrush, oldMode)
def OnLeftDown(self, event):
"""
Handles the ``wx.EVT_LEFT_DOWN`` for :class:`HSVWheel`.
:param `event`: a :class:`MouseEvent` event to be processed.
"""
point = wx.Point(event.GetX(), event.GetY())
self._mouseIn = False
if self.InCircle(point):
self._mouseIn = True
if self._mouseIn:
self.CaptureMouse()
self.TrackPoint(point)
def OnLeftUp(self, event):
"""
Handles the ``wx.EVT_LEFT_UP`` for :class:`HSVWheel`.
:param `event`: a :class:`MouseEvent` event to be processed.
"""
if self.GetCapture():
self.ReleaseMouse()
self._mouseIn = False
def OnMotion(self, event):
"""
Handles the ``wx.EVT_MOTION`` for :class:`HSVWheel`.
:param `event`: a :class:`MouseEvent` event to be processed.
"""
point = wx.Point(event.GetX(), event.GetY())
if self.GetCapture() and self._mouseIn:
self.TrackPoint(point)
def InCircle(self, pt):
"""
Returns whether a point is inside the HSV wheel or not.
:param `pt`: an instance of :class:`Point`.
"""
return Distance(pt, self._mainDialog._centre) <= RADIUS
def TrackPoint(self, pt):
"""
Track a mouse event inside the HSV colour wheel.
:param `pt`: an instance of :class:`Point`.
"""
if not self._mouseIn:
return
dc = wx.ClientDC(self)
self.DrawMarkers(dc)
mainDialog = self._mainDialog
colour = mainDialog._colour
colour.h = int(rad2deg(AngleFromPoint(pt, mainDialog._centre)))
if colour.h < 0:
colour.h += 360
colour.s = int(scaletomax(Distance(pt, mainDialog._centre)))
if colour.s > 255:
colour.s = 255
mainDialog.CalcRects()
self.DrawMarkers(dc)
colour.ToRGB()
mainDialog.SetSpinVals()
mainDialog.CalcCuboid()
mainDialog.DrawRGB()
mainDialog.DrawBright()
mainDialog.DrawAlpha()
class BaseLineCtrl(wx.Control):
"""
Base class used to hold common code for the Alpha channel control and the
brightness palette control.
"""
def __init__(self, parent):
"""
Default class constructor.
Used internally. Do not call it in your code!
:param `parent`: the control parent window.
"""
wx.Control.__init__(self, parent, size=(20, 200), style=wx.NO_BORDER)
self.SetBackgroundStyle(wx.BG_STYLE_CUSTOM)
self._mainDialog = wx.GetTopLevelParent(self)
self.Bind(wx.EVT_SIZE, self.OnSize)
self.Bind(wx.EVT_ERASE_BACKGROUND, self.OnEraseBackground)
self.Bind(wx.EVT_LEFT_DOWN, self.OnLeftDown)
self.Bind(wx.EVT_LEFT_UP, self.OnLeftUp)
self.Bind(wx.EVT_MOTION, self.OnMotion)
def OnEraseBackground(self, event):
"""
Handles the ``wx.EVT_ERASE_BACKGROUND`` for :class:`BaseLineCtrl`.
:param `event`: a :class:`EraseEvent` event to be processed.
:note: This is intentionally empty to reduce flicker.
"""
pass
def OnLeftDown(self, event):
"""
Handles the ``wx.EVT_LEFT_DOWN`` for :class:`BaseLineCtrl`.
:param `event`: a :class:`MouseEvent` event to be processed.
"""
point = wx.Point(event.GetX(), event.GetY())
theRect = self.GetClientRect()
if not theRect.Contains(point):
event.Skip()
return
self.CaptureMouse()
self.TrackPoint(point)
def OnLeftUp(self, event):
"""
Handles the ``wx.EVT_LEFT_UP`` for :class:`BaseLineCtrl`.
:param `event`: a :class:`MouseEvent` event to be processed.
"""
if self.GetCapture():
self.ReleaseMouse()
def OnMotion(self, event):
"""
Handles the ``wx.EVT_MOTION`` for :class:`BaseLineCtrl`.
:param `event`: a :class:`MouseEvent` event to be processed.
"""
point = wx.Point(event.GetX(), event.GetY())
if self.GetCapture():
self.TrackPoint(point)
def OnSize(self, event):
"""
Handles the ``wx.EVT_SIZE`` for :class:`BaseLineCtrl`.
:param `event`: a :class:`SizeEvent` event to be processed.
"""
self.Refresh()
def DoGetBestSize(self):
"""
Overridden base class virtual. Determines the best size of the control.
:note: Overridden from :class:`Control`.
"""
return wx.Size(24, 208)
def BuildRect(self):
""" Internal method. """
brightRect = wx.Rect(*self.GetClientRect())
brightRect.x += 2
brightRect.y += 6
brightRect.width -= 4
brightRect.height -= 8
return brightRect
def AcceptsFocusFromKeyboard(self):
"""
Can this window be given focus by keyboard navigation? If not, the
only way to give it focus (provided it accepts it at all) is to click
it.
:note: This method always returns ``False`` as we do not accept focus from
the keyboard.
:note: Overridden from :class:`Control`.
"""
return False
def AcceptsFocus(self):
"""
Can this window be given focus by mouse click?
:note: This method always returns ``False`` as we do not accept focus from
mouse click.
:note: Overridden from :class:`Control`.
"""
return False
class BrightCtrl(BaseLineCtrl):
"""
Implements the drawing, mouse handling and sizing routines for the brightness
palette control.
"""
def __init__(self, parent):
"""
Default class constructor.
Used internally. Do not call it in your code!
:param `parent`: the control parent window.
"""
BaseLineCtrl.__init__(self, parent)
self.Bind(wx.EVT_PAINT, self.OnPaint)
def OnPaint(self, event):
"""
Handles the ``wx.EVT_PAINT`` for :class:`BrightCtrl`.
:param `event`: a :class:`PaintEvent` event to be processed.
"""
dc = wx.AutoBufferedPaintDC(self)
dc.SetBackground(wx.Brush(self.GetParent().GetBackgroundColour()))
dc.Clear()
colour = self._mainDialog._colour.GetPyColour()
brightRect = self.BuildRect()
target_red = colour.Red()
target_green = colour.Green()
target_blue = colour.Blue()
h, s, v = colorsys.rgb_to_hsv(target_red / 255.0, target_green / 255.0,
target_blue / 255.0)
v = 1.0
vstep = 1.0/(brightRect.height-1)
for y_pos in range(brightRect.y, brightRect.height+brightRect.y):
r, g, b = [c * 255.0 for c in colorsys.hsv_to_rgb(h, s, v)]
colour = wx.Colour(int(r), int(g), int(b))
dc.SetPen(wx.Pen(colour, 1, wx.SOLID))
dc.DrawRectangle(brightRect.x, y_pos, brightRect.width, 1)
v = v - vstep
dc.SetPen(wx.BLACK_PEN)
dc.SetBrush(wx.TRANSPARENT_BRUSH)
dc.DrawRectangle(brightRect)
self.DrawMarkers(dc)
def TrackPoint(self, pt):
"""
Tracks a mouse action inside the palette control.
:param `pt`: an instance of :class:`Point`.
"""
brightRect = self.BuildRect()
d = brightRect.GetBottom() - pt.y
d *= 255.0
d /= brightRect.height
if d < 0:
d = 0
if d > 255:
d = 255
mainDialog = self._mainDialog
colour = mainDialog._colour
mainDialog.DrawMarkers()
colour.v = int(d)
colour.ToRGB()
mainDialog.SetSpinVals()
mainDialog.CalcRects()
mainDialog.CalcCuboid()
mainDialog.DrawMarkers()
mainDialog.DrawAlpha()
def DrawMarkers(self, dc=None):
"""
Draws square markers used with mouse gestures.
:param `dc`: an instance of :class:`DC`.
"""
if dc is None:
dc = wx.ClientDC(self)
colour = self._mainDialog._colour
brightRect = self.BuildRect()
y = int(colour.v/255.0*brightRect.height)
y = brightRect.GetBottom() - y
brightMark = wx.Rect(brightRect.x-2, y-4, brightRect.width+4, 8)
oldPen, oldBrush, oldMode = dc.GetPen(), dc.GetBrush(), dc.GetLogicalFunction()
dc.SetPen(wx.Pen(wx.WHITE, 2))
dc.SetBrush(wx.TRANSPARENT_BRUSH)
dc.SetLogicalFunction(wx.XOR)
dc.DrawRectangle(brightMark)
RestoreOldDC(dc, oldPen, oldBrush, oldMode)
class AlphaCtrl(BaseLineCtrl):
"""
Implements the drawing, mouse handling and sizing routines for the alpha
channel control.
"""
def __init__(self, parent):
"""
Default class constructor.
Used internally. Do not call it in your code!
:param `parent`: the control parent window.
"""
BaseLineCtrl.__init__(self, parent)
self.Bind(wx.EVT_PAINT, self.OnPaint)
def OnPaint(self, event):
"""
Handles the ``wx.EVT_PAINT`` for :class:`AlphaCtrl`.
:param `event`: a :class:`PaintEvent` event to be processed.
"""
pdc = wx.PaintDC(self)
dc = wx.GCDC(pdc)
mem_dc = wx.MemoryDC()
fullRect = self.GetClientRect()
bmp = wx.Bitmap(fullRect.width, fullRect.height)
mem_dc.SelectObject(bmp)
rect = self.BuildRect()
backBrush = wx.Brush(self.GetParent().GetBackgroundColour())
mem_dc.SetBackground(backBrush)
mem_dc.Clear()
mem_dc.SetBrush(wx.WHITE_BRUSH)
mem_dc.DrawRectangle(rect)
DrawCheckerBoard(mem_dc, rect, checkColour)
self.DrawAlphaShading(mem_dc, rect)
mem_dc.DestroyClippingRegion()
self.DrawMarkers(mem_dc)
mem_dc.SetBrush(wx.TRANSPARENT_BRUSH)
mem_dc.SetPen(wx.BLACK_PEN)
mem_dc.DrawRectangle(rect)
mem_dc.SelectObject(wx.NullBitmap)
pdc.DrawBitmap(bmp, 0, 0)
def DrawAlphaShading(self, dc, rect):
"""
Draws the alpha shading on top of the checkerboard.
:param `dc`: an instance of :class:`DC`;
:param `rect`: the :class:`AlphaCtrl` client rectangle.
"""
gcdc = wx.GCDC(dc)
colour = self._mainDialog._colour.GetPyColour()
alpha = 255
vstep = 255.0*2/(rect.height-1)
r, g, b = colour.Red(), colour.Green(), colour.Blue()
colour_gcdc = wx.Colour(r, g, b, alpha)
gcdc.SetBrush(wx.TRANSPARENT_BRUSH)
for y_pos in range(rect.y, rect.height+rect.y, 2):
colour_gcdc = wx.Colour(r, g, b, int(alpha))
gcdc.SetPen(wx.Pen(colour_gcdc, 1, wx.SOLID))
gcdc.DrawRectangle(rect.x, y_pos, rect.width, 2)
alpha = alpha - vstep
def TrackPoint(self, pt):
"""
Tracks a mouse action inside the Alpha channel control.
:param `pt`: an instance of :class:`Point`.
"""
alphaRect = self.BuildRect()
d = alphaRect.GetBottom() - pt.y
d *= 255.0
d /= alphaRect.height
if d < 0:
d = 0
if d > 255:
d = 255
self._mainDialog._colour._alpha = int(d)
self.Refresh()
self._mainDialog.SetSpinVals()
def DrawMarkers(self, dc=None):
"""
Draws square markers used with mouse gestures.
:param `dc`: an instance of :class:`DC`.
"""
if dc is None:
dc = wx.ClientDC(self)
colour = self._mainDialog._colour
alphaRect = self.BuildRect()
y = int(colour._alpha/255.0*alphaRect.height)
y = alphaRect.GetBottom() - y
alphaMark = wx.Rect(alphaRect.x-2, y-4, alphaRect.width+4, 8)
oldPen, oldBrush, oldMode = dc.GetPen(), dc.GetBrush(), dc.GetLogicalFunction()
dc.SetPen(wx.Pen(wx.WHITE, 2))
dc.SetBrush(wx.TRANSPARENT_BRUSH)
dc.SetLogicalFunction(wx.XOR)
dc.DrawRectangle(alphaMark)
RestoreOldDC(dc, oldPen, oldBrush, oldMode)
class ColourPanel(wx.Panel):
"""
Simple custom class used to display "old" and "new" colour panels, with alpha
blending capabilities.
"""
def __init__(self, parent, style=wx.SIMPLE_BORDER):
"""
Default class constructor.
Used internally. Do not call it in your code!
:param `parent`: the control parent window;
:param `style`: the :class:`ColourPanel` window style.
"""
wx.Panel.__init__(self, parent, style=style)
self._mainDialog = wx.GetTopLevelParent(self)
self.Bind(wx.EVT_PAINT, self.OnPaint)
self.Bind(wx.EVT_ERASE_BACKGROUND, self.OnEraseBackground)
self.Bind(wx.EVT_SIZE, self.OnSize)
self._colour = Colour(wx.WHITE)
def OnPaint(self, event):
"""
Handles the ``wx.EVT_PAINT`` for :class:`ColourPanel`.
:param `event`: a :class:`PaintEvent` event to be processed.
"""
pdc = wx.PaintDC(self)
dc = wx.GCDC(pdc)
mem_dc = wx.MemoryDC()
rect = self.GetClientRect()
bmp = wx.Bitmap(rect.width, rect.height)
mem_dc.SelectObject(bmp)
backBrush = wx.Brush(self.GetParent().GetBackgroundColour())
mem_dc.SetBackground(backBrush)
mem_dc.Clear()
mem_dc.SetBrush(wx.WHITE_BRUSH)
mem_dc.DrawRectangle(rect)
DrawCheckerBoard(mem_dc, rect, checkColour, box=10)
gcdc = wx.GCDC(mem_dc)
colour_gcdc = wx.Colour(self._colour.r, self._colour.g, self._colour.b, self._colour._alpha)
gcdc.SetBrush(wx.Brush(colour_gcdc))
gcdc.SetPen(wx.Pen(colour_gcdc))
gcdc.DrawRectangle(rect)
mem_dc.SelectObject(wx.NullBitmap)
dc.DrawBitmap(bmp, 0, 0)
def OnEraseBackground(self, event):
"""
Handles the ``wx.EVT_ERASE_BACKGROUND`` for :class:`ColourPanel`.
:param `event`: a :class:`EraseEvent` event to be processed.
:note: This is intentionally empty to reduce flicker.
"""
pass
def OnSize(self, event):
"""
Handles the ``wx.EVT_SIZE`` for :class:`ColourPanel`.
:param `event`: a :class:`SizeEvent` event to be processed.
"""
self.Refresh()
def RefreshColour(self, colour):
"""
Refresh the panel after a colour/alpha change.
:param `colour`: the new background colour of :class:`ColourPanel`.
"""
self._colour = colour
self.Refresh()
def AcceptsFocusFromKeyboard(self):
"""
Can this window be given focus by keyboard navigation? If not, the
only way to give it focus (provided it accepts it at all) is to click
it.
:note: This method always returns ``False`` as we do not accept focus from
the keyboard.
:note: Overridden from :class:`Panel`.
"""
return False
def AcceptsFocus(self):
"""
Can this window be given focus by mouse click?
:note: This method always returns ``False`` as we do not accept focus from
mouse click.
:note: Overridden from :class:`Panel`.
"""
return False
class CustomPanel(wx.Control):
"""
This panel displays a series of custom colours (chosen by the user) just like
the standard :class:`ColourDialog`.
"""
def __init__(self, parent, colourData):
"""
Default class constructor.
Used internally. Do not call it in your code!
:param `parent`: the control parent window;
:param `colourData`: an instance of :class:`ColourData`.
"""
wx.Control.__init__(self, parent, style=wx.NO_BORDER)
self.SetBackgroundStyle(wx.BG_STYLE_CUSTOM)
self._colourData = colourData
self._customColours = [None]*16
self._mainDialog = wx.GetTopLevelParent(self)
self.InitializeColours()
self._smallRectangleSize = wx.Size(20, 16)
self._gridSpacing = 4
self._customColourRect = wx.Rect(2, 2, (8*self._smallRectangleSize.x) + (7*self._gridSpacing),
(2*self._smallRectangleSize.y) + (1*self._gridSpacing))
self.Bind(wx.EVT_PAINT, self.OnPaint)
self.Bind(wx.EVT_ERASE_BACKGROUND, self.OnEraseBackground)
self.Bind(wx.EVT_SIZE, self.OnSize)
self.Bind(wx.EVT_LEFT_DOWN, self.OnLeftDown)
def InitializeColours(self):
""" Initializes the 16 custom colours in :class:`CustomPanel`. """
curr = self._colourData.GetColour()
self._colourSelection = -1
for i in range(16):
c = self._colourData.GetCustomColour(i)
if c.IsOk():
self._customColours[i] = self._colourData.GetCustomColour(i)
else:
self._customColours[i] = wx.Colour(255, 255, 255)
if c == curr:
self._colourSelection = i
def DoGetBestSize(self):
"""
Overridden base class virtual. Determines the best size of the control.
:note: Overridden from :class:`Control`.
"""
return self._customColourRect.width+4, self._customColourRect.height+4
def OnPaint(self, event):
"""
Handles the ``wx.EVT_PAINT`` for :class:`CustomPanel`.
:param `event`: a :class:`PaintEvent` event to be processed.
"""
dc = wx.AutoBufferedPaintDC(self)
dc.SetBackground(wx.Brush(self.GetParent().GetBackgroundColour()))
dc.Clear()
self.PaintCustomColours(dc)
self.PaintHighlight(dc, True)
def OnEraseBackground(self, event):
"""
Handles the ``wx.EVT_ERASE_BACKGROUND`` for :class:`CustomPanel`.
:param `event`: a :class:`EraseEvent` event to be processed.
:note: This is intentionally empty to reduce flicker.
"""
pass
def OnSize(self, event):
"""
Handles the ``wx.EVT_SIZE`` for :class:`CustomPanel`.
:param `event`: a :class:`SizeEvent` event to be processed.
"""
self.Refresh()
def OnLeftDown(self, event):
"""
Handles the ``wx.EVT_LEFT_DOWN`` for :class:`CustomPanel`.
:param `event`: a :class:`MouseEvent` event to be processed.
"""
x, y = event.GetX(), event.GetY()
selX = (x - self._customColourRect.x)//(self._smallRectangleSize.x + self._gridSpacing)
selY = (y - self._customColourRect.y)//(self._smallRectangleSize.y + self._gridSpacing)
ptr = selX + selY*8
dc = wx.ClientDC(self)
self.PaintHighlight(dc, False)
self._colourSelection = ptr
self._mainDialog._colour = Colour(self._customColours[self._colourSelection])
self.PaintCustomColour(dc, selX, selY)
self.PaintHighlight(dc, True)
self._mainDialog.DrawAll()
def PaintCustomColours(self, dc):
"""
Draws all the 16 subpanels with their custom colours.
:param `dc`: an instance of :class:`DC`.
"""
for i in range(2):
for j in range(8):
ptr = i*8 + j
x = (j*(self._smallRectangleSize.x+self._gridSpacing)) + self._customColourRect.x
y = (i*(self._smallRectangleSize.y+self._gridSpacing)) + self._customColourRect.y
dc.SetPen(wx.BLACK_PEN)
brush = wx.Brush(self._customColours[ptr])
dc.SetBrush(brush)
dc.DrawRectangle(x, y, self._smallRectangleSize.x, self._smallRectangleSize.y)
def PaintHighlight(self, dc, draw=True):
"""
Highlight the current custom colour selection (if any).
:param `dc`: an instance of :class:`DC`;
:param `draw`: whether to draw a thin black border around the selected custom
colour or not.
"""
if self._colourSelection < 0:
return
# Number of pixels bigger than the standard rectangle size
# for drawing a highlight
deltaX = deltaY = 2
# User-defined colours
y = self._colourSelection//8
x = self._colourSelection - (y*8)
x = (x*(self._smallRectangleSize.x + self._gridSpacing) + self._customColourRect.x) - deltaX
y = (y*(self._smallRectangleSize.y + self._gridSpacing) + self._customColourRect.y) - deltaY
if draw:
dc.SetPen(wx.BLACK_PEN)
else:
dc.SetPen(wx.LIGHT_GREY_PEN)
dc.SetBrush(wx.TRANSPARENT_BRUSH)
dc.DrawRectangle(x, y, (self._smallRectangleSize.x + (2*deltaX)), (self._smallRectangleSize.y + (2*deltaY)))
def PaintCustomColour(self, dc, selX, selY):
"""
Paints a newly added custom colour subpanel.
:param `dc`: an instance of :class:`DC`;
:param `selX`: the x coordinate of the custom colour subpanel;
:param `selY`: the y coordinate of the custom colour subpanel.
"""
dc.SetPen(wx.BLACK_PEN)
brush = wx.Brush(self._customColours[self._colourSelection])
dc.SetBrush(brush)
ptr = selX*8 + selY
x = (selX*(self._smallRectangleSize.x+self._gridSpacing)) + self._customColourRect.x
y = (selY*(self._smallRectangleSize.y+self._gridSpacing)) + self._customColourRect.y
dc.DrawRectangle(x, y, self._smallRectangleSize.x, self._smallRectangleSize.y)
dc.SetBrush(wx.NullBrush)
def AddCustom(self, colour):
"""
Adds a user-chosen colour to the list of custom colours.
:param `colour`: an instance of :class:`Colour`.
"""
self._colourSelection += 1
self._colourSelection = self._colourSelection%16
dc = wx.ClientDC(self)
self._customColours[self._colourSelection] = colour.GetPyColour()
self._colourData.SetCustomColour(self._colourSelection, self._customColours[self._colourSelection])
self.PaintCustomColours(dc)
class CubeColourDialog(wx.Dialog):
"""
This is the CubeColourDialog main class implementation.
"""
def __init__(self, parent, colourData=None, agwStyle=CCD_SHOW_ALPHA):
"""
Default class constructor.
:param `colourData`: a standard :class:`ColourData` (as used in :class:`ColourDialog`);
:param `agwStyle`: can be either ``None`` or ``CCD_SHOW_ALPHA``, depending if you want
to hide the alpha channel control or not.
"""
wx.Dialog.__init__(self, parent, id=wx.ID_ANY, title=_("CubeColourDialog: Choose Colour"),
pos=wx.DefaultPosition, size=(900, 900), style=wx.DEFAULT_DIALOG_STYLE)
if colourData:
self._colourData = colourData
else:
self._colourData = wx.ColourData()
self._colourData.SetColour(wx.Colour(128, 128, 128))
self._colour = Colour(self._colourData.GetColour())
self._oldColour = Colour(self._colourData.GetColour())
self._inMouse = False
self._initOver = False
self._inDrawAll = False
self._agwStyle = agwStyle
self.mainPanel = wx.Panel(self, -1)
self.hsvSizer_staticbox = wx.StaticBox(self.mainPanel, -1, _("HSB"))
self.rgbValueSizer_staticbox = wx.StaticBox(self.mainPanel, -1, _("RGB Values"))
self.hsvValueSizer_staticbox = wx.StaticBox(self.mainPanel, -1, _("HSB Values"))
self.rgbSizer_staticbox = wx.StaticBox(self.mainPanel, -1, _("RGB"))
self.alphaSizer_staticbox = wx.StaticBox(self.mainPanel, -1, _("Alpha"))
self.alphaValueSizer_staticbox = wx.StaticBox(self.mainPanel, -1, _("Alpha"))
self.rgbBitmap = RGBCube(self.mainPanel)
self.hsvBitmap = HSVWheel(self.mainPanel)
self.brightCtrl = BrightCtrl(self.mainPanel)
self.alphaCtrl = AlphaCtrl(self.mainPanel)
self.showAlpha = wx.CheckBox(self.mainPanel, -1, _("Show Alpha Control"))
self.customColours = CustomPanel(self.mainPanel, self._colourData)
self.addCustom = wx.Button(self.mainPanel, -1, _("Add to custom colours"))
self.okButton = wx.Button(self.mainPanel, -1, _("OK"))
self.cancelButton = wx.Button(self.mainPanel, -1, _("Cancel"))
self.oldColourPanel = ColourPanel(self.mainPanel, style=wx.SIMPLE_BORDER)
self.newColourPanel = ColourPanel(self.mainPanel, style=wx.SIMPLE_BORDER)
self.redSpin = wx.SpinCtrl(self.mainPanel, -1, "180", min=0, max=255,
style=wx.SP_ARROW_KEYS)
self.greenSpin = wx.SpinCtrl(self.mainPanel, -1, "180", min=0, max=255,
style=wx.SP_ARROW_KEYS)
self.blueSpin = wx.SpinCtrl(self.mainPanel, -1, "180", min=0, max=255,
style=wx.SP_ARROW_KEYS)
self.hueSpin = wx.SpinCtrl(self.mainPanel, -1, "0", min=0, max=359,
style=wx.SP_ARROW_KEYS)
self.saturationSpin = wx.SpinCtrl(self.mainPanel, -1, "", min=0, max=255,
style=wx.SP_ARROW_KEYS)
self.brightnessSpin = wx.SpinCtrl(self.mainPanel, -1, "", min=0, max=255,
style=wx.SP_ARROW_KEYS)
self.alphaSpin = wx.SpinCtrl(self.mainPanel, -1, "", min=0, max=255,
style=wx.SP_ARROW_KEYS)
self.accessCode = wx.TextCtrl(self.mainPanel, -1, "", style=wx.TE_READONLY)
self.htmlCode = wx.TextCtrl(self.mainPanel, -1, "", style=wx.TE_READONLY)
self.webSafe = wx.TextCtrl(self.mainPanel, -1, "", style=wx.TE_READONLY)
self.htmlName = wx.TextCtrl(self.mainPanel, -1, "", style=wx.TE_READONLY)
self.SetProperties()
self.DoLayout()
self.spinCtrls = [self.redSpin, self.greenSpin, self.blueSpin,
self.hueSpin, self.saturationSpin, self.brightnessSpin]
for spin in self.spinCtrls:
spin.Bind(wx.EVT_SPINCTRL, self.OnSpinCtrl)
self.Bind(wx.EVT_SPINCTRL, self.OnAlphaSpin, self.alphaSpin)
self.Bind(wx.EVT_BUTTON, self.OnOk, self.okButton)
self.Bind(wx.EVT_BUTTON, self.OnCancel, self.cancelButton)
self.Bind(wx.EVT_BUTTON, self.OnAddCustom, self.addCustom)
self.Bind(wx.EVT_CHECKBOX, self.OnShowAlpha)
self.Bind(wx.EVT_CLOSE, self.OnCloseWindow)
self.Bind(wx.EVT_CHAR_HOOK, self.OnKeyUp)
self.Centre(wx.BOTH)
wx.CallAfter(self.InitDialog)
def SetProperties(self):
""" Sets some initial properties for :class:`CubeColourDialog` (sizes, values). """
self.okButton.SetDefault()
self.oldColourPanel.SetMinSize((-1, 50))
self.newColourPanel.SetMinSize((-1, 50))
self.redSpin.SetMinSize((60, -1))
self.greenSpin.SetMinSize((60, -1))
self.blueSpin.SetMinSize((60, -1))
self.hueSpin.SetMinSize((60, -1))
self.saturationSpin.SetMinSize((60, -1))
self.brightnessSpin.SetMinSize((60, -1))
self.alphaSpin.SetMinSize((60, -1))
self.showAlpha.SetValue(1)
self.accessCode.SetInitialSize((80, -1))
self.webSafe.SetInitialSize((80, -1))
self.htmlCode.SetInitialSize((80, -1))
def DoLayout(self):
""" Layouts all the controls in the :class:`CubeColourDialog`. """
dialogSizer = wx.BoxSizer(wx.VERTICAL)
mainSizer = wx.GridBagSizer(10, 5)
hsvValueSizer = wx.StaticBoxSizer(self.hsvValueSizer_staticbox, wx.VERTICAL)
hsvGridSizer = wx.GridSizer(2, 3, 2, 10)
rgbValueSizer = wx.StaticBoxSizer(self.rgbValueSizer_staticbox, wx.HORIZONTAL)
rgbGridSizer = wx.GridSizer(2, 3, 2, 10)
alphaValueSizer = wx.StaticBoxSizer(self.alphaValueSizer_staticbox, wx.VERTICAL)
alphaGridSizer = wx.BoxSizer(wx.VERTICAL)
customSizer = wx.BoxSizer(wx.VERTICAL)
buttonSizer = wx.BoxSizer(wx.VERTICAL)
accessSizer = wx.BoxSizer(wx.VERTICAL)
panelSizer = wx.BoxSizer(wx.VERTICAL)
htmlSizer1 = wx.BoxSizer(wx.HORIZONTAL)
htmlSizer2 = wx.BoxSizer(wx.VERTICAL)
htmlSizer_a = wx.BoxSizer(wx.VERTICAL)
htmlSizer_b = wx.BoxSizer(wx.VERTICAL)
hsvSizer = wx.StaticBoxSizer(self.hsvSizer_staticbox, wx.HORIZONTAL)
rgbSizer = wx.StaticBoxSizer(self.rgbSizer_staticbox, wx.VERTICAL)
alphaSizer = wx.StaticBoxSizer(self.alphaSizer_staticbox, wx.VERTICAL)
mainSizer.Add(self.showAlpha, (0, 0), (1, 1), wx.LEFT|wx.TOP, 10)
htmlLabel1 = wx.StaticText(self.mainPanel, -1, _("HTML Code"))
htmlLabel2 = wx.StaticText(self.mainPanel, -1, _("Web Safe"))
htmlSizer_a.Add(htmlLabel1, 0, wx.TOP, 3)
htmlSizer_b.Add(htmlLabel2, 0, wx.TOP, 3)
htmlSizer_a.Add(self.htmlCode, 0, wx.TOP, 3)
htmlSizer_b.Add(self.webSafe, 0, wx.TOP, 3)
htmlSizer1.Add(htmlSizer_a, 0)
htmlSizer1.Add(htmlSizer_b, 0, wx.LEFT, 10)
mainSizer.Add(htmlSizer1, (1, 0), (1, 1), wx.LEFT|wx.RIGHT, 10)
htmlLabel3 = wx.StaticText(self.mainPanel, -1, _("HTML Name"))
htmlSizer2.Add(htmlLabel3, 0, wx.TOP|wx.BOTTOM, 3)
htmlSizer2.Add(self.htmlName, 0)
mainSizer.Add(htmlSizer2, (1, 1), (1, 1), wx.LEFT|wx.RIGHT, 10)
customLabel = wx.StaticText(self.mainPanel, -1, _("Custom Colours"))
customSizer.Add(customLabel, 0, wx.BOTTOM, 3)
customSizer.Add(self.customColours, 0)
customSizer.Add(self.addCustom, 0, wx.TOP|wx.ALIGN_LEFT|wx.ALIGN_CENTER_VERTICAL, 5)
mainSizer.Add(customSizer, (0, 2), (2, 2), wx.ALIGN_CENTER|wx.LEFT|wx.RIGHT, 5)
rgbSizer.Add(self.rgbBitmap, 0, wx.ALL, 15)
mainSizer.Add(rgbSizer, (2, 0), (1, 1), wx.ALL|wx.EXPAND, 10)
hsvSizer.Add(self.hsvBitmap, 0, wx.ALL, 15)
hsvSizer.Add(self.brightCtrl, 0, wx.RIGHT|wx.TOP|wx.BOTTOM, 15)
mainSizer.Add(hsvSizer, (2, 1), (1, 1), wx.ALL|wx.EXPAND, 10)
alphaSizer.Add(self.alphaCtrl, 0, wx.TOP|wx.ALIGN_CENTER, 15)
mainSizer.Add(alphaSizer, (2, 2), (1, 1), wx.ALL|wx.EXPAND, 10)
oldLabel = wx.StaticText(self.mainPanel, -1, _("Old Colour"))
panelSizer.Add(oldLabel, 0, wx.BOTTOM, 3)
panelSizer.Add(self.oldColourPanel, 0, wx.BOTTOM|wx.EXPAND, 20)
newLabel = wx.StaticText(self.mainPanel, -1, _("New Colour"))
accessLabel = wx.StaticText(self.mainPanel, -1, _("MS Access Code"))
accessSizer.Add(accessLabel, 0, wx.BOTTOM, 3)
accessSizer.Add(self.accessCode, 0)
panelSizer.Add(newLabel, 0, wx.BOTTOM, 3)
panelSizer.Add(self.newColourPanel, 0, wx.EXPAND)
panelSizer.Add(0, 0, 1, wx.EXPAND)
panelSizer.Add(accessSizer, 0, wx.TOP, 5)
mainSizer.Add(panelSizer, (2, 3), (1, 1), wx.ALL|wx.EXPAND, 10)
redLabel = wx.StaticText(self.mainPanel, -1, _("Red"))
rgbGridSizer.Add(redLabel, 0)
greenLabel = wx.StaticText(self.mainPanel, -1, _("Green"))
rgbGridSizer.Add(greenLabel, 0)
blueLabel = wx.StaticText(self.mainPanel, -1, _("Blue"))
rgbGridSizer.Add(blueLabel, 0)
rgbGridSizer.Add(self.redSpin, 0, wx.EXPAND)
rgbGridSizer.Add(self.greenSpin, 0, wx.EXPAND)
rgbGridSizer.Add(self.blueSpin, 0, wx.EXPAND)
rgbValueSizer.Add(rgbGridSizer, 1, 0, 0)
mainSizer.Add(rgbValueSizer, (3, 0), (1, 1), wx.LEFT|wx.RIGHT|wx.BOTTOM|wx.EXPAND, 10)
hueLabel = wx.StaticText(self.mainPanel, -1, _("Hue"))
hsvGridSizer.Add(hueLabel, 0)
saturationLabel = wx.StaticText(self.mainPanel, -1, _("Saturation"))
hsvGridSizer.Add(saturationLabel, 0)
brightnessLabel = wx.StaticText(self.mainPanel, -1, _("Brightness"))
hsvGridSizer.Add(brightnessLabel, 0)
hsvGridSizer.Add(self.hueSpin, 0, wx.EXPAND)
hsvGridSizer.Add(self.saturationSpin, 0, wx.EXPAND)
hsvGridSizer.Add(self.brightnessSpin, 0, wx.EXPAND)
hsvValueSizer.Add(hsvGridSizer, 1, wx.EXPAND)
mainSizer.Add(hsvValueSizer, (3, 1), (1, 1), wx.LEFT|wx.RIGHT|wx.BOTTOM|wx.EXPAND, 10)
alphaLabel = wx.StaticText(self.mainPanel, -1, _("Alpha"))
alphaGridSizer.Add(alphaLabel, 0)
alphaGridSizer.Add(self.alphaSpin, 0, wx.EXPAND|wx.TOP, 10)
alphaValueSizer.Add(alphaGridSizer, 1, wx.EXPAND)
mainSizer.Add(alphaValueSizer, (3, 2), (1, 1), wx.LEFT|wx.RIGHT|wx.BOTTOM|wx.EXPAND, 10)
buttonSizer.Add(self.okButton, 0, wx.BOTTOM, 3)
buttonSizer.Add(self.cancelButton, 0)
mainSizer.Add(buttonSizer, (3, 3), (1, 1), wx.ALIGN_CENTER|wx.LEFT|wx.RIGHT, 5)
self.mainPanel.SetAutoLayout(True)
self.mainPanel.SetSizer(mainSizer)
mainSizer.Fit(self.mainPanel)
mainSizer.SetSizeHints(self.mainPanel)
if self.GetAGWWindowStyleFlag() & CCD_SHOW_ALPHA == 0:
mainSizer.Hide(self.showAlpha)
mainSizer.Hide(alphaSizer)
mainSizer.Hide(alphaValueSizer)
dialogSizer.Add(self.mainPanel, 1, wx.EXPAND)
self.SetAutoLayout(True)
self.SetSizer(dialogSizer)
dialogSizer.Fit(self)
dialogSizer.SetSizeHints(self)
self.Layout()
self.mainSizer = mainSizer
self.dialogSizer = dialogSizer
self.alphaSizers = [alphaSizer, alphaValueSizer]
def InitDialog(self):
""" Initialize the :class:`CubeColourDialog`. """
hsvRect = self.hsvBitmap.GetClientRect()
self._centre = wx.Point(hsvRect.x + hsvRect.width//2, hsvRect.y + hsvRect.height//2)
self._redLen = Distance(Vertex, Top)
self._greenLen = Distance(Vertex, Left)
self._blueLen = Distance(Vertex, Right)
self.CalcSlopes()
self.CalcCuboid()
self.CalcRects()
self.SetSpinVals()
self._initOver = True
wx.CallAfter(self.Refresh)
def CalcSlopes(self):
""" Calculates the line slopes in the RGB colour cube. """
self._lines = {RED: LineDescription(), GREEN: LineDescription(), BLUE: LineDescription}
self._lines[RED].slope = Slope(Top, Vertex)
self._lines[GREEN].slope = Slope(Left, Vertex)
self._lines[BLUE].slope = Slope(Right, Vertex)
for i in range(3):
self._lines[i].x = Vertex.x
self._lines[i].y = Vertex.y
self._lines[i].c = FindC(self._lines[i])
def CalcCuboid(self):
""" Calculates the RGB colour cube vertices. """
rLen = (self._colour.r*self._redLen)/255.0
gLen = (self._colour.g*self._greenLen)/255.0
bLen = (self._colour.b*self._blueLen)/255.0
lines = [LineDescription() for i in range(12)]
self._cuboid = [None]*8
self._cuboid[0] = Vertex
self._cuboid[1] = PointOnLine(Vertex, Top, int(rLen), self._redLen)
self._cuboid[3] = PointOnLine(Vertex, Left, int(gLen), self._greenLen)
self._cuboid[7] = PointOnLine(Vertex, Right, int(bLen), self._blueLen)
lines[0] = self._lines[RED]
lines[1] = self._lines[GREEN]
lines[2] = self._lines[BLUE]
lines[3].slope = self._lines[GREEN].slope
lines[3].x = self._cuboid[1].x
lines[3].y = self._cuboid[1].y
lines[3].c = FindC(lines[3])
lines[4].slope = self._lines[RED].slope
lines[4].x = self._cuboid[3].x
lines[4].y = self._cuboid[3].y
lines[4].c = FindC(lines[4])
lines[5].slope = self._lines[BLUE].slope
lines[5].x = self._cuboid[3].x
lines[5].y = self._cuboid[3].y
lines[5].c = FindC(lines[5])
lines[6].slope = self._lines[GREEN].slope
lines[6].x = self._cuboid[7].x
lines[6].y = self._cuboid[7].y
lines[6].c = FindC(lines[6])
lines[10].slope = self._lines[BLUE].slope
lines[10].x = self._cuboid[1].x
lines[10].y = self._cuboid[1].y
lines[10].c = FindC(lines[10])
lines[11].slope = self._lines[RED].slope
lines[11].x = self._cuboid[7].x
lines[11].y = self._cuboid[7].y
lines[11].c = FindC(lines[11])
self._cuboid[2] = Intersection(lines[3], lines[4])
self._cuboid[4] = Intersection(lines[5], lines[6])
self._cuboid[6] = Intersection(lines[10], lines[11])
lines[7].slope = self._lines[RED].slope
lines[7].x = self._cuboid[4].x
lines[7].y = self._cuboid[4].y
lines[7].c = FindC(lines[7])
lines[8].slope = self._lines[BLUE].slope
lines[8].x = self._cuboid[2].x
lines[8].y = self._cuboid[2].y
lines[8].c = FindC(lines[8])
self._cuboid[5] = Intersection(lines[7], lines[8])
def CalcRects(self):
""" Calculates the brightness control user-selected rect. """
pt = PtFromAngle(self._colour.h, self._colour.s, self._centre)
self._currentRect = wx.Rect(pt.x - RECT_WIDTH, pt.y - RECT_WIDTH,
2*RECT_WIDTH, 2*RECT_WIDTH)
def DrawMarkers(self, dc=None):
"""
Draws the markers for all the controls.
:param `dc`: an instance of :class:`DC`. If `dc` is ``None``, a :class:`ClientDC` is
created on the fly.
"""
if dc is None:
dc = wx.ClientDC(self)
self.hsvBitmap.DrawMarkers()
self.rgbBitmap.DrawMarkers()
self.brightCtrl.DrawMarkers()
def DrawRGB(self):
""" Refreshes the RGB colour cube. """
self.rgbBitmap.Refresh()
def DrawHSB(self):
""" Refreshes the HSB colour wheel. """
self.hsvBitmap.Refresh()
def DrawBright(self):
""" Refreshes the brightness control. """
self.brightCtrl.Refresh()
def DrawAlpha(self):
""" Refreshes the alpha channel control. """
self.alphaCtrl.Refresh()
def SetSpinVals(self):
""" Sets the values for all the spin controls. """
self.redSpin.SetValue(self._colour.r)
self.greenSpin.SetValue(self._colour.g)
self.blueSpin.SetValue(self._colour.b)
self.hueSpin.SetValue(self._colour.h)
self.saturationSpin.SetValue(self._colour.s)
self.brightnessSpin.SetValue(self._colour.v)
self.alphaSpin.SetValue(self._colour._alpha)
self.SetPanelColours()
self.SetCodes()
def SetPanelColours(self):
""" Assigns colours to the colour panels. """
self.oldColourPanel.RefreshColour(self._oldColour)
self.newColourPanel.RefreshColour(self._colour)
def SetCodes(self):
""" Sets the HTML/MS Access codes (if any) in the text controls. """
colour = rgb2html(self._colour)
self.htmlCode.SetValue(colour)
self.htmlCode.Refresh()
if colour in HTMLCodes:
colourName, access, webSafe = HTMLCodes[colour]
self.webSafe.SetValue(webSafe)
self.accessCode.SetValue(access)
self.htmlName.SetValue(colourName)
else:
self.webSafe.SetValue("")
self.accessCode.SetValue("")
self.htmlName.SetValue("")
def OnCloseWindow(self, event):
"""
Handles the ``wx.EVT_CLOSE`` event for :class:`CubeColourDialog`.
:param `event`: a :class:`CloseEvent` event to be processed.
"""
self.EndModal(wx.ID_CANCEL)
def OnKeyUp(self, event):
"""
Handles the ``wx.EVT_CHAR_HOOK`` event for :class:`CubeColourDialog`.
:param `event`: a :class:`KeyEvent` event to be processed.
"""
if event.GetKeyCode() == wx.WXK_ESCAPE:
self.EndModal(wx.ID_CANCEL)
event.Skip()
def ShowModal(self):
"""
Shows :class:`CubeColourDialog` as a modal dialog. Program flow does
not return until the dialog has been dismissed with `EndModal`.
:note: Overridden from :class:`Dialog`.
"""
return wx.Dialog.ShowModal(self)
def SetAGWWindowStyleFlag(self, agwStyle):
"""
Sets the :class:`CubeColourDialog` window style flags.
:param `agwStyle`: can only be ``CCD_SHOW_ALPHA`` or ``None``.
"""
show = self.GetAGWWindowStyleFlag() & CCD_SHOW_ALPHA
self._agwStyle = agwStyle
self.mainSizer.Show(self.alphaSizers[0], show)
self.mainSizer.Show(self.alphaSizers[1], show)
self.mainSizer.Fit(self.mainPanel)
self.mainSizer.SetSizeHints(self.mainPanel)
self.mainSizer.Layout()
self.dialogSizer.Fit(self)
self.dialogSizer.SetSizeHints(self)
self.Layout()
self.Refresh()
self.Update()
def GetAGWWindowStyleFlag(self):
"""
Returns the :class:`CubeColourDialog` window style flags.
:see: :meth:`~CubeColourDialog.SetAGWWindowStyleFlag` for a list of possible flags.
"""
return self._agwStyle
def OnOk(self, event):
"""
Handles the Ok ``wx.EVT_BUTTON`` event for :class:`CubeColourDialog`.
:param `event`: a :class:`CommandEvent` event to be processed.
"""
self.EndModal(wx.ID_OK)
def OnCancel(self, event):
"""
Handles the Cancel ``wx.EVT_BUTTON`` event for :class:`CubeColourDialog`.
:param `event`: a :class:`CommandEvent` event to be processed.
"""
self.OnCloseWindow(event)
def OnAddCustom(self, event):
"""
Handles the Add Custom ``wx.EVT_BUTTON`` event for :class:`CubeColourDialog`.
:param `event`: a :class:`CommandEvent` event to be processed.
"""
self.customColours.AddCustom(self._colour)
def OnShowAlpha(self, event):
"""
Shows/hides the alpha channel control in :class:`CubeColourDialog`.
:param `event`: a :class:`CommandEvent` event to be processed.
"""
agwStyle = self.GetAGWWindowStyleFlag()
show = event.IsChecked()
if show:
agwStyle |= CCD_SHOW_ALPHA
else:
agwStyle &= ~CCD_SHOW_ALPHA
self.SetAGWWindowStyleFlag(agwStyle)
def OnSpinCtrl(self, event):
"""
Handles the ``wx.EVT_SPINCTRL`` event for RGB and HSB colours.
:param `event`: a :class:`SpinEvent` event to be processed.
"""
obj = event.GetEventObject()
position = self.spinCtrls.index(obj)
colourVal = event.GetInt()
attribute, maxVal = colourAttributes[position], colourMaxValues[position]
self.AssignColourValue(attribute, colourVal, maxVal, position)
def OnAlphaSpin(self, event):
"""
Handles the ``wx.EVT_SPINCTRL`` event for the alpha channel.
:param `event`: a :class:`SpinEvent` event to be processed.
"""
colourVal = event.GetInt()
originalVal = self._colour._alpha
if colourVal != originalVal and self._initOver:
if colourVal < 0:
colourVal = 0
if colourVal > 255:
colourVal = 255
self._colour._alpha = colourVal
self.DrawAlpha()
def AssignColourValue(self, attribute, colourVal, maxVal, position):
""" Common code to handle spin control changes. """
originalVal = getattr(self._colour, attribute)
if colourVal != originalVal and self._initOver:
if colourVal < 0:
colourVal = 0
if colourVal > maxVal:
colourVal = maxVal
setattr(self._colour, attribute, colourVal)
if position < 3:
self._colour.ToHSV()
else:
self._colour.ToRGB()
self.DrawAll()
def DrawAll(self):
""" Draws all the custom controls after a colour change. """
if self._initOver and not self._inDrawAll:
self._inDrawAll = True
dc1 = wx.ClientDC(self.hsvBitmap)
self.hsvBitmap.DrawMarkers(dc1)
dc2 = wx.ClientDC(self.rgbBitmap)
self.rgbBitmap.DrawMarkers(dc2)
self.rgbBitmap.DrawLines(dc2)
dc3 = wx.ClientDC(self.brightCtrl)
self.brightCtrl.DrawMarkers(dc3)
dc4 = wx.ClientDC(self.alphaCtrl)
self.alphaCtrl.DrawMarkers(dc4)
self.CalcCuboid()
self.CalcRects()
self.DrawRGB()
self.DrawHSB()
self.DrawBright()
self.DrawAlpha()
self.SetSpinVals()
self._inDrawAll = False
def GetColourData(self):
""" Returns a wxPython compatible :class:`ColourData`. """
self._colourData.SetColour(self._colour.GetPyColour())
return self._colourData
def GetRGBAColour(self):
""" Returns a 4-elements tuple of red, green, blue, alpha components. """
return (self._colour.r, self._colour.g, self._colour.b, self._colour._alpha)
def GetHSVAColour(self):
""" Returns a 4-elements tuple of hue, saturation, brightness, alpha components. """
return (self._colour.h, self._colour.s, self._colour.v, self._colour._alpha)
if __name__ == '__main__':
import wx
# Our normal wxApp-derived class, as usual
app = wx.App(0)
colourData = wx.ColourData()
colourData.SetColour(wx.SystemSettings.GetColour(wx.SYS_COLOUR_3DFACE))
dlg = CubeColourDialog(None, colourData)
if dlg.ShowModal() == wx.ID_OK:
# If the user selected OK, then the dialog's wx.ColourData will
# contain valid information. Fetch the data ...
colourData = dlg.GetColourData()
h, s, v, a = dlg.GetHSVAColour()
# ... then do something with it. The actual colour data will be
# returned as a three-tuple (r, g, b) in this particular case.
colour = colourData.GetColour()
r, g, b, alpha = colour.Red(), colour.Green(), colour.Blue(), colour.Alpha()
print(("You selected (RGBA): %d, %d, %d, %d"%(r, g, b, alpha)))
print(("You selected (HSVA): %d, %d, %d, %d"%(h, s, v, a)))
# Once the dialog is destroyed, Mr. wx.ColourData is no longer your
# friend. Don't use it again!
dlg.Destroy()
app.MainLoop()