I am trying to add a QPushButton widget into a QGroupBox such as:
self.btn = QtGui.QPushButton('Push Button')
self.grp_box = QtGui.QGroupBox('Button Section')
self.grp_box.addWidget(self.btn)
When trying to run the code, I got this error : AttributeError: 'NoneType' object has no attribute 'addWidget'
After some online checking, it seems that QGroupBox only allows setLayout, meaning I will need to use QVBoxLayout or QHBoxLayout etc.
Is there anyway to get around this, adding in a widget without the use of any layout(s)? I am using PyQt.
First create your main layout = QHBoxLayout()
main_layout = QHBoxLayout()
Then create the group box:
group_box = QGroupBox("Group Box")
Create the group box layout:
group_box_layout = QVBoxLayout()
Add widgets to the group box layout like this:
group_box_layout.addWidget(QCheckBox("Check Box 1"))
group_box_layout.addWidget(QCheckBox("Check Box 2"))
group_box_layout.addWidget(QCheckBox("Check Box 3"))
Assign the group box layout to the group box:
group_box.setLayout(group_box_layout)
Assing the group box to the main layout:
main_layout.addWidget(group_box)
And add this at the end:
widget = QWidget()
widget.setLayout(layout1)
self.setCentralWidget(widget)
Related
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:
I have a tkinter interface that uses ttk widgets and would like to have a ttk.MenuButton with gray arrows, in macOSX. Is that possible?
I am creating my Menubutton using this code (self.topframe is a ttk.Frame object):
self.label_menu_btn = ttk.Label(self.topframe, font=self.btnFont, foreground=self.btnTxtColor, text="Copiar…")
self.menu_btn = ttk.Menubutton (self.topframe, text="•••")
self.menu_btn.menu = Menu (self.menu_btn, tearoff=0)
self.menu_btn["menu"] = self.menu_btn.menu
self.menu_btn.menu.add_command(label="Número de objeto", command=self.copiar_obj_num, accelerator="Command+c")
This is what my button looks like:
And this is what I have found in another app, similar to what I want to accomplish:
To do this with ttk you need to first edit the style, then apply it to the widget. It will look something like this.
s = ttk.Style()
s.configure('MyStyle.TMenubutton', background='pink')
var = tk.StringVar()
widget = ttk.OptionMenu(root, var, 'ANY', 'ANY', '0', '1', style="MyStyle.TMenubutton")
Where "MyStyle" is the name of the style you are creating and "TMenubutton" is the name of the style you are forking from.
If you are wanting to make the color of the button, where the '...' is, gray then all you would have to do is insert the 'bg' option in the ttk.Menubutton line like this:
self.menu_btn = ttk.Menubutton (self.topframe, text="•••", bg= "gray")
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()
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.
For example, lets create a Treeview widget using a class as follows:
class FiltersTree:
def __init__(self, master, filters):
self.master = master
self.filters = filters
self.treeFrame = Frame(self.master)
self.treeFrame.pack()
self._create_treeview()
self._populate_root()
def _create_treeview(self):
self.dataCols = ['filter', 'attribute']
self.tree = ttk.Treeview(self.master, columns = self.dataCols, displaycolumns = '#all')
Populate root, insert children as usual. At the end of the codeblock, you can see where I want to put a Combobox in the tree, using a Combo object:
def _populate_root(self):
# a Filter object
for filter in self.filters:
top_node = self.tree.insert('', 'end', text=filter.name)
# a Field object
for field in filter.fields:
mid_node = self.tree.insert(top_node, 'end', text = field.name)
# insert field attributes
self.insert_children(mid_node, field)
def insert_children(self, parent, field):
name = self.tree.insert(parent, 'end', text = 'Field name:',
values = [field.name])
self.tree.insert(parent, 'end', text = 'Velocity: ',
values = [Combo(self)]) # <--- Combo object
...
Next the class definition of Combo follows. The way I understand it, the combobox widget inherits from and must be placed inside the Labelframe widget from ttk:
class Combo(ttk.Frame):
def __init__(self, master):
self.opts = ('opt1', 'opt2', 'etc')
self.comboFrame = ttk.Labelframe(master, text = 'Choose option')
self.comboFrame.pack()
self.combo = ttk.Combobox(comboFrame, values=self.opts, state='readonly')
self.combo.current(1)
self.combo.pack()
So is this completely wrong? I want to have the ability to change between units (eg m/s, ft/s, etc) from within the Treeview widget.
Any suggestions, plz?
The treeview widget doesn't support embedded widgets. The values for the values attribute are treated as strings.
By default, a Treeview is a static display of a forest of lists of strings. However, with work, after carefully reading Treeview references, one can make a Treeview fairly interactive. For this question, I would bind left click to an event handler that compares the mouse x,y to the bounding box (.bbox) for the units attribute cell. If in the box, display a Combobox, initialized with the current value (such as 'flops'), directly on top of the units attribute cell.
Tkinter.ttk Treeview reference and Tcl/tk treeview reference
Of course, it might be easier to put the Treeview in a frame with with a separate Combobox.