Horizontal scrolling won't activate for ttk Treeview widget - python-2.7

I'm using the ttk Treeview widget to implement a folder/path selection dialog. It's all working as expected except that my horizontal scrollbar won't activate. No matter how wide the folder path goes horizontally, and no matter how narrow the window, the horizontal slider never appears. Vertical scrolling is working perfectly though.
I'm figuring it's either some kind of limitation when you only use one column in the treeview, or just a newbie mistake with configuring and connecting the widgets. I'd bet on the latter.
Example with dialog widened to show full folder depth:
Dialog narrowed to the point where horizontal scrolling should activate (but doesn't):
Here's my GUI layout code:
winDirSel = tk.Toplevel()
winDirSel.title('Select Test Directory...')
tvwDirSel = ttk.Treeview(winDirSel,
height=10,padding=3,
show='tree')
lblTestDir = tk.Label(winDirSel, relief=tk.SUNKEN,
justify=tk.LEFT, anchor=tk.W,
textvariable=ctrlTestDir,width=80)
scbHDirSel = ttk.Scrollbar(winDirSel,
orient=tk.HORIZONTAL,
command=tvwDirSel.xview)
scbVDirSel = ttk.Scrollbar(winDirSel,
orient=tk.VERTICAL,
command=tvwDirSel.yview)
tvwDirSel.configure(xscrollcommand=scbHDirSel.set,
yscrollcommand=scbVDirSel.set)
lblTestDir.grid(row=0,column=0,sticky=tk.EW)
tvwDirSel.grid(row=1,column=0,sticky=tk.NSEW)
scbVDirSel.grid(row=1,column=1,sticky=tk.NS)
scbHDirSel.grid(row=2,column=0,sticky=tk.EW)
winDirSel.rowconfigure(1,weight=1)
winDirSel.columnconfigure(0,weight=1)

OK, after some playing with minwidth and stretch, I think I have a better handle on it. The horizontal scrolling is triggered by the column-edge going out of the window's bounds, not the content of the column. So you can use these parameters to force the column to be wider and thus force the scrolling.
The problem though is that you then lose the automatic adjustment of the column width to suit the width of the tree itself. You either have to force it very wide to accommodate any (assumed) likely folder depth, or you live with folder names getting truncated at the right boundary of the column.
So bottom line: it's just a limitation of the widget itself. (At least with respect to its behavior on my platform, MS Windows.)

Here's what I finally came up with to display a TreeView of files which are lazy-loaded (thanks to this answer) which is inside a PanedWindow (SplitterWindow in wxPython terms) along with a Notebook. The scrollbars are auto-displayed/hidden as needed, thanks to this example.
import os
import Tkinter as tk
import ttk as ttk
from ScrolledText import ScrolledText
class App(object):
def __init__(self, master, path):
splitter = tk.PanedWindow(master, orient=tk.HORIZONTAL)
# left-side
frame_left = tk.Frame(splitter)
self.tree = ttk.Treeview(frame_left, show='tree')
ysb = ttk.Scrollbar(frame_left, orient='vertical', command=self.tree.yview)
xsb = ttk.Scrollbar(frame_left, orient='horizontal', command=self.tree.xview)
# right-side
frame_right = tk.Frame(splitter)
nb = ttk.Notebook(frame_right)
page1 = ttk.Frame(nb)
page2 = ttk.Frame(nb)
text = ScrolledText(page2)
# overall layout
splitter.add(frame_left)
splitter.add(frame_right)
splitter.pack(fill=tk.BOTH, expand=1)
# left-side widget layout
self.tree.grid(row=0, column=0, sticky='NSEW')
ysb.grid(row=0, column=1, sticky='ns')
xsb.grid(row=1, column=0, sticky='ew')
# left-side frame's grid config
frame_left.columnconfigure(0, weight=1)
frame_left.rowconfigure(0, weight=1)
# right-side widget layout
text.pack(expand=1, fill="both")
nb.add(page1, text='One')
nb.add(page2, text='Two')
nb.pack(expand=1, fill="both")
# setup
self.tree.configure(yscrollcommand=lambda f, l:self.autoscroll(ysb,f,l), xscrollcommand=lambda f, l:self.autoscroll(xsb,f,l))
# use this line instead of the previous, if you want the scroll bars to always be present, but grey-out when uneeded instead of disappearing
#self.tree.configure(yscrollcommand=ysb.set, xscrollcommand=xsb.set)
self.tree.heading('#0', text='Project tree', anchor='w')
self.tree.column("#0",minwidth=1080, stretch=True)
# add default tree node
abspath = os.path.abspath(path)
self.nodes = dict()
self.insert_node('', abspath, abspath)
self.tree.bind('<<TreeviewOpen>>', self.open_node)
def autoscroll(self, sbar, first, last):
"""Hide and show scrollbar as needed."""
first, last = float(first), float(last)
if first <= 0 and last >= 1:
sbar.grid_remove()
else:
sbar.grid()
sbar.set(first, last)
def insert_node(self, parent, text, abspath):
node = self.tree.insert(parent, 'end', text=text, open=False)
if os.path.isdir(abspath):
self.nodes[node] = abspath
self.tree.insert(node, 'end')
def open_node(self, event):
node = self.tree.focus()
abspath = self.nodes.pop(node, None)
if abspath:
self.tree.delete(self.tree.get_children(node))
for p in os.listdir(abspath):
self.insert_node(node, p, os.path.join(abspath, p))
if __name__ == '__main__':
root = tk.Tk()
root.geometry("800x600")
app = App(root, path='.')
root.mainloop()

import tkinter as tk
import tkinter.ttk as ttk
import tkinter.font as tk_font
class TreeListBox:
def __init__(self, master, root, dict_group):
self.master = master
self.root = root
self.dict_group = dict_group
self.level = 0
self.setup_widget_tree()
self.build_tree(self.root, '')
def setup_widget_tree(self):
container_tree = tk.Frame(self.master, width=250, height=300)
container_tree.propagate(False)
container_tree.pack(side="left", fill='y')
self.tree = ttk.Treeview(container_tree, show="tree", selectmode='browse')
fr_y = tk.Frame(container_tree)
fr_y.pack(side='right', fill='y')
tk.Label(fr_y, borderwidth=1, relief='raised', font="Arial 8").pack(side='bottom', fill='x')
sb_y = tk.Scrollbar(fr_y, orient="vertical", command=self.tree.yview)
sb_y.pack(expand='yes', fill='y')
fr_x = tk.Frame(container_tree)
fr_x.pack(side='bottom', fill='x')
sb_x = tk.Scrollbar(fr_x, orient="horizontal", command=self.tree.xview)
sb_x.pack(expand='yes', fill='x')
self.tree.configure(yscrollcommand=sb_y.set, xscrollcommand=sb_x.set)
self.tree.pack(fill='both', expand='yes')
def build_tree(self, parent, id_stroki):
self.level += 1
id = self.tree.insert(id_stroki, 'end', text=parent)
# -----------------
col_w = tk_font.Font().measure(parent)
if col_w > 1000:
col_w -= 400
elif col_w > 500:
col_w -= 200
elif col_w > 300:
col_w -= 100
col_w = col_w + 25 * self.level
if col_w > self.tree.column('#0', 'width'):
self.tree.column('#0', width=col_w)
# -----------------
for element in sorted(self.dict_group[parent]):
self.build_tree(element, id)
self.level -= 1
if __name__ == '__main__':
dict_group = {'Nomenclature': ['ABC1', 'ABC2'],
'ABC1': ['ABC3', 'ABC4'],
'ABC2': ['ABC5'],
'ABC3': ['ABC______________________________________6'],
'ABC4': ['ABC--------------------------------------8'],
'ABC5': ['ABC######################################9'],
'ABC______________________________________6': [],
'ABC--------------------------------------8': [],
'ABC######################################9': []
}
root = tk.Tk()
myTest = TreeListBox(root, 'Nomenclature', dict_group)
root.mainloop()

Related

How can i disable the selection highlighted green in table widget using pyqt4

Here is my sample code.I need to remove green highlighted selection in my table widget for that i wrote QtGui.QAbstractItemView.NoSelection but using this line i am not able to move my cursor up,down ,left and right in table widget.Can any one please tell me how to disable the green selection and i want to move my cursor using with arrow keys in keyboard..Please help me..Thank you in advance..
Given below is my code:
import sys
from PyQt4 import QtCore,QtGui
global data_arraylist,count,pushBtnList,lineeditList
class Table_Program(QtGui.QMainWindow ):
def __init__(self,config=None, parent=None):
super(Table_Program, self).__init__(parent)
global data_arraylist,count,pushBtnList,lineeditList
self.scrollArea_left = QtGui.QScrollArea(widgetResizable=True)
self.mainw2 = QtGui.QWidget()
self.mainw2.showFullScreen()
self.scrollArea_left.setWidget(self.mainw2)
self.newvbox = QtGui.QGridLayout(self.mainw2)
self.table = QtGui.QTableWidget()
self.table_item = QtGui.QTableWidgetItem()
self.table.setRowCount(1)
self.table.verticalHeader().hide()
self.table.setColumnCount(4)
self.table.setHorizontalHeaderLabels(("S.no, Item Description,Qty,Rate(Rs:),"",").split(','))
self.newvbox.addWidget(self.table,0,0)
self.table.setColumnWidth(2,170)
self.table.setColumnWidth(3,200)
self.btn1 = QtGui.QPushButton(icon=QtGui.QIcon("./plus1.png"))
self.btn1.setIconSize(QtCore.QSize(30,20))
self.table.setCellWidget(0,0,self.btn1)
self.btn1.clicked.connect(self.insert_rows)
self.table.setItem(0,4,QtGui.QTableWidgetItem(str(" ")))
self.setCentralWidget(self.scrollArea_left)
# QtGui.QShortcut(QtGui.QKeySequence(QtCore.Qt.Key_Up), self, self.moveup)
def insert_rows(self,typ=None):
layout_item = QtGui.QHBoxLayout()
cellWidget = QtGui.QWidget()
self.itemlineedit = QtGui.QLineEdit()
self.itemlineedit.setFrame(False)
self.search_btn = QtGui.QPushButton(icon=QtGui.QIcon('search.png'))
self.search_btn.setIconSize(QtCore.QSize(20,20))
layout_item.addWidget(self.itemlineedit)
layout_item.addStretch()
layout_item.addWidget(self.search_btn)
cellWidget.setLayout(layout_item)
global pushBtnList
ct = self.table.rowCount()
self.table.insertRow(ct-1)
self.btn = QtGui.QPushButton()
self.btn.setIcon(QtGui.QIcon("./delete.png"))
self.btn.setIconSize(QtCore.QSize(30,60))
self.table.setItem(ct-1,0,QtGui.QTableWidgetItem(str(ct)))
self.table.setCellWidget(ct -1,5,self.btn)
index = QtCore.QPersistentModelIndex(self.table.model().index(ct -1, 5))
self.quantybutton = QtGui.QLineEdit()
self.itemlineedit.textChanged.connect(partial(self.product_rate,ct))
completer = QtGui.QCompleter()
self.itemlineedit.setCompleter(completer)
self.model = QtGui.QStringListModel()
self.itemlineedit.resize(100,50)
completer.setModel(self.model)
self.item_rate = QtGui.QLabel()
self.table.setCellWidget(ct-1, 3,self.item_rate)
self.table.setCellWidget(ct-1, 2, self.quantybutton)
self.table.setCellWidget(ct -1, 1, cellWidget)
self.table.setRowHeight(ct-1,50)
self.table.setColumnWidth(1,170)
def get_data(self,model):
model.setStringList(["item1","item2","item3"])
def product_rate(self,text,index):
self.quantybutton.setText("1")
self.item_rate.setText(str("20"))
self.get_data(self.model)
if __name__ == "__main__":
app = QtGui.QApplication(sys.argv)
tb = Table_Program()
tb.show()
tb.resize(600,300)
sys.exit(app.exec_())
A simple solution is to implement a custom QStyle that disables the highlight option in the QTableWidget:
from PyQt4 import QtCore, QtGui
class CustomStyle(QtGui.QCommonStyle):
def drawPrimitive(self, element, option, painter, widget):
if element == QtGui.QStyle.PE_PanelItemViewItem:
option.state &= ~QtGui.QStyle.State_Selected
super(CustomStyle, self).drawPrimitive(element, option, painter, widget)
if __name__ == '__main__':
import sys
app = QtGui.QApplication(sys.argv)
w = QtGui.QTableWidget(5, 4)
w.setStyle(CustomStyle()) # <--- set style
w.show()
sys.exit(app.exec_())

Reduce size of edit box in CellRendererText in Gtk+ Python

I am trying to reduce the size of edit box that appears while editing a cell of treeview in Gtk+ Python.
Here is my code :
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk
class CellRendererTextWindow(Gtk.Window):
def __init__(self):
Gtk.Window.__init__(self, title="CellRendererText Example")
self.set_default_size(200, 200)
self.liststore = Gtk.ListStore(str, str)
self.liststore.append(["Fedora", "http://fedoraproject.org/"])
self.liststore.append(["Slackware", "http://www.slackware.com/"])
self.liststore.append(["Sidux", "http://sidux.com/"])
treeview = Gtk.TreeView(model=self.liststore)
select_render = Gtk.CellRendererToggle()
select_render.set_property('activatable', True)
select_render.set_property("radio", True)
select_render.connect('toggled', self.on_toggle)
select_column = Gtk.TreeViewColumn(" %s" %('Select'), select_render,
active=0)
select_column.set_clickable(True)
treeview.append_column(select_column)
renderer_text = Gtk.CellRendererText()
renderer_text.set_property("editable", True)
column_text = Gtk.TreeViewColumn("Text", renderer_text, text=0)
treeview.append_column(column_text)
renderer_editabletext = Gtk.CellRendererText()
renderer_editabletext.set_property("editable", True)
column_editabletext = Gtk.TreeViewColumn("Editable Text",
renderer_editabletext, text=1)
treeview.append_column(column_editabletext)
renderer_editabletext.connect("edited", self.text_edited)
self.add(treeview)
def text_edited(self, widget, path, text):
self.liststore[path][1] = text
def on_toggle(self, cell, path):
print "Toggled"
win = CellRendererTextWindow()
win.connect("destroy", Gtk.main_quit)
win.show_all()
Gtk.main()
Here is the output of this code:
When I try to edit cell it appears like this :
What I want is that, I want to reduce the size of that edit box which appears while editing cell because it is overlapping other cells of row I want that edit box to appear within its column width, Notice I don't want to affect column or cell size in this order.
I tried to set various properties like: width, width-chars, max-width-chars etc.
max-width-chars shows some impact but then it reduces the size of column as well.
Previously it was happening in gtk2, but in Gtk3+ it is showing such kind of impact.
Do anybody have any solution to this? I am really stuck with it.
The edit box of gtk cell renderer text has some fixed width.
So when you distribute the each columns with some space the overlap of edit box won't happen.
Here in your code I have simple increase the width of the windows size, in that way the cellrendererText gets enough space to open the edit box without overlapping with other box
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk
class CellRendererTextWindow(Gtk.Window):
def __init__(self):
Gtk.Window.__init__(self, title="CellRendererText Example")
self.set_default_size(400, 200)
self.liststore = Gtk.ListStore(str, str)
self.liststore.append(["Fedora", "http://fedoraproject.org/"])
self.liststore.append(["Slackware", "http://www.slackware.com/"])
self.liststore.append(["Sidux", "http://sidux.com/"])
treeview = Gtk.TreeView(model=self.liststore)
treeview.set_grid_lines(Gtk.TreeViewGridLines.BOTH)
select_render = Gtk.CellRendererToggle()
select_render.set_property('activatable', True)
select_render.set_property("radio", True)
select_render.connect('toggled', self.on_toggle)
select_column = Gtk.TreeViewColumn(" %s" %('Select'), select_render, active=0)
select_column.set_clickable(True)
treeview.append_column(select_column)
renderer_text = Gtk.CellRendererText()
renderer_text.set_property("editable", True)
column_text = Gtk.TreeViewColumn("Text", renderer_text, text=0)
column_text.set_sizing(Gtk.TreeViewColumnSizing.FIXED)
column_text.set_expand(True)
treeview.append_column(column_text)
renderer_editabletext = Gtk.CellRendererText()
renderer_editabletext.set_property("editable", True)
column_editabletext = Gtk.TreeViewColumn("Editable Text", renderer_editabletext, text=1)
treeview.append_column(column_editabletext)
renderer_editabletext.connect("edited", self.text_edited)
self.add(treeview)
def text_edited(self, widget, path, text):
self.liststore[path][1] = text
def on_toggle(self, cell, path):
print "Toggled"
win = CellRendererTextWindow()
win.connect("destroy", Gtk.main_quit)
win.show_all()
Gtk.main()
Here is the output

Tkinter assign buttons to entries in dynamically created widgets

How can I access an Entry content with pressing the corresponding Button in dynamically created widgets?
Below is the best I come up with so far. Thank you for any help.
from Tkinter import *
class App(object):
def __init__(self, master):
self.master = master
self.mf = Frame(self.master)
self.l = ["white", "red", "blue", "brown"]
self.font = ("Arial", 30)
self.c, self.r = 1, 0
self.cc, self.rr = 0, 0
self.bel = []
for c in self.l:
action = self.print_entry
self.e = Entry(self.mf, bg=c, width=10, font=self.font)
self.e.grid(row=self.r, column=self.c)
self.b = Button(self.mf, bg=c, text=c, font=self.font)
self.b.grid(row=self.rr, column=self.cc)
self.b.config(command=action)
self.bel.append((self.b, self.e))
self.rr += 1
self.r += 1
self.mf.pack()
def print_entry(self): # this function prints the content of the entry
pass
def main():
root = Tk()
display = App(root)
root.mainloop()
if __name__=="__main__":
main()
You can pass a reference to the entry widget into the command, using lambda or functools.partial. For example:
self.b.config(command= lambda entry=self.e: action(entry))
...
def print_entry(self, entry):
print("the entry is '%s'" % entry.get())
By the way, using self.b and self.e is pointless, since those variables will only ever hold references to the last button and last entry. You should either use a local variable, and/or append the values to a list.

Why is the tkinter window blank?

My code uses pygame in order to play all MIDI files within its location, however A Tkinter Slider Window is supposed to show up but it doesn't. And I don't know why.
import os,fnmatch,pygame
import Tkinter as tk
pygame.mixer.init()
root = tk.Tk()
List = []
Song = 0
def getThrottle(event):
Volume = Throttle.get()
print "|"*((Volume)*50/100)
def Update():
List = []
for file in os.listdir('.'):
if fnmatch.fnmatch(file, '*.mid'):
List.append(file)
return List
class Slider:
def _Init_(self,root):
self.Throttle = self.Scale(master, from_=0, to=100, tickinterval=10, length=200, orient=HORIZONTAL, command=getThrottle)
self.Throttle.set(0)
self.Throttle.pack()
List = Update()
S = Slider()
root.mainloop()
print List
while True:
while Song <= len(List):
pygame.mixer.music.load(List[Song])
pygame.mixer.music.play(1)
while pygame.mixer.music.get_busy() == True:
List=Update()
Song = 3
Song = 0

Cannot populate radio button correctly - Tkinter

I am trying out few things with Tkinter as an exercise.
In my example app i want user to select one option from drop down list. Based on this selection i want to populate the list of radio button. I have created a list of values to populate in dropdown list and a dictionary for radio button. please check the code.
See below some working and not working examples:
As you can see from the picture. The first window works well. Second also. However the third window is not quite correct. The option '410' remains. I am making subsequent selections without closing the application.
I think perhaps i am not declaring the variables at the right place. It would be very helpful if some one can have a look at the code and rectify it.
Code:
from Tkinter import *
import ttk
class App(Frame):
def __init__(self,parent):
Frame.__init__(self)
self.parent = parent
self.v = IntVar()
#self.radio_value = []
#self.i = 0
self.GUI()
def GUI(self):
self.master.title('Example')
self.pack(fill = BOTH, expand = 1)
self.options = ['a1','a2','a3','a4','a5']
self.box_value = StringVar()
self.box = ttk.Combobox( self, textvariable=self.box_value)
self.box.bind("<<ComboboxSelected>>", self.set_Radio)
self.box['values'] = self.options
self.box.current(0)
self.box.grid(row = 0, column = 0 )
self.choices = {'a1':['30', '70', '140', '410'], 'a2': ['a', 'b', 'c'], 'a3': ['x', 'y', 'z'], 'a4':['p', 'q', 'r'], 'a5': ['l', 'm', 'n']}
def set_Radio(self,parent):
i = 0
radio_value = []
if self.box_value.get() in self.choices.keys():
radio_value = self.choices[self.box_value.get()]
print radio_value
for t in radio_value:
i = i+1
b = Radiobutton(self, text=t, variable=self.v, value=t)
b.grid(row = i, column = 0)
def main():
root = Tk()
root.geometry('250x250')
app1= App(root)
root.mainloop()
if __name__ == '__main__':
main()
The problem is that you don't delete the old radiobuttons before creating the new radiobuttons. One solution is to put them in an invisible frame. When you delete the frame, the radiobuttons will automatically be destroyed. Another solution is to keep a reference to them so that you can destroy them individually later.
Here's an example of keeping a reference:
def __init-_(self, parent):
...
self.radios = []
def set_Radio(self,parent):
for widget in self.radios:
widget.destroy()
self.radios = []
...
for t in radio_value:
...
b = Radiobutton(...)
self.radios.append(b)
...