mirror of
https://github.com/jtgans/g13gui.git
synced 2025-06-20 00:14:09 -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_HEIGHT
|
||||
from g13gui.bitwidgets.widget import Widget
|
||||
from g13gui.bitwidgets.rectangle import Rectangle
|
||||
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)]
|
||||
CHECKMARK = [(0, 3), (1, 4), (4, 1), (1, 4)]
|
||||
XMARK = [(0, 0), (4, 4), (2, 2), (4, 0), (0, 4)]
|
||||
BLANK = []
|
||||
|
||||
BOUNDS = (5, 5)
|
||||
|
||||
def transformTo(self, offsetx, offsety):
|
||||
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.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):
|
||||
return self._children[buttonNum]
|
||||
|
||||
@ -39,8 +54,8 @@ class ButtonBar(Widget):
|
||||
self.removeChild(self._children[buttonNum])
|
||||
|
||||
self._children[buttonNum] = button
|
||||
position = self._positionForButton(buttonNum)
|
||||
button.position = position
|
||||
button.position = self._positionForSlot(buttonNum)
|
||||
button.bounds = self._buttonBounds()
|
||||
button.parent = self
|
||||
|
||||
self.addChange(ChangeType.ADD, 'child', button)
|
||||
@ -61,24 +76,6 @@ class ButtonBar(Widget):
|
||||
self.addChange(ChangeType.REMOVE, 'child', button)
|
||||
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):
|
||||
if self.visible:
|
||||
for child in self._children:
|
||||
@ -92,23 +89,84 @@ class ButtonBar(Widget):
|
||||
|
||||
# Dividing lines
|
||||
for slot in range(0, ButtonBar.MAX_BUTTONS):
|
||||
position = list(self._positionForSlot(slot))
|
||||
position[0] -= 2
|
||||
ctx.line(tuple(position) + (position[0], DISPLAY_HEIGHT),
|
||||
(x, y) = self._positionForSlot(slot)
|
||||
x -= 1
|
||||
ctx.line((x, y) + (x, DISPLAY_HEIGHT),
|
||||
fill=1)
|
||||
|
||||
|
||||
class Button(Widget):
|
||||
def __init__(self, glyph, fill=True):
|
||||
class Glyph(Widget):
|
||||
def __init__(self, x, y, glyph=Glyphs.BLANK, fill=True):
|
||||
Widget.__init__(self)
|
||||
self.glyph = glyph
|
||||
self.position = (x, y)
|
||||
self.fill = fill
|
||||
self.bounds = (5, 5)
|
||||
|
||||
def draw(self, ctx):
|
||||
if self._visible:
|
||||
xformedGlyph = self._glyph.transformTo(*self._position)
|
||||
ctx.line(xformedGlyph, fill=self.fill)
|
||||
|
||||
@property
|
||||
def glyph(self):
|
||||
return self._glyph
|
||||
|
||||
@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
|
||||
def glyph(self):
|
||||
|
@ -6,6 +6,7 @@ from g13gui.bitwidgets.x11displaydevice import X11DisplayDevice
|
||||
from g13gui.bitwidgets.screen import Screen
|
||||
from g13gui.bitwidgets.button import Button
|
||||
from g13gui.bitwidgets.button import Glyphs
|
||||
from g13gui.bitwidgets.button import Glyph
|
||||
from g13gui.bitwidgets.label import Label
|
||||
from g13gui.bitwidgets.fonts import Fonts
|
||||
|
||||
@ -23,7 +24,18 @@ class ButtonTests(unittest.TestCase):
|
||||
self.dd.shutdown()
|
||||
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):
|
||||
self.dd.name = 'testExButton'
|
||||
ctx = self.display.getContext()
|
||||
exButton = Button(Glyphs.XMARK)
|
||||
exButton.show()
|
||||
@ -44,6 +56,7 @@ class ButtonTests(unittest.TestCase):
|
||||
self.display.commit()
|
||||
|
||||
def testButtonBar(self):
|
||||
self.dd.name = 'testButtonBar'
|
||||
exButton = Button(Glyphs.XMARK)
|
||||
upButton = Button(Glyphs.UP_ARROW)
|
||||
downButton = Button(Glyphs.DOWN_ARROW)
|
||||
@ -58,6 +71,7 @@ class ButtonTests(unittest.TestCase):
|
||||
self.screen.nextFrame()
|
||||
|
||||
def testLabelButton(self):
|
||||
self.dd.name = 'testLabelButton'
|
||||
testButton = Label(0, 0, "Test", font=Fonts.TINY)
|
||||
self.screen.buttonBar.addChild(testButton)
|
||||
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):
|
||||
def __init__(self, x, y, w, h, radius=0, fill=True):
|
||||
def __init__(self, x, y, w, h, fill=True):
|
||||
Widget.__init__(self)
|
||||
self.position = (x, y)
|
||||
self.bounds = (w, h)
|
||||
self.radius = radius
|
||||
self.fill = fill
|
||||
|
||||
def draw(self, ctx):
|
||||
@ -14,14 +13,5 @@ class Rectangle(Widget):
|
||||
points = (self._position[0], self._position[1],
|
||||
self._position[0] + self._bounds[0],
|
||||
self._position[1] + self._bounds[1])
|
||||
ctx.rounded_rectangle(*points,
|
||||
radius=self._radius,
|
||||
ctx.rectangle(points,
|
||||
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)
|
||||
|
||||
self._children = []
|
||||
self.parent = None
|
||||
self.visible = False
|
||||
self.valid = False
|
||||
self.position = (0, 0)
|
||||
self.bounds = (0, 0)
|
||||
self.fill = False
|
||||
self._parent = None
|
||||
self._visible = False
|
||||
self._valid = False
|
||||
self._position = (0, 0)
|
||||
self._bounds = (0, 0)
|
||||
self._fill = 0
|
||||
|
||||
@property
|
||||
def position(self):
|
||||
@ -28,7 +28,8 @@ class Widget(Subject, Observer):
|
||||
len(xy) != 2 or \
|
||||
type(xy[0]) != int or \
|
||||
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)
|
||||
|
||||
@property
|
||||
@ -41,7 +42,8 @@ class Widget(Subject, Observer):
|
||||
len(wh) != 2 or \
|
||||
type(wh[0]) != int or \
|
||||
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)
|
||||
|
||||
@property
|
||||
|
@ -14,6 +14,21 @@ class X11DisplayDevice(DisplayDevice, threading.Thread):
|
||||
self._running = False
|
||||
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):
|
||||
self._display = display.Display()
|
||||
self.createWindow()
|
||||
@ -55,9 +70,8 @@ class X11DisplayDevice(DisplayDevice, threading.Thread):
|
||||
foreground=self._screen.black_pixel,
|
||||
background=self._screen.white_pixel)
|
||||
|
||||
self._win.set_wm_name(self._name)
|
||||
self._win.set_wm_icon_name(self._name)
|
||||
self._win.set_wm_class('bitwidgets', self._name)
|
||||
self._setName()
|
||||
|
||||
self._win.set_wm_normal_hints(
|
||||
flags=(Xutil.PPosition | Xutil.PSize | Xutil.PMinSize),
|
||||
min_width=160,
|
||||
|
Loading…
Reference in New Issue
Block a user