I'm currently writing a program like a task manager (similar to what windows have) and I'm using tree view in gtk+ 3 for the GUI. The problem is, by making the treeview update 'live', the scrollbar stops to function correctly. Everytime the treeview is refreshed, the scrollbar goes to the top of the page. This is the line used to update the treeview:
GObject.timeout_add_seconds(1, self.update_treeview)
And this is the function update_treeview:
def update_treeview(self):
self.process_list_store.clear()
p_list = create_list(self.properties, create_list_of_processes())
for p in p_list:
self.process_list_store.append(list(p))
return True
This is how I created the treeview if it matters:
def create_treeview(process_list_store):
treeview = gtk.TreeView(process_list_store)
for i, col_title in enumerate(['Name', 'pid', 'Username', 'Memory usage', 'Cpu usage']):
renderer = gtk.CellRendererText()
column = gtk.TreeViewColumn(col_title, renderer, text=i)
column.set_sort_column_id(i)
column.set_min_width(110)
column.set_resizable(i)
treeview.append_column(column)
return treeview
The treeview is inside a scrolled window object:
self.scrolled_window = gtk.ScrolledWindow(hexpand=True, vexpand=True)
self.scrolled_window.set_policy(gtk.PolicyType.NEVER, gtk.PolicyType.AUTOMATIC)
I looked for perhaps other ways to update the treeview, but I wasn't able to find a solution. I'll appreciate any help to deal with this problem, thanks!
EDIT: here is the complete code:
import psutil
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk as gtk
from gi.repository import GObject
class MainWindow(gtk.Window):
def __init__(self):
gtk.Window.__init__(self, title="Task Manager")
self.set_default_size(1000, 500)
#basic objects in the program
self.set_border_width(10)
self.notebook = gtk.Notebook()
self.grid1 = gtk.Grid(row_spacing=20)
self.notebook.set_scrollable(True)
self.add(self.notebook)
#Processes Tab
self.tab1 = gtk.Box()
self.set_border_width(10)
self.notebook.append_page(self.tab1, gtk.Label("Processes"))
self.properties = ['name', 'pid', 'username', 'memory_percent', 'cpu_percent']
p_list = create_list(self.properties, create_list_of_processes())
print p_list
#create a list store object for the tree view
self.process_list_store = gtk.ListStore(str, int, str, str, str)
for p in p_list:
self.process_list_store.append(list(p))
#create a window that can be scrolled
self.scrolled_window = gtk.ScrolledWindow(hexpand=True, vexpand=True)
self.scrolled_window.set_policy(gtk.PolicyType.NEVER, gtk.PolicyType.AUTOMATIC)
self.grid1.attach(self.scrolled_window, 0, 0, 8, 10)
#create a tree view and add it to the window
self.tree_view = create_treeview(self.process_list_store)
self.scrolled_window.add(self.tree_view)
self.tab1.add(self.grid1)
GObject.timeout_add_seconds(1, self.update_treeview)
def update_treeview(self):
self.process_list_store.clear()
p_list = create_list(self.properties, create_list_of_processes())
for p in p_list:
self.process_list_store.append(list(p))
return True
#create a list of dictionaries of processes and info about them
def create_list_of_processes():
list_of_processes = []
for proc in psutil.process_iter():
try:
pinfo = proc.as_dict(attrs=['pid', 'name', 'username', 'memory_percent', 'cpu_percent'])
except psutil.NoSuchProcess:
pass
else:
if not pinfo['username']:
pinfo['username'] = 'SYSTEM'
pinfo['cpu_percent'] = str(pinfo['cpu_percent'] / psutil.cpu_count()) + '%'
pinfo['memory_percent'] = str(pinfo['memory_percent'])[0:6]
pinfo['memory_percent'] = str(pinfo['memory_percent']) + '%'
list_of_processes.append(pinfo)
return list_of_processes
#create a tree view object
def create_treeview(process_list_store):
treeview = gtk.TreeView(process_list_store)
for i, col_title in enumerate(['Name', 'pid', 'Username', 'Memory usage', 'Cpu usage']):
renderer = gtk.CellRendererText()
column = gtk.TreeViewColumn(col_title, renderer, text=i)
column.set_sort_column_id(i)
column.set_min_width(110)
column.set_resizable(i)
treeview.append_column(column)
return treeview
#create a list based on the desired properties
def create_list(properties, p_dict):
p_list = []
for dict in p_dict:
small_p_list = []
for prop in properties:
small_p_list.append(dict[prop])
p_list.append(small_p_list)
return p_list
def main():
window = MainWindow()
window.connect("delete-event", gtk.main_quit)
window.show_all()
gtk.main()
if __name__ == '__main__':
main()
This update works for me:
def update_treeview(self):
adjustment = self.scrolled_window.get_vadjustment()
value = adjustment.get_value()
self.process_list_store.clear()
p_list = create_list(self.properties, create_list_of_processes())
for p in p_list:
self.process_list_store.append(list(p))
GObject.idle_add(adjustment.set_value, value)
return True
And here are the docs.
Unfortunately, this causes the window to flicker. The ultimate way is to update the treeview, line by line. But this is pretty hard code to write.