QScrollArea won't scroll - pyqt4

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.

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)

How to add a scrollbar to an overlapping canvas in Python tkinter

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)

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]

Tkinter Treeview heading styling

I want to change the background colour of the treeview headings. I have identified the element option of the Treeview.Heading layout responsible for this: Treeheading.cell. The problem is that this setting does not work on the 'vista' theme (Due to drawing issues I assume).
working code (theme looks terrible though):
from tkinter import *
from tkinter import ttk
p=Tk()
separator = PanedWindow(p,bd=0,bg="#202322",sashwidth=2)
separator.pack(fill=BOTH, expand=1)
_frame = Frame(p,bg="#383838")
t=ttk.Treeview(_frame)
t["columns"]=("first","second")
t.column("first",anchor="center" )
t.column("second")
t.heading("first",text="first column")
t.heading("second",text="second column")
t.insert("",0,"dir1",text="directory 1")
t.insert("dir1","end","dir 1",text="file 1 1",values=("file 1 A","file 1 B"))
id=t.insert("","end","dir2",text="directory 2")
t.insert("dir2","end",text="dir 2",values=("file 2 A","file 2 B"))
t.insert(id,"end",text="dir 3",values=("val 1 ","val 2"))
t.insert("",0,text="first line",values=("first line 1","first line 2"))
t.tag_configure("ttk",foreground="black")
ysb = ttk.Scrollbar(orient=VERTICAL, command= t.yview)
xsb = ttk.Scrollbar(orient=HORIZONTAL, command= t.xview)
t['yscroll'] = ysb.set
t['xscroll'] = xsb.set
print(ttk.Style().theme_names())
ttk.Style().theme_use('default')
ttk.Style().configure("Treeview", background="#383838",foreground="white")
ttk.Style().configure("Treeview.Heading",background = "blue",foreground="Black")
p.configure(background='black')
t.grid(in_=_frame, row=0, column=0, sticky=NSEW)
ysb.grid(in_=_frame, row=0, column=1, sticky=NS)
xsb.grid(in_=_frame, row=1, column=0, sticky=EW)
_frame.rowconfigure(0, weight=1)
_frame.columnconfigure(0, weight=1)
separator.add(_frame)
w = Text(separator)
separator.add(w)
p.mainloop()
my attempt using 'vista' theme:
ttk.Style().element_create("Treeheading.cell","from","default")
ttk.Style().configure("Treeview", background="#383838",foreground="white")
ttk.Style().configure("Treeview.Heading",background = "Blue")
element_create has worked in other instances of this problem but with different widgets.
Thank you, any help would be appreciated.
working in python 3. Also the code is not mine, I found it and used it to test.
You are on the right track but need to change the border element rather than the cell element. As you are working on Windows, the treeview cells are being displayed using a system provided theme element from the Visual Styles API. In this case it is a HP_HEADERITEM part from the HEADER class. As this is drawn by the system theme engine you don't get to customise it from Tk aside from selecting alternate looks according to the state.
If you must customise the look of the header then you have to replace the theme part with one that Tk can customise and the default theme is a good choice. I would also recommend that you define this as a custom style so that you can re-style specific widgets and not necessarily all of them.
style = ttk.Style()
style.element_create("Custom.Treeheading.border", "from", "default")
style.layout("Custom.Treeview.Heading", [
("Custom.Treeheading.cell", {'sticky': 'nswe'}),
("Custom.Treeheading.border", {'sticky':'nswe', 'children': [
("Custom.Treeheading.padding", {'sticky':'nswe', 'children': [
("Custom.Treeheading.image", {'side':'right', 'sticky':''}),
("Custom.Treeheading.text", {'sticky':'we'})
]})
]}),
])
style.configure("Custom.Treeview.Heading",
background="blue", foreground="white", relief="flat")
style.map("Custom.Treeview.Heading",
relief=[('active','groove'),('pressed','sunken')])
What we are doing is defining a new widget style using the same layout as for the standard treeview style and replacing the border element. While we have not defined the other custom elements, these are looked up hierarchically so in the absence of a Custom.Treeheading.text it will use a Treeheading.text.
To use this, we set the style of the treeview widget:
t=ttk.Treeview(_frame, style="Custom.Treeview")
Ends up looking like this on Windows 10:

tkinter treeview: how to disable widget?

We're building a GUI interface with Python+tkinter.
The problem is when we want to set the view mode of an entity. I need to set the view mode or state of the treeview widget as 'disabled'.
How can we solve it?
Thanks for any support.
UPDATE
self.frmTab01.trvDetailorder.configure(selectmode='none')
I'm looking for a solution in which appart from disable the selection, affect the visualization of the widget just like an entry widget.
nbro is right, you need to change the Treeview style to make it look disabled. In addition, I also deactivated the possibility to open/close items when the Treeview is disabled using binding tricks on the mouse click.
In my example I added an entry so that you can compare the look on the two widgets. If you are using OS X or Windows, you might need to change the theme (style.theme_use("clam") should do) because their default themes are not very customizable.
from tkinter import Tk
from tkinter.ttk import Treeview, Style, Button, Entry
root = Tk()
def toggle_state():
if "disabled" in tree.state():
e.state(("!disabled",))
tree.state(("!disabled",))
# re-enable item opening on click
tree.unbind('<Button-1>')
else:
e.state(("disabled",))
tree.state(("disabled",))
# disable item opening on click
tree.bind('<Button-1>', lambda e: 'break')
style = Style(root)
# get disabled entry colors
disabled_bg = style.lookup("TEntry", "fieldbackground", ("disabled",))
disabled_fg = style.lookup("TEntry", "foreground", ("disabled",))
style.map("Treeview",
fieldbackground=[("disabled", disabled_bg)],
foreground=[("disabled", "gray")],
background=[("disabled", disabled_bg)])
e = Entry()
e.insert(0, "text")
e.pack()
tree = Treeview(root, selectmode='none')
tree.pack()
tree.insert("", 0, iid="1", text='1')
tree.insert("1", 0, iid='11', text='11')
Button(root, text="toggle", command=toggle_state).pack()
root.mainloop()

Resources