Clearing all Labels from a tkinter window - python

So I'm new to tkinter, but I've got what I want working, up to a certain point.
I'm not sure I've set it up correctly, but I've got a world map with buttons on the right, and an events log on the left, which fills up with labels as stuff happens.
Issue is that after a little while, the whole log fills up.
What is the best way to delete all the labels, or maybe delete the oldest (top) label each time?
Here's what I mean:
Defined here:
root=Tk()
Map=PhotoImage(file="C:/Users/Willam/Desktop/CWProgram/map2.gif")
background=Label(root,image=Map).place(x=100,y=0,relwidth=1,relheight=1)
Title=Label(root,text=' LOG').pack(anchor=NW)
And I create my labels like this:
info=Label(root,text='Select a sector to move units from',wraplength=170)
info.pack(anchor=NW)
I tried the usual info.destoy() and info.forget(), but these only work on the last label used in that function.
Should I have grouped all labels or something?

As PM 2Ring suggested it is usually useful to append labels to a list for future ref:
tmp = Label(...)
labels.append(tmp)
then just:
foreach label in labels: label.destroy()
If you do not want a list, and you're sure you want to clear everything in root:
foreach label in root.children.values(): label.destroy()
The children dict always holds the objects contained within. If you want to keep the map label, you will have to make your own list as I showed, without appending info into it.

I would recommend using:
info.pack_forget()
For each pack you created you must do it in the format:
packname.pack_forget()
Which if you have a lot of packs is impractical, but otherwise it works very well.
This also makes it very easy to selectively remove some labels and leave others as it will not purge all packs that you placed.

Just use:
root.children.clear
After clearing screen just input map and functions again...

Related

how to read a datalabel's text in python-pptx

I want to read in a given datalabel's text.
What I have tried:
print(plot.series[0].points[0].data_label.text_frame.text)
Snippet above tries to print the 1st series' first point which is '16' but it prints nothing.
How can I obtain what is in the datalabel?
I want to read the text in, concat something new to it and reinsert it into the data label. Something like this
dltext = plot.series[0].points[0].data_label.text_frame.text
plot.series[0].points[0].data_label.text_frame.text = dltext + "Foo"
The data_label.text_frame only contains text if you put it there explicitly. Otherwise what is rendered is a function of the value of that data-point and the settings .show_value and show_percent, etc. documented here: https://python-pptx.readthedocs.io/en/latest/api/chart.html#pptx.chart.datalabel.DataLabels
If you want to match what shows to the user you'll need to duplicate that logic.
If you wanted to accomplish that for the general case, it would take some doing because you'd need to compute the effective value of properties like DataLabel.show_value, which would require reverse-engineering the style hierarchy for that setting.
But the 95% solution would just be to assume what is showing is the value and go with that. That's the default data label, at least for bar charts (pie charts may default to percent).

Put Qt objects from QtCreator into a Python list

I have a QtWidget, built in QtCreator. Let's say there are 10 QLineEdits in the widget named Edit0 to Edit9. The data that is supposed to go in them is stored in a Python list data. Is there a way to put those 10 QLineEdits in a Python list so I can basically assign their values like:
for index in range(len(data)):
Edit[index].setText('{:.2}'.format(data[index]))
Since you're using python, you can access fields via string:
for index in range(len(data)):
getattr(ui, f"Edit{index}").setText('{:.2}'.format(data[index]))
But relying on the name is an ugly style.
You can also iterate over the layout which contains the edits.
Here is how to do it in C++, the method names are the same in python.
But that becomes ugly if you want to add something else to the layout, e.g., a Button.
Neither of these methods do scale.
If you have many edits in a fixed pattern, you should consider creating them yourself with your code and put them in a list in the first place.
edits = []
for index in range(42):
edit = QLineEdit()
edits.append(edit)
ui.some_layout.addWidget(edit) # add edit to the layout
# later:
for edit, data in zip(edits, data):
edit.setText('{:.2}'.format(data[index]))
However, it seems to me like you're building a table.
Do you know QListWidget, QTableWidget, QListView and QTableView?

How do I numerically position a barcode in reportlab?

