PyQt5 - try to build a gui with dynamic content - python

I would like to realize a gui to show fathers and their children. If you click on a child, it will be father and his children will fill the lables on the screen.
The family tree is an object "familytree". The Gui uses customized pyqt-classes. You could see below the initial screen.
I'm stuck with the evaluation of the signal.
def kinderInitialisieren(self):
# kinder
self.kinderName = [0 for kind in range(10)]
self.kinderNr = [0 for kind in range(10)]
for kind in range(10):
kinderLabel = TextFeld("Kind: ")
self.gestaltung.addWidget(kinderLabel, 1 + kind , 0)
self.kinderName[kind] = ButtonFeelsLikeLabel("Name")
self.gestaltung.addWidget(self.kinderName[kind], 1 + kind, 1)
self.kinderName[kind].pressed.connect( lambda n=kind: self.welcherKnopfGedrueckt(n))
Lambda-function delivers the index of the clicked button (I realized the names of children as buttons). But this index is like caged in the namespace of the class/function. For getting the children of person/button no.1 I've to access the object "family tree". How to break the namespace-prison?
GUI Family tree

Related

Create a Tree of Object Recursively (Python)

i would like to create a tree of objects called State.
Each State has a list of 4 robots and each states have differents robots coordinates.
The goal is to create a graph which will be solved by a Breadth First Search Algorithm.
(The original game is RicochetRobot maybe you guys know it).
class State:
def __init__(self,parent,childs,robots,cpt,):
self.parent = parent
self.childs = childs
self.robots = robots
self.cpt = cpt
I created a function create_child to do this
def create_child(self,depth):
#UP
#Depth is the tree height
if depth==0:
print("STOP")
return
else :
for i in range(4):
#Copying the robots of the current object
temp = copy.deepcopy(self.robots)
#Getting robot to move index
temp_robot = temp[i]
#removing this robot from the list
temp.pop(i)
#Moving up the robot and inserting it in the list
#Moving up is a simple function which increment the y of the robot
temp.insert(i,moving_up(temp_robot,create_board(init_robot())))
#Adding a new child into childs list
self.childs.append(State(self,self.childs,temp,self.cpt+1))
#Decrementing the depth
depth-=1
#Recursivity
for child in self.childs:
child.create_child(depth)
My problem is that when depth = 0 , it print STOP,but it doesnt return None, and the function keep going.
Anynone knows from where it come froms?
Also, if you can give advice on how to make my tree in an easier way it would be nice.
Alright so i think i found a solution , here it is :
depth = 1
parent = Initial_State
for j in range(depth):
for i in range(len(parent.childs)-1):
parent.childs[i].create_child()
parent = parent.childs[i]
It seems to be working. I dont use recursion anymore, i do it in a more iterative way and outside the function and it seems good.
Thank you

How to get state of each checkbox generated through a loop, as soon as it is checked/unchecked in tkinter?

I'm trying add/remove an item (text of checkbox) to/from a list whenever a checkbox is checked/unchecked in tkinter.
My idea was to add a command to the checkbutton, like:
cb = Checkbutton(master,...,command=some_fun)
but I cannot think of a way to define the function. I was thinking the function should contain the widget attribute cget('text'), but the problem is I have many checkboxes made with the help of a loop.
I guess the question is: how can I reference the checkbox whose state got changed and is therefore calling the function some_fun?
The way I generated the checkboxes is:
cb_identities = []
for i in range(cb_max_num):
cb = Checkbutton(frame_data,bg="white")
cb_identities.append(cb)
And then I'm dynamically changing them depending on some radiobuttons:
def fun_chck(): #shows or hides checkbuttons based on radiobutton input
data = read_data(rb_var.get())
for i in range(cb_max_num):
cbname = (cb_identities[i])
if len(data)-1 < i:
cbname.grid_forget()
else:
cbname.config(text=data[i]) #I would place some_fun here, which gets text option of checked box
cbname.grid(row=i,column=1,sticky=W)
Update! I managed with the following code for anyone interested:
cb_var_init = [0] * cb_max_num #create the initial list of inactive checkbuttons, all 0
input_params=[] #list which needs to be populated/depopulated based on checkbutton state
def get_data(data): #populates a list with parameter from checked checkbuttons,
global cb_var_init
cb_var_list = list(map(lambda var: var.get(),list(cb_var.values())))
for i in range(len(data)):
if cb_var_list[i] > cb_var_init[i]:
input_params.append(data[i])
elif cb_var_list[i] < cb_var_init[i]:
input_params.remove(data[i])
cb_var_init = cb_var_list
return(input_params)
cb_var is a dictionary of IntVars, and data is a list of checkbuttons' names.
As for the command on each checkbutton, I used cbname.config(text=data[i],command=lambda: get_data(data)) as suggested in another topic for functions with arguments.
Now each time I check a checkbutton, I immediately get a list of parameters which should show in the next Frame, which is dynamically updated.

