How to add a scrollbar to an overlapping canvas in Python tkinter - python-3.x

I am trying to make an application for a school project, one of the features is a messaging service where users can message each other, this is accessed via a button in which the messaging GUI is loaded. I have already added a canvas as the background for the main GUI at the start of the program but for the message interface I have added another canvas which overlaps and will be using a scrollbar to scroll through the messages and display them.
In essence my problem is that I would like to position another canvas on top of the main canvas using co-ordinates and add a scrollbar which only fits to the right hand side of this smaller canvas.
Thanks in advance :)
I have tried to add a scrollbar to it using pack(), grid(), place() and canvas.create_window() however in all instances the scrollbar either does not appear, does not fill the entire right hand side of the second canvas, or is not placed in the correct position. The closest I got was with the place() function where I have managed to position the scrollbar and canvas using "relx", "rely" and "relheight" however the scrollbar is not scrolling the canvas.
root = Tk() #creates the GUI
root.title("Eclass")
root.iconbitmap(default='icon.ico') #sets icon
root.state('zoomed')
backgroundcolour = root.cget("bg")
screen_width = root.winfo_screenwidth() - 15
screen_height = root.winfo_screenheight()
canvas = Canvas(root, width = screen_width, height = screen_height)
def messaging():
canvas.delete("all")
msg_canvas = Canvas(canvas, width = 1530, height = 730, bg = "red")
#canvas.create_window(1123,600, window = msg_canvas) this is where I tried to add the second canvas using the create_Window function
msg_canvas.place(relx=0.186, rely=0.227)
msg_scrollbar = Scrollbar(canvas, orient="vertical",command=msg_canvas.yview)
msg_scrollbar.place(relx=0.975,rely=0.2295, relheight = 0.717)
msg_canvas.config(scrollregion=msg_canvas.bbox(ALL))
I expect the canvas to be placed within the current co-ordinates given via relx and rely or in previous trial the co-ordinates in canvas.create_window(), I then expect the msg_scrollbar to be on the right hand side of msg_canvas and fill to its Y (the height of the scrollbar should be the same as the height of the canvas). In actuality the canvas and scrollbar are in the correct co-ordinates however the scrollbar does not scroll the msg_canvas even after moving it.

, In essence, my problem is that I would like to position another canvas on top of the main canvas using co-ordinates and add a scrollbar which only fits the right-hand side of this smaller canvas.
I recommend against using place. Both pack and grid are better for creating code that is responsive to changes in resolution, window size, and fonts.
IMHO, the simplest solution is to use pack to put the main canvas at the bottom, then in the remaining space put the scrollbar on the right and the secondary canvas on the left. Note: the order is important, as each time you call pack it will use up all of the space on the given side, reducing the space available to future widgets.
canvas.pack(side="bottom", fill="both", expand=True)
msg_scrollbar.pack(side="right", fill="y")
msg_canvas.pack(side="left", fill="both", expand=True)
You can accomplish the same thing with grid, but it requires additional lines of code to configure row and column weights. With grid order isn't as important since you are explicitly setting rows and columns.
msg_canvas.grid(row=0, column=0, sticky="nsew")
msg_scrollbar.grid(row=0, column=1, sticky="ns")
canvas.grid(row=1, column=0, columnspan=2, sticky="nsew")
root.grid_rowconfigure((0,1), weight=1)
root.grid_columnconfigure(0, weight=1)

Related

How to align items with "side=BOTTOM"/"anchor=S" properly?

