How to change Bokeh button label when clicked? - bokeh

For example, I have
button = Button(label="0", type="success")
When this button is selected, I would like to change the label to "1", and vice versa. Is there a simple way to achieve this?
edit: RadioButtonGroup seems to be the widget I need. It doesn't seem to have a title attribute though. How can I position text next to the widget?

To change the label on a Button use a callback with the .on_click:
b = Button(label='0')
def changeLabel(button):
if button.label == '0':
button.label = '1'
else:
button.label = '0'
b.on_click(lambda : changeLabel(b))
Instead of using the lambda function you could use global b inside of changeLabel:
b = Button(label='0')
def changeLabel():
if b.label == '0':
b.label = '1'
else:
b.label = '0'
b.on_click(changeLabel)
The second one is easier to understand, but I prefer the first version. They do the same, in the end.

Related

Directly show the menu when its action is added to a QToolBar

I have a menu that I want to add to a QToolBar.
I know that I can add the menuAction() of the menu to the tool bar, but while that will properly show the "menu hint" on its side and popup the menu by clicking on it, clicking the main area of the button will have no effect.
That action is not supposed to have any result when triggered: the menu is used to set the font color in a text editor, and since it automatically updates its icon based on the current color, making it checkable (to set/unset the font color) is ineffective.
What I want is that the menu will be shown, no matter where the user clicks.
I know that I can add the action, then use widgetForAction() to get the actual QToolButton, and then change its popupMode, but since I know that I will have more situations like this, I was looking for a better approach.
This answer suggests to use QPushButton instead, and add that button to the toolbar, but that solution is not ideal: QPushButton is styled slightly differently from the default QToolButton, and, as the documentation suggests, even if I use a QToolButton it will not respect the ToolButtonStyle.
Here is a basic MRE of my current code. Please consider that the ColorMenu class is intended to be extended for other features (background text, colors for table borders and backgrounds, etc) by using subclasses:
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
class ColorMenu(QMenu):
def __init__(self, parent=None):
super().__init__(parent)
self.setTitle('Text color')
self.group = QActionGroup(self)
iconSize = self.style().pixelMetric(QStyle.PM_LargeIconSize)
pm = QPixmap(iconSize, iconSize)
pm.fill(self.palette().text().color())
self.defaultAction = self.addAction(QIcon(pm), 'Default color')
self.defaultAction.setCheckable(True)
self.group.addAction(self.defaultAction)
self.addSeparator()
self.customColorAction = self.addAction('Custom color')
self.customColorAction.setVisible(False)
self.customColorAction.setCheckable(True)
self.group.addAction(self.customColorAction)
self.addSeparator()
self.baseColorActions = []
colors = {}
# get valid global colors
for key, value in Qt.__dict__.items():
if (
isinstance(value, Qt.GlobalColor)
and 1 < value < 19
):
# make names more readable
if key.startswith('light'):
key = 'light {}'.format(key[5:].lower())
elif key.startswith('dark'):
key = 'dark {}'.format(key[4:].lower())
colors[value] = key.capitalize()
# more logical sorting of global colors
for i in (2, 4, 5, 6, 3, 7, 13, 8, 14, 9, 15, 10, 16, 11, 17, 12, 18):
color = QColor(Qt.GlobalColor(i))
pm = QPixmap(iconSize, iconSize)
pm.fill(color)
action = self.addAction(QIcon(pm), colors[i])
action.setData(color)
action.setCheckable(True)
self.group.addAction(action)
self.baseColorActions.append(action)
self.setColor(None)
def setColor(self, color):
if isinstance(color, QBrush) and color.style():
color = color.color()
elif isinstance(color, (Qt.GlobalColor, int):
color = QColor(color)
if instance(color, QColor) and color.isValid():
for action in self.baseColorActions:
if action.data() == color:
self.setIcon(action.icon())
action.setChecked(True)
self.customColorAction.setVisible(False)
break
else:
iconSize = self.style().pixelMetric(QStyle.PM_LargeIconSize)
pm = QPixmap(iconSize, iconSize)
pm.fill(color)
icon = QIcon(pm)
self.setIcon(icon)
self.customColorAction.setIcon(icon)
self.customColorAction.setData(color)
self.customColorAction.setVisible(True)
self.customColorAction.setChecked(True)
return
self.setIcon(self.defaultAction.icon())
self.defaultAction.setChecked(True)
self.customColorAction.setVisible(False)
class Editor(QMainWindow):
def __init__(self):
super().__init__()
self.editor = QTextEdit()
self.setCentralWidget(self.editor)
self.formatMenu = self.menuBar().addMenu('Format')
self.colorMenu = ColorMenu(self)
self.formatMenu.addMenu(self.colorMenu)
self.toolbar = QToolBar('Format')
self.addToolBar(Qt.TopToolBarArea, self.toolbar)
self.toolbar.addAction(self.colorMenu.menuAction())
self.editor.currentCharFormatChanged.connect(self.updateColorMenu)
self.colorMenu.triggered.connect(self.setTextColor)
def setTextColor(self, action):
# assume that the action.data() has a color value, if not, revert to the default
if action.data():
self.editor.setTextColor(action.data())
else:
tc = self.editor.textCursor()
fmt = tc.charFormat()
fmt.clearForeground()
tc.setCharFormat(fmt)
def updateColorMenu(self, fmt):
self.colorMenu.setColor(fmt.foreground())
app = QApplication([])
editor = Editor()
editor.show()
app.exec()
A possibility is to use a subclass of QMenu and implement a specialized function that will return a dedicated action.
This is a bit of a hack/workaround, but may be effective in some situations.
That new action will:
be created by providing the tool bar, which will become its parent (to ensure proper deletion if the tool bar is destroyed);
forcibly show the menu when triggered;
update itself (title and icon) whenever the menu action is changed;
class ColorMenu(QMenu):
# ...
def toolBarAction(self, toolbar):
def triggerMenu():
try:
button = toolbar.widgetForAction(action)
if isinstance(button, QToolButton):
button.showMenu()
else:
print('Warning: action triggered from somewhere else')
except (TypeError, RuntimeError):
# the action has been destroyed
pass
def updateAction():
try:
action.setIcon(self.icon())
action.setText(self.title())
except (TypeError, RuntimeError):
# the action has been destroyed
pass
action = QAction(self.icon(), self.title(), toolbar)
action.triggered.connect(triggerMenu)
self.menuAction().changed.connect(updateAction)
action.setMenu(self)
return action
class Editor(QMainWindow):
def __init__(self):
# ...
# replace the related line with the following
self.toolbar.addAction(self.colorMenu.toolBarAction(self.toolbar))

D365 New Button creates price line with empty line

After adding logic about creating price for object in grid it's always created "one line more" which is empty.
So, if there is need to be created two lines, it will be created 3 lines and that one addition will be empty.
Is there something what I missing in code?
[Control("CommandButton")]
class AreaActionPaneNew
{
void clicked()
{
PMCParameters contractParameters = PMCParameters::find();
PMETmpRentalObjectArea groupedAreaList; // Group by area_type and cost_type
PMERentalObjectPrice priceList;
date workingDate = currWorkingDate.dateValue();
;
super();
// Get grouped area values. Values are summed up by area_type and ancost_type
groupedAreaList = PMERentalObjectAreaCl::getRentalAreaPrCostType(pmeRentalobject.RentalObjectId, userSetting.validFrom(), userSetting.validTo() , workingDate);
ttsbegin;
while select groupedAreaList
{
select forupdate firstonly priceList
where priceList.RentalObjectId == pmeRentalObject.RentalObjectId &&
priceList.RentalCostType == groupedAreaList.RentalCostTypeId &&
priceList.AreaType == groupedAreaList.Areatype && priceList.ValidFrom == pmeRentalObject.ValidFrom;
if (!priceList)
priceList.initValue();
priceList.RentalObjectId = pmeRentalObject.RentalObjectId;
priceList.RentalCostType = groupedAreaList.RentalCostTypeId;
priceList.ValidFrom = pmeRentalobject.ValidFrom;
priceList.AreaType = groupedAreaList.Areatype;
priceList.Amount = groupedAreaList.Price;
priceList.Area = groupedAreaList.AreaValue;
priceList.Quantity = groupedAreaList.RentalQty;
if (!priceList)
priceList.Period = contractParameters.ReportPeriod;
if (priceList)
priceList.update();
else
priceList.insert();
}
ttscommit;
pmeRentalObjectPrice_ds.research();
}
}
The code looks like it only updates/inserts without creating a blank line.
From your attribute, you are using a Command Button (see here), which may have an associated command, such as New, which effectively pushes Ctrl+N, and would explain why you have a blank line.
The simplest way to check is just create a regular Button and override the clicked method, then copy/paste your code and push both buttons and see if they have different behavior.
Check the Command property on the button and see if there's something there. Try commenting out the super(); call.
You should perhaps consider just a Button or a Menu Item Button with an associated object.

kivy button ID's within functions

I am working on a fantasy football type app for a school project.
We have created a scrollview with a list of characters in a team within it, each assigned to a button. on press of the button a new scrollview displaying a second list of 'inactive character buttons' is displayed, allowing the user to press one to swap the first and second character from team to team.
our issue comes from a difficulty in managing to 'locate' which button is pressed in order to tell our swap function which two characters to swap on the list. Is it possible to retain the id of a button and call it into a new function on press of said button?
Our code is a bit messy, but is displayed bellow:
class SMApp(App):
teamlist = []
idvar = ""
btnlist = []
def popupfunc(self, event):
"""
creates a popup asking if the user wishes to swap a character from team to subs
then proceeds to allow user to choose who swaps
"""
def subscroll(self):
"""
opens scroll list of substitute characters in a popup
"""
sublist = []
curs.execute('SELECT * FROM Subs')
for row in curs:
sublist.append([row[0], row[2]])
layout = GridLayout(cols=2, spacing=10, size_hint_y=None)
layout.bind(minimum_height=layout.setter('height'))
for i in range(len(sublist)):
btn = Button(text=str(sublist[i][0]), size_hint_y=None, height=40)
layout.add_widget(btn)
lbl = Label(text=str(sublist[i][1]), size_hinty=None, height=40)
layout.add_widget(lbl)
root = ScrollView(size_hint=(None, None), size=(400, 400))
root.add_widget(layout)
popup2 = Popup(content=root, size=(7, 10), size_hint=(0.55, 0.8), title="list of subs")
popup2.open()
box = BoxLayout()
btn1 = Button(text='yeah ok')
btn2 = Button(text='nope')
popup1 = Popup(content=box, size=(10, 10), size_hint=(0.3, 0.3), title="add to team?")
btn2.bind(on_press=popup1.dismiss)
btn1.bind(on_press=subscroll)
box.add_widget(btn1)
box.add_widget(btn2)
popup1.open()
def build(self):
curs.execute('SELECT * FROM Team')
for row in curs:
self.teamlist.append([row[0], row[2]])
layout = GridLayout(cols=2, spacing=10, size_hint_y=None)
layout.bind(minimum_height=layout.setter('height'))
for i in range(len(self.teamlist)):
btn = Button(text=str(self.teamlist[i][0]), size_hint_y=None, height=40, id=str(i))
btn.bind(on_press=self.popupfunc)
self.btnlist.append(btn)
layout.add_widget(btn)
lbl = Label(text=str(self.teamlist[i][1]), size_hinty=None, height=40)
layout.add_widget(lbl)
for item in self.btnlist:
print item.id
root = ScrollView(size_hint=(None, None), size=(400, 400),
pos_hint={'center_x':.5, 'center_y':.5})
root.add_widget(layout)
return root
if __name__ == '__main__':
SMApp().run()
Each of the btn = Button(...) you create is a different object, therefore you can tell which is pressed. The thing is what way you'll choose.
You can use:
str(your button) and get a specific object address(?) like 0xAABBCCEE
bad, don't do that
Button(id='something', ...)
ids from kv language
or create own widget with a property for specific identificator. Then you'd use a loop for the parent's children which would check for identificator and do something:
for child in layout.children:
if child.id == 'something':
# do something
And it seems you'd need this loop inside your subscroll, or access that layout some other way.

Getting my buttons to add text to a list

I'm having a problem getting my buttons to actually do something, when one of the buttons on the top left are clicked, I want a text box in the frame below it adding the name of that button to a list but getting really stuck with it, any help would be much appreciated, thank you
Here is my code:
import tkinter
import tkinter as tk
root = tk.Tk()
root.geometry('1000x600')
var=tk.StringVar()
Frame1 = tk.Frame(root)
Frame1.configure(background='light blue',height='300',width='500')
Frame1.grid(sticky="nsew",row='0',column='0')
Frame2 = tk.Frame(root)
Frame2.configure(background='grey',height='300',width='500')
Frame2.grid(sticky="nsew",row='0',column='1')
Frame3 = tk.Frame(root)
Frame3.configure(background='grey',height='300',width='500')
Frame3.grid(sticky="nsew",row='1',column='0')
Frame4 = tk.Frame(root)
Frame4.configure(background='light blue',height='300',width='500')
Frame4.grid(sticky="nsew",row='1',column='1')
def PrintOrder():
LabelOrder = tk.Label(Frame3,text="DONUT ORDER")
LabelOrder.grid(row='0',column='0')
return
Button1 = tk.Button(Frame1,text="Apple Cinnamon",height='2',width='15',padx=10, pady=5,command=PrintOrder).grid(row='0',column='0')
Button2 = tk.Button(Frame1,text="Strawberry",height='2',width='15',padx=10, pady=5,command=PrintOrder).grid(row='0',column='2')
Button3 = tk.Button(Frame1,text="Custard",height='2',width='15',padx=10, pady=5,command=PrintOrder).grid(row='0',column='4')
Button4 = tk.Button(Frame1,text="Sugar Ring",height='2',width='15',padx=10, pady=5,command=PrintOrder).grid(row='2',column='0')
Button5 = tk.Button(Frame1,text="Chocolate Caramel",height='2',width='15',padx=10, pady=5,command=PrintOrder).grid(row='2',column='2')
Button6 = tk.Button(Frame1,text="Lemon Circle",height='2',width='15',padx=10, pady=5,command=PrintOrder).grid(row='2',column='4')
Button7 = tk.Button(Frame1,text="Blueberry Blaster",height='2',width='15',padx=10, pady=5,command=PrintOrder).grid(row='4',column='0')
Button8 = tk.Button(Frame1,text="Strawberry Surprise",height='2',width='15',padx=10, pady=5,command=PrintOrder).grid(row='4',column='2')
Button9 = tk.Button(Frame1,text="Simple Sugar",height='2',width='15',padx=10, pady=5,command=PrintOrder).grid(row='4',column='4')
Label1 = tk.Label(Frame2,text="Donut special 6 for the price of 5",height='5',width='30').grid(row='0',column='0')
Button10 = tk.Button(Frame2,text="SPECIAL",padx=5, pady=5,height='5',width='20').grid(row='2',column='0')
Button11 = tk.Button(Frame3,text="ORDER TOTAL",padx=5, pady=5,height='5',width='20').grid(row='2',column='0')
Button12 = tk.Button(Frame4,text="RUNNING TOTAL",padx=5, pady=5,height='5',width='20').grid(row='2',column='0')
root.mainloop()
The problem is that when you call the PrintOrder function, it doesn't necessarily know what button called it.
To fix this, use a lambda: [see http://effbot.org/zone/tkinter-callbacks.htm, "Passing Arguments to Callbacks"]
Also, you'll need variable strings to work with the list.
Above your PrintOrder function, write this (move the Label creation outside of the function):
LabelContents = tk.StringVar()
LabelOrder = tk.Label(Frame3, textvariable=LabelContents)
LabelOrder.grid(row='0', column='0')
then you can update the text in the label.
So change the PrintOrder function to
def PrintOrder(flavor):
LabelContents.set(LabelContents.get() + '\n' + flavor)
return
This appends the flavor, with a new line, to the label.
then in the button:
Button(text="lemon", command=lambda: PrintOrder("lemon"))
Of course you can modify to fit your program.
For the reasoning behind the lambda, see the link.
Hope that helps!

How can I remove onEvent from button widget in Corona?

I am trying to remove onEvent listener from button widget. I tried to assign nil to onEvent attribute but it didn't work and lastly I tried this:
buttonWidget : removeEventListener("touch", buttonWidget.onEvent)
I have several button like that and it just stopped all button's event listeners. What do you suggest? How can I remove the event listener for one button widget? Thanks.
Here is how I create my button widgets:
for i=0,2 do
for j=0,8 do
count=count+1
letterBtn[count] = widget.newButton{
id = alphabet[count],
left = 5+j*50,
top = H-160+i*50,
label = alphabet[count],
width = 45,
height = 45,
font = nil,
fontSize = 18,
labelColor = { default = {0,0,0}, over = {255,255,255}},
onEvent = btnOnEventHandler
};
end
end
Can you tell me how can I remove onEvent later?
Okey, I tried Button: setEnabled(false) but still it disables all buttons not just one. I already tried your second advice but it gives the same result. I am copying the rest of the code. Can you please look at it and tell me what I am missing?
local function checkLetter(e)
if(guessWord) then
for i=1, #guessWord do
local c = guessWord:sub(i,i)
if c==e.target.id then
letter[i].text = e.target.id
letterCount = letterCount +1
print("letterCount"..letterCount)
e.target:setEnabled(false)
end
end
if (letterCount == #guessWord and not hanged) then
timer.performWithDelay(500, function()
letterCount=0
rightWGuess = rightWGuess+1
for k,v in pairs(notGuessedWord) do
if v == guessWord then
notGuessedWord[k]=nil
end
end
enableButtons()
startGame() end ,1)
end
end
end
local function btnOnEventHandler(e)
if(e.phase == "began") then
checkLetter(e)
print(e.target.id)
end
return true
end
If you want to temporarily (or permanently) stop a button from responding to touch events, you can use Button:setEnabled(false).
The following worked for me for removing a listener from just 2 buttons. Button 1 and 3 stopped responding to events as expected while 2, 4, and 5 still did.
Update: To disable, you have to do it on the 'ended' phase or Corona gets confused.
widget = require 'widget'
local function btnOnEventHandler(event)
print('Event', event.target.id, event.phase)
if event.phase == 'ended' then
-- Disable the button so it can't be clicked again
-- Must disable in the end state or Corona gets
-- confused
event.target:setEnabled(false)
end
end
local buttons = {}
for i=1,5 do
buttons[i] = widget.newButton{
id = 'button' .. i,
left = display.contentCenterX - 50,
top = 60 * i,
label = 'Button ' .. i,
width = 100,
height = 50,
onEvent = btnOnEventHandler
}
end
buttons[1]:removeEventListener('touch', buttons[1].onEvent)
buttons[3]:removeEventListener('touch', buttons[3].onEvent)

Resources