python non-specific argument - python

I have the code
class Button(object):
'''A simple Button class to represent a UI Button element'''
def __init__(self, text = "button"):
'''Create a Button and assign it a label'''
self.label = text
def press(self):
'''Simply print that the button was pressed'''
print("{0} was pressed".format(self.label))
class ToggleButton(Button):
def __init__(self, text, state=True):
super(ToggleButton, self).__init__(text)
self.state = state
def press(self):
super(ToggleButton, self).press()
self.state = not self.state
print('{0} is now'.format(self.label), 'ON' if self.state else 'OFF')
When I input
tb = ToggleButton("Test", False)
tb.press()
tb.press()
it works fine and returns
Test was pressed
Test is now ON
Test was pressed
Test is now OFF
but what I want is to have the text parameter optional, so that if I input
b = ToggleButton()
b.press()
it will return
ToggleButton was pressed
ToggleButton is now OFF
any help would be much appreciated!

Follow the example of the state parameter and give text a default value as well.
class ToggleButton(Button):
def __init__(self, text="ToggleButton", state=True):
super(ToggleButton, self).__init__(text)
self.state = state

Consider something adaptive such as this:
class ToggleButton(Button):
def __init__(self, *args, **kwargs):
super(ToggleButton, self).__init__(*args)
self.state = kwargs.get('state', True)

Related

kivy custom widget how to access attributes set using kvlang

I am trying to implement an html style SelectBox widget in kivy using the kivy.uix.DropDown.
In the actual application I want to use it like this using kvlang:
SelectBox:
id: appeui
label: "Select an application"
SelectOption:
label: 'Option 1'
value: 1
SelectOption:
label: 'Option 2'
value: 2
SelectOption:
label: 'Option 3'
value: 3
To implement this widget, I have defined the SelectBox and SelectOption below. In the constructor of the SelectBox I am checking properties on the SelectBox, that I am expecting to be set in kvlang. One is the label which is then used as the label for the button. And I am also checking the children of the SelectBox and moving all of them (of type SelecOption) to the DropDown. The problem I am encountering is that in the constructor of SelectBox, there is no label argument, and no children yet either.
I think the SelectBox instantiation already happens before the kvlang stuff is read. So during instantiation the attributes defined in kvlang for SelectBox and also its children aren't known yet.
Is there any other function that gets called after the widget is actually built by kvlang? Or any other way I can actually act upon the way the widget was defined in kvlang?
from kivy.graphics import Color, Rectangle
from kivy.uix.button import Button
from kivy.uix.dropdown import DropDown
from kivy.uix.label import Label
from kivy.uix.widget import Widget
class SelectOption(Label):
def __init__(self,
*args,
label='option',
value=None,
background_color=Color(rgba=(1, 1, 1, 1)),
**kwargs):
super().__init__(*args, **kwargs)
self.label = label
self.value = value
self.background_color = background_color
self.bind(on_touch_down=self.select)
with self.canvas.before:
Color(self.background_color)
Rectangle(pos=self.pos, size=self.size)
def select(self, *args):
self.parent.select(self.value)
class SelectBox(Widget):
def __init__(self, label='Select an option', *args, **kwargs):
super().__init__(*args, **kwargs)
self.dropdown = DropDown()
self.value = None
self.button = Button(text=label)
self.add_widget(self.button)
self.button.id = 'dropdown_label'
self.button.size_hint = (None, None)
self.button.height = '32dp'
self.button.bind(on_release=self.openDropDown)
self.dropdown.bind(on_select=self.set_value)
for child in self.children:
if isinstance(child, SelectOption):
self.dropdown.add_widget(child)
self.remove_widget(child)
def openDropDown(self, *args):
self.dropdown.open(self.button)
def set_value(self, instance, value):
self.ids.dropdown_label.text = value
Ok, I am getting somewhere. The issue here is to just not use the constructor, but use events instead. You can define properties on the SelectBox and use events to intercept changes in that property. When the property is defined in kvlang, this will trigger the event. So in my case I could do this:
class SelectBox(StackLayout, EventDispatcher):
label = StringProperty('Select an option')
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.dropdown = DropDown()
self.value = None
self.button = Button(text=self.label)
self.button.id = 'dropdown_label'
self.add_widget(self.button)
self.button.bind(on_release=self.openDropDown)
self.dropdown.bind(on_select=self.set_value)
def on_label(self, instance, value):
print("Label changed")
self.button.text = value
def add_widget(self, child):
print(f"Adding widget {child}")
if isinstance(child, SelectOption):
self.dropdown.add_widget(child)
else:
super().add_widget(child)
def openDropDown(self, *args):
print("Opening dropdown")
self.dropdown.open(self.button)
def set_value(self, instance, value):
self.ids.dropdown_label.text = value
The on_label event gets called as soon as the label property is set in kvlang, and then I just change the button text.
For the child widgets, I am doing something similar by intercepting add_widget. If its a SelectOption, then add it to the dropdown instead of adding it to the SelectBox.
There are still issues with this code, but they are not directly related to this question.

