mirror of
https://github.com/jtgans/g13gui.git
synced 2025-06-20 08:23:50 -04:00
bitwidgets: New widgets!
This adds a whole new ListView widget (and associated ListItem widget) so that we can handle selecting items from a list. Additionally, it brings in rectangle tests, and sadly removes the nice rounded rectangle from its design. - The button module gained a Glyph widget, and Buttons are now composites containing both a Rectangle and a Glyph. - ButtonBar has been fixed to match the above Button change, which produces a much more correct layout of glyphs and their drawing inside of each ButtonBar slot. - Added a Button test for Glyphs. - Made X11DisplayDevice update its output window name when it's assigned to, making it possible for each test to indicate which test it belongs to. - Created ListView and ListItem. - Made Widget not use its accessors for its constructor, which would create a ton of erronous observer events down the chain. - Made some of Widget's setters ValueErrors more specific.
This commit is contained in:
parent
5cc4e81dd5
commit
60d58e8392
@ -4,6 +4,7 @@ from builtins import property
|
|||||||
from g13gui.bitwidgets import DISPLAY_WIDTH
|
from g13gui.bitwidgets import DISPLAY_WIDTH
|
||||||
from g13gui.bitwidgets import DISPLAY_HEIGHT
|
from g13gui.bitwidgets import DISPLAY_HEIGHT
|
||||||
from g13gui.bitwidgets.widget import Widget
|
from g13gui.bitwidgets.widget import Widget
|
||||||
|
from g13gui.bitwidgets.rectangle import Rectangle
|
||||||
from g13gui.observer.subject import ChangeType
|
from g13gui.observer.subject import ChangeType
|
||||||
|
|
||||||
|
|
||||||
@ -16,6 +17,9 @@ class Glyphs(enum.Enum):
|
|||||||
UP_ARROW = [(2, 4), (2, 0), (4, 2), (0, 2), (2, 0)]
|
UP_ARROW = [(2, 4), (2, 0), (4, 2), (0, 2), (2, 0)]
|
||||||
CHECKMARK = [(0, 3), (1, 4), (4, 1), (1, 4)]
|
CHECKMARK = [(0, 3), (1, 4), (4, 1), (1, 4)]
|
||||||
XMARK = [(0, 0), (4, 4), (2, 2), (4, 0), (0, 4)]
|
XMARK = [(0, 0), (4, 4), (2, 2), (4, 0), (0, 4)]
|
||||||
|
BLANK = []
|
||||||
|
|
||||||
|
BOUNDS = (5, 5)
|
||||||
|
|
||||||
def transformTo(self, offsetx, offsety):
|
def transformTo(self, offsetx, offsety):
|
||||||
return [(offsetx + x, offsety + y) for (x, y) in self.value]
|
return [(offsetx + x, offsety + y) for (x, y) in self.value]
|
||||||
@ -31,6 +35,17 @@ class ButtonBar(Widget):
|
|||||||
self.position = (0, ButtonBar.TOP_LINE)
|
self.position = (0, ButtonBar.TOP_LINE)
|
||||||
self.bounds = (DISPLAY_WIDTH, DISPLAY_HEIGHT - ButtonBar.TOP_LINE)
|
self.bounds = (DISPLAY_WIDTH, DISPLAY_HEIGHT - ButtonBar.TOP_LINE)
|
||||||
|
|
||||||
|
def _buttonBounds(self):
|
||||||
|
width = (DISPLAY_WIDTH // ButtonBar.MAX_BUTTONS) - 2
|
||||||
|
height = DISPLAY_HEIGHT - ButtonBar.TOP_LINE
|
||||||
|
return (width, height)
|
||||||
|
|
||||||
|
def _positionForSlot(self, buttonNum):
|
||||||
|
slotWidth = DISPLAY_WIDTH / ButtonBar.MAX_BUTTONS
|
||||||
|
slotX = (buttonNum * slotWidth)
|
||||||
|
slotY = ButtonBar.TOP_LINE + 1
|
||||||
|
return (int(slotX), int(slotY))
|
||||||
|
|
||||||
def button(self, buttonNum):
|
def button(self, buttonNum):
|
||||||
return self._children[buttonNum]
|
return self._children[buttonNum]
|
||||||
|
|
||||||
@ -39,8 +54,8 @@ class ButtonBar(Widget):
|
|||||||
self.removeChild(self._children[buttonNum])
|
self.removeChild(self._children[buttonNum])
|
||||||
|
|
||||||
self._children[buttonNum] = button
|
self._children[buttonNum] = button
|
||||||
position = self._positionForButton(buttonNum)
|
button.position = self._positionForSlot(buttonNum)
|
||||||
button.position = position
|
button.bounds = self._buttonBounds()
|
||||||
button.parent = self
|
button.parent = self
|
||||||
|
|
||||||
self.addChange(ChangeType.ADD, 'child', button)
|
self.addChange(ChangeType.ADD, 'child', button)
|
||||||
@ -61,24 +76,6 @@ class ButtonBar(Widget):
|
|||||||
self.addChange(ChangeType.REMOVE, 'child', button)
|
self.addChange(ChangeType.REMOVE, 'child', button)
|
||||||
self.notifyChanged()
|
self.notifyChanged()
|
||||||
|
|
||||||
def _positionForSlot(self, buttonNum):
|
|
||||||
slotWidth = DISPLAY_WIDTH / ButtonBar.MAX_BUTTONS
|
|
||||||
slotX = (buttonNum * slotWidth)
|
|
||||||
slotY = ButtonBar.TOP_LINE + 2
|
|
||||||
|
|
||||||
return (slotX, slotY)
|
|
||||||
|
|
||||||
def _positionForButton(self, buttonNum):
|
|
||||||
(slotX, slotY) = self._positionForSlot(buttonNum)
|
|
||||||
slotWidth = DISPLAY_WIDTH / ButtonBar.MAX_BUTTONS
|
|
||||||
slotHeight = DISPLAY_HEIGHT - ButtonBar.TOP_LINE - 2
|
|
||||||
|
|
||||||
(width, height) = self._children[buttonNum].bounds
|
|
||||||
x_pos = int(slotX + (slotWidth / 2) - (width / 2))
|
|
||||||
y_pos = int(slotY + (slotHeight / 2) - (height / 2))
|
|
||||||
|
|
||||||
return (x_pos, y_pos)
|
|
||||||
|
|
||||||
def draw(self, ctx):
|
def draw(self, ctx):
|
||||||
if self.visible:
|
if self.visible:
|
||||||
for child in self._children:
|
for child in self._children:
|
||||||
@ -92,23 +89,84 @@ class ButtonBar(Widget):
|
|||||||
|
|
||||||
# Dividing lines
|
# Dividing lines
|
||||||
for slot in range(0, ButtonBar.MAX_BUTTONS):
|
for slot in range(0, ButtonBar.MAX_BUTTONS):
|
||||||
position = list(self._positionForSlot(slot))
|
(x, y) = self._positionForSlot(slot)
|
||||||
position[0] -= 2
|
x -= 1
|
||||||
ctx.line(tuple(position) + (position[0], DISPLAY_HEIGHT),
|
ctx.line((x, y) + (x, DISPLAY_HEIGHT),
|
||||||
fill=1)
|
fill=1)
|
||||||
|
|
||||||
|
|
||||||
class Button(Widget):
|
class Glyph(Widget):
|
||||||
def __init__(self, glyph, fill=True):
|
def __init__(self, x, y, glyph=Glyphs.BLANK, fill=True):
|
||||||
Widget.__init__(self)
|
Widget.__init__(self)
|
||||||
self.glyph = glyph
|
self.glyph = glyph
|
||||||
|
self.position = (x, y)
|
||||||
self.fill = fill
|
self.fill = fill
|
||||||
self.bounds = (5, 5)
|
|
||||||
|
@property
|
||||||
def draw(self, ctx):
|
def glyph(self):
|
||||||
if self._visible:
|
return self._glyph
|
||||||
xformedGlyph = self._glyph.transformTo(*self._position)
|
|
||||||
ctx.line(xformedGlyph, fill=self.fill)
|
@glyph.setter
|
||||||
|
def glyph(self, glyph):
|
||||||
|
self.setProperty('glyph', glyph)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def bounds(self):
|
||||||
|
return Glyphs.BOUNDS.value
|
||||||
|
|
||||||
|
def draw(self, ctx):
|
||||||
|
if self.visible:
|
||||||
|
(x, y) = self.position
|
||||||
|
xformedGlyph = self.glyph.transformTo(x, y)
|
||||||
|
ctx.line(xformedGlyph, fill=self._fill)
|
||||||
|
|
||||||
|
|
||||||
|
class Button(Widget):
|
||||||
|
def __init__(self, glyph, fill=True):
|
||||||
|
Widget.__init__(self)
|
||||||
|
self._rect = Rectangle(*self.position, *self.bounds, fill=False)
|
||||||
|
self._rect.show()
|
||||||
|
self.addChild(self._rect)
|
||||||
|
|
||||||
|
self._glyph = Glyph(*self.position, glyph=glyph)
|
||||||
|
self._glyph.fill = fill
|
||||||
|
self._glyph.show()
|
||||||
|
self.addChild(self._glyph)
|
||||||
|
|
||||||
|
self.pressed = False
|
||||||
|
self.fill = fill
|
||||||
|
|
||||||
|
self.registerObserver(self)
|
||||||
|
self.changeTrigger(self._updatePositionAndBounds,
|
||||||
|
changeType=ChangeType.MODIFY,
|
||||||
|
keys={'position', 'bounds'})
|
||||||
|
self.changeTrigger(self._updateStates,
|
||||||
|
changeType=ChangeType.MODIFY,
|
||||||
|
keys={'pressed'})
|
||||||
|
|
||||||
|
def _updatePositionAndBounds(self, subject, changeType, key, data):
|
||||||
|
self._rect.position = self.position
|
||||||
|
self._rect.bounds = self.bounds
|
||||||
|
|
||||||
|
(x, y) = self.position
|
||||||
|
(w, h) = self.bounds
|
||||||
|
(glyphW, glyphH) = self._glyph.bounds
|
||||||
|
glyphX = x + (w // 2) - (glyphW // 2)
|
||||||
|
glyphY = y + (h // 2) - (glyphH // 2)
|
||||||
|
|
||||||
|
self._glyph.position = (glyphX, glyphY)
|
||||||
|
|
||||||
|
def _updateStates(self, subject, changeType, key, data):
|
||||||
|
self._rect.fill = self.pressed
|
||||||
|
self._glyph.fill = not self.pressed
|
||||||
|
|
||||||
|
@property
|
||||||
|
def pressed(self):
|
||||||
|
return self._pressed
|
||||||
|
|
||||||
|
@pressed.setter
|
||||||
|
def pressed(self, pressed):
|
||||||
|
self.setProperty('pressed', pressed)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def glyph(self):
|
def glyph(self):
|
||||||
|
@ -6,6 +6,7 @@ from g13gui.bitwidgets.x11displaydevice import X11DisplayDevice
|
|||||||
from g13gui.bitwidgets.screen import Screen
|
from g13gui.bitwidgets.screen import Screen
|
||||||
from g13gui.bitwidgets.button import Button
|
from g13gui.bitwidgets.button import Button
|
||||||
from g13gui.bitwidgets.button import Glyphs
|
from g13gui.bitwidgets.button import Glyphs
|
||||||
|
from g13gui.bitwidgets.button import Glyph
|
||||||
from g13gui.bitwidgets.label import Label
|
from g13gui.bitwidgets.label import Label
|
||||||
from g13gui.bitwidgets.fonts import Fonts
|
from g13gui.bitwidgets.fonts import Fonts
|
||||||
|
|
||||||
@ -23,7 +24,18 @@ class ButtonTests(unittest.TestCase):
|
|||||||
self.dd.shutdown()
|
self.dd.shutdown()
|
||||||
self.dd.join()
|
self.dd.join()
|
||||||
|
|
||||||
|
def testGlyph(self):
|
||||||
|
self.dd.name = 'testGlyph'
|
||||||
|
ctx = self.display.getContext()
|
||||||
|
glyph = Glyph(10, 10, Glyphs.CHECKMARK)
|
||||||
|
glyph.fill = True
|
||||||
|
glyph.show()
|
||||||
|
glyph.draw(ctx)
|
||||||
|
|
||||||
|
self.display.commit()
|
||||||
|
|
||||||
def testExButton(self):
|
def testExButton(self):
|
||||||
|
self.dd.name = 'testExButton'
|
||||||
ctx = self.display.getContext()
|
ctx = self.display.getContext()
|
||||||
exButton = Button(Glyphs.XMARK)
|
exButton = Button(Glyphs.XMARK)
|
||||||
exButton.show()
|
exButton.show()
|
||||||
@ -44,6 +56,7 @@ class ButtonTests(unittest.TestCase):
|
|||||||
self.display.commit()
|
self.display.commit()
|
||||||
|
|
||||||
def testButtonBar(self):
|
def testButtonBar(self):
|
||||||
|
self.dd.name = 'testButtonBar'
|
||||||
exButton = Button(Glyphs.XMARK)
|
exButton = Button(Glyphs.XMARK)
|
||||||
upButton = Button(Glyphs.UP_ARROW)
|
upButton = Button(Glyphs.UP_ARROW)
|
||||||
downButton = Button(Glyphs.DOWN_ARROW)
|
downButton = Button(Glyphs.DOWN_ARROW)
|
||||||
@ -58,6 +71,7 @@ class ButtonTests(unittest.TestCase):
|
|||||||
self.screen.nextFrame()
|
self.screen.nextFrame()
|
||||||
|
|
||||||
def testLabelButton(self):
|
def testLabelButton(self):
|
||||||
|
self.dd.name = 'testLabelButton'
|
||||||
testButton = Label(0, 0, "Test", font=Fonts.TINY)
|
testButton = Label(0, 0, "Test", font=Fonts.TINY)
|
||||||
self.screen.buttonBar.addChild(testButton)
|
self.screen.buttonBar.addChild(testButton)
|
||||||
self.screen.buttonBar.showAll()
|
self.screen.buttonBar.showAll()
|
||||||
|
195
g13gui/g13gui/bitwidgets/listview.py
Normal file
195
g13gui/g13gui/bitwidgets/listview.py
Normal file
@ -0,0 +1,195 @@
|
|||||||
|
from g13gui.bitwidgets.widget import Widget
|
||||||
|
from g13gui.bitwidgets.button import ButtonBar
|
||||||
|
from g13gui.bitwidgets.button import Glyph
|
||||||
|
from g13gui.bitwidgets.button import Glyphs
|
||||||
|
from g13gui.bitwidgets.rectangle import Rectangle
|
||||||
|
from g13gui.bitwidgets.label import Label
|
||||||
|
from g13gui.bitwidgets.fonts import Fonts
|
||||||
|
from g13gui.bitwidgets.fonts import FontManager
|
||||||
|
from g13gui.bitwidgets import DISPLAY_WIDTH
|
||||||
|
from g13gui.bitwidgets import DISPLAY_HEIGHT
|
||||||
|
from g13gui.observer.subject import ChangeType
|
||||||
|
|
||||||
|
|
||||||
|
class ListView(Widget):
|
||||||
|
def __init__(self, model, markedIdx=None, font=Fonts.SMALL):
|
||||||
|
Widget.__init__(self)
|
||||||
|
self.model = model
|
||||||
|
self._font = font
|
||||||
|
|
||||||
|
self.position = (0, 0)
|
||||||
|
self.bounds = (DISPLAY_WIDTH, ButtonBar.TOP_LINE - 1)
|
||||||
|
|
||||||
|
self._markedIdx = markedIdx
|
||||||
|
self._selectionIdx = 0
|
||||||
|
self._visibilityOffset = 0
|
||||||
|
|
||||||
|
self._setup()
|
||||||
|
self.update()
|
||||||
|
|
||||||
|
@property
|
||||||
|
def model(self):
|
||||||
|
return self._model
|
||||||
|
|
||||||
|
@model.setter
|
||||||
|
def model(self, model):
|
||||||
|
self._model = sorted(list(model))
|
||||||
|
|
||||||
|
@property
|
||||||
|
def selectionIndex(self):
|
||||||
|
return self._selectionIdx
|
||||||
|
|
||||||
|
@selectionIndex.setter
|
||||||
|
def selectionIndex(self, value):
|
||||||
|
self._selectionIdx = value
|
||||||
|
|
||||||
|
def selection(self):
|
||||||
|
items = sorted(self._model)
|
||||||
|
return items[self._selectionIdx]
|
||||||
|
|
||||||
|
def markedItem(self):
|
||||||
|
items = sorted(self._model)
|
||||||
|
return items[self._markedIdx]
|
||||||
|
|
||||||
|
def nextSelection(self):
|
||||||
|
maxIdx = len(self._model) - 1
|
||||||
|
idx = self.selectionIndex
|
||||||
|
|
||||||
|
idx += 1
|
||||||
|
|
||||||
|
if idx > maxIdx:
|
||||||
|
idx = maxIdx
|
||||||
|
|
||||||
|
maxVisibleItem = self._visibilityOffset + self._numVisibleItems - 1
|
||||||
|
if idx > maxVisibleItem:
|
||||||
|
self._visibilityOffset += 1
|
||||||
|
|
||||||
|
self.selectionIndex = idx
|
||||||
|
self.update()
|
||||||
|
|
||||||
|
def prevSelection(self):
|
||||||
|
idx = self.selectionIndex
|
||||||
|
idx -= 1
|
||||||
|
if idx < 0:
|
||||||
|
idx = 0
|
||||||
|
|
||||||
|
if idx < self._visibilityOffset:
|
||||||
|
self._visibilityOffset -= 1
|
||||||
|
|
||||||
|
self.selectionIndex = idx
|
||||||
|
self.update()
|
||||||
|
|
||||||
|
def markSelection(self):
|
||||||
|
self._markedIdx = self.selectionIndex
|
||||||
|
|
||||||
|
@property
|
||||||
|
def markedIndex(self):
|
||||||
|
return self._markedIdx
|
||||||
|
|
||||||
|
@markedIndex.setter
|
||||||
|
def markedIndex(self, value):
|
||||||
|
self._markedIdx = value
|
||||||
|
|
||||||
|
def update(self):
|
||||||
|
items = sorted(self._model)
|
||||||
|
startIdx = self._visibilityOffset
|
||||||
|
endIdx = self._visibilityOffset + self._numVisibleItems
|
||||||
|
maxItemIdx = len(items) - 1
|
||||||
|
|
||||||
|
for idx in range(startIdx, endIdx):
|
||||||
|
name = ''
|
||||||
|
if idx <= maxItemIdx:
|
||||||
|
name = items[idx]
|
||||||
|
|
||||||
|
itemIdx = idx - startIdx
|
||||||
|
item = self._items[itemIdx]
|
||||||
|
item.text = name
|
||||||
|
item.isSelected = (idx == self._markedIdx)
|
||||||
|
item.isHighlighted = (idx == self.selectionIndex)
|
||||||
|
|
||||||
|
def _setup(self):
|
||||||
|
self._items = []
|
||||||
|
|
||||||
|
li = ListItem(0, 'Wqpj', font=self._font)
|
||||||
|
(_, self._liHeight) = li.bounds
|
||||||
|
self._numVisibleItems = self._bounds[1] // self._liHeight
|
||||||
|
|
||||||
|
for i in range(0, self._numVisibleItems):
|
||||||
|
y = int(self._liHeight * i)
|
||||||
|
li = ListItem(y, '', font=self._font)
|
||||||
|
li.show()
|
||||||
|
self.addChild(li)
|
||||||
|
self._items.append(li)
|
||||||
|
|
||||||
|
self._items[self.selectionIndex].isHighlighted = True
|
||||||
|
|
||||||
|
|
||||||
|
class ListItem(Widget):
|
||||||
|
def __init__(self, ypos, text,
|
||||||
|
is_selected=False,
|
||||||
|
is_highlighted=False,
|
||||||
|
font=Fonts.SMALL):
|
||||||
|
Widget.__init__(self)
|
||||||
|
|
||||||
|
self._text = text
|
||||||
|
self._isSelected = is_selected
|
||||||
|
self._isHighlighted = is_highlighted
|
||||||
|
|
||||||
|
self._font = font
|
||||||
|
(_, self._fontHeight) = FontManager.getFont(self._font).getsize('Wqpj')
|
||||||
|
|
||||||
|
self.position = (0, ypos)
|
||||||
|
self.bounds = (DISPLAY_WIDTH, self._fontHeight + 3)
|
||||||
|
|
||||||
|
self._setup()
|
||||||
|
|
||||||
|
@property
|
||||||
|
def text(self):
|
||||||
|
return self._text
|
||||||
|
|
||||||
|
@text.setter
|
||||||
|
def text(self, text):
|
||||||
|
self._text = text
|
||||||
|
self._label.text = text
|
||||||
|
|
||||||
|
@property
|
||||||
|
def isHighlighted(self):
|
||||||
|
return self._isHighlighted
|
||||||
|
|
||||||
|
@isHighlighted.setter
|
||||||
|
def isHighlighted(self, is_highlighted):
|
||||||
|
self._isHighlighted = is_highlighted
|
||||||
|
self._updateStates()
|
||||||
|
|
||||||
|
@property
|
||||||
|
def isSelected(self):
|
||||||
|
return self._isSelected
|
||||||
|
|
||||||
|
@isSelected.setter
|
||||||
|
def isSelected(self, is_selected):
|
||||||
|
self._isSelected = is_selected
|
||||||
|
self._updateStates()
|
||||||
|
|
||||||
|
def _updateStates(self):
|
||||||
|
self._rect.fill = self.isHighlighted
|
||||||
|
self._label.fill = not self.isHighlighted
|
||||||
|
|
||||||
|
self._indicator.fill = self.isHighlighted ^ self.isSelected
|
||||||
|
|
||||||
|
def _setup(self):
|
||||||
|
self._rect = Rectangle(*self.position, *self.bounds,
|
||||||
|
fill=self.isHighlighted)
|
||||||
|
self._rect.show()
|
||||||
|
self.addChild(self._rect)
|
||||||
|
|
||||||
|
self._indicator = Glyph(*self.position, Glyphs.CHECKMARK)
|
||||||
|
self._indicator.position = (2, (self.position[1] + self._fontHeight // 2) - 1)
|
||||||
|
self._indicator.show()
|
||||||
|
self.addChild(self._indicator)
|
||||||
|
|
||||||
|
self._label = Label(10, self.position[1] + 2, self._text,
|
||||||
|
fill=not self.isHighlighted, font=self._font)
|
||||||
|
self._label.show()
|
||||||
|
self.addChild(self._label)
|
||||||
|
|
||||||
|
self._updateStates()
|
41
g13gui/g13gui/bitwidgets/listview_tests.py
Normal file
41
g13gui/g13gui/bitwidgets/listview_tests.py
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
import unittest
|
||||||
|
import time
|
||||||
|
|
||||||
|
from g13gui.bitwidgets.display import Display
|
||||||
|
from g13gui.bitwidgets.x11displaydevice import X11DisplayDevice
|
||||||
|
from g13gui.bitwidgets.screen import Screen
|
||||||
|
from g13gui.bitwidgets.listview import ListView
|
||||||
|
|
||||||
|
|
||||||
|
class ListViewTests(unittest.TestCase):
|
||||||
|
def setUp(self):
|
||||||
|
self.dd = X11DisplayDevice(self.__class__.__name__)
|
||||||
|
self.dd.start()
|
||||||
|
time.sleep(0.25)
|
||||||
|
self.display = Display(self.dd)
|
||||||
|
self.screen = Screen(self.display)
|
||||||
|
|
||||||
|
def tearDown(self):
|
||||||
|
time.sleep(1)
|
||||||
|
self.dd.shutdown()
|
||||||
|
self.dd.join()
|
||||||
|
|
||||||
|
def testEmptyList(self):
|
||||||
|
lv = ListView([])
|
||||||
|
lv.show()
|
||||||
|
self.screen.addChild(lv)
|
||||||
|
self.screen.nextFrame()
|
||||||
|
|
||||||
|
def testFullLists(self):
|
||||||
|
lv = ListView([
|
||||||
|
'One',
|
||||||
|
'Two',
|
||||||
|
'Three'
|
||||||
|
])
|
||||||
|
lv.show()
|
||||||
|
self.screen.addChild(lv)
|
||||||
|
self.screen.nextFrame()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
unittest.main()
|
@ -2,11 +2,10 @@ from g13gui.bitwidgets.widget import Widget
|
|||||||
|
|
||||||
|
|
||||||
class Rectangle(Widget):
|
class Rectangle(Widget):
|
||||||
def __init__(self, x, y, w, h, radius=0, fill=True):
|
def __init__(self, x, y, w, h, fill=True):
|
||||||
Widget.__init__(self)
|
Widget.__init__(self)
|
||||||
self.position = (x, y)
|
self.position = (x, y)
|
||||||
self.bounds = (w, h)
|
self.bounds = (w, h)
|
||||||
self.radius = radius
|
|
||||||
self.fill = fill
|
self.fill = fill
|
||||||
|
|
||||||
def draw(self, ctx):
|
def draw(self, ctx):
|
||||||
@ -14,14 +13,5 @@ class Rectangle(Widget):
|
|||||||
points = (self._position[0], self._position[1],
|
points = (self._position[0], self._position[1],
|
||||||
self._position[0] + self._bounds[0],
|
self._position[0] + self._bounds[0],
|
||||||
self._position[1] + self._bounds[1])
|
self._position[1] + self._bounds[1])
|
||||||
ctx.rounded_rectangle(*points,
|
ctx.rectangle(points,
|
||||||
radius=self._radius,
|
|
||||||
fill=self._fill)
|
fill=self._fill)
|
||||||
|
|
||||||
@property
|
|
||||||
def radius(self):
|
|
||||||
return self._radius
|
|
||||||
|
|
||||||
@radius.setter
|
|
||||||
def radius(self, radius):
|
|
||||||
self._setProperty('radius', radius)
|
|
||||||
|
36
g13gui/g13gui/bitwidgets/rectangle_tests.py
Normal file
36
g13gui/g13gui/bitwidgets/rectangle_tests.py
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
import unittest
|
||||||
|
import time
|
||||||
|
|
||||||
|
from g13gui.bitwidgets import DISPLAY_WIDTH
|
||||||
|
from g13gui.bitwidgets import DISPLAY_HEIGHT
|
||||||
|
from g13gui.bitwidgets.display import Display
|
||||||
|
from g13gui.bitwidgets.x11displaydevice import X11DisplayDevice
|
||||||
|
from g13gui.bitwidgets.screen import Screen
|
||||||
|
from g13gui.bitwidgets.rectangle import Rectangle
|
||||||
|
|
||||||
|
|
||||||
|
class RectangleTests(unittest.TestCase):
|
||||||
|
def setUp(self):
|
||||||
|
self.dd = X11DisplayDevice(self.__class__.__name__)
|
||||||
|
self.dd.start()
|
||||||
|
time.sleep(0.25)
|
||||||
|
self.display = Display(self.dd)
|
||||||
|
self.screen = Screen(self.display)
|
||||||
|
|
||||||
|
def tearDown(self):
|
||||||
|
time.sleep(1)
|
||||||
|
self.dd.shutdown()
|
||||||
|
self.dd.join()
|
||||||
|
|
||||||
|
def testRect(self):
|
||||||
|
self.dd.name = 'testRect'
|
||||||
|
ctx = self.display.getContext()
|
||||||
|
rect = Rectangle(0, 0, DISPLAY_WIDTH, DISPLAY_HEIGHT - 1, fill=True)
|
||||||
|
rect.show()
|
||||||
|
rect.draw(ctx)
|
||||||
|
|
||||||
|
self.display.commit()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
unittest.main()
|
@ -11,12 +11,12 @@ class Widget(Subject, Observer):
|
|||||||
Observer.__init__(self)
|
Observer.__init__(self)
|
||||||
|
|
||||||
self._children = []
|
self._children = []
|
||||||
self.parent = None
|
self._parent = None
|
||||||
self.visible = False
|
self._visible = False
|
||||||
self.valid = False
|
self._valid = False
|
||||||
self.position = (0, 0)
|
self._position = (0, 0)
|
||||||
self.bounds = (0, 0)
|
self._bounds = (0, 0)
|
||||||
self.fill = False
|
self._fill = 0
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def position(self):
|
def position(self):
|
||||||
@ -28,7 +28,8 @@ class Widget(Subject, Observer):
|
|||||||
len(xy) != 2 or \
|
len(xy) != 2 or \
|
||||||
type(xy[0]) != int or \
|
type(xy[0]) != int or \
|
||||||
type(xy[1]) != int:
|
type(xy[1]) != int:
|
||||||
raise ValueError('Position must be a tuple of length 2')
|
raise ValueError('Position must be a tuple of length 2 (got %s)'
|
||||||
|
% (repr(xy)))
|
||||||
self.setProperty('position', xy)
|
self.setProperty('position', xy)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@ -41,7 +42,8 @@ class Widget(Subject, Observer):
|
|||||||
len(wh) != 2 or \
|
len(wh) != 2 or \
|
||||||
type(wh[0]) != int or \
|
type(wh[0]) != int or \
|
||||||
type(wh[1]) != int:
|
type(wh[1]) != int:
|
||||||
raise ValueError('Position must be a tuple of length 2')
|
raise ValueError('Bounds must be a tuple of length 2 (got: %s)'
|
||||||
|
% (repr(wh)))
|
||||||
self.setProperty('bounds', wh)
|
self.setProperty('bounds', wh)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
|
@ -14,6 +14,21 @@ class X11DisplayDevice(DisplayDevice, threading.Thread):
|
|||||||
self._running = False
|
self._running = False
|
||||||
self._name = name
|
self._name = name
|
||||||
|
|
||||||
|
@property
|
||||||
|
def name(self):
|
||||||
|
return self._name
|
||||||
|
|
||||||
|
@name.setter
|
||||||
|
def name(self, name):
|
||||||
|
self._name = name
|
||||||
|
if self._win:
|
||||||
|
self._setName()
|
||||||
|
|
||||||
|
def _setName(self):
|
||||||
|
self._win.set_wm_name(self._name)
|
||||||
|
self._win.set_wm_icon_name(self._name)
|
||||||
|
self._win.set_wm_class('bitwidgets', self._name)
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
self._display = display.Display()
|
self._display = display.Display()
|
||||||
self.createWindow()
|
self.createWindow()
|
||||||
@ -55,9 +70,8 @@ class X11DisplayDevice(DisplayDevice, threading.Thread):
|
|||||||
foreground=self._screen.black_pixel,
|
foreground=self._screen.black_pixel,
|
||||||
background=self._screen.white_pixel)
|
background=self._screen.white_pixel)
|
||||||
|
|
||||||
self._win.set_wm_name(self._name)
|
self._setName()
|
||||||
self._win.set_wm_icon_name(self._name)
|
|
||||||
self._win.set_wm_class('bitwidgets', self._name)
|
|
||||||
self._win.set_wm_normal_hints(
|
self._win.set_wm_normal_hints(
|
||||||
flags=(Xutil.PPosition | Xutil.PSize | Xutil.PMinSize),
|
flags=(Xutil.PPosition | Xutil.PSize | Xutil.PMinSize),
|
||||||
min_width=160,
|
min_width=160,
|
||||||
|
Loading…
Reference in New Issue
Block a user