How to temporarily remove (not delete and replace) Tkinter widget without using just .place?

I hope this isn't a duplicate but I could not find this anywhere:
I have been using Tkinter for application writing for awhile (even 2 games in it, but I use Pygame for that now.)
My question is, how would I do a ".pack" then ".unpack" ( remove temporarily ) without using a dirty trick of ".place( x = someLARGE_Number, y = anotherLARGE_Number )"
Ex:
root = Tk()
root.geometry( "300x300" )
root.title( "Test" )
label1 = Label( root, text = "Hi" )
label1.place( x = 1, y = 1 )
root.mainloop()
and when I remove it temp. I type something like:
label1.place( x = 3333, y = 3333 )
I know pack and unpack exist, but you can't unpack an object that has been placed nor can you place an item precisely using pack so how would I remove it neater? It looks gross on code and probably placing a lot of stuff away or on screen sometimes can be barely seen move 1 at a time. If this is a duplicate, please comment something to help, but I could not find something like it.
Restatement:
PROBLEM: Clean way to temporarily remove an item, without destroying it or using a .place() method with a pair of large, offscreen, numbers.
TRIED: .place( x = LARGE, y = LARGE ) and that's it, .unpack requires something to be .pack(ed) and you can't (from what I know) place items where you want via .pack.
NEED: Help or a link to an answered question like mine!
Use a .place_forget() method, that is associated with the type of a "geometry-manager" your have selected when the widget was created:
label1.place_forget()
After that, you can make it appear again by placing it again:
label1.place(x=1, y=1)
Just note that it's not always beneficial to use the .place() / .place_forget() geometry manager.
You may also use .grid() / .grid_forget() or .pack() / .pack_forget() methods for other geometry managers, where grid-arrangement or pack-layout fits better your GUI-setup/configuration/resizing needs.
One may inspect doc-string attributes .__doc__ for all the instance-methods:
>>> print label.place_forget.__doc__
Unmap this widget.
>>> print label.grid_forget.__doc__
Unmap this widget.
>>> print label.pack_forget.__doc__
Unmap this widget and do not use it for the packing order.
You can inquire as to whether a widget is "mapped" with a geometry manager (grid, pack, or place) and then "forget" it as needed:
def unmap(widget):
result = False
if widget and widget.winfo_exists() and widget.winfo_ismapped():
result = True
geom_mgr = widget.winfo_manager()
if geom_mgr == 'grid':
widget.grid_forget()
elif geom_mgr == 'pack':
widget.pack_forget()
elif geom_mgr == 'place':
widget.place_forget()
else:
result = False
return result
Note that calling a "forget" routine that does not match the mapping routine does not throw an exception. So, if you pack() a widget, you can call grid_forget() on that widget without causing an error or having any effect. In that case, only pack_forget() would hide the widget.

What is the most effective way to incremente a large number of values in Python?