I have a GUI app in which i have a BOTTOM scrollbar and a BOTTOM status bar, both at the bottom. I want to align them in such a way that scrollbar comes above the status bar at bottom like this ( in official notepad ) :
Here statusbar is at bottom and scrollbar is at top.
However what i made is :
Here statusbar is at top and scrollbar is at bottom.
I also tried anchor=S but that also didn't worked and throwed the same result.
CODE : ( Status Bar )
statusbar = Frame(textarea)
statusbar.pack(side=BOTTOM, fill=X)
statusbar_Content1 = Label(statusbar, anchor=SW, width=18, text="Ln 1, col 1")
statusbar_Content2 = Label(statusbar, anchor=SW, text="100%")
statusbar_Content3 = Label(statusbar, anchor=SW, width=15, text="Windows (CRLF)")
statusbar_Content4 = Label(statusbar, anchor=SW, width=13, text="UTF-8")
statusbar_Content4.pack(side=RIGHT)
Label(statusbar, anchor=SW, text="|", state=DISABLED).pack(side=RIGHT)
statusbar_Content3.pack(side=RIGHT)
Label(statusbar, anchor=SW, text="|", state=DISABLED).pack(side=RIGHT)
statusbar_Content2.pack(side=RIGHT)
Label(statusbar, anchor=SW, text="|", state=DISABLED).pack(side=RIGHT)
statusbar_Content1.pack(side=RIGHT)
Label(statusbar, anchor=SW, text="|", state=DISABLED).pack(side=RIGHT)
CODE : ( Scroll Bar )
Scroll2 = Scrollbar(root, orient=HORIZONTAL)
Scroll2.pack(side=BOTTOM, fill=X)
Scroll2.config(command=textarea.xview)
CODE : ( Scrollbar is bound with Text widget )
textarea = Text(root, font=fontsmain, yscrollcommand=Scroll.set,xscrollcommand=Scroll2.set, undo=True)
textarea.pack(expand=True, fill=BOTH)
CONCLUSION :
Is there any way of setting these such that SCROLLBAR come above STATUSBAR and vise versa ?
pack works by placing a widget on one side of unallocated space. That means that the results are dependent on the order that widgets are packed. This is called the stacking order.
For example, if you pack the scrollbar first, it will be placed at the very bottom of the window since no space has yet been allocated to anything. When you later call pack on the statusbar, the very bottom of the window has already been allocated so it will be placed at the bottom of the free space above the scrollbar.
Knowing this, when you change the order in which you call pack, you change the order in which the widgets appear in the window. So, a simple solution is to make sure you pack the statusbar before you pack the scrollbar.
pack also has options that let you specify whether a widget is before or after some other widget in the ordering. For example, you can pack the scrollbar and then pack the statusbar but tell it to appear before the scrollbar in the stacking order. When you are using side='bottom', that will result in the statusbar appearing below the scrollbar.
Notice the difference between the following three examples:
statusbar.pack(side="bottom", fill="x")
scrollbar.pack(side="bottom", fill="x")
scrollbar.pack(side="bottom", fill="x")
statusbar.pack(side="bottom", fill="x")
scrollbar.pack(side="bottom", fill="x")
statusbar.pack(side="bottom", fill="x", before=scrollbar)

python tkinter, Scrollbar missing knob(thumb)

【Summary】
By tkinter, python. I set scrollbar on Canvas ... Then that has been succeeded.
But missing knob in scrollbar.
【Background】
This is my application that developing now.
App's purpose is simple. Get some icons from target URL, Then put as tile in Window.
Application Window graphic
As you see, Can't put all icons in initial window size.
So I want to use scrollbar, Then scrolldown to show below icons.
Now succeeded put scrollbar at right side. But in that bar missing knob(thumb).
So this isn't working as scrollbar (TωT)
【Question】
How to make code to this vertical scrollbar working?
This is scrollbar build section in my src file.
Already exists scrollbar, It's almost fine... But maybe missing something.
# Make vertical scrollbar to see all stickers -----------------------
outCV = tk.Canvas(self.iconsFrame, width=GUIController.__windowWidth, height=GUIController.__windowHeight)
scrollbar = tk.Scrollbar(self.iconsFrame, orient=tk.VERTICAL)
scrollbar.config(command=outCV.yview)
scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
outCV.configure(yscrollcommand=scrollbar.set)
outCV.pack()
# --------------------------------------------------------------------
gridRow = 0
gridCol = 0
for i, tkimg in enumerate(self.tkimgs) :
# Put icons as tile.
Please give me your knowledge.
(FYI) https://github.com/we-yu/L_SL/blob/develop/Canvas_in_Canvas/src/GUICtrl.py Line:196
I found already how to solve it by myself.
There are 2 points in my case.
Should be set scrollregion
Scrollbar works for only Canvas. So, on my case. Need overlapped frames and canvases.
Solving way
self.outerCV = tk.Canvas(self.iconsFrame, width=GUIController.__windowWidth, height=GUIController.__windowHeight)
scrollbar = tk.Scrollbar(self.iconsFrame, orient=tk.VERTICAL)
scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
scrollbar.config(command=self.outerCV.yview)
self.outerCV.config(scrollregion=(0, 0, 2000, 2000), yscrollcommand=scrollbar.set)
self.outerCV.pack(fill=tk.BOTH)
galleryFrame = tk.Frame(self.outerCV)
self.galleryTag = self.outerCV.create_window((0, 0), window=galleryFrame, anchor=tk.NW, width=self.outerCV.cget('width'))
Put Basement-frame at bottom of window. Then put Canvas on that. Scrollbar attaches on this Canvas.
But if put Canvas on this Canvas, Scrollbar works for scroll, But window won't be working.
So, put frame on scrollbar-canvas. Then Main graphic canvases on that frame.
[Canvas-1][Canvas-2][Canvas-3]...
[Frame for gallery]
[Canvas for Scrollbar]
[Basement frame]