how can I start again when pressing image?

How can I start again when pressing image?
I want it to start again when I press the last images or 2/3 second later
class MyButton(ButtonBehavior, Image):
def __init__(self, **kwargs):
super(MyButton, self).__init__(**kwargs)
self.source = 's'
def on_press(self):
a = (random.randint(1, 1014124315124124))
if (int(a) % 2 == 0):
self.source = 's'
else:
self.source = 'd'
class SampleApp(App):
def build(self):
return MyButton()
SampleApp().run()
Look into kivy's Clock.schedule_once. The basic idea is, when the button is pressed, you will set it to a new image, and schedule another function (let's call it resetImage) to run in 2/3 seconds. Then when resetImage is called, it sets the button's image back to normal.
You'd want something like Clock.schedule_once(resetImage, 0.66).

How to only do add_widget once in Kivy

I have a dynamic Screen which is generated based on a button you clicked on another screen. Issue is dat every time I enter the Screen, the buttons are regenerated and added to the existing buttons.
The reason is that I use the on_enter method, but I don't know how I can use on_kv_post for example, as these events happen on starting the app.
How can I initialise the screen every time I return to this screen?
class ClientEnvsGrid(Screen):
envProp = StringProperty('')
def __init__(self, **kwargs):
super(ClientEnvsGrid, self).__init__(**kwargs)
def on_enter(self, *args):
clientProp = self.manager.get_screen('clientlist').clientProp
try:
client_filepath = os.path.join('clients', clientProp, "environments.json")
client_file = open(client_filepath)
clientdata = json.loads(client_file.read())
print(clientdata)
self.ids.clientlabel.text = clientdata["clientname"]
for envs in clientdata["environments"]:
print(envs["name"])
envbutton = Button(text=envs["name"])
envbutton.bind(on_press=lambda *args: self.pressed('envbtn', *args))
self.ids.environments.add_widget(envbutton)
except:
print("No client data found")
self.manager.current = 'clientlist'
def pressed(self, instance, *args):
self.envProp = args[0].text
I've managed to fix it to include clear_widgets in the environments GridLayout in the on_leave event.
def on_leave(self, *args):
self.ids.environments.clear_widgets()

Python kivy shows only last label which generated in loop

