Fixed colourchooser palette and colour_slider controls.

Palette box and slider box are now sized to fit their content, making the
whole area accessible. Colour slider is now clickable.
This commit is contained in:
tom surace
2016-11-14 15:28:47 -07:00
parent 5da4bc6c57
commit 2d51ef79d8
5 changed files with 114 additions and 29 deletions

View File

@@ -80,13 +80,16 @@ class Canvas(wx.Window):
"""
def __init__(self, parent, id,
pos=wx.DefaultPosition,
size=wx.DefaultSize,
style=wx.SIMPLE_BORDER):
style=wx.SIMPLE_BORDER,
forceClientSize=None):
"""Creates a canvas instance and initializes the off-screen
buffer. Also sets the handler for rendering the canvas
automatically via size and paint calls from the windowing
system."""
wx.Window.__init__(self, parent, id, pos, size, style)
wx.Window.__init__(self, parent, id, pos, style=style)
if forceClientSize:
self.SetMaxClientSize(forceClientSize)
self.SetMinClientSize(forceClientSize)
# Perform an intial sizing
self.ReDraw()
@@ -96,7 +99,7 @@ class Canvas(wx.Window):
self.Bind(wx.EVT_PAINT, self.onPaint)
def MakeNewBuffer(self):
size = self.GetSize()
size = self.GetClientSize()
self.buffer = BitmapBuffer(size[0], size[1],
self.GetBackgroundColour())

View File

@@ -67,6 +67,7 @@ class PyColourBox(wx.Panel):
"""Sets the box's current couple to the given tuple."""
self.colour = colour
self.colour_box.SetBackgroundColour(wx.Colour(*self.colour))
self.colour_box.Refresh()
def Update(self):
wx.Panel.Update(self)

View File

@@ -28,6 +28,7 @@ from __future__ import absolute_import
# Tags: phoenix-port
import wx
import wx.lib.newevent as newevent
from . import pycolourbox
from . import pypalette
@@ -37,6 +38,19 @@ from . import intl
from .intl import _ # _
ColourChangedEventBase, EVT_COLOUR_CHANGED = newevent.NewEvent()
class ColourChangedEvent(ColourChangedEventBase):
"""Adds GetColour()/GetValue() for compatibility with ColourPickerCtrl and colourselect"""
def __init__(self, newColour):
super().__init__(newColour = newColour)
def GetColour(self):
return self.newColour
def GetValue(self):
return self.newColour
class PyColourChooser(wx.Panel):
"""A Pure-Python implementation of the colour chooser dialog.
@@ -169,6 +183,8 @@ class PyColourChooser(wx.Panel):
self.palette = pypalette.PyPalette(self, -1)
self.colour_slider = pycolourslider.PyColourSlider(self, -1)
self.colour_slider.Bind(wx.EVT_LEFT_DOWN, self.onSliderDown)
self.colour_slider.Bind(wx.EVT_MOTION, self.onSliderMotion)
self.slider = wx.Slider(
self, self.idSCROLL, 86, 0, self.colour_slider.HEIGHT - 1,
style=wx.SL_VERTICAL, size=(15, self.colour_slider.HEIGHT)
@@ -185,7 +201,6 @@ class PyColourChooser(wx.Panel):
self.palette.Bind(wx.EVT_LEFT_DOWN, self.onPaletteDown)
self.palette.Bind(wx.EVT_LEFT_UP, self.onPaletteUp)
self.palette.Bind(wx.EVT_MOTION, self.onPaletteMotion)
self.mouse_down = False
self.solid = pycolourbox.PyColourBox(self, -1, size=(75, 50))
slabel = wx.StaticText(self, -1, _("Solid Colour"))
@@ -366,11 +381,17 @@ class PyColourChooser(wx.Panel):
s = 0
return self.hsvToColour((h, s, v))
def updateDisplayColour(self, colour):
"""Update the displayed color box (solid) and send the EVT_COLOUR_CHANGED"""
self.solid.SetColour(colour)
evt = ColourChangedEvent(newColour=colour)
wx.PostEvent(self, evt)
def UpdateColour(self, colour):
"""Updates displayed colour and HSV controls with the new colour"""
# Set the color info
self.solid.SetColour(colour)
self.updateDisplayColour(colour)
self.colour_slider.SetBaseColour(colour)
self.colour_slider.ReDraw()
@@ -404,22 +425,55 @@ class PyColourChooser(wx.Panel):
self.hentry.SetValue("%.2f" % (h))
self.sentry.SetValue("%.2f" % (s))
self.ventry.SetValue("%.2f" % (v))
def onColourSliderClick(self, y):
"""Shared helper for onSliderDown()/onSliderMotion()"""
v = self.colour_slider.GetVFromClick(y)
self.setSliderToV(v)
# Now with the slider updated, update all controls
colour = self.getColourFromControls()
self.updateDisplayColour(colour) # Update display
self.UpdateEntries(colour)
# We don't move on the palette...
# width, height = self.palette.GetSize()
# x = width * h
# y = height * (1 - s)
# self.palette.HighlightPoint(x, y)
def onSliderDown(self, event):
"""Handle mouse click on the colour slider palette"""
self.onColourSliderClick(event.GetY())
def onSliderMotion(self, event):
"""Handle mouse-down drag on the colour slider palette"""
if event.LeftIsDown():
self.onColourSliderClick(event.GetY())
def onPaletteDown(self, event):
"""Stores state that the mouse has been pressed and updates
the selected colour values."""
self.mouse_down = True
self.doPaletteClick(event.GetX(), event.GetY())
# Prevent mouse from leaving window, so that we will also get events
# when mouse is dragged along the edges of the rectangle.
self.palette.CaptureMouse()
def onPaletteUp(self, event):
"""Stores state that the mouse is no longer depressed."""
self.mouse_down = False
self.palette.ReleaseMouse() # Must call once for each CaputreMouse()
def onPaletteMotion(self, event):
"""Updates the colour values during mouse motion while the
mouse button is depressed."""
if self.mouse_down:
if event.LeftIsDown():
self.doPaletteClick(event.GetX(), event.GetY())
def onPaletteCaptureLost(self, event):
pass # I don't think we have to call ReleaseMouse in this event
def doPaletteClick(self, m_x, m_y):
"""Updates the colour values based on the mouse location
@@ -433,7 +487,7 @@ class PyColourChooser(wx.Panel):
colour = self.getColourFromControls()
self.solid.SetColour(colour) # Update display
self.updateDisplayColour(colour) # Update display
self.UpdateEntries(colour)
# Highlight a fresh selected area
@@ -443,7 +497,7 @@ class PyColourChooser(wx.Panel):
"""Updates the display to reflect the new "Value"."""
value = self.slider.GetValue()
colour = self.getColourFromControls()
self.solid.SetColour(colour)
self.updateDisplayColour(colour)
self.UpdateEntries(colour)
def getValueAsFloat(self, textctrl):
@@ -537,8 +591,12 @@ def main():
def onClick(self, cmdEvt):
with CCTestDialog(self, self.box.GetColour()) as dialog:
dialog.chooser.Bind(EVT_COLOUR_CHANGED, self.onColourChanged)
dialog.ShowModal()
self.box.SetColour(dialog.chooser.GetValue())
def onColourChanged(self, event):
self.box.SetColour(event.GetValue())
class App(wx.App):
def OnInit(self):