How to fix canvas size not following the configuration

I created a canvas and specified the height using (width, height), but when it comes to the GUI, it doesn't change the size at all.
atEdit=Frame(Attend,borderwidth=0)
atEdit.grid(row=2,column=0,columnspan=5,sticky='news',pady=(0,10),padx=10)
scrollbar = Scrollbar(atEdit)
scrollbar.grid(row=0,column=5,sticky='ns')
canvas = Canvas(atEdit,bg='black',height=20)
canvas.grid(row=0,column=0,columnspan=5,sticky='news')
scrollbar.config(command = canvas.yview)
left = Frame(canvas,borderwidth=0,bg='#dbdbdb',height=2)
left.grid(row=0,column=0,sticky='news')
right=Frame(canvas,borderwidth=0,bg='white',height=2)
right.grid(row=0,column=1,columnspan=4,sticky='news')
left.bind('<Configure>',lambda event,canvas=canvas:self.onFrameConfigure(canvas))
right.bind('<Configure>',lambda event,canvas=canvas:self.onFrameConfigure(canvas))
I add labels to the 'left' and 'right', which increases the size of the frames, but it shouldn't grow larger than the specified size. Is there anyway to make the canvas a fixed size, even if I add more labels to it, and be able to scroll through it using the scrollbar?
def onFrameConfigure(self,canvas):
canvas.configure(scrollregion=canvas.bbox("all"))

how to create a 4-way split screen in pygame