I have a bit of a specific case that I can't seem to figure out a solution to. I'm writing a shipping label template object in ReportLab for Python. I have the following code that creates a barcode as a drawing.
uspsBarcode = createBarcodeDrawing('USPS_4State', value=self.imbval, routing=self.zip4.replace('-',''))
print uspsBarcode.getBounds() # print out of position and scale
Using that code, I later add it to a shape group, and that shape group gets returned. So I need the barcode to be positioned relative to the object. I can't seem to find any way to pass positioning to this, even though I've dug through the inheritance. Though as you can see from the print, positioning is set somwhere.
Just for anyone else that runs across this issue. It turns out that if you put the barcode drawing in a shape group, the shape group container can be moved around numerically with the shift function.
uspsBarcode = shapes.Group()
bc = createBarcodeDrawing('USPS_4State', value=self.imbVal, routing=self.zip4.replace('-',''))
uspsBarcode.add(bc)
uspsBarcode.shift(self.x+(s*0.2), self.y)

PyQt Sort List of Radio Buttons

I am trying to dynamically create a list of radio buttons that represents the open COM ports on my computer. Creating and displaying the list the first time is easy enough since I can just sort the ports to be in numerical order and then add their corresponding radio button to my vertical layout.
However, if the user inserts a new device which creates a new COM port, I have to find some way to add the new button in the correct place since it might not be in the right numerical order. So far, the only way I have been able to do this is to just get rid of all the buttons and then re-add them after sorting the list since addWidget doesn't let me specify where to add the widget. This method seems really inefficient, and I am assuming there is a simpler way, but I just have not found it yet.
Instead of using addWidget(), determine the index in the list of buttons to place the new one, and use QBoxLayout.insertWidget(index, widget) to insert it there:
newButton = QRadioButton(...)
newText = newButton.text()
index = 0
for button in get_buttons():
if button.text() >= newText:
break
index += 1
layout.insertWidget(index, newButton)

How to stop infinite recursion when Python objects trigger each other's updates?

I'm using PyGTK and the gtk.Assistant widget. On one page I have six comboboxes, which initially have the same contents (six numbers). When the users selects a number in one of those comboboxes, this number should no longer be available in the other five boxes (unless it's present as a duplicate in the original list). Hence I would like to always update the contents.
I have tried the following approach (just a few code snippets here), but (of course...) it just jumps into infinite recursion once the process has been triggered:
# 'combo_list' is a list containing the six comboboxes
def changed_single_score(self, source_combo, all_scores, combo_list, indx_in_combo_list):
scores = all_scores.split(', ')
for i in range(6):
selected = self.get_active_text(combo_list[i])
if selected in scores:
scores.remove(selected)
# 'scores' only contains the items that are still available
for indx in range(6):
# don't change the box which triggered the update
if not indx == indx_in_combo_list:
# the idea is to clear each list and then repopulate it with the
# remaining available items
combo_list[indx].get_model().clear()
for item in scores:
combo_list[indx].append_text(item)
# '0' is appended so that swapping values is still possible
combo_list[indx].append_text('0')
The above function is called when a change occurs in one of the comboboxes:
for indx in range(6):
for score in self.selected['scores'].split(', '):
combo_list[indx].append_text(score)
combo_list[indx].connect('changed', self.changed_single_score, self.selected['scores'], combo_list, indx)
Perhaps I ought to mention that I'm new to Python, OOP, and also rather new to GUI-programming. I'm probably being really stupid here, and/or overlooking the obvious solution, but I have so far been unable to figure out how to stop each box from triggering updating of all other boxes once it itself has been updated.
Thanks in advance for your replies - any help would be greatly appreciated.
The simplest fix for this sort of problem is generally to figure out if you're going to need to change the contents of the object (the combobox, in your case) and then only apply changes if you're actually changing something. This way you'll only propagate update events as far as they do something.
This should look something like:
# '0' is appended so that swapping values is still possible
items = [item for item in scores] + ['0']
for indx in range(6):
# don't change the box which triggered the update
if not indx == indx_in_combo_list:
# I'm not 100% sure that this next line is correct, but it should be close
existing_values = [model_item[0] for model_item in combolist[indx].get_model()]
if existing_values != items:
# the idea is to clear each list and then repopulate it with the
# remaining available items
combo_list[indx].get_model().clear()
for item in items:
combo_list[indx].append_text(item)
This is a pretty general approach (even some build systems use it). The main requirement is that things actually do settle. In your case it should settle immediately.

Categories

Resources