View File

@@ -54,7 +54,7 @@ class PyColourSlider(canvas.Canvas):
# drawing function
self.SetBaseColour(colour)
canvas.Canvas.__init__(self, parent, id, size=(self.WIDTH, self.HEIGHT))
canvas.Canvas.__init__(self, parent, id, forceClientSize=(self.WIDTH, self.HEIGHT))
def SetBaseColour(self, colour):
"""Sets the base, or target colour, to use as the central colour
@@ -66,11 +66,17 @@ class PyColourSlider(canvas.Canvas):
the slider."""
return self.base_colour
def GetValue(self, pos):
"""Returns the colour value for a position on the slider. The position
must be within the valid height of the slider, or results can be
unpredictable."""
return self.buffer.GetPixelColour(0, pos)
def GetVFromClick(self, pos):
"""
Returns the HSV value "V" based on the location of a mouse click at y offset "pos"
"""
_, height = self.GetClientSize()
if pos < 0:
return 1 # Snap to max
if pos >= height - 1:
return 0 # Snap to 0
return 1 - (pos / self.HEIGHT)
def DrawBuffer(self):
"""Actual implementation of the widget's drawing. We simply draw

View File

@@ -37,6 +37,9 @@ import colorsys
from wx.lib.embeddedimage import PyEmbeddedImage
# Size of Image
IMAGE_SIZE = (200,192)
Image = PyEmbeddedImage(
"iVBORw0KGgoAAAANSUhEUgAAAMgAAADACAYAAABBCyzzAAAABHNCSVQICAgIfAhkiAAACwNJ"
"REFUeJztnc16o0YQRZHt8SJZJO//lMkiWWQsKwsLK7S49dNdjTyTczYYhBBd7vqoom7B6bIs"
@@ -112,7 +115,7 @@ class PyPalette(canvas.Canvas):
HORIZONTAL_STEP = 2
VERTICAL_STEP = 4
def __init__(self, parent, id):
"""Creates a palette object."""
# Load the pre-generated palette XPM
@@ -124,21 +127,35 @@ class PyPalette(canvas.Canvas):
self.palette = Image.GetBitmap()
self.point = None
canvas.Canvas.__init__ (self, parent, id, size=(200, 192))
canvas.Canvas.__init__ (self, parent, id, forceClientSize=IMAGE_SIZE)
def DoGetBestClientSize(self):
"""Overridden to create a client window that exactly fits our bitmap"""
return self.palette.GetSize()
def xInBounds(self, x):
"""Limit x to [0,width)"""
if x < 0:
x = 0
if x >= self.buffer.width:
x = self.buffer.width - 1
return x
def yInBounds(self, y):
"""Limit y to [0,height)"""
if y < 0:
y = 0
if y >= self.buffer.height:
y = self.buffer.height - 1
return y
def GetValue(self, x, y):
"""Returns a colour value at a specific x, y coordinate pair. This
is useful for determining the colour found a specific mouse click
in an external event handler."""
if x < 0:
x = 0
if y < 0:
y = 0
if x >= self.buffer.width:
x = self.buffer.width - 1
if y >= self.buffer.height:
y = self.buffer.height - 1
x = self.xInBounds(x)
y = self.yInBounds(y)
return self.buffer.GetPixelColour(x, y)
def DrawBuffer(self):
@@ -154,7 +171,7 @@ class PyPalette(canvas.Canvas):
def HighlightPoint(self, x, y):
"""Highlights an area of the palette with a little circle around
the coordinate point"""
self.point = (x, y)
self.point = (self.xInBounds(x), self.yInBounds(y))
self.ReDraw()
def ClearPoint(self):