kivyMD list update Icon - python-3.x

I am using the following function to create an MDlist TwoLineIconListItem in a scrollview. What I would like to do is change the icon in another function. I thought something like x.icon = 'New_icon' might work but didn't. Not sure where to look to get the desired result.
def rule_list(self):
'''Query of all rules and generates a list view under the rule tab....not really working all the way yet'''
db.execute('''SELECT * from rules''')
self.rows = db.fetchall()
for r in self.rows:
self.rule = f'{self.cfg["host"]}:{self.cfg["port"]}/api/firewall/filter/getRule/{r[2]}'
rules = TwoLineIconListItem(
text=r[1],
secondary_text=r[2],
on_release=lambda x: threading.Thread(
target=self.rule_on_click, args=(x.secondary_text, x), daemon=True).start()
)
self.check = requests.get(url=self.rule, auth=(
self.key, self.secret), verify=False)
if self.check.status_code == 200:
check_rule = json.loads(self.check.text)
if check_rule['rule']['enabled'] == '1':
rules.add_widget(IconLeftWidget(
icon='checkbox-marked-circle-outline'
))
else:
rules.add_widget(IconLeftWidget(
icon='checkbox-blank-circle-outline'
))
self.root.ids.ruleList.add_widget(rules)

I solved this by using the following in the function that had the logic for assigning the correct icon.
x.children[0].children[0].icon = new_icon

Related

Tkinter: displaying label with automatically updated variable