I have a project where i have to create a 4 way split screen using pygame. On this screen i have to draw the same image on each of the screen just have different view of the image. I just can not figure out how to create this 4 way split screen using pygame.
I need my screen to be divided like above so i can draw my points onto each section.
I have been looking around and I can not find anything like this so any help would be great
thanks
In addition to the surface you have that gets rendered to the display, likely called something like screen, you should create another surface which all of the "action" gets drawn to. You can then use a Rect object for each quadrant of the screen which will represent the "camera" (assuming each quadrant doesn't necessarily need to show exactly the same image). When you draw back to screen, you use each camera Rect object to select a portion of the game space to draw to a specific quadrant.
# canvas will be a surface that captures the entirety of the "action"
canvas = pygame.Surface((800, 600))
# the following are your "camera" objects
# right now they are taking up discrete and even portions of the canvas,
# but the idea is that they can move and possibly cover overlapping sections
# of the canvas
p1_camera = pygame.Rect(0,0,400,300)
p2_camera = pygame.Rect(400,0,400,300)
p3_camera = pygame.Rect(0,300,400,300)
p4_camera = pygame.Rect(400,300,400,300)
On each update, you would then use these "camera" objects to blit various portions of the canvas back to the screen surface.
# draw player 1's view to the top left corner
screen.blit(canvas, (0,0), p1_camera)
# player 2's view is in the top right corner
screen.blit(canvas, (400, 0), p2_camera)
# player 3's view is in the bottom left corner
screen.blit(canvas, (0, 300), p3_camera)
# player 4's view is in the bottom right corner
screen.blit(canvas, (400, 300), p4_camera)
# then you update the display
# this can be done with either display.flip() or display.update(), the
# uses of each are beyond this question
display.flip()
There is no functions to split screen. But you can draw 4 views directly on screen or you can draw on 4 surfaces (pygame.Surface) and than blit surfaces on screen.
Since you were looking for a way to split the screen in to 4 sections and draw some points on to them I'd suggest creating 4 subsurface surfaces of the original "canvas" image for convenience.
These surfaces would act as your player(split screen) canvasses which can easily be modified.
This will enable the usage of normalized coordinates for player specific drawing purposes.
Assuming you have a screen surface set up
# Image(Surface) which will be refrenced
canvas = pygame.Surface((800, 600))
# Camera rectangles for sections of the canvas
p1_camera = pygame.Rect(0,0,400,300)
p2_camera = pygame.Rect(400,0,400,300)
p3_camera = pygame.Rect(0,300,400,300)
p4_camera = pygame.Rect(400,300,400,300)
# subsurfaces of canvas
# Note that subx needs refreshing when px_camera changes.
sub1 = canvas.subsurface(p1_camera)
sub2 = canvas.subsurface(p2_camera)
sub3 = canvas.subsurface(p3_camera)
sub4 = canvas.subsurface(p4_camera)
Now drawing on any of of the subsurfaces with these normalized coordinates
# Drawing a line on each split "screen"
pygame.draw.line(sub2, (255,255,255), (0,0), (0,300), 10)
pygame.draw.line(sub4, (255,255,255), (0,0), (0,300), 10)
pygame.draw.line(sub3, (255,255,255), (0,0), (400,0), 10)
pygame.draw.line(sub4, (255,255,255), (0,0), (400,0), 10)
# draw player 1's view to the top left corner
screen.blit(sub1, (0,0))
# player 2's view is in the top right corner
screen.blit(sub2, (400, 0))
# player 3's view is in the bottom left corner
screen.blit(sub3, (0, 300))
# player 4's view is in the bottom right corner
screen.blit(sub4, (400, 300))
# Update the screen
pygame.display.update()
Note that modifications to the subsurface pixels will affect the canvas as well. I'd recommend reading the full documentation on subsurfaces.

QScrollArea won't scroll

I am trying to put a QtableWidget inside a QScrollArea (only one widget) to be able to scroll it vertically and horizontaly (I have reasons not to use scrollbars in Qtablewidget ). However, no scrollbar appears even though the tableWidget can’t fit inside the window so I set QtCore.Qt.ScrollBarAlwaysOn, and now they are there but they are gray and still I can't scroll.
Here is my code:
class Table(QtGui.QDialog):
def __init__(self, parent=None):
super(Table, self).__init__(parent)
layout = QtGui.QGridLayout()
tableWidget = QtGui.QTableWidget()
#.... set up and populate tableWidget here 1000rows-10col ....
myScrollArea = QtGui.QScrollArea()
myScrollArea.setWidgetResizable(True)
myScrollArea.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOn)
myScrollArea.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOn)
myScrollArea.setWidget(tableWidget)
layout.addWidget(myScrollArea)
self.setLayout(layout)
self.setMinimumSize(1000, 700)
I am begginer with PyQt and I don't really understand layouts and containers, so I can't figure out what I'm doing wrong. Please point me in right direction, help would be appreciated.
QtScrollBar by default has horizontal and vertical scrollBar.
tablewidget by default has horizontal and vertical scrollBar. so i have made it off.
just using the resize event i have resized width and height of tablewidget.
class MainWin(QtGui.QDialog):
def __init__(self,parent=None):
QtGui.QDialog.__init__(self,parent)
self.table =QtGui.QTableWidget(100,4)
self.table.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
self.table.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
lay = QtGui.QGridLayout()
self.sc = QtGui.QScrollArea()
self.sc.setWidget(self.table)
lay.addWidget(self.sc,0,0)
self.setLayout(lay)
def resizeEvent(self,event):
self.table.resize(self.sc.width(),self.sc.height())
def main():
app=QtGui.QApplication(sys.argv)
win=MainWin()
win.show()
sys.exit(app.exec_())
main()
I finally get it:
I've used resizeColumnsToContents() and resizeRowsToContents() to make the columns/rows of the table adjust to the data - text, but that doesn't do the same thing with the Table itself - table height and width stays the same. So in order to make table to be sized around the rows and columns I've used this:
self.table.resizeRowsToContents()
self.table.resizeColumnsToContents()
self.table.setFixedSize(self.table.horizontalHeader().length(), self.table.verticalHeader().length())
and now I can scroll with QScrollArea's scrollbars through entire table.

Resources