tkinter treeview: how to disable widget? - python-3.x

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()

Related

Get system-default labels from QMessageBox standard buttons

I want to show the text from the 'Yes' and the 'No' button in the informative text of a QMessageBox, but I don't see how I can get these labels from the buttons.
from PyQt5.QtWidgets import *
import sys
app = QApplication(sys.argv)
msgbox = QMessageBox()
msgbox.setStandardButtons(msgbox.Yes | msgbox.No)
info_text = "Click '{yes}' to confirm. Click '{no}' to abort."
msgbox.setInformativeText(info_text)
if msgbox.exec_() == msgbox.Yes:
print("Confirmed")
else:
print("Aborted")
By calling setStandardButtons, the button order and the button labels will be set to the default for the current operating system and the current language setting. How can I obtain these defaults so that I can use them for the slots in the string info_text?
I thought about using the buttons attribute from the QMessageBox object, which is a list of QPushButton objects. I can read the labels from there, but I don't see how I could determine whether the first element in the list is the Yes or the No button.
Okay, I was being stupid: along side the buttons attribute, there is also the button() method, which takes as its argument the button type that I want to retrieve. I can then use text() to get the label. Finally, the hotkey marker & has to be stripped from the label:
info_text = "Click '{yes}' to confirm. Click '{no}' to abort.".format(
yes=msgbox.button(msgbox.Yes).text().replace("&", ""),
no=msgbox.button(msgbox.No).text().replace("&", ""))
msgbox.setInformativeText(info_text)

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:

In Python3/tkinter Is there a way to change the background color of the arrows in a ttk.Menubutton?

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")

How to prevent a closing tab QTabWidget? PyQT4

With this code:
QtCore.QObject.connect(self.tabWidget, QtCore.SIGNAL("tabCloseRequested(int)"),
self.tabWidget.removeTab)
I can close any tab QTabWidget, and the names of these tabs are:
work_1
work_2
work_3
But I want the tab work_1 never closes.
Use Index did not work for two reasons:
The tabs can be dynamically moved by this code:
self.tabWidget.setMovable (True)
That makes the Index are constantly changing.
The user has the ability to add new tabs.
Tabs can be identified by their widgets, and the widgets can be identified by their objectName (or some other unique attribute):
self.tabWidget.tabCloseRequested.connect(sef.removeTab)
...
def removeTab(self, index):
widget = self.tabWidget.widget(index)
if widget is not None and widget.objectName() != 'work_1':
self.tabWidget.removeTab(index)
or perhaps more simply:
if widget is not None and widget is not self.work_1:
self.tabWidget.removeTab(index)

PyQt QComboBox setting number of visible items in dropdown

I'm working on an application in PyQt that takes an object dictionary and allows you to plot the variables streaming from a robot in real time. One of the things I'm working on to enable this is a drop down menu. Unfortunately, we have a couple hundred variables, so my PyQt Combobox pops up from the top of the screen to the bottom with items when clicked. I'd like to limit the number of items displayed at a time to 20, with the ability to scroll to see the rest. I've tried using the documented setMaxVisibleItems method, but it doesn't affect the drop down at all. Any recommendations?
Code here:
#!/usr/bin/env python
from PyQt4.QtCore import Qt
from PyQt4.QtGui import QComboBox, QApplication
from cli.parc2_od import cli_od
import sys
app = QApplication(sys.argv)
items = cli_od.OD.keys() #Items to populate dropdown.
combo = QComboBox()
#The solution:
combo.setStyleSheet("QComboBox { combobox-popup: 0; }")
combo.setMaxVisibleItems(10)
combo.addItems(items)
combo.resize(300, 30)
combo.show()
sys.exit(app.exec_())
According to the documentation:
The property maxVisibleItems is ignored for non-editable comboboxes in styles that returns true for `QStyle::SH_ComboBox_Popup such as the Mac style or the Gtk+ Style.
And you can override that SH_ComboBox_Popup style hint with a style sheet:
combo.setStyleSheet("QComboBox { combobox-popup: 0; }");

Resources