Concatenate Multiple QSplitter in PyQt5 - python-3.x

I have 3 QWidgets which I want to concatenate. Basically it should look like this:
| |
| |
QWidget 1 | QWidget 2 | QWidget 3
| |
| |
QSplitter1 QSplitter2
What I tried so far is:
1) Adding each widget to the corresponding QSplitter
widget1 = QWidget()
widget2 = QWidget()
widget3 = QWidget()
splitter1 = QSplitter(self)
splitter2 = QSplitter(self)
splitter1.addWidget(widget1)
splitter1.addWidget(widget2)
splitter2.addWidget(widget2)
splitter2.addWidget(widget3)
Here I get one moveable splitter, which should be splitter2
2) Adding the complete splitter1 Widget to splitter2 as first argument
widget1 = QWidget()
widget2 = QWidget()
widget3 = QWidget()
splitter1 = QSplitter(self)
splitter2 = QSplitter(self)
splitter1.addWidget(widget1)
splitter1.addWidget(widget2)
splitter2.addWidget(splitter1)
splitter2.addWidget(widget3)
Here only the first splitter is active.
What is the correct approach doing this?

QSplitter is a container widget, and it behaves similarly to a QBoxLayout, adding the possibility of resizing the items. This means that you (normally) only need one splitter, no matter how many widgets you are adding in the same orientation.
What you are referring to as "splitters", are actually the splitter handles (QSplitterHandle classes), so the structure is actually this:
QSplitter -------------------------------------+
| | | |
h H H |
a A A |
n N N |
d QWidget 1 D QWidget 2 D QWidget 3 |
l L L |
e E E |
| | | |
+---------------+---------------+--------------+
So, that's just as easy as this:
widget1 = QWidget()
widget2 = QWidget()
widget3 = QWidget()
splitter = QSplitter(self)
splitter.addWidget(widget1)
splitter.addWidget(widget2)
splitter.addWidget(widget3)

Related

Scoll bar cant locate at the right side of the listbox using grid (Python/Tkinter)

i need little help..below is my codes:
root = Tk()
w, h = root.winfo_screenwidth(), root.winfo_screenheight()
root.geometry("%dx%d+0+0" % (w, h))
f_right = Frame(root,width = 400,height=h)
f_right.pack(side=RIGHT)
my_scrollbar = Scrollbar(f_right,width=30,orient=VERTICAL)
mylist = Listbox(f_right,width=25,height = h, yscrollcommand = my_scrollbar.set,font=('ariel',12),justify='center')
mylist.grid(row=3,column=0,columnspan=2) # row0,row1,row2,column0,column1 has some button and label design
my_scrollbar.config(command = mylist.yview)
my_scrollbar.grid(row=3,column=2,sticky=NS)
for i in range(1000):
mylist.insert(END,"a"+str(i))
root.mainloop()
i try this code with integer items and scroll bar works, but when i try add the item with string, my scroll bar could not work.. please help
Since the listbox occupies column 0 and 1 due to columnspan=2, and the scrollbar is put in column 1 which is covered by the listbox.
Move the scrollbar to column 2 and also set the sticky option to NS:
my_scrollbar.grid(row=3,column=2,sticky=NS)

Maze solver won't backtrack in Python

