I translated TreeView gtk2 tooltip in Gtk3
It's almost running but I have problem to manage coordinate of tooltip window.
I Would like the tooltip window locate near the mouse et follow it near. Instead of my project where the tooltip window is located in the corner of screen
my code is
#!/usr/bin/env python
# coding: utf-8
'''
warning data not update !!!!!!!!!!!!!
TreeViewTooltips.py
Provides TreeViewTooltips, a class which presents tooltips for cells,
columns and rows in a Gtk.TreeView.
------------------------------------------------------------
This file includes a demo. Just execute the file:
python TreeViewTooltips.py
------------------------------------------------------------
To use, first subclass TreeViewTooltips and implement the get_tooltip()
method; see below. Then add any number of Gtk.TreeVew widgets to a
TreeViewTooltips instance by calling the add_view() method. Overview
of the steps:
# 1. subclass TreeViewTooltips
class MyTooltips(TreeViewTooltips):
# 2. overriding get_tooltip()
def get_tooltip(...):
...
# 3. create an instance
mytips = MyTooltips()
# 4. Build up your Gtk.TreeView.
myview = Gtk.TreeView()
...# create columns, set the model, etc.
# 5. Add the view to the tooltips
mytips.add_view(myview)
How it works: the add_view() method connects the TreeView to the
"motion-notify" event with the callback set to a private method.
Whenever the mouse moves across the TreeView the callback will call
get_tooltip() with the following arguments:
get_tooltip(view, column, path)
where,
view: the Gtk.TreeView instance.
column: the Gtk.TreeViewColumn instance that the mouse is
currently over.
path: the path to the row that the mouse is currently over.
Based on whether or not column and path are checked for specific
values, get_tooltip can return tooltips for a cell, column, row or the
whole view:
Column Checked Path Checked Tooltip For...
Y Y cell
Y N column
N Y row
N N view
get_tooltip() should return None if no tooltip should be displayed.
Otherwise the return value will be coerced to a string (with the str()
builtin) and stripped; if non-empty, the result will be displayed as
the tooltip. By default, the tooltip popup window will be displayed
centered and just below the pointer and will remain shown until the
pointer leaves the cell (or column, or row, or view, depending on how
get_tooltip() is implemented).
'''
from gi.repository import Gtk
class TreeViewTooltips_gtk3:
"""tooltip atttach to Treeview in Gtk3
this window is moving to follow row of Treeview"""
def __init__(self):
self.win = Gtk.Window()
self.win.set_decorated(False)
self.win.set_default_size(-1, -1)
self.label = Gtk.Label()
self.win.add(self.label)
# by default, the tooltip is enabled
self.__enabled = True
def __show(self, tooltip, x, y):
"""in order to move the tooltip near row of Treeview"""
self.label.set_markup(tooltip)
w,h = self.win.get_preferred_width()
# move the window
self.win.move(*self.location(x,y,w,h))
# show it
self.win.show_all()
return self.win
def __leave_handler(self, view, event):
"""when the pointer leaves the view, hide the tooltip"""
self.win.hide()
def enable(self):
'Enable the tooltip'
self.__enabled = True
def disable(self):
'Disable the tooltip'
self.__enabled = False
def location(self, x, y, w, h):
'''Given the x,y coordinates of the pointer and the width and
height (w,h) demensions of the tooltip window, return the x, y
coordinates of the tooltip window.
The default location is to center the window on the pointer
and 4 pixels below it.
# en clair c'est un décalage de position de la fenetre qui s'ouvre qui donne l'info
'''
return x - w/2, y + 4
def tooltip_callback(self,treeview, x, y, keyboard_mode, tooltip):
""" in order to collect miscellaneous elemnt to build data with row of treeview"""
"""3eme règle implementer ça pour afficher un tooltip customisé en fonction des data du tree view"""
x, y = treeview.convert_widget_to_bin_window_coords(x, y)
if not self.__enabled :
"""if you don't want tooltip display just return False !!!!"""
return False
try:
path, column, cell_x, cell_y = treeview.get_path_at_pos(x, y)
iter = model.get_iter(path)
#print 'tooltip=',self.get_tooltip(column, self.cust_col,path)
treeview.set_tooltip_window(self.__show(self.get_tooltip(column, self.cust_col,path),x,y))
# necessary to return True in order to display window tooltip
print "path is valid :-)"
return True
except:
# to prevent case when path it's not valid
print "perhaps path it's not valid ????"
# not possible to display something
return False
def add_view(self, view):
"""add a Gtk.TreeView to the tooltip
check if view is instance of Gtk.TreeView
and build connector and tooltip enable"""
assert isinstance(view, Gtk.TreeView), \
('This handler should only be connected to '
'instances of Gtk.TreeView')
# first condition in gtk3
# set True the property "has-tooltip"
view.set_property("has-tooltip", True)
# second condition in gtk3
view.connect('query-tooltip',self.tooltip_callback)
# hide tooltip when mouse out of widget
view.connect("leave-notify-event", self.__leave_handler)
def get_tooltip(self, view, column, path):
"""in order to secure customized implementation in your projet"""
'See the module doc string for a description of this method'
raise NotImplemented, 'Subclass must implement get_tooltip()'
if __name__ == '__main__':
class DemoTips_Gtk3(TreeViewTooltips_gtk3):
"""demo play how to """
def __init__(self, customer_column):
"""
init class
customer_column is an instance of Gtk.TreeViewColumn and
is being used in the Gtk.TreeView to show customer names."""
self.cust_col = customer_column
# call base class init
TreeViewTooltips_gtk3.__init__(self)
def get_tooltip(self, column,cust_col,path):
"""By checking both column and path we have a cell-based tooltip"""
model = view.get_model()
customer = model[path][2]
if column is cust_col:
"""here there is lot of information on row"""
return '<big>%s %s</big>\n<i>%s</i>' % (customer.fname,
customer.lname,
customer.notes)
else:
"""here basic data only !!!!"""
return ('<big><u>Generic Column Tooltip</u></big>\n'
'Unless otherwise noted, all\n phone number is %s ') % customer.phone
# Here's our customer
class Customer:
def __init__(self, fname, lname, phone, notes):
self.fname = fname
self.lname = lname
self.phone = phone
self.notes = notes
# create a bunch of customers
customers = []
for fname, lname, phone, notes in [
('Joe', 'Schmoe', '555-1212', 'Likes to Morris dance.'),
('Jane', 'Doe', '555-2323',
'Wonders what the hell\nMorris dancing is.'),
('Phred', 'Phantastic', '900-555-1212', 'Dreams of Betty.'),
('Betty', 'Boop', '555-3434', 'Dreams in b&w.'),
('Red Sox', 'Fan', '555-4545',
"Still livin' 2004!\nEspecially after 2006.")]:
customers.append(Customer(fname, lname, phone, notes))
# Build our model and view
model = Gtk.ListStore(str, str, object)
for c in customers:
model.append(['%s %s' % (c.fname, c.lname), c.phone, c])
view = Gtk.TreeView(model)
# two columns, name and phone
cell = Gtk.CellRendererText()
cell.set_property('xpad', 20)
namecol = Gtk.TreeViewColumn('Customer Name', cell, text=0)
namecol.set_min_width(200)
view.append_column(namecol)
cell = Gtk.CellRendererText()
phonecol = Gtk.TreeViewColumn('Phone', cell, text=1)
view.append_column(phonecol)
# finally, connect the tooltip, specifying the name column as the
# column we want the tooltip to popup over.
tips = DemoTips_Gtk3(namecol)
tips.add_view(view)
# We're going to demonstrate enable/disable. First we need a
# callback function to connect to the toggled signal.
def toggle(button):
if button.get_active():
tips.disable()
else:
tips.enable()
# create a checkbutton and connect our handler
check = Gtk.CheckButton('Check to disable view tooltips')
check.connect('toggled', toggle)
check.set_tooltip_markup('This is a standard Gtk tooltip.\n'
'Compare me to the tooltips above.')
# create a VBox to pack the view and checkbutton
vbox = Gtk.VBox()
vbox.pack_start(view,False,False,2)
vbox.pack_start(check,False,False,2)
vbox.show_all()
# pack the vbox into a simple dialog and run it
dialog = Gtk.Dialog('TreeViewTooltips Demo')
close = dialog.add_button(Gtk.STOCK_CLOSE,Gtk.ResponseType.NONE)
# add a tooltip for the close button
close.set_tooltip_markup('Click to end the demo.')
dialog.set_default_size(400,400)
dialog.vbox.pack_start(vbox,False,False,2)
dialog.run()
Who could give me elemnt in order to put tooltip with relative coordinate with TreeView ?
thank by advance
I'am sorry for my bad english
You are working with local coordinates, so you need to refer to the dialog position to get the root coordinates. Another issue with your script is that the main window is a dialog, therefore in the foreground, the tooltips appear below it (I have fixed that but then deleted the code (oops). It works, just change your dialog for a normal window). As a general note, it is good for all widget to know its parent to refer to it when the root coordinates are required.
Please see modified (but not cleaned up) code below:
#!/usr/bin/env python
# coding: utf-8
from gi.repository import Gtk
class TreeViewTooltips_gtk3:
"""tooltip atttach to Treeview in Gtk3
this window is moving to follow row of Treeview"""
def __init__(self):
self.win = Gtk.Window()
self.win.set_decorated(False)
self.win.set_default_size(-1, -1)
self.label = Gtk.Label()
self.win.add(self.label)
# by default, the tooltip is enabled
self.__enabled = True
def __show(self, tooltip, x, y):
"""in order to move the tooltip near row of Treeview"""
self.label.set_markup(tooltip)
w,h = self.win.get_preferred_width()
# move the window
self.win.move(*self.location(x,y,w,h))
# show it
self.win.show_all()
return self.win
def __leave_handler(self, view, event):
"""when the pointer leaves the view, hide the tooltip"""
self.win.hide()
def enable(self):
'Enable the tooltip'
self.__enabled = True
def disable(self):
'Disable the tooltip'
self.__enabled = False
def location(self, x, y, w, h):
'''Given the x,y coordinates of the pointer and the width and
height (w,h) demensions of the tooltip window, return the x, y
coordinates of the tooltip window.
The default location is to center the window on the pointer
and 4 pixels below it.
# en clair c'est un décalage de position de la fenetre qui s'ouvre qui donne l'info
'''
return x - w/2, y + 4
def tooltip_callback(self,treeview, x, y, keyboard_mode, tooltip):
""" in order to collect miscellaneous elemnt to build data with row of treeview"""
"""3eme règle implementer ça pour afficher un tooltip customisé en fonction des data du tree view"""
root_x, root_y = self.dialog.get_position() #get the root coordinates
x, y = treeview.convert_widget_to_bin_window_coords(x, y)
if not self.__enabled :
"""if you don't want tooltip display just return False !!!!"""
return False
try:
path, column, cell_x, cell_y = treeview.get_path_at_pos(x, y)
iter = model.get_iter(path)
#print 'tooltip=',self.get_tooltip(column, self.cust_col,path)
#add the root coordinates to local coordinates
treeview.set_tooltip_window(self.__show(self.get_tooltip(column, self.cust_col,path),root_x+x,root_y+y))
# necessary to return True in order to display window tooltip
print("path is valid :-)")
return True
except:
# to prevent case when path it's not valid
print("perhaps path it's not valid ????")
# not possible to display something
return False
def add_view(self, view):
"""add a Gtk.TreeView to the tooltip
check if view is instance of Gtk.TreeView
and build connector and tooltip enable"""
assert isinstance(view, Gtk.TreeView), \
('This handler should only be connected to '
'instances of Gtk.TreeView')
# first condition in gtk3
# set True the property "has-tooltip"
view.set_property("has-tooltip", True)
# second condition in gtk3
view.connect('query-tooltip',self.tooltip_callback)
# hide tooltip when mouse out of widget
view.connect("leave-notify-event", self.__leave_handler)
def get_tooltip(self, view, column, path):
"""in order to secure customized implementation in your projet"""
'See the module doc string for a description of this method'
raise NotImplemented('Subclass must implement get_tooltip()')
if __name__ == '__main__':
class DemoTips_Gtk3(TreeViewTooltips_gtk3):
"""demo play how to """
def __init__(self, customer_column):
"""
init class
customer_column is an instance of Gtk.TreeViewColumn and
is being used in the Gtk.TreeView to show customer names."""
self.cust_col = customer_column
# call base class init
TreeViewTooltips_gtk3.__init__(self)
def get_tooltip(self, column,cust_col,path):
"""By checking both column and path we have a cell-based tooltip"""
model = view.get_model()
customer = model[path][2]
if column is cust_col:
"""here there is lot of information on row"""
return '<big>%s %s</big>\n<i>%s</i>' % (customer.fname,
customer.lname,
customer.notes)
else:
"""here basic data only !!!!"""
return ('<big><u>Generic Column Tooltip</u></big>\n'
'Unless otherwise noted, all\n phone number is %s ') % customer.phone
# Here's our customer
class Customer:
def __init__(self, fname, lname, phone, notes):
self.fname = fname
self.lname = lname
self.phone = phone
self.notes = notes
# create a bunch of customers
customers = []
for fname, lname, phone, notes in [
('Joe', 'Schmoe', '555-1212', 'Likes to Morris dance.'),
('Jane', 'Doe', '555-2323',
'Wonders what the hell\nMorris dancing is.'),
('Phred', 'Phantastic', '900-555-1212', 'Dreams of Betty.'),
('Betty', 'Boop', '555-3434', 'Dreams in b&w.'),
('Red Sox', 'Fan', '555-4545',
"Still livin' 2004!\nEspecially after 2006.")]:
customers.append(Customer(fname, lname, phone, notes))
# Build our model and view
model = Gtk.ListStore(str, str, object)
for c in customers:
model.append(['%s %s' % (c.fname, c.lname), c.phone, c])
view = Gtk.TreeView(model)
# two columns, name and phone
cell = Gtk.CellRendererText()
cell.set_property('xpad', 20)
namecol = Gtk.TreeViewColumn('Customer Name', cell, text=0)
namecol.set_min_width(200)
view.append_column(namecol)
cell = Gtk.CellRendererText()
phonecol = Gtk.TreeViewColumn('Phone', cell, text=1)
view.append_column(phonecol)
# finally, connect the tooltip, specifying the name column as the
# column we want the tooltip to popup over.
tips = DemoTips_Gtk3(namecol)
tips.add_view(view)
# We're going to demonstrate enable/disable. First we need a
# callback function to connect to the toggled signal.
def toggle(button):
if button.get_active():
tips.disable()
else:
tips.enable()
# create a checkbutton and connect our handler
check = Gtk.CheckButton('Check to disable view tooltips')
check.connect('toggled', toggle)
check.set_tooltip_markup('This is a standard Gtk tooltip.\n'
'Compare me to the tooltips above.')
# create a VBox to pack the view and checkbutton
vbox = Gtk.VBox()
vbox.pack_start(view,False,False,2)
vbox.pack_start(check,False,False,2)
vbox.show_all()
# pack the vbox into a simple dialog and run it
dialog = Gtk.Dialog('TreeViewTooltips Demo')
tips.dialog = dialog #give tips the main window reference
close = dialog.add_button(Gtk.STOCK_CLOSE,Gtk.ResponseType.NONE)
# add a tooltip for the close button
close.set_tooltip_markup('Click to end the demo.')
dialog.set_default_size(400,400)
dialog.vbox.pack_start(vbox,False,False,2)
dialog.run()
Related
In the tkinter app I'm building (win10 python 3.8) each time I open a new file I get a new list that I distribute to textboxes (Ok), combobox(ok), etc. When I open the first list checkbox loop builds ok, next file called checkboxes don't change. I can't update checkboxes, I mean, remove the previous list and insert another one. In the example I used to buttons (in app askopenfilename), lists build one bellow other. I need one replacing the other. I believe I need to use grid.clear() or destroy, but how? Thanks in advance.
from tkinter import *
root = Tk()
root.geometry('400x400')
my_friends = ['Donald', 'Daisy', 'Uncle Scrooge', 'Ze Carioca']
my_heroes = ['Captain America', 'Hulk', 'Spider Man', 'Black Widow',
'Wanda Maximoff', 'Vision', 'Winter Soldier']
what_list = ' '
def list_my_friends():
global what_list
what_list = my_friends
create_cbox()
def list_my_heroes():
global what_list
what_list = my_heroes
create_cbox()
def create_cbox():
for index, friend in enumerate(what_list):
current_var = tk.StringVar()
current_box = tk.Checkbutton(root, text= friend,
variable = current_var,
onvalue = friend,
offvalue = '')
current_box.pack()
button1= tk.Button(root, text = "my_friends",command=lambda:list_my_friends()).pack()
button2= tk.Button(root, text = "my_heroes",command=lambda:list_my_heroes()).pack()
root.mainloop()
You can put those checkbuttons in a frame, then it is more easier to remove old checkbuttons before populating new checkbuttons:
def create_cbox():
# remove existing checkbuttons
for w in container.winfo_children():
w.destroy()
# populate new checkbuttons
for friend in what_list:
current_var = tk.StringVar()
# use container as the parent
current_box = tk.Checkbutton(container, text=friend,
variable=current_var,
onvalue=friend,
offvalue='')
current_box.pack(anchor='w')
tk.Button(root, text="my_friends", command=list_my_friends).pack()
tk.Button(root, text="my_heroes", command=list_my_heroes).pack()
# frame to hold the checkbuttons
container = tk.Frame(root)
container.pack(padx=10, pady=10)
worked with little adjustments as I'm working in class:
class PageEight(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent) # parent is Frame
self.controller = controller
# to adequate to widgets outside the self.container_pg8:
self.rowspan_list = len(what_list)
#container:
self.container_pg8 = tk.Frame(self)
self.container_pg8.grid(row=2,
rowspan = self.rowspan_list,
column=1,)
I have placed multiple Combobox in the grid as shown below.
These comboboxes are stored in array with corresponding row, column for eg as shown below.
self.c_arr[row,column] = ttk.Combobox(what_ev,
textvariable=self.c_it_val[row,column],
values=list(self.c_list[row,column].keys()))
self.c_arr[row,column].bind("<<ComboboxSelected>>", lambda
x:self.c_capture(what_ev, <Want to pass row, column information as well>))
Whenever the function bound to the combobox is called/triggered, I wanted to pass the respective row, column number to the called function along with other variables.
How to get/retrieve the respective widgets row,column number/location details placed on the grid?
UPDATE: Adding with an Example code below.
The below code create a 3x3 COmbobox and binded with a function to get called when any of the respective combobox is selected. When gets selected I wanted to get the value selected in that box and print it.
Example Code:
import Tkinter
from Tkinter import *
from Tkinter import Tk, StringVar
import ttk
import tkFont
class Application:
def __init__(self, parent):
self.parent = parent
self.sco1 = None
self.c_int_val = StringVar()
self.box = {}
self.box_value = {}
self.box_int_list = {}
self.combo()
def combo(self):
for row in range (3):
for column in range(3):
self.box_int_list[row,column] = {key: None for key in range(1,10)}
self.box_value[row,column] = StringVar()
self.box_value[row,column].set("0")
self.box[row,column] = ttk.Combobox(self.parent, textvariable=self.box_value[row,column], values = list(self.box_int_list[row,column].keys()), state='readonly', width=39)
self.box[row,column].bind("<<ComboboxSelected>>", lambda x:self.print_selected_value("Eurekaa", len(self.box_int_list[row,column])))
self.box[row,column].grid(row=row, column=column)
def print_selected_value(self, what_name, list_len, *args):
print "Value selected is: %s"%(self.box[row,column].get())
print what_name
print list_len
if __name__ == '__main__':
root = Tk()
app = Application(root)
root.mainloop()
But since I wasnt able to get the row,column info, was unable to get the value from the selected combobox. How can that be done ?
If you pass the event to your callback, you can use grid_info on the widget associated with the event.
def callback(event, row, column):
print(row, column)
cb.bind("<<ComboboxSelected>>", callback)
If you want to pass the information, there's nothing preventing you from doing that using lambda or functools.partial:
c.bind("<<ComboboxSelected>>",
lambda event, row=row, column=column: callback(event, row, column))
I developed a simple Python application doing some stuff, then I decided to add a simple GUI using Tkinter.
The problem is that, while the I call a function called startprocess and begin doing stuff which is processor heavy and the window freezes.
I know it's a common problem and I've already read that I should use multithreads (very complicated, because the function updates the GUI too) or divide my code in different function, each one working for a little time. anyways is there any modification needed in below code to avoid GUI freezing?
import threading
import tkinter as tk
from tkinter import filedialog
from tkinter import messagebox
import os, datetime, sys, subprocess
import parselog_v1
# diplay messagebox window
def MessageBox(windowLable,msg):
messagebox.showinfo(windowLable, msg)
# check if Dir empty
def checkDirEmpty(work_path):
if os.path.isdir(work_path):
if not os.listdir(work_path):
print ("No Files found in directory")
MessageBox('Log Parser', 'No Files found in directory.')
else:
return True
# launch app in center of screen
def center_window(width=300, height=200):
# get screen width and height
screen_width = root.winfo_screenwidth()
screen_height = root.winfo_screenheight()
# calculate position x and y coordinates
x = (screen_width/2) - (width/2)
y = (screen_height/2) - (height/2)
root.geometry('%dx%d+%d+%d' % (width, height, x, y))
# application frame
class Application(tk.Frame):
def __init__(self, master=None):
tk.Frame.__init__(self, master)
self.pack()
self.createWidgets()
self.master.title("Log Parser")
def createWidgets(self):
self.Run_Main = tk.Button(self)
self.Run_Main["text"] = "Browse for logs"
self.Run_Main["fg"] = "blue"
self.Run_Main["command"] = self.startProcess
self.Run_Main.pack(side='left',padx=0)
self.QUIT = tk.Button(self)
self.QUIT["text"] = "Quit!"
self.QUIT["fg"] = "red"
self.QUIT["command"] = self.quit
self.QUIT.pack(side='right',padx=5)
def startProcess(self):
global Src_foldername
Src_foldername = filedialog.askdirectory()
Src_foldername = Src_foldername.replace("/", "\\")
print("Source folder: " + Src_foldername)
if checkDirEmpty(Src_foldername):
# process logs
# multithread
print("Processing...")
self.refresh()
threading.Thread(target=parselog_v1.main(Src_foldername))
# scroll text inside application frame
class scrollTxtArea:
def __init__(self, root):
frame = tk.Frame(root)
frame.pack()
self.textPad(frame)
return
class IORedirector(object):
'''A general class for redirecting I/O to this Text widget.'''
def __init__(self, text_area):
self.text_area = text_area
class StdoutRedirector(IORedirector):
'''A class for redirecting stdout to this Text widget.'''
def textPad(self, frame):
# add a frame and put a text area into it
textPad = tk.Frame(frame)
self.text = tk.Text(textPad, height=21, width=68)
self.text.config()
# add a vertical scroll bar to the text area
scroll = tk.Scrollbar(textPad)
self.text.configure(yscrollcommand=scroll.set,background="black", foreground="green")
# pack everything
self.text.pack(side=tk.LEFT, pady=2)
scroll.pack(side=tk.RIGHT, fill=tk.Y)
textPad.pack(side=tk.TOP)
self.text.insert("end", "Begin by selecting log folder..." + "\n")
self.text.configure(state='disabled') # disable text editing
sys.stdout = (self) # to begin logging stdio to GUI
return
def write(self, txt):
self.text.configure(state='normal')
self.text.insert('end', txt)
self.text.configure(state='disabled')
root = tk.Tk()
root.resizable(width=False, height=False)
center_window(500, 300) # launch in center of screen
app = Application(master=root)
scrollFrame = scrollTxtArea(root)
app.mainloop()
root.destroy()
You use thread in wrong way.
First: target= needs function name without () and arguments.
You can assign arguments to args= (it have to be tuple even if you have only one argument)
threading.Thread(target=parselog_v1.main, args=(Src_foldername,) )
Now your code runs parselog_v1.main as normal function, waits for result and it will assign this result as function name to taget= - so you have something like this:
result = parselog_v1.main(Src_foldername)
threading.Thread(target=result)
It stops mainloop so it can't get mouse/keyboard events, refresh window, etc. so it looks like window freeze.
Second: after you create thread correctly you have to start it
my_thread = threading.Thread(target=parselog_v1.main, args=(Src_foldername,) )
my_thread.start()
Hy guys,
in my executable program there is a toolbar. Well, the user decides to move the toolbar. Now the toolbar is floating. I know I have to conntect the floating-signals that is emittted when the toolbar ist arranged by the user. How can I save the new position of the toolbar? I know the method of adding the toolbar to the main window with a position:self.addToolBar( Qt.LeftToolBarArea , toolbar_name). In the handle_floating()-method you see what I want: There I want to get the position currently, but how? You also see I have just added one member variable, named self.toolbar_pos, to hold the position of the toolbar. My idea is, when application is terminated I want to serialize this value to a file, and later, when application is ran again its will read that file and set the toolbar accordingly. But this is no problem. Currently I don't have no idea to get the position of the toolbar.
I need your help :)
#!/usr/bin/python
# -*- coding: utf-8 -*-
import sys
from PyQt4 import QtGui, QtCore
class Example(QtGui.QMainWindow):
def __init__(self, parent=None):
QtGui.QMainWindow.__init__(self, parent)
self.toolbar_pos = None
self.initUI()
def initUI(self):
exitAction = QtGui.QAction(QtGui.QIcon('exit24.png'), 'Exit', self)
exitAction.setShortcut('Ctrl+Q')
exitAction.triggered.connect(QtGui.qApp.quit)
self.toolbar = QtGui.QToolBar(self)
self.toolbar.setToolButtonStyle(QtCore.Qt.ToolButtonTextBesideIcon)
self.addToolBar(self.toolbar )
self.toolbar.addAction(exitAction)
self.toolbar.setAllowedAreas(QtCore.Qt.TopToolBarArea
| QtCore.Qt.BottomToolBarArea
| QtCore.Qt.LeftToolBarArea
| QtCore.Qt.RightToolBarArea)
self.addToolBar( QtCore.Qt.LeftToolBarArea , self.toolbar )
self.toolbar.topLevelChanged.connect(self.handle_floating)
def handle_floating(self, event):
# The topLevel parameter is true
# if the toolbar is now floating
if not event:
# If the toolbar no longer floats,
# then calculate the position where the
# toolbar is located currently.
self.toolbar_pos = None
print "Get position: ?"
def main():
app = QtGui.QApplication(sys.argv)
ex = Example()
ex.setGeometry(300, 300, 300, 200)
ex.setWindowTitle('Toolbar example')
ex.show()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
The QMainWindow class already has APIs for this: i.e. saveState and restoreState. These can be used to save and restore the state of all the toolbars and dock-widgets in your application.
To use them, you first need to make sure that all your toolbars and dock-widgets are given a unique object-name when they are created:
class Example(QtGui.QMainWindow):
...
def initUI(self):
...
self.toolbar = QtGui.QToolBar(self)
self.toolbar.setObjectName('foobar')
Then you can override closeEvent to save the state:
class Example(QtGui.QMainWindow):
...
def closeEvent(self, event):
with open('/tmp/test.conf', 'wb') as stream:
stream.write(self.saveState().data())
(NB: I've just used a temporary file here for testing, but it would obviously be much better to use something like QSettings in your real application).
Finally, you can restore the state that was saved previously:
class Example(QtGui.QMainWindow):
def __init__(self, parent=None):
...
self.initUI()
try:
with open('/tmp/test.conf', 'rb') as stream:
self.restoreState(QtCore.QByteArray(stream.read()))
except IOError:
pass
I was wondering if there is any way for me to see what the User has selected among the list displaying, let's say: ["Apple","Orange","Grapes"] right after they select either one of them?
Like when user clicks the optionbox, and clicks Apple, Tkinter will return something
then if he switches his selection to, let's say, Orange, then that will also return something on the spot.
Thanks!
How to put parameter correctly?
from Tkinter import *
def option_changed(a):
print "the user chose the value {}".format(variable.get())
print a
master = Tk()
a = "Foo"
variable = StringVar(master)
variable.set("Apple") # default value
variable.trace("w", option_changed(a))
w = OptionMenu(master, variable, "Apple", "Orange", "Grapes")
w.pack()
mainloop()
Trace the StringVar.
from Tkinter import *
def option_changed(*args):
print "the user chose the value {}".format(variable.get())
print a
master = Tk()
a = "Foo"
variable = StringVar(master)
variable.set("Apple") # default value
variable.trace("w", option_changed)
w = OptionMenu(master, variable, "Apple", "Orange", "Grapes")
w.pack()
mainloop()
Here, option_changed will be called whenever the user chooses an option from the option menu.
You can wrap the trace argument in a lambda to specify your own parameters.
def option_changed(foo, bar, baz):
#do stuff
#...
variable.trace("w", lambda *args: option_changed(qux, 23, "hello"))
When I come across widgets with annoying interfaces - such as OptionMenu, I generally will write a class around it to abstract away the annoying attributes. In this case, I really dislike the verbosity of using the StringVar every time I want to create a dropdown, so I simply created a DropDown class which includes the StringVar within the class (written in Python 3.5, but translates easily to all):
class DropDown(tk.OptionMenu):
"""
Classic drop down entry
Example use:
# create the dropdown and grid
dd = DropDown(root, ['one', 'two', 'three'])
dd.grid()
# define a callback function that retrieves the currently selected option
def callback():
print(dd.get())
# add the callback function to the dropdown
dd.add_callback(callback)
"""
def __init__(self, parent, options: list, initial_value: str=None):
"""
Constructor for drop down entry
:param parent: the tk parent frame
:param options: a list containing the drop down options
:param initial_value: the initial value of the dropdown
"""
self.var = tk.StringVar(parent)
self.var.set(initial_value if initial_value else options[0])
self.option_menu = tk.OptionMenu.__init__(self, parent, self.var, *options)
self.callback = None
def add_callback(self, callback: callable):
"""
Add a callback on change
:param callback: callable function
:return:
"""
def internal_callback(*args):
callback()
self.var.trace("w", internal_callback)
def get(self):
"""
Retrieve the value of the dropdown
:return:
"""
return self.var.get()
def set(self, value: str):
"""
Set the value of the dropdown
:param value: a string representing the
:return:
"""
self.var.set(value)
Example usage:
# create the dropdown and grid, this is the ONLY required code
dd = DropDown(root, ['one', 'two', 'three'])
dd.grid()
# optionally, define a callback function that retrieves the currently selected option then add that callback to the dropdown
def callback():
print(dd.get())
dd.add_callback(callback)
Edited to add: Not long after creating this post, I got annoyed with a few other attributes of tk and ended up creating a package called tk_tools to make dropdowns and checkbuttons easier along with addressing other annoyances.