last day i came across on very irritate behave my program.
Mabye at first i show screenshot, describe error, and finnaly shows code.
As you can see, Coin is a button which contains a few sub-buttons. I generate these widgets in loop and dynamically add to layer.
But it works correctly only in last iterating.
Please look now at code.
Here is a code "Coin Button: class.
Note this, that for example: button apx, Refresh, H, DH and D is a member of one class
class SubButtonRefreshCoinData(Button):
def __init__(self, coin_name, **kwargs):
super(SubButtonRefreshCoinData, self).__init__(**kwargs)
self.CoinName = coin_name
self.text = "Refresh"
def on_press(self):
PopupNotImplementedItem().open()
class SubButtonCoinName(Button):
def __init__(self, **kwargs):
super(SubButtonCoinName, self).__init__(**kwargs)
self.text = r'[color=ff3333]{}[/color]'.format(kwargs["text"])
self.markup = True
self.font_size='20sp'
class SubButtonGoToCoinHistory(Button):
def __init__(self, coin_name, **kwargs):
super(SubButtonGoToCoinHistory, self).__init__(**kwargs)
self.CoinName = coin_name
self.text = "H"
def on_press(self):
subprocess.Popen(f'py HistoryWindow.py {self.CoinName}', shell=True)
class SubButtonDeleteCoin(Button):
def __init__(self, coin_name, **kwargs):
super(SubButtonDeleteCoin, self).__init__(**kwargs)
self.CoinName = coin_name
self.text = "D"
def on_press(self):
PopupNotImplementedItem().open()
class SubButtonDeleteCoinHistory(Button):
def __init__(self, coin_name, **kwargs):
super(SubButtonDeleteCoinHistory, self).__init__(**kwargs)
self.CoinName = coin_name
self.text = "DH"
print("sdfecvsdcdfwreafsq3456tedcqr4536wtger34r5cedwt4g5aced erf34")
def on_press(self):
PopupNotImplementedItem().open()
Now, please take a look on the Builder class these pieces:
class CoinButton(FloatLayout):
def __init__(self, coin_name, **kwargs):
super(CoinButton, self).__init__(**kwargs)
self.CoinName = coin_name
topHalfLayout = BoxLayout(pos_hint={"top":1}, size_hint = [1,0.49], orientation = "horizontal")
topHalfLayout.add_widget(SubButtonCoinName(text=str(self.CoinName)))
topHalfLayout.add_widget(SubButtonRefreshCoinData(self.CoinName))
self.add_widget(topHalfLayout)
downHalfLayout = BoxLayout(pos_hint={"down":1}, size_hint = [1,0.49], orientation = "horizontal")
downHalfLayout.add_widget(SubButtonGoToCoinHistory(self.CoinName))
downHalfLayout.add_widget(SubButtonDeleteCoinHistory(self.CoinName))
downHalfLayout.add_widget(SubButtonDeleteCoin(self.CoinName))
self.add_widget(downHalfLayout)
As you can see, everything seems be correct, but only ONE picece of class is visible.
In class SubButtonDeleteCoinHistory i tired primitive debug this problem to see did this code is already run. As i saw in console, text was printed 3 times, that is correct value.
I am not really sure what is happening here, but I take a shot. I believe that every time a CoinButton is initialized, the bottom button were added with respect to the FloatLayout which and using the pos_hint: down push the button off the kivy app window. But for the life of me, when changing the down value to various ints and floats, there was no change. The best approach was to use pos_hint 'y' since we know that the height of the button is 0.49 the top can start at 0.5 upto 0.99 and the bottom can start at 0 upto 0.49.
topHalfLayout = BoxLayout(pos_hint={"y":0.5},size_hint = [1,0.49], orientation = "horizontal")
......
downHalfLayout = BoxLayout(pos_hint={"y":0},size_hint = [1,0.49], orientation = "horizontal")
Another better approach that I used was to create another BoxLayout which holds the top and bottom layouts, allowing the FloatLayout to add the widget as one thing. It provides better spacing in my opinion. 'outside[ [top/bottom] ]outside'
outside = BoxLayout(pos_hint={"top":1},size_hint = [1,2*0.49], orientation = "vertical")
......
topHalfLayout = BoxLayout(size_hint = [1,0.49], orientation = "horizontal")
......
outside.add_widget(topHalfLayout)
......
downHalfLayout = BoxLayout(size_hint = [1,0.49], orientation = "horizontal")
......
outside.add_widget(downHalfLayout)
self.add_widget(outside)