I know there are a lot of questions about this subject, but after long researches I didn't find any that could solve my problem.
I'm trying to display with a label (using tkinter) a variable that I get from the I²C bus. The variable is therefore updated very regularly and automatically. The rest of the window should stay available for the user.
For now, the only way I found to display the label with the updated variable and to keep the rest of the window available for the user is to do so:
window = tk.Tk()
window.title("Gestionnaire de périphériques")
window.minsize(1024,600)
labelValThermo = tk.Label(a_frame_in_the_main_window,text = "")
labelValThermo.grid(row = 1, column = 1)
while True:
if mcp.get_hot_junction_temperature() != 16.0625:
labelValThermo.configure(text = "Température thermocouple: {} °C".format(mcp.get_hot_junction_temperature()))
window.update()
time.sleep(0.75)
The variable that comes from the I²C and got updated is mcp.get_hot_junction_temperature
The fact is that I know it's not the best way to force the update in an infinite loop. This should be the role of the mainloop(). I found out that the after() method could solve my problem but I don't know how to run it. I tried the following code that didn't work:
def displayThermoTemp():
if mcp.get_hot_junction_temperature() != 16.0625:
labelValThermo.configure(text = "Température thermocouple: {} °C".format(mcp.get_hot_junction_temperature()))
labelValThermo.after(500,displayThermoTemp)
window = tk.Tk()
labelValThermo = tk.Label(thermoGraphFrame,text = "")
labelValThermo.after(500, displayThermoTemp)
labelValThermo.grid(row = 1, column = 1)
window.mainloop()
Does anyone have the right syntax ?
How To use after() ?
The after() calls the function callback after the given delay in ms. Just define it inside the given function and it'll run just like a while loop till you call after_cancel(id) .
Here is an example:
import tkinter as tk
Count = 1
root = tk.Tk()
label = tk.Label(root, text=Count, font = ('', 30))
label.pack()
def update():
global Count
Count += 1
label['text'] = Count
root.after(100, update)
update()
root.mainloop()
Update your function with this and call it once before mainloop().
def displayThermoTemp():
if mcp.get_hot_junction_temperature() != 16.0625:
labelValThermo.configure(text = "Température thermocouple: {} °C".format(mcp.get_hot_junction_temperature()))
labelValThermo.after(500,displayThermoTemp)
# 100ms = 0.1 secs
window(100, displayThermoTemp)
after has the following syntax:
after(delay_ms, callback=None, *args)
You need to place the command in your callback function and update the window on which the widget is (in your case the labelValThermo looks to be on the root window:
def displayThermoTemp():
if mcp.get_hot_junction_temperature() != 16.0625:
labelValThermo.configure(text = "Température thermocouple: {} °C".format(mcp.get_hot_junction_temperature()))
root.after(500,displayThermoTemp)

Updating text in a QLineEdit text box generated from a for loop reading a dict

I have 2 QLineEdit boxes generated using a for loop that are pulling the names and initial text values from a dictionary. The code I have updates the dictionary values just fine and it generates the edit boxes correctly. What I am having problems with is actually updating the values on the gui for the user to see. I am using pyside2 and python 3.7.2 on windows 10 pro.
If I 'hard code' the QLineEdit boxes it works as expected using self.display1.setText("asdf") . The way I'm generating the inputs I'm unable to figure out how to do that other than updating the dictionary value that is assigned to the text of the edit box. I've tried calling update() and repaint() at time of button click and after the values in the dictionary are modified. I've also tried redefining the widgets by calling a function that remade the inputs and did .addWidget(). This, as expected, generated new ones below the edit boxes that were already generated. However, the text in the boxes were correct.
This is the loop that generates the inputs
for val, key in self.inputDict.items():
self.inputs = QLineEdit(key, self)
self.inputs.setText(key)
self.vertCol.addWidget(self.inputs)
The relevant dict: self.inputDict = {"display1": "", "display2": ""}.
This is the loop that generates the buttons(it's in a loop because this is is just 1 function of this program I'm building, other buttons roll different sided die)
for key, val in self.buttonDict.items():
self.buttons = QPushButton(key, self)
self.buttons.setToolTip("D20 die rolled: 1-10=low, 11-20=high; Coin Flip: 0=Heads(Good) 1=Tails(Bad)")
self.buttons.clicked.connect(partial(self.handlehlgb))
self.vertCol.addWidget(self.buttons)
The relevant button dict self.buttonDict = {"High-Low-Good-Bad": "hlgb"}
And this is the function that I'm trying to get to work:
def handlehlgb(self):
self.coinFlip = randrange(2)
self.rollD20 = randint(1, 20)
if self.coinFlip <= 0:
self.inputDict.update({'display1': 'Good'})
print(self.inputDict["display1"])
else:
self.inputDict.update({'display2': 'Bad'})
print(self.inputDict["display2"])
if self.rollD20 <= 10:
self.inputDict.update({'display2': 'Low'})
print(self.inputDict["display2"])
else:
self.inputDict.update({'display2': 'High'})
print(self.inputDict["display2"])
It's meant to display Good/Bad determined off of a 'coin flip' and High/Low based off of a D20 die roll.
I'm expecting it to update the display on the GUI, but it doesn't. It will print the correct value of the displays in the console. Here's a pastebin of what I'm working with: https://pastebin.com/H6LAnbnH
I'm relatively new with python and especially with pyside/pyqt. So any help would be greatly appreciated!!
I may have misunderstood, but it seems that if you simply add a setText after rolling the dice:
self.inputDict.update({'display1': 'Good'})
self.inputs.setText(self.inputDict["display2"])
the text is updated in the QLineEdit.
Then you may want to make self.inputs a dictionary, so you can change both QLineEdits:
self.inputs = {}
for val, key in self.inputDict.items():
self.inputs[key] = QLineEdit(key, self)
...
self.inputDict.update({'display1': 'Good'})
self.inputs['display1'].setText(self.inputDict['display1'])
(wait ... now that I copy-paste your code, I see that you wrote val,key in ..., when you probably wanted it to be key,val in ...). Could it be simply that?
[edit]
here's what it becomes with both ideas:
class MainWidget(QWidget):
def __init__(self):
super(MainWidget, self).__init__()
self.buttonDict = {"High-Low-Good-Bad": "hlgb"}
self.inputDict = {"display1": "", "display2": ""}
self.vertCol = QVBoxLayout(self)
self.inputs = {}
for key, val in self.inputDict.items():
self.inputs[key] = QLineEdit(key, self)
self.inputs[key].setText(val)
print("Key - " + key)
print("Val - " + val)
self.vertCol.addWidget(self.inputs[key])
for key, val in self.buttonDict.items():
self.buttons = QPushButton(key, self)
self.buttons.setToolTip("D20 die rolled: 1-10=low, 11-20=high; Coin Flip: 0=Heads(Good) 1=Tails(Bad)")
self.buttons.clicked.connect(partial(self.handlehlgb))
#self.buttons.clicked.connect(self.inputs.update())
self.vertCol.addWidget(self.buttons)
def handlehlgb(self):
self.coinFlip = randrange(2)
self.rollD20 = randint(1, 20)
if self.coinFlip <= 0:
self.inputDict.update({'display1': 'Good'})
self.inputs['display1'].setText('Good')
print(self.inputDict["display1"])
else:
self.inputDict.update({'display2': 'Bad'})
self.inputs['display1'].setText('Bad')
print(self.inputDict["display2"])
if self.rollD20 <= 10:
self.inputDict.update({'display2': 'Low'})
self.inputs['display2'].setText('Low')
print(self.inputDict["display2"])
else:
self.inputDict.update({'display2': 'High'})
self.inputs['display2'].setText('High')
print(self.inputDict["display2"])

Access variable value by its name as String (groovy)

I've done some research but I haven't found a working code for my case. I have two variables named test and test2 and I want to put them in a map in the format [test:valueof(test), test2:valueof(test2)]
My piece of code is the following:
def test="HELLO"
def test2="WORLD"
def queryText = "\$\$test\$\$ \$\$test2\$\$ this is my test"
def list = queryText.findAll(/\$\$(.*?)\$\$/)
def map = [:]
list.each{
it = it.replace("\$\$", "")
map.putAt(it, it)
}
queryText = queryText.replaceAll(/\$\$(.*?)\$\$/) { k -> map[k[1]] ?: k[0] }
System.out.println(map)
System.out.println(queryText)
Output:
Desired output:
"HELLO WORLD this is my test"
I think I need something like map.putAt(it, eval(it))
EDIT
This is the way I get my inputs. the code goes into the 'test' script
The ones on the right are the variable names inside the script, the left column are the values (that will later be dynamic)
You are almost there.
The solution is instead of putting the values into separate variables put them into the script binding.
Add this at the beginning ( remove the variables test and test2):
def test="HELLO"
def test2="WORLD"
binding.setProperty('test', test)
binding.setProperty('test2', test2)
and change this:
{ k -> map[k[1]] ?: k[0] }
to this:
{ k -> evaluate(k[1]) }
It should be very simple if you could use TemplateEngine.
def text = '$test $test2 this is my test'
def binding = [test:'HELLO', test2:'WORLD']
def engine = new groovy.text.SimpleTemplateEngine()
def template = engine.createTemplate(text).make(binding)
def result = 'HELLO WORLD this is my test'
assert result == template.toString()
You can test quickly online Demo
Final working code, thanks to all, in particular to dsharew who helped me a lot!
#input String queryText,test,test2,test3
def list = queryText.findAll(/\$\$(.*?)\$\$/)
def map = [:]
list.each{
it = it.replace("\$\$", "")
map.putAt(it, it)
}
queryText = queryText.replaceAll(/\$\$(.*?)\$\$/) { k -> evaluate(k[1]) }
return queryText

How to check all variables created by class function and use them?

In short; I started coding a few days ago and thought trying to make a simple text based adventure will let me face a lot of problems that I will encounter in other harder projects as well. My class init function produces items with some variables, one of which is it's equipment slot position [0-6]. I would like to unequip a slot, but the way I have it set up at the moment requires me to know which item is in that particular slot.
In english: unequip("mainhand"), mainhand has slotnumber 0. Get the info of all equipped items and check which one has the corresponding slotnumber, then remove that particular item.
(Some items have 2 slotnumbers ("althand") which means I will have to find a way to make sure I remove the right item from the list, but that is something I can do later). For now, I can't seem to figure out how to dynamically call items and do stuff with them.
PS. I am pretty sure I can do this in a way more phytonic manner and any suggestions are welcome, but regardless of this, I would still like to know how to dynamically call the function.
The code with which I try this:
def Unequip(Slotname): #Slotname is just a string, so I could say: unequip("Mainhand")
for count,i in enumerate(Item.slotname): #Item.slotname is a list of strings for slots which corresponds with Item.Slot which are the values determining the which slot is occupied.
if Slotname == Item.slotname[count]: #so when the name I put into the function equals the Item.slotname, I know which number in Item.Slot I need.
for items in Item: #Here we enter the problem (again, I understand that this code could be a lot better and would love some suggestions).
#Item is a object, but it's __init__ functions produces items, such as item1, item2 etc. I would like to check if any of these items is currently in my Item.Equipped and has the Item.Slot value I want to remove.
#I tried global(), locals() and dir(Item) but none give me what I need. I really hope this makes it clear.
if Item.Slot[count] == items.slot and items.name == Item.Equipped: #I need a susbtitute for the items variable which will represent item1, item2 or item3 etc. So I can do Item.Slot.remove(item2.slot).
Slot = Item.Slot.remove(items.slot)
Equipped = Item.Equipped.remove(items.name)
Player.stats = list(map(operator.sub,list(Player.stats),self.itemstats))
elif Item.Slot[i] == items.altslot and Items.name == items.Equipped:
pass
Full code (I tried using self etc, but it may not be super readable, my apologies), it includes a item.unequip function but this requires me to select the particular item instead of just the slot from which I want my item to be removed
Edit1: Removed all unneeded stuff per request:
import random
import operator
class Item:
Equipped = []
Slot = []
Inventory = []
num_of_items = 0
statnames = ["Strength", "Agility", "Dexterity", "Wisdom", "Constitution", "Intelligence"]
slotname = ["MainHand","Offhand","Head", "Necklace","Chest","Legs", "Cape" ]
def __init__(self, name, itemstats, slot, altslot = None, atk = None, Def = None):
self.itemstats = itemstats #itemstats in order: S, A, D, W, C, I
self.name = name
Item.num_of_items += 1
self.slot = slot
self.altslot = altslot
if atk != None and atk != 0:
self.atk = atk
if Def != None and Def != 0:
self.Def = Def
def Unequip(Slotname):
for count,i in enumerate(Item.slotname):
if Slotname == Item.slotname[count]:
for items in dir(Item):
if Item.Slot[count] == items.slot and items.name == Item.Equipped:
Slot = Item.Slot.remove(items.slot)
Equipped = Item.Eqiupped.remove(items.name)
Player.stats = list(map(operator.sub,list(Player.stats),self.itemstats))
elif Item.Slot[i] == items.altslot and Items.name == items.Equipped:
pass
class Player:
stats= [8,8,8,8,8,8]
item1 = Item("Sword of damaocles",[5, 1, 0,1,2,-2],0,1,20)
item2 = Item("Helmet of steel",[9,9,9,9,9,9],2,None,0,20)

Label keeps on appearing

SO I am using Python 3.4 and tkinter.
And when I call a function again n again which contains a label, the label keeps on appearing in window but previous label doesn't go away?
How can I remove any printed label from GUI window as soon as function is called and then display new one?
Here is the code:-
#def prestart():
#here I check if number of match is okay, if not, user is redirected to setting else, I call start()
def start():
#CPU Choice
cpu_choice = Label(historyframe, text = "CPU Choosed: {}".format(dict['cpu_choice']))
#Played Match
#played_num_of_match = Label(scoreframe, text = "Number of Matches Played: {}".format(int(dict['match_played'])))
#Display Status
status_disp = Label(scoreframe, text = "Current Status: {}".format(dict['status']))
if(int(dict['match_played']) < int(dict['num_of_match'])):
playframe.grid(row = 1, column = 0)
historyframe.grid(row = 2, column = 1)
status_disp.pack(fill=X)
elif(int(dict['match_played']) == int(dict['num_of_match'])):
playframe.grid(row = 1, column = 0)
historyframe.grid(row = 2, column = 1)
status_disp.pack(fill=X)
cp = dict['cpu_point']
up = dict['user_point']
result(cp, up)
cpu_choice.pack(fill = X)
scoreframe.grid(row = 2, column = 0)
This function just updates the display!
def send_value(x):
#Here I run logic of game and change value of key in dictionary and call start() at end of change.
Now, the choice buttons are not in any definition as they don't need to be called again n again. I just make playframe disappear n appear!
Here is the code for them:-
#Display Question
question = Label(playframe, text = "Rock? Paper? Scissor?")
#Rock
rock = Button(playframe, text = "Rock!", command = lambda: send_value("ROCK"))
#Paper
paper = Button(playframe, text = "Paper!", command = lambda: send_value("PAPER"))
#Scissor
scissor = Button(playframe, text = "Scissor!", command = lambda: send_value("SCISSOR"))
So when user clicks Rock/Paper/Scissor, I just change key value in dictionary! But if I keep the label outside function, it doesn't get auto updated!
Everything else is working perfectly. I'll kind of now start to make code cleaner.
Try something like this instead of creating a new label every time:
import Tkinter as tk
class Window():
def __init__(self, root):
self.frame = tk.Frame(root)
self.frame.pack()
self.i = 0
self.labelVar = tk.StringVar()
self.labelVar.set("This is the first text: %d" %self.i)
self.label = tk.Label(self.frame, text = self.labelVar.get(), textvariable = self.labelVar)
self.label.pack(side = tk.LEFT)
self.button = tk.Button(self.frame, text = "Update", command = self.updateLabel)
self.button.pack(side = tk.RIGHT)
def updateLabel(self):
self.i += 1
self.labelVar.set("This is new text: %d" %self.i)
root = tk.Tk()
window = Window(root)
root.mainloop()
Important points:
1) A class is used, as it is much easier to pass values around when all Tkinter objects and variables are member variables, accessible from all of your GUI functions.
2) updateLabel does not create a new Label. It simply updates the StringVar() object to hold new text every time you call the function. This is accomplished with the textvariable = self.labelVar keyword when creating my Label widget.
PS: This is done in Python 2.5 so for this code to work for you, change Tkinter to tkinter
EDIT 06/19/2015:
If you want to implement something similar to what I have with your code, without using a class, you'll need to pass around references to your variables.
1) Change start:
Your Labels cpu_choice, status_disp, etc. should be created outside of the function; likely in the same location as question, rock, paper, scissors, etc. You will also pack them outside of the function as well. Same with all the calls to .grid inside of start; you shouldn't need to call pack or grid more than once: right when you create the widget.
The following lines:
playframe.grid(row = 1, column = 0)
historyframe.grid(row = 2, column = 1)
status_disp.pack(fill=X)
Can be done outside of the function as well; you execute these 3 statements under both the if and the elif conditions. This means they aren't really conditional statements; they are done regardless of the validity of the condition.
2) Create a StringVar for both cpu_choice & status_disp & edit the Labels as follows (remember, outside of the function):
cpu_choice_text = StringVar()
cpu_choice_text.set("Set this to whatever is shown at the start of the game")
cpu_choice = Label(historyframe, text = cpu_choice_text.get(), textvariable = cpu_choice_text)
cpu_choice.pack(fill = X)
# And do this same thing for status_disp
3) When you call start, you will now pass it cpu_choice_text & status_disp_text (or whatever they are called). Instead of trying to change the text field of the Label frame, you may now use a set call on the StringVar which is connected to the Label & the Label will automatically update. Example:
def start(cpu_choice_text, status_disp_text):
cpu_choice.set(text = "CPU Choice: {}".format(dict['cpu_choice']))
...
Alternatively, wrap it all in a class and make it much easier for yourself by using self on every Tkinter variable & widget. In this way you won't need to pass variables to your functions, just access member variables directly as I have with self.i, self.labelVar in my example.
Each time you call start you create new labels and use grid to place them in the same spot as the old labels. The best solution is to only create the labels once, but if you insist on creating new labels each time start is called, you need to delete the old labels first.
You can use the destroy() method of a label to destroy it, though for that to work you must keep a global reference of the label.

Resources