hey I'm new and I've a problem
I'm trying to create a TreeView with a ListStore. The ListStore should get a list from another class but it seems I can't fill my ListStore.
so here's the code
TreeView and ListStore:
labelDB = gtk.Label('Database')
self.storeDB = gtk.ListStore(str, str, int, str)
rentexDB = gtk.CellRendererText()
colDB1 = gtk.TreeViewColumn('Name', rentexDB)
colDB2 = gtk.TreeViewColumn('URL', rentexDB)
colDB3 = gtk.TreeViewColumn('Bitrate', rentexDB)
colDB4 = gtk.TreeViewColumn('Format', rentexDB)
self.fillStore()
pageDB = gtk.TreeView(model=self.storeDB)
pageDB.append_column(colDB1)
pageDB.append_column(colDB2)
pageDB.append_column(colDB3)
pageDB.append_column(colDB4)
showData.append_page(pageDB, labelDB)
and the fillStore:
def fillStore(self):
dataInStore = self.record.useDB()
if dataInStore is not None:
self.storeDB.clear()
for row in dataInStore:
#print row
self.storeDB.append(row)
return self.storeDB
when I print the row's I can see the List's I want to store in my ListStore
but the ListStore is always empty
I hope someone can help me, please
below is a demo app:
#!/usr/bin/env python3
from gi.repository import Gtk
class Demo:
def __init__(self):
self.window = Gtk.Window()
self.window.set_default_size(640, 480)
self.window.connect('delete-event', self.on_app_exit)
scrolledwindow = Gtk.ScrolledWindow()
self.window.add(scrolledwindow)
self.liststore = Gtk.ListStore(str, str, int, str)
# You need to use self.fillStore() instead.
self.liststore.append(['Debian', 'i386', 0, 'ok'])
self.liststore.append(['Fedora', 'amd64', 1, 'ok'])
self.liststore.append(['Ubuntu', 'i386', 2, 'ok'])
treeview = Gtk.TreeView(model=self.liststore)
scrolledwindow.add(treeview)
names = ('Name', 'URL', 'Bitrate', 'Format')
for i in range(4):
renderer_text = Gtk.CellRendererText()
column = Gtk.TreeViewColumn(names[i], renderer_text, text=i)
treeview.append_column(column)
def fillStore(self):
pass
def run(self):
self.window.show_all()
Gtk.main()
def on_app_exit(self, widget, event=None):
Gtk.main_quit()
if __name__ == '__main__':
demo = Demo()
demo.run()
Related
I am teaching myself Python Gtk+3 using the readthedocs tutorial. I'm currently working on drag and drop and want to create a TreeView where every line is dragable. To do this, my code reads in a list of telecommands and puts them in a TreeView. This TreeView works and I can drag the single lines around. When I drop them into the designated drop area however, nothing happens.
My current goal is for the program to just print the text "Received data", whenever I drop something. Later on, I want the program to print out the single commands, that have been dropped.
My code looks as follows(I followed this example):
import gi
gi.require_version("Gtk", "3.0")
from gi.repository import Gtk, Gdk
from csv import reader
# get data from file and format it
with open ("TC_list.csv", "r") as read_command_list:
csv_reader = reader(read_command_list)
list_of_commands = list(csv_reader)
list_header = list_of_commands.pop(0)
for command in list_of_commands:
command[0] = int(command[0])
command[1] = int(command[1])
DRAG_ACTION = Gdk.DragAction.COPY
class DragDropWindow(Gtk.Window):
def __init__(self):
Gtk.Window.__init__(self, title="Drag and Drop Demo")
self.grid = Gtk.Grid()
self.grid.set_column_homogeneous(True)
self.grid.set_row_homogeneous(True)
self.add(self.grid)
self.grid.set_size_request(1200, 1000)
self.listview = DragSourceListView()
self.drop_area = DropArea()
self.grid.attach(self.listview, 0, 0, 1, 1)
self.grid.attach_next_to(self.drop_area, self.listview, \
Gtk.PositionType.BOTTOM, 1, 1)
class DragSourceListView(Gtk.Grid):
def __init__(self):
Gtk.Grid.__init__(self)
# Creating ListStore model
self.telecommand_liststore = Gtk.ListStore(int, int, str, str)
for telecommand_ref in list_of_commands:
self.telecommand_liststore.append(list(telecommand_ref))
self.current_filter_telecommand = None
# Creating the filter, feeding it with the liststore model
self.telecommand_filter = self.telecommand_liststore.filter_new()
# setting the filter function
self.telecommand_filter.set_visible_func(self.telecommand_filter_func)
# creating the treeview, making it use the filter a model, adding columns
self.treeview = Gtk.TreeView.new_with_model(Gtk.TreeModelSort(self.telecommand_filter))
for i, column_title in enumerate(list_header):
renderer = Gtk.CellRendererText()
column = Gtk.TreeViewColumn(column_title, renderer, text=i)
column.set_sort_column_id(i)
self.treeview.append_column(column)
# set up drag-source
self.treeview.enable_model_drag_source(Gdk.ModifierType.BUTTON1_MASK, [], DRAG_ACTION)
self.treeview.connect("drag-data-get", self.on_drag_data_get)
# setting up layout, treeview in scrollwindow
self.scrollable_treelist = Gtk.ScrolledWindow()
self.scrollable_treelist.set_vexpand(True)
self.scrollable_treelist.set_hexpand(True)
self.attach(self.scrollable_treelist, 0, 1, 8, 10)
self.scrollable_treelist.add(self.treeview)
def telecommand_filter_func(self, model, iter, data):
if (
self.current_filter_telecommand is None
or self.current_filter_telecommand == "None"
):
return True
else:
return model[iter][0] == self.current_filter_telecommand
def on_drag_data_get(self, widget, drag_context, dat, info, time):
selected_tc = self.get_selected_items()[0]
selected_iter = self.get_model().get_iter(selected_tc)
class DropArea(Gtk.Label):
def __init__(self):
Gtk.Label.__init__(self)
self.set_label("Drop Area")
self.drag_dest_set(Gtk.DestDefaults.ALL, [], DRAG_ACTION)
self.connect("drag-data-received", self.on_drag_data_received)
def on_drag_data_received(self, widget, drag_context, x, y, data, info, time):
print("Received data")
win = DragDropWindow()
win.connect("destroy", Gtk.main_quit)
win.show_all()
Gtk.main()
What am I overlooking here? Why doesn't the drop-area do anything?
Try to set gtk_tree_view_set_reorderable to True.
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.
So the below code is part of a bigger project but in general, I have a QTableWidget which is populated from a database. One of the items is a combobox which when the user selects a milestone option from the combobox I want to be able to know which row and column the combo box that was select is in so I can then apply a fixed rate to the value of a cell in the same row. All I need help on is how to track which cell (row, column) the combo box that was selected is in.
Please note im using other tabs that are not shown and is why my code is setup the way it is. I found some other help but am not a very experience python programmer so I got stuck.
#!/usr/local/bin/python
# -*- coding: latin9 -*-
import sys, os , random
from PyQt4 import QtGui, QtCore
from PyQt4.QtCore import pyqtSlot,SIGNAL,SLOT
import time
import json
import openpyxl
import Lists
class CashflowTab(QtGui.QDialog):
'''
Parent for all tab widgets of the REPO GUI
'''
def __init__(self, parent = None):
super(CashflowTab, self).__init__()
if parent != None:
self.setParent(parent)
self.initUI()
self.loadSettings()
def loadSettings(self):
'''
read settings and read db
'''
fh = open('settings.json')
self.settings = json.load(fh)
fh.close()
#load db
dbpath = self.settings['dbpath']
self.db = Lists.SQLiteHandler(dbpath)
self.repo = Lists.WCNList('ROSQL')
try:
self.db.read(self.repo)
except:
pass
class WCNSelectTab(CashflowTab):
'''
Window for WCN selection
'''
def __init__(self, parent = None):
super(WCNSelectTab, self).__init__(parent)
if parent != None:
self.setParent(parent)
def initUI(self):
global wbsitem, WCNSelectTab_object, linequerycheck
linequerycheck = 'False'
wbsitem = 'null'
QtGui.QApplication.setStyle(QtGui.QStyleFactory.create("cleanlooks"))
gbox = QtGui.QGridLayout(self)
self.projectlist = QtGui.QTableWidget()
self.projectlist.setSelectionBehavior(QtGui.QAbstractItemView.SelectRows)
#self.projectlist.setSelectionMode(QtGui.QAbstractItemView.ExtendedSelection)
self.projectlist.setColumnCount(3)
self.projectlist.setHorizontalHeaderLabels(QtCore.QStringList(['WBS','Service','Milestone']))
self.projectlist.setColumnWidth(0, 100)
self.projectlist.setColumnWidth(1, 100)
self.projectlist.setColumnWidth(2, 150)
gbox.addWidget(self.projectlist,5,0,3,6)
self.getAwardBudget()
def getAwardBudget(self):
global wbs_details
wbs_details = []
wbs_details.append(["123", "Service 1"])
wbs_details.append(["456", "Service 2"])
print wbs_details
self.projectlist.setRowCount(len(wbs_details))
for n,item in enumerate(wbs_details):
qitem = QtGui.QTableWidgetItem(item[0])
self.projectlist.setItem(n, 0, qitem)
qitem = QtGui.QTableWidgetItem(item[1])
self.projectlist.setItem(n, 1, qitem)
milestone_options = ["Award","Mobilization","Survey"]
milestonecombo = QtGui.QComboBox()
for t in milestone_options:
milestonecombo.addItem(t)
milestonecombo.setFixedWidth(150)
self.projectlist.setCellWidget(n, 2, milestonecombo)
class RepoGUI(QtGui.QMainWindow):
'''
Main Widget for REPO helper
'''
def __init__(self):
super(RepoGUI, self).__init__()
#self.mode = mode
self.initUI()
def initUI(self):
global approval, approval_names, username, approval_names
self.tabs = QtGui.QTabWidget()
self.setCentralWidget(self.tabs)
self.tabs.setAutoFillBackground(1)
fh = open('settings.json')
settings = json.load(fh)
fh.close()
if settings['WCNsubmit'] == 1:
self.tabs.addTab(WCNSelectTab(), 'WCN Creation')
self.setWindowTitle('Work Completion Notification')
self.setGeometry(300, 150, 1400, 800)
self.setStyleSheet('font-size: %ipt' %settings['fontsize'])
self.show()
def main():
app = QtGui.QApplication(sys.argv)
ex = RepoGUI()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
A possible solution is to use indexAt() since the position of the QComboBox is relative to the viewport(), but to obtain the QComboBox that was selected we use sender().
for n,item in enumerate(wbs_details):
qitem = QtGui.QTableWidgetItem(item[0])
self.projectlist.setItem(n, 0, qitem)
qitem = QtGui.QTableWidgetItem(item[1])
self.projectlist.setItem(n, 1, qitem)
milestone_options = ["Award","Mobilization","Survey"]
milestonecombo = QtGui.QComboBox()
milestonecombo.addItems(milestone_options)
milestonecombo.setFixedWidth(150)
milestonecombo.currentIndexChanged[str].connect(self.onCurrentIndexChanged)
self.projectlist.setCellWidget(n, 2, milestonecombo)
def onCurrentIndexChanged(self, text):
combobox = self.sender()
ix = self.projectlist.indexAt(combobox.pos())
print(ix.row(), ix.column(), text)
Another possible solution is to use the property:
for n,item in enumerate(wbs_details):
qitem = QtGui.QTableWidgetItem(item[0])
self.projectlist.setItem(n, 0, qitem)
qitem = QtGui.QTableWidgetItem(item[1])
self.projectlist.setItem(n, 1, qitem)
milestone_options = ["Award","Mobilization","Survey"]
milestonecombo = QtGui.QComboBox()
milestonecombo.addItems(milestone_options)
milestonecombo.setFixedWidth(150)
milestonecombo.setProperty("row", n)
milestonecombo.setProperty("column", 1)
milestonecombo.currentIndexChanged[str].connect(self.onCurrentIndexChanged)
self.projectlist.setCellWidget(n, 2, milestonecombo)
def onCurrentIndexChanged(self, text):
combobox = self.sender()
r = combobox.property("row").toPyObject()
c = combobox.property("column").toPyObject()
print(r, c, text)
I have a Gtk.ComboBoxText that should be used as a trigger to activate a row in a Gtk.TreeView. I know how to trigger the combo box by activating the respective row in the tree view. But vice versa is beyond my scope. I learned that I need to pass Gtk.TreePath and Gtk.TreeViewColumn to the function row_activated(), but I don't know how to implement these correctly and where to put the row ID in my function self.combo_changed(). This is an example of the issue:
from gi.repository import Gtk
class MainWindow(Gtk.Window):
def __init__(self):
Gtk.Window.__init__(self)
self.set_default_size(200, 100)
self.set_border_width(10)
self.grid = Gtk.Grid(row_spacing=10)
self.add(self.grid)
self.combo = Gtk.ComboBoxText()
self.combo.connect("changed", self.combo_changed)
self.grid.attach(self.combo, 0, 0, 1, 1)
self.liststore = Gtk.ListStore()
self.liststore.set_column_types([int, str])
self.treeview = Gtk.TreeView(self.liststore)
self.treeview.set_activate_on_single_click(True)
self.treeview.connect('row-activated', self.list_changed)
self.grid.attach(self.treeview, 0, 1, 1, 1)
cols = ["ID", "Animal"]
self.treeviewcolumn = []
self.cellrenderertext = []
for i in range(len(cols)):
self.cellrenderertext.append(Gtk.CellRendererText())
self.treeviewcolumn.append(Gtk.TreeViewColumn(cols[i]))
self.treeviewcolumn[i].pack_start(self.cellrenderertext[i], True)
self.treeviewcolumn[i].add_attribute(self.cellrenderertext[i], "text", i)
self.treeview.append_column(self.treeviewcolumn[i])
animals = ["Dog", "Cat", "Mouse"]
self.rowiter = []
for i in range(len(animals)):
self.combo.append_text(animals[i])
self.rowiter.append([self.liststore.append([i, animals[i]])])
self.combo.set_active(0)
def list_changed(self, widget, row, data2):
self.combo.set_active(int(row.to_string()))
def combo_changed(self, widget):
print(widget.get_active()) # the ID of the requested row
#self.treeview.row_activated(Gtk.TreePath(), Gtk.TreeViewColumn())
def quit_window(self, widget, data=None):
Gtk.main_quit()
win = MainWindow()
win.show_all()
win.connect("delete-event", win.quit_window)
Gtk.main()
I discovered that I also need Gtk.TreeView.set_cursor() to achieve my goal:
def combo_changed(self, widget):
row = widget.get_active() # the ID of the requested row
print(row)
self.treeview.row_activated(Gtk.TreePath(row), Gtk.TreeViewColumn(None))
self.treeview.set_cursor(Gtk.TreePath(row))
probably I'm less than a newbie in both python and GTK. Here my problem:
I've created a class to display a windows with a search bar and a treeview.
I fill the tree reading from an SQLITEDB.
Fine.
I'd like to use the search bar in order to select an item in the treeview (so performing, a db search and fill the treeview with the result)
I'm blocked on the callback since I cannot understand how to pass the user inserted data to a specific function.
Below some lines from my (STUPID) code.
class SecondWin:
def __init__(self):
self.win = gtk.Window(gtk.WINDOW_TOPLEVEL)
self.win.connect("delete_event", self.delete_event)
self.win.set_title("Gmail Phonebook")
self.win.set_size_request(600, 300) #Larghez1za, Altezza
self.win.set_border_width(8)
self.win.set_position(gtk.WIN_POS_CENTER)
self.win.connect("destroy", self.destroy)
self.table = gtk.Table(8, 8,True)
self.cerca_bottone = gtk.Button("Cerca")
self.cerca_bottone.set_size_request(70, 30) #Larghezza, Altezza
self.cerca_bottone.connect( "clicked", self.cercainrubrica) #Call back sul bottone
self.table.attach(self.cerca_bottone, 0, 1, 0, 1,gtk.SHRINK,gtk.SHRINK)
# Search BAR
self.hbox = gtk.HBox()
self.entry = gtk.Entry(30)
self.entry.connect("activate", self.cercainrubrica) #call back su enter
self.hbox.pack_end(self.entry)
self.table.attach(self.hbox, 1, 8, 0, 1,gtk.EXPAND|gtk.FILL|gtk.SHRINK,
gtk.EXPAND|gtk.FILL|gtk.SHRINK,0,0)
self.vbox = gtk.VBox(False, 8)
self.sw = gtk.ScrolledWindow()
self.sw.set_shadow_type(gtk.SHADOW_ETCHED_IN)
self.sw.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
self.vbox.pack_start(self.sw, True, True, 0)
self.table.attach(self.vbox, 1, 8, 1, 9,gtk.EXPAND|gtk.FILL|gtk.SHRINK,
gtk.EXPAND|gtk.FILL|gtk.SHRINK,10,10)
rubrica=mydb.find_record_complex("%", colonne_test[0])
store = self.create_model(rubrica) #THIS WORK FINE DISPLAYING THE SELECTION
HERE MY FUNCION
self.cercainrubrica ??
treeView=gtk.TreeView(store)
treeView.connect("row-activated", self.on_activated)
treeView.set_rules_hint(True)
self.sw.add(treeView)
self.create_columns(treeView,nomi_colonne)
self.statusbar = gtk.Statusbar()
self.win.add(self.table)
self.win.show_all()
THIS IS MY CALL BACK
def cercainrubrica( self, w, data=None):
name = self.entry.get_text()
capperi=mydb.find_record_complex(name, colonne_test[0])
store = self.create_model(capperi)
return store
def delete_event(self, widget, event, data=None):
return gtk.FALSE
def destroy(self, widget, data=None):
return gtk.main_quit()
def main(self):
gtk.main()
def create_model(self,records):
store = gtk.ListStore(str, str, str,str)
for record in records:
store.append([record[1], record[2], record[3],record[4]])
return store
def create_columns(self, treeView,intestazione_colonne):
i = 0
for nomecolonna in intestazione_colonne:
rendererText = gtk.CellRendererText()
column = gtk.TreeViewColumn(intestazione_colonne[i], rendererText, text=i)
column.set_sort_column_id(i)
treeView.append_column(column)
i += 1
def on_activated(self, widget, row, col):
model = widget.get_model()
text = model[row][0] + ", " + model[row][1] + ", " + model[row][2]
self.statusbar.push(0, text)
if __name__ == "__main__":
second = SecondWin()
second.main()
ANY HELP IS REALLY APPRECIATED
Not 100% sure what you want to do but if you want to filter what the treeview is showing based on the entered text, you should take a look at GtkTreeModelFilter. You can use it as a TreeModel and set your own VisibleFunc that decides if a row should be visible or not based on the entered text. When the text changes, just call refilter(): that will call VisibleFunc() for every row.
Sorry for not having a python example or docs for you, I hope that still helps...