There are a couple of maze questions similar to this one but none of them ever really go into the why it won't work.
I don't need precise answers. I just need to know why this particular thing doesn't work.
This is the bit of my class Maze that I need help with.
ysize in my example is 10
xsize is 10
xend is 20 (changing it to 19 messes with the results and doesn't draw anything)
yend is 10 (changing it to 9 does this too)
class Maze:
def __init__(self):
self.maze = []
self.xstart = None
self.ystart = None
self.xend = None
self.yend = None
self.xsize = None
self.ysize = None
def read_maze(self, filename):
maze_list = []
f_maze = open(filename)
size = f_maze.readline().split() #
start = f_maze.readline().split() #
end = f_maze.readline().split() #
self.xstart = int(start[1])
self.ystart = int(start[0])
self.xend = (int(end[1])*2)
self.yend = (int(end[0])*2)
self.xsize = (int(size[1])*2)
self.ysize = (int(size[0])*2)
lines = f_maze.readlines()
for line in lines:
maze_list.append(list(line[:len(line)]))
self.maze = maze_list # Assigns to class
def __str__(self):
return ("".join(''.join(line) for line in self.maze))
def solve(self, x, y):
if y > (self.ysize) or x > (self.xsize):
print("1")
return False
if self.maze[y][x] == self.maze[self.yend][self.xend]:
print("2")
return True
if self.maze[y][x] != " ":
print("3")
return False
self.maze[y][x] = "o" # MARKING WITH o for path already taken.
if self.solve(x+1,y) == True:
return True
elif self.solve(x,y+1) == True:
return True
elif self.solve(x-1,y) == True:
return True
elif self.solve(x,y-1) == True:
return True
self.maze[y][x] = " " # ELSE I want it to be replaced with space
return False
This is the current result.
---------------------
|ooooooooooooo| | |
|-+-+-+ +-+-+o+ + +-|
| | | |o| |
| +-+-+ + +-+-+-+ + |
| | | | | |
|-+-+ + + + +-+ +-+-|
| | |
|-+ +-+-+-+-+-+ +-+ |
| | | |
---------------------
I want it like this:
---------------------
|ooooooo | | |
|-+-+-+o+-+-+ + + +-|
| | o| | | |
| +-+-+o+ +-+-+-+ + |
| o| | | | |
|-+-+ +o+ + +-+ +-+-|
| |ooooooooooooo|
|-+ +-+-+-+-+-+ +-+o|
| | | o|
---------------------
I don't know how to fix the indentation format here I apologize. That is my whole code. These are my test statements:
maze = Maze()
maze.read_maze(filename)
maze.solve(maze.xstart, maze.ystart)
print(maze)
The files go in this format saved as .txt files.
5 10
1 1
5 10
---------------------
| | | |
|-+-+-+ +-+-+ + + +-|
| | | | | |
| +-+-+ + +-+-+-+ + |
| | | | | |
|-+-+ + + + +-+ +-+-|
| | |
|-+ +-+-+-+-+-+ +-+ |
| | | |
---------------------
The problem is that as your file stands, xend, yend is (10, 20). To debug why it's not working, you can print(self.maze[self.yend][self.xend]) which returns a dash "-". Now, when your recursive call's (x, y) pair reaches its first dash, it tests True for the line
if self.maze[y][x] == self.maze[self.yend][self.xend]:
and thinks it has solved the maze. Rather, we want to test
if (y, x) == (self.yend, self.xend):
That is, test the coordinates, not the value of the square.
Another point: examining the actual location of the goal, we see that it's here:
+-+ +-+ |
| |
--------- <= this corner is the goal!
Which is unreachable if moving in strictly cardinal directions. Moving the goal a square up or to the left would put it within bounds of the solver algorithm.
This was sufficient to get the code working for me and hopefully is enough to get you moving again.

GridSizer is not placing my elements correctly

what im trying to do is make a 4X3 Gridsizer and attach my object on each spot but when I run it , it appears to run fine but everything is smooshed to the left side. I don't see what im doing wrong Below is the code for that section of my program
class F_WaferMaps ( wx.Frame ):
def __init__( self, parent, dateBegin , dateEnd):
wx.Frame.__init__ ( self, parent, id = wx.ID_ANY, title = wx.EmptyString, pos = wx.DefaultPosition, size = wx.Size( 800,500 ), style = wx.DEFAULT_FRAME_STYLE|wx.TAB_TRAVERSAL )
self.SetSizeHintsSz( wx.DefaultSize, wx.DefaultSize )
gSizer1 = wx.GridSizer( 4, 3, 3,3 )
waferDesign = WaferInfo(die_size = (.3,.45),center_xy= (10 ,6.5), dia= 6.0 , edge_excl= 0.0 )
waferInfo = pd.read_excel("file.xlsx", index_col= None, na_values=['NA'] , usecols=[18,4,5,21,2])
df2 = waferInfo[(waferInfo.FILE_FINISH_TS >= dateBegin) & (waferInfo.FILE_FINISH_TS <= dateEnd) ]
waferSubSet = df2[['X', 'Y' , 'HB', 'TIME' , 'LOT']]
df3 = waferSubSet[~(waferSubSet[['X_COORD', 'Y_COORD', 'LOT_WF']].duplicated(keep = 'last'))]
a = 0
self.hbox = wx.BoxSizer(wx.HORIZONTAL)
self.vbox = wx.BoxSizer(wx.VERTICAL)
waferNum = 0
for lotID, wafNum in df3.groupby(['LOT_WF']):
df4 = wafNum[['X_COORD', 'Y_COORD', 'HB_NUM']]
print(wafNum)
print("\n")
my_list = df4.transform(tuple, axis=1).tolist()
self.panels = wm_core.WaferMapPanel(self, my_list, waferDesign, data_type='discrete'.lower())
gSizer1.Add(self.panels,1)
waferNum += 1
self.SetSizer( gSizer1 )
self.Layout()
self.Centre( wx.BOTH )
so I want my output to look like
A B C
D E F
G H I
J K M
but instead im getting something like
A
B
C
D
E
etc
Below is the class of the WaferMapApp like requested in the comments
def _init_ui(self):
"""Create the UI Elements and bind various events."""
# Create items to add to our layout
self.canvas = FloatCanvas.FloatCanvas(self,
BackgroundColor="BLACK",
)
# Initialize the FloatCanvas. Needs to come before adding items!
self.canvas.InitAll()
# Create the legend
self._create_legend()
# Draw the die and wafer objects (outline, crosshairs, etc) on the canvas
self.draw_die()
if self.plot_die_centers:
self.draw_die_center()
self.draw_wafer_objects()
# Bind events to the canvas
self._bind_events()
# Create layout manager and add items
self.hbox = wx.BoxSizer(wx.HORIZONTAL)
self.hbox.Add(self.legend, 0, wx.EXPAND)
self.hbox.Add(self.canvas, 1, wx.EXPAND)
self.SetSizer(self.hbox)
im not sure if is the layout of this that is causing the issue

pygame sets position of all instances of class to the same value for some reason?

I'm making a snake game in pygame and i've run into a problem. Whenever the snake eats food, the player gains a point. From that point on a tail is created behind the snake whenever it moves. Only the tails the number of player points away from the snake head will be drawn while the rest are deleted. The problem arises when i'm creating the tails. Whenever i create an instance of the tail, i have to get the position of the snake head and subtract away a value equal to the snake size in the opposite direction. That's where the tail will be drawn. However the position of all the tails are set to the same value for some reason and i cant't figure out why that is. I'm using my own library so i cant post it in here but iv'e determined it's not the cause.
import pygame as pg
from random import randrange
import widget
# disp -> display properties
disp = widget.get_json("config", ["display"])
food = widget.Surface(image="../images/food.png", pos=[0, 0])
def set_food_pos(snake):
while True:
pos = [randrange(0, disp["size"][_], disp["cell"]) for _ in range(2)]
safe = 0
for tail in snake.tails:
if tail.pos != pos: safe += 1
if safe == len(snake.tails):
food.pos = pos
food.rect.topleft = food.pos
break
class Snake(widget.Sprite):
""" Snake: main playable sprite """
SIZE = [disp["cell"]] * 2
KEYS = [[276, 275], [273, 274]]
def __init__(self):
self.image = pg.image.load("../images/snake_head.png")
self.pos = widget.VEC(0, 0)
super().__init__(pg.sprite.GroupSingle)
self.axis, self.orient, self.do_move = 0, 1, False
self.past, self.delay = pg.time.get_ticks(), 150
self.speed, self.vel = disp["cell"], [-1, 1]
self.alive, self.points = True, 0
self.tails = [self]
def control(self, key):
axis = [0 if key in Snake.KEYS[0] else 1][0]
if axis != self.axis:
if self.do_move:
self.axis = axis
self.orient = Snake.KEYS[axis].index(key)
self.do_move = False
def time_base_movement(self):
now = pg.time.get_ticks()
if now - self.past >= self.delay:
self.do_move = True
self.pos[self.axis] += self.vel[self.orient] * self.speed
self.past = pg.time.get_ticks()
def eat_food(self):
if food.rect.contains(self.rect):
set_food_pos(self)
self.points += 1
def create_tail(self):
if self.points:
if self.do_move:
pos = [_ for _ in self.rect.topleft]
pos[self.axis] += self.vel[::-1][self.orient] * 20
tail = widget.Sprite(image="../images/snake_head.png", pos=pos)
self.tails.insert(0, tail)
def render_tails(self, surface):
if self.points > 0:
tails = self.tails[:-1]
for tail in tails[0:self.points]: tail.group.draw(surface)
[self.tails.remove(tail) for tail in tails[self.points:]]
def check_boundary_collision(self):
for _ in range(2):
if self.pos[_] > disp["size"][_] - Snake.SIZE[_]:self.alive = False
elif self.pos[_] < 0: self.alive = False
for tail in self.tails[:-1]:
if tail.rect.contains(self.rect): self.alive = False
def reset_properties(self):
if self.alive == False:
print([tail.pos for tail in self.tails[:-1]])
self.tails = [self]
self.do_move = False
self.pos = widget.VEC([0, 0])
self.rect.topleft = self.pos
self.axis, self.orient = 0, 1
self.points, self.alive = 0, True
set_food_pos(self)
def update(self):
if self.alive:
self.time_base_movement()
self.check_boundary_collision()
self.reset_properties()
self.rect.topleft = self.pos
I figured it out, it seems python doesn't create a new copy of an attribute each time it is assigned to a different attribue. Instead the new attribute points to the assigned attribute. The fix for this is the "deepcopy" method in the built in module "copy".
Exe: new_value = copy.deepcopy(old_value)

Draw a scale which ranges from red to green in Tkinter

I would like to draw a scale which ranges from red to green. I managed to do a scale which ranges from green to yellow. Now I have 2 possible solutions :
Either drawing 2 gradients : one from red to yellow and one from yellow to green. Then I can link the 2 drawings.
Or the better solution I think : Drawing one gradient from red to green with a checkpoint on the yellow.
How to implement this second solution ?
Here is my code from green to yellow. You can test it.
import tkinter as tk
class Example(tk.Frame):
def __init__(self, parent):
tk.Frame.__init__(self, parent)
f = GradientFrame(root)
f.pack(fill="both", expand=True)
class GradientFrame(tk.Canvas):
'''A gradient frame which uses a canvas to draw the background'''
def __init__(self, parent, borderwidth=1, relief="sunken"):
tk.Canvas.__init__(self, parent, borderwidth=borderwidth, relief=relief)
self._color1 = "green"
self._color2 = "yellow"
self._color3 = "red"
self.bind("<Configure>", self._draw_gradient)
def _draw_gradient(self, event=None):
'''Draw the gradient'''
self.delete("gradient")
width = 200
height = 50
limit = width
(r1,g1,b1) = self.winfo_rgb(self._color1)
(r2,g2,b2) = self.winfo_rgb(self._color2)
(r3,g3,b3) = self.winfo_rgb(self._color3)
r_ratio = float((r2-r1)) / limit
g_ratio = float((g2-g1)) / limit
b_ratio = float((b2-b1)) / limit
for i in range(limit):
nr = int(r1 + (r_ratio * i))
ng = int(g1 + (g_ratio * i))
nb = int(b1 + (b_ratio * i))
color = "#%4.4x%4.4x%4.4x" % (nr,ng,nb)
self.create_line(0,i,height,i, tags=("gradient",), fill=color)
self.lower("gradient")
for i in range(limit):
nr = int(r1 + (r_ratio * i))
ng = int(g1 + (g_ratio * i))
nb = int(b1 + (b_ratio * i))
color = "#%4.4x%4.4x%4.4x" % (nr,ng,nb)
self.create_line(0,i,height,i, tags=("gradient",), fill=color)
self.lower("gradient")
if __name__ == "__main__":
root = tk.Tk()
Example(root).pack(fill="both", expand=True)
root.mainloop()
Without the generalization or general aesthetics of the above code, I offer you a simple solution (implemented against the red to green gradient) here:
import tkinter
def rgb(r, g, b):
return "#%s%s%s" % tuple([hex(c)[2:].rjust(2, "0") for c in (r, g, b)])
root = tkinter.Tk()
root.title("Gradient")
gradient1 = tkinter.Canvas(root, width=255*2, height=50)
gradient1.pack()
gradient2 = tkinter.Canvas(root, width=255*2, height=50)
gradient2.pack()
for x in range(0, 256):
r = x
g = 255-x
gradient1.create_rectangle(x*2, 0, x*2 + 2, 50, fill=rgb(r, g, 0),
outline=rgb(r, g, 0))
for x in range(0, 256):
r = x*2 if x < 128 else 255
g = 255 if x < 128 else 255 - (x-128)*2
gradient2.create_rectangle(x*2, 0, x*2 + 2, 50, fill=rgb(r, g, 0),
outline=rgb(r, g, 0))
I believe it's what you meant by "yellow checkpoints". Thanks for the challenge! :^)

Resources