Reduce size of edit box in CellRendererText in Gtk+ Python - python-2.7

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

Related

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

combobox value is not selecting in python when it is created dynamically

I created a Tkinter window Form, in that based on user selection i dynamically replace the combobox with deleting the Exist one.But the Problem is when i do like this the value selected in combobox is not updated.It is always displaying the default vale. see the following code
from Tkinter import *
import ttk
final=[]
field_0=['1','0']
field_1=['1','2','23','45','6']
field_2=['2','5','7','8','9']
class header:
def __init__(self,root):
self.parent=root
self.row_number= 1
self.value=0
root.title(" Select fields ")
root.minsize(width=450, height=530)
root.maxsize(width=450, height=550)
frame=Frame(root,height=20,relief=FLAT)
frame.grid(row=0,column=0,sticky=W)
Label(frame,text="Enter value:").grid(row=0,column=0,padx=70,pady=5,sticky=W)
entryValue=StringVar()
port_e = Entry(root,width=5,textvariable=entryValue)
port_e.delete(0,END)
port_e.insert(0,'0')
port_e.grid(row=0,column=0,padx=100,pady=5)
self.value=self.value+1
self.change_row=self.row_number
self.combocreate(self.change_row,root,field_0)
self.change_row=self.change_row+1
self.combocreate(self.change_row,root,field_1)
def combocreate(self,row_number,msg_frame,field):
comboBoxValue = [] # 'request'command for sink only
self.box_value=StringVar()
self.combo=ttk.Combobox(root,textvariable=self.box_value,state='readonly')
self.combo['values'] = tuple(field)
self.combo.set(field[1])
self.combo.grid(row = row_number, column = 1)
self.combo.bind("<<ComboboxSelected>>",self.selected_field)
final.append(self.combo)
def selected_field(self,event):
global cnt_sel
print final[0].get()
if(final[0].get()=='1'):
self.control=Label(root,text="Choose one type").grid(row=self.change_row,column=0,padx=20,pady=5,sticky=W)
self.combocreate(self.change_row,root,field_1)
final[1]=final[2] #replacing combobox dynamically based on selection
del final[2]
elif(final[0].get()=='0'):
Label(root,text="Choose zerotype ").grid(row=self.change_row,column=0,padx=20,pady=5,sticky=W)
self.combocreate(self.change_row,root,field_2)
final[1]=final[2]
del final[2]
else:
pass
if __name__=="__main__":
root=Tk()
app=header(root)
root.mainloop()
The problem with your code is that every time combocreate is called, it automatically binds the Combobox to self.selected_field, so whenever you change the value of the type Combobox, you call self.selected_field, and it automatically resets the type to the default. To fix the problem, you should only bind the first Combobox. This should fix the problem:
Replace
self.change_row=self.row_number
self.combocreate(self.change_row,root,field_0)
self.change_row=self.change_row+1
self.combocreate(self.change_row,root,field_1)
With
self.change_row=self.row_number
self.combocreate(self.change_row,root,field_0)
final[0].bind("<<ComboboxSelected>>",self.selected_field)
self.change_row=self.change_row+1
self.combocreate(self.change_row,root,field_1)
And
def combocreate(self,row_number,msg_frame,field):
comboBoxValue = [] # 'request'command for sink only
self.box_value=StringVar()
self.combo=ttk.Combobox(root,textvariable=self.box_value,state='readonly')
self.combo['values'] = tuple(field)
self.combo.set(field[1])
self.combo.grid(row = row_number, column = 1)
self.combo.bind("<<ComboboxSelected>>",self.selected_field)
final.append(self.combo)
With
def combocreate(self,row_number,msg_frame,field):
comboBoxValue = [] # 'request'command for sink only
self.box_value=StringVar()
self.combo=ttk.Combobox(root,textvariable=self.box_value,state='readonly')
self.combo['values'] = tuple(field)
self.combo.set(field[1])
self.combo.grid(row = row_number, column = 1)
final.append(self.combo)

Unexpected Output In Python While Using MoviePy

Hi i added a simple GUI in this script
A python script to automatically summarize soccer videos based on the crowd's reactions
The GUI script is the following
from Tkinter import *
from tkFileDialog import askopenfilename
from soccer_reacts import video_edit
class MyFrame(Frame):
def __init__(self):
master = Tk()
Label(master, text="Please Insert Video Path With Browse", width=30).grid(row=0)
Frame.__init__(self)
self.master.title("Video Editor")
self.master.geometry('{}x{}'.format(300, 200))
self.master.rowconfigure(5, weight=1)
self.master.columnconfigure(5, weight=1)
self.grid(sticky=W+E+N+S)
self.button = Button(self, text="Browse", command=self.load_file, width=15)
self.button.grid(row=1, column=0, sticky=W)
self.button2 = Button(self, text="Start", command=self.vid_reactions, width=15)
self.button2.grid(row=2, column=0, sticky=W)
def load_file(self):
fname = askopenfilename(filetypes=(("MP4 files", "*.mp4"),("All files", "*.*") ))
if fname:
self.fname = fname
def vid_reactions(self):
print("[*]Starting operation")
print("[*]File : "+self.fname)
video_edit(self.fname)
print("[*]Operation Finished")
if __name__ == "__main__":
MyFrame().mainloop()
End this is the new code of soccer cuts
import numpy as np # for numerical operations
from moviepy.editor import VideoFileClip, concatenate
def video_edit(file_name):
clip = VideoFileClip(file_name)
cut = lambda i: clip.audio.subclip(i,i+1).to_soundarray(fps=22000)
volume = lambda array: np.sqrt(((1.0*array)**2).mean())
volumes = [volume(cut(i)) for i in range(0,int(clip.audio.duration-2))]
averaged_volumes = np.array([sum(volumes[i:i+10])/10
for i in range(len(volumes)-10)])
increases = np.diff(averaged_volumes)[:-1]>=0
decreases = np.diff(averaged_volumes)[1:]<=0
peaks_times = (increases * decreases).nonzero()[0]
peaks_vols = averaged_volumes[peaks_times]
peaks_times = peaks_times[peaks_vols>np.percentile(peaks_vols,90)]
final_times=[peaks_times[0]]
for t in peaks_times:
if (t - final_times[-1]) < 60:
if averaged_volumes[t] > averaged_volumes[final_times[-1]]:
final_times[-1] = t
else:
final_times.append(t)
final = concatenate([clip.subclip(max(t-5,0),min(t+5, clip.duration))
for t in final_times])
final.to_videofile(file_name) # low quality is the default
When i run the new code, the output is a mp4 file, with the sound of the match
but with no video. I ve checked all the changes i made and i cannot find something wrong. Can anyone help?
I do not why but changing last line from
final.to_videofile(file_name)
to final.write_videofile('The result.mp4')
was a solution

How to pass id of a Tkinter Scale through command

I am using Tkinter to create a GUI for a program I am writing that will adjust some Zigbee controlled LED lights that I have. I am using a loop to create multiple copies of a Scale that I'm going to use as a brightness slider. I manage to create the sliders properly, but I am having difficulties actually adjust the sliders correctly. Here's my code:
import simplejson as json
import requests # submits http requests
from Tkinter import *
from ttk import Frame, Button, Label, Style, Notebook
# MD5 hash from http://www.miraclesalad.com/webtools/md5.php
myhash = "d9ffaca46d5990ec39501bcdf22ee7a1"
appname = "dddd" # name content isnt relevant
num_lights = int(3)
class hueApp(Frame):
def __init__(self, parent):
Frame.__init__(self, parent)
self.parent = parent
self.initUI()
def initUI(self, *args, **kwds):
# title the app window
self.parent.title("Hue controller")
self.style = Style()
# create grid layout
self.columnconfigure(0, pad=3)
self.columnconfigure(1, pad=3)
self.columnconfigure(2, pad=3)
self.rowconfigure(0, pad=3)
self.scale=[]
self.val=[]
for i in range(num_lights):
print 'i=', i, type(i)
self.val.append(i+1)
print 'val=', self.val, type(self.val)
self.scale.append(Scale(self, from_=255, to_=0, command=lambda i=self.val: self.brightness_adj(i,light_id=i)))
print self.scale[i]
print 'i = ', i, type(i), '\n\n'
self.scale[i].set(150)
self.scale[i].grid(row=1, column=i)
if i == 2:
print '\n', self.scale, '\n'
print self.val, '\n'
self.scale[i].set(200)
self.centerWindow
self.pack()
def brightness_adj(self,light_val, light_id):
#global bri_val
print 'light_id:', light_id, type(light_id)
print 'light_val:', light_val, type(light_val)
print self.val[int(light_id)]
#print int(light_id)
bri_val = self.scale[light_id-1].get()
print bri_val
light = light_id
global huehub
huehub = "http://192.168.0.100/api/"+ myhash + "/lights/" + str(light)
#brightness_logic()
reply = requests.get(huehub)
a=json.loads(reply.text)
#print bri_val
payload = json.dumps({"bri":bri_val})
sethuehub = huehub + "/state"
reply = requests.put(sethuehub, data=payload)
def centerWindow(self):
w = 250
h = 150
sw = self.parent.winfo_screenwidth()
sh = self.parent.winfo_screenheight()
x = (sw-w)/2
y = (sh-h)/2
self.parent.geometry('%dx%d+%d+%d' % (w, h, x, y))
def main():
root=Tk() #the root window is created
app=hueApp(root) #create an instance of the application class
root.mainloop()
if __name__ == '__main__':
main()
I realize that this code probably gives an error when you try to run it. Basically my problem is that the command for each scale is only send brightness_adj the value of the scale, but I can't get it to pass through the id of the light. I was trying to do this by sending through the index of the self.scale list that it is appended into when it is created. I need to know which light is being adjusted so that I can send a new brightness to the corresponding light bulb. I hope I was clear enough. Thanks in advance!
I'm a little confused about what you're trying to do with the line that assigns callback functions to the scale widgets:
self.scale.append(Scale(self, from_=255, to_=0, command=lambda i=self.val: self.brightness_adj(i,light_id=i)))
since self.val is a list, and you're sending it as both the light_val and the light_id arguments, which I would think should be integers.
Possible fix:
I'm guessing that you want each callback to send a different ID to the brightness_adj function depending on which scale it's assigned to. Here's how I would fix this up:
Add this function to your hueApp class namespace:
def brightnessCallbackFactory(self, id):
return lambda light_val:self.brightness_adj(light_val, id)
Then change the callback assignment line from the above to this:
self.scale.append(Scale(self, from_=255, to_=0, command=self.brightnessCallbackFactory(i)))
That should create callback functions that retain the ID value in their internal namespace and assign them to the corresponding scale widget.

Horizontal scrolling won't activate for ttk Treeview widget

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