Python - wxPython custom button -> unbound method __init__()? what?

After looking at questions like this it doesn't make sense that my __init__(self, parrent, id) would be throwing a unbound error? help?
main.py
import wx
from customButton import customButton
from wxPython.wx import *
class MyFrame(wx.Frame):
def __init__(self, parent, ID, title):
wxFrame.__init__(self, parent, ID, title,
wxDefaultPosition, wxSize(400, 400))
# Non-important code here...
# This is the first declaration of the Button1
# This is also where the ERROR is thrown.
# Omitting this line causes the window to execute
# flawlessly.
self.Button1 = customButton.__init__(self, parent, -1)
# ... finishes in a basic wx.program style...
customButton.py
# I've included all of the code in the file
# because have no idea where the bug/error happens
import wx
from wxPython.wx import *
class Custom_Button(wx.PyControl):
# The BMP's
Over_bmp = None #wxEmptyBitmap(1,1,1) # When the mouse is over
Norm_bmp = None #wxEmptyBitmap(1,1,1) # The normal BMP
Push_bmp = None #wxEmptyBitmap(1,1,1) # The down BMP
def __init__(self, parent, id, **kwargs):
wx.PyControl.__init__(self,parent, id, **kwargs)
# Set the BMP's to the ones given in the constructor
#self.Over_bmp = wx.Bitmap(wx.Image(MOUSE_OVER_BMP, wx.BITMAP_TYPE_ANY).ConvertToBitmap())
#self.Norm_bmp = wx.Bitmap(wx.Image(NORM_BMP, wx.BITMAP_TYPE_ANY).ConvertToBitmap())
#self.Push_bmp = wx.Bitmap(wx.Image(PUSH_BMP, wx.BITMAP_TYPE_ANY).ConvertToBitmap())
#self.Pos_bmp = self.pos
self.Bind(wx.EVT_LEFT_DOWN, self._onMouseDown)
self.Bind(wx.EVT_LEFT_UP, self._onMouseUp)
self.Bind(wx.EVT_LEAVE_WINDOW, self._onMouseLeave)
self.Bind(wx.EVT_ENTER_WINDOW, self._onMouseEnter)
self.Bind(wx.EVT_ERASE_BACKGROUND,self._onEraseBackground)
self.Bind(wx.EVT_PAINT,self._onPaint)
self._mouseIn = self._mouseDown = False
def _onMouseEnter(self, event):
self._mouseIn = True
def _onMouseLeave(self, event):
self._mouseIn = False
def _onMouseDown(self, event):
self._mouseDown = True
def _onMouseUp(self, event):
self._mouseDown = False
self.sendButtonEvent()
def sendButtonEvent(self):
event = wx.CommandEvent(wx.wxEVT_COMMAND_BUTTON_CLICKED, self.GetId())
event.SetInt(0)
event.SetEventObject(self)
self.GetEventHandler().ProcessEvent(event)
def _onEraseBackground(self,event):
# reduce flicker
pass
def _onPaint(self, event):
dc = wx.BufferedPaintDC(self)
dc.SetFont(self.GetFont())
dc.SetBackground(wx.Brush(self.GetBackgroundColour()))
dc.Clear()
dc.DrawBitmap(self.Norm_bmp)
# draw whatever you want to draw
# draw glossy bitmaps e.g. dc.DrawBitmap
if self._mouseIn: # If the Mouse is over the button
dc.DrawBitmap(self, self.Mouse_over_bmp, self.Pos_bmp, useMask=False)
if self._mouseDown: # If the Mouse clicks the button
dc.DrawBitmap(self, self.Push_bmp, self.Pos_bmp, useMask=False)
You don't create an object like this:
self.Button1 = customButton.__init__(self, parent, -1)
you do it like this:
self.Button1 = customButton(parent, -1)
__init__ is an implicitly invoked method during object creation.
Don't call __init__() explicitly unless you know you need to.
self.Button1 = customButton(parent, -1)

Categories

Resources