Okay, sorry if my problem seems a bit rough. I'll try to explain it in a figurative way, I hope this is satisfactory.
10 children. 5 boxes. Each child chooses three boxes. Each box is opened:
- If it contains something, all children selected this box gets 1 point
- Otherwise, nobody gets a point.
My question is about what I put in bold. Because in my code, there are lots of kids and lots of boxes.
Currently, I proceed as follows:
children = {"child_1" : 0, ... , "child_10": 0}
gp1 = ["child_3", "child_7", "child_10"] #children who selected the box 1
...
gp5 = ["child_2", "child_5", "child_8", "child_10"]
boxes = [(0,gp1), (0,gp2), (1,gp3), (1,gp4), (0,gp5)]
for box in boxes:
if box[0] == 1: #something inside
for child in box[1]:
children[child] += 1
I worry mainly about the for loop that assigns each child an extra point. Because in my final code, I have many many children, I fear that doing so would slow the program too.
Is there a more efficient way for all children of the same group may have their point faster?
Represent children as indices into arrays, not as strings:
childrenScores = [0] * 10
gp1 = [2,6,9] # children who selected box 1
...
gp5 = [1,4,7,9]
boxes = [(0,gp1), (0,gp2), (1,gp3), (1,gp4), (0,gp5)]
Then, you can store childrenScores as a NumPy array and use advanced indexing:
childrenScores = np.zeros(10, dtype=int)
...
for box in boxes:
if box[0]:
childrenScores[box[1]] += 1 # NumPy advanced indexing
This still involves a loop somewhere, but the loop is deep inside NumPy instead, which should provide a meaningful speedup.
The only speed up that I can think of is to use numpy arrays and stream the sum operation.
children[child] += np.ones(len(children[child]))
You should benchmark the operation and see if that is too slow for your business case.
What I would do
In the gpX lists don't save the "name of the child" (e.g. "child_10") but save a reference to the child's number of points.
How to do that
Using the fact that lists are objects in python, you can:
Change the children dict to look like: children = {"child_0": [0], "child_1": [0], ...} and so on.
When you assign to group, don't assign the key but assign the value (e.g. gp1.append(children["child_0"])).
The loop should then look like: for child in box[1]: child[0]+=1. This WILL update the children dict.
EDIT:
Why this is faster:
Because you leave out the part where you search for children[child], which might be costly.
This technique works because by storing the totals in a mutable type, and appending those values to the group lists, both the dict value and each box's list value will point to the same list entries, and changing one will change the other.
Two general points:
(1) Based on what you've told us, there's no reason to focus your energy on minor performance optimizations. Your time would be better spent thinking about ways to make your data structures less awkward and more communicative. A bunch of interrelated dicts, lists, and tuples quickly becomes difficult to maintain. For an alternative, see the example below.
(2) As the game designer, you understand that events follow a certain sequence: first the kids select their boxes, and later they discover whether they get points for them. But you don't have to implement it that way. A kid can choose a box and get points (or not) immediately. If there's a need to preserve the child's ignorance about such outcomes, the parts of your algorithm that depend on such ignorance can enforce that veil of secrecy as needed. The upshot: there is no need for a box to loop through its children, awarding points to each one; instead, award the points immediately to kids as boxes are selected.
import random
class Box(object):
def __init__(self, name):
self.name = name
self.prize = random.randint(0,1)
class Child(object):
def __init__(self, name):
self.name = name
self.boxes = []
self.score = 0
self._score = 0
def choose(self, n, boxes):
bs = random.sample(boxes, n)
for b in bs:
self.boxes.append(b)
self._score += b.prize
def reveal_score(self):
self.score = self._score
boxes = [Box(i) for i in range(5)]
kids = [Child(i) for i in range(10)]
for k in kids:
k.choose(3, boxes)
# Later in the game ...
for k in kids:
k.reveal_score()
print (k.name, k.score), '=>', [(b.name, b.prize) for b in k.boxes]
One way or another, you're going to be looping over the children, and your answer appears to avoid looping over children who don't get any points.
It might be slightly faster to use filter or itertools.ifilter to pick the boxes that have something in them:
import itertools
...
for box in itertools.ifilter(lambda x: x[0], boxes):
for child in box[1]
children[child] += 1
If you don't need to immediately print the number of points for every child, you could calculate it on demand, thus saving time. This could help if you only need to query a child every now and then for how many points it has. You can cache each result as you obtain is so you don't go about calculating it again the next time you need it.
Firstly, you'll need to know which groups a child belongs to. We'll store this information as map we'll call childToGroupsMap, which will map each child to an array holding the boxes it belongs to, like so:
childToGroupsMap = {}
for child in children:
childToGroupsMap[child[0]] = []
for box in boxes:
for child in box[1]:
if (box[1] not in childToGroupsMap[child]):
childToGroupsMap[child].append(box[1])
This constructs the reverse map from children to boxes.
It'll also help to have a map from each box to a boolean representing whether it's been opened:
boxToOpenedMap = {}
for box in boxes:
boxToOpenedMap[box[1]] = box[0]
Now, when someone queries how many points a child has, you can go through each of the boxes it belongs to (using childToGroupsMap, of course), and simply count how many of those boxes have been mapped to 1 in the map boxes:
def countBoxesForChild(child):
points = 0
for box in childToGroupsMap[child]
if boxToOpenedMap[box] == 1:
points += 1
return points
To make this better, you can cache the resulting number of points. Make a map like so:
childToPointsCalculated = {}
for child in children:
childToPointsCalculated[child[0]] = -1
Where the -1 denotes that we don't yet know how many points this child has.
Finally, you can modify your countBoxesForChild function to exploit the cache:
def countBoxesForChild(child):
if childToPointsCalculated[child] != -1
return childToPointsCalculated[child]
points = 0
for box in childToGroupsMap[child]
if boxToOpenedMap[box] == 1:
points += 1
childToPointsCalculated[child] = points
return points

Python Recursion through objects and child objects, Print child depth numbers

I have a simple class with an attribute that can contain a list of objects of the same class
class BoxItem:
def __init__(self, name, **kw):
self.name = name
self.boxItems = []
... #more attributes here
box1 = BoxItem('Normal Box')
box2 = BoxItem('Friendly Box')
box3 = BoxItem('Cool Box')
box4 = BoxItem('Big Box', [box1, box2]) #contains some children
example = BoxItem('Example Box', [box4,box3]) #contains another level of children
Working with our 'example' box object, I would like to maneuver through the depths of all possible box children it may have, and print out the objects formatted like so:
1 Example Box
1.1 Big Box
1.1.1 Normal Box
1.1.2 Friendly Box
1.2 Cool Box
The Tabbing between isn't needed, just wanting to show the tree format clearly. I am able to walk down the objects themselves and print out their titles, but I am not able to print out the front numbers that show the parent/child relationship. (1, 1.1, 1.2...)
Thanks in advance for your help :)
Edit
Here is what I have been working with so far
def print_boxes(box_list):
node_count = 0
for box in box_list:
node_count += 1
print str(node_count)+' '+box.name #prints out the root box
recursive_search(box,node_count)
def recursive_search(box,node_count): #recursive automatically
level = 0
for child in box.boxItems:
level += 1
for x in range(len(child.boxItems)):
print x+1 #this prints out some proper numbers
print "level: "+str(level) #experiment with level
print child.name #prints out child box name
recursive_search(child,node_count) #runs the function again inside the function
I think it might be more helpful to you if I post a working example of how to do this, as opposed to going through where you code is having problems. We might get to the point of understanding a lot faster that way. Your code has the correct idea that it needs to track the depth as it goes. But the only thing it is missing is a sense of nested depth (tree). It only knows the previous node_count, and then its current child count.
My example uses a closure to start the depth tracking object, and then creates an inner function to do the recursive part.
def recurse(box):
boxes = not isinstance(box, (list, tuple)) and [box] or box
depth = [1]
def wrapped(box):
depthStr = '.'.join([str(i) for i in depth])
print "%s %s" % (depthStr, box.name)
depth.append(1)
for child in box.boxItems:
wrapped(child)
depth[-1] += 1
depth.pop()
for box in boxes:
wrapped(box)
depth[0] += 1
Sample output from your examples:
>>> recurse(example)
1 Example Box
1.1 Big Box
1.1.1 Normal Box
1.1.2 Friendly Box
1.2 Cool Box
>>> recurse([example, example])
1 Example Box
1.1 Big Box
1.1.1 Normal Box
1.1.2 Friendly Box
1.2 Cool Box
2 Example Box
2.1 Big Box
2.1.1 Normal Box
2.1.2 Friendly Box
2.2 Cool Box
Breaking this down:
We first accept a box argument, and automatically convert it locally to a list, if you had only passed in a single box item. That way you can pass either one box objects, or a list/tuple of them.
depth is our depth tracker. Its a list of ints that we will build up and shrink down as the recursion happens. It starts at 1 for the first item / first level. Over time it can look like this: [1,1,2,3,1] depending on how deep it traverses. This is the major difference between my code and yours. Each recursion has access to this state.
Now we have this inner wrapped function. Its going to take a current box item and print it, and then iterate over its children. We get our print string by joining the current depth list, and then the name.
Every time we drop down into a child list, we add a starting level 1 to our depth list, and when we come out of that child loop, we pop it back off again. For every child in that loop, we increment that last item up.
Outside of that wrapped inner function, we then start the whole thing by looping over our initial boxes, calling wrapped and then incrementing our first level.
The inner wrapped function uses the depth list in a closure. I am willing to bet that others can offer some further improvements on this, but its what I came up with for an example.
Note about the args for the function
We could have also designed recurse to instead take a variable length argument list, instead of checking for a list. It would look like this (and would get rid of that first boxes = check):
def recurse(*boxes):
#boxes will always come in as a tuple no matter what
>>> recurse(example)
>>> recurse(example, example, example)
And if you originally start with a list of box items, you can pass it by doing:
>>> boxes = [example, example, example]
>>> recurse(*example) # this will unpack your list into args
You have two options:
Keep track of the additional information as an additional parameter in your recursion, e.g. myRecursiveFunction(..., ancestry=[])
Have each BoxItem keep track of its parent, whenever it is embedded in a BoxItem (in the __init__ constructor, set child.parent = self for each child). This is bad if you plan to have a BoxItem in more than one box.

Categories