Related
I am trying to control some valves with a GUI and update the display when the button is pressed. The following code produces the correct initial display, but when I click one of the checkboxes I always get the value of i=2 and when unchecked I get i=0. I also have no idea how to update the docked window so that the message will toggle from close to open and vise versa. I do see the resultant value in self.states change in the array, but am always left with ['open', 'closed', 'open', 'closed', 'closed', 'closed'] Though this is changed the red closed on the right of the screen does not change to `open
class ApplicationWindow(gui.QMainWindow):
def __init__(self):
self.nCheckBoxes=6
self.states = ['closed']*self.nCheckBoxes
gui.QMainWindow.__init__(self)
self.setAttribute(core.Qt.WA_DeleteOnClose)
self.setWindowTitle("PiView")
self.file_menu = gui.QMenu('&File', self)
self.file_menu.addAction('&Quit', self.fileQuit, core.Qt.CTRL + core.Qt.Key_Q)
self.menuBar().addMenu(self.file_menu)
self.help_menu = gui.QMenu('&Help', self)
self.menuBar().addSeparator()
self.menuBar().addMenu(self.help_menu)
self.help_menu.addAction('&About', self.about)
self.help_menu.addAction('&Docs', self.doc)
self.main_widget = gui.QWidget(self)
l = gui.QVBoxLayout(self.main_widget)
self.dc = MyMplCanvas(self.main_widget, width=5, height=4, dpi=100)
l.addWidget(self.dc)
self.main_widget.setFocus()
self.setCentralWidget(self.main_widget)
self.dw = self.createDockWindows(self.dc)
self.statusBar().showMessage("Initialized", 2000)
def createDockWindows(self, MyMplCanvas):
"""
Create all the dock widgets
"""
self.drawValves()
self.drawGraphAdjustments()
self.drawStartAndStop()
def drawValves(self):
cboxes = gui.QDockWidget("Controls", self)
cboxes.setAllowedAreas(core.Qt.LeftDockWidgetArea)
w = gui.QWidget()
#layout = gui.QVBoxLayout()
layout = gui.QGridLayout()
w.setLayout(layout)
self.c = [0]*self.nCheckBoxes
# Create self.nCheckBoxes
msgBox = gui.QLabel()
msgBox.setText("States")
font = gui.QFont()
font.setBold(True)
msgBox.setFont(font)
msgBox.setStyleSheet("color: rgb(255,0,0)")
layout.addWidget(msgBox,0,1)
for i in range(self.nCheckBoxes):
self.c[i] = gui.QCheckBox("Valve " + str(i))
self.c[i].setChecked(False)
#self.c[i].stateChanged.connect(lambda:self.btnstate(self.c[i]))
#self.c[i].stateChanged.connect(self.checkedBox)
self.c[i].stateChanged.connect(lambda i: self.checkedBox(i))
layout.addWidget(self.c[i],i+1,0)
# Messages
msgBox = gui.QLabel()
msgBox.setText(self.states[i])
if self.states[i] == 'closed':
msgBox.setStyleSheet("color: rgb(255,0,0)")
else:
msgBox.setStyleSheet("color: rgb(0,255,0)")
layout.addWidget(msgBox,i+1,1)
spacerItem = gui.QSpacerItem(20,40, gui.QSizePolicy.Minimum, gui.QSizePolicy.Expanding)
layout.addItem(spacerItem)
cboxes.setWidget(w)
self.addDockWidget(core.Qt.LeftDockWidgetArea, cboxes)
def checkedBox(self,i):
sender = self.sender()
self.states[i] = 'open' if 'close' else 'close'
# For debugging
print i
print self.states
If I understand correctly I can just actuate the valve like I would normally in the function checkedBox.
This is what is displayed
I want to be able to click a checkbox and the message just to the right to change from closed to open, and vise versa.
The problem arises because the default value of i takes the value sent by the signal (ie state: Unchecked = 0, PartiallyChecked = 1, Checked = 2), to solve this we will add a further value to the function: Index of the checkbox. Also if you want to change the text of QLabel you must be able to access them so I have created a new list with labels. To make the change we will use the states list, for this implement the function updateLabels.
class ApplicationWindow(gui.QMainWindow):
def __init__(self):
self.nCheckBoxes=6
self.states = ['closed']*self.nCheckBoxes
gui.QMainWindow.__init__(self)
self.setAttribute(core.Qt.WA_DeleteOnClose)
self.setWindowTitle("PiView")
self.file_menu = gui.QMenu('&File', self)
#self.file_menu.addAction('&Quit', self.fileQuit, core.Qt.CTRL + core.Qt.Key_Q)
self.menuBar().addMenu(self.file_menu)
self.help_menu = gui.QMenu('&Help', self)
self.menuBar().addSeparator()
self.menuBar().addMenu(self.help_menu)
#self.help_menu.addAction('&About', self.about)
#self.help_menu.addAction('&Docs', self.doc)
self.main_widget = gui.QWidget(self)
l = gui.QVBoxLayout(self.main_widget)
self.dc = MyMplCanvas(self.main_widget, width=5, height=4, dpi=100)
l.addWidget(self.dc)
self.main_widget.setFocus()
self.setCentralWidget(self.main_widget)
self.dw = self.createDockWindows(self.dc)
self.statusBar().showMessage("Initialized", 2000)
def createDockWindows(self, MyMplCanvas):
"""
Create all the dock widgets
"""
self.drawValves()
#self.drawGraphAdjustments()
#self.drawStartAndStop()
def drawValves(self):
cboxes = gui.QDockWidget("Controls", self)
cboxes.setAllowedAreas(core.Qt.LeftDockWidgetArea)
w = gui.QWidget()
#layout = gui.QVBoxLayout()
layout = gui.QGridLayout()
w.setLayout(layout)
self.c = []
self.l = []
# Create self.nCheckBoxes
msgBox = gui.QLabel()
msgBox.setText("States")
font = gui.QFont()
font.setBold(True)
msgBox.setFont(font)
msgBox.setStyleSheet("color: rgb(255,0,0)")
layout.addWidget(msgBox,0,1)
for i in range(self.nCheckBoxes):
checkBox = gui.QCheckBox("Valve " + str(i))
checkBox.setChecked(False)
checkBox.stateChanged.connect(lambda state, p=i: self.checkedBox(state, p))
self.c.append(checkBox)
layout.addWidget(self.c[i],i+1,0)
msgBox = gui.QLabel()
msgBox.setText(self.states[i])
self.l.append(msgBox)
layout.addWidget(msgBox,i+1,1)
self.update_labels()
spacerItem = gui.QSpacerItem(20,40, gui.QSizePolicy.Minimum, gui.QSizePolicy.Expanding)
layout.addItem(spacerItem)
cboxes.setWidget(w)
self.addDockWidget(core.Qt.LeftDockWidgetArea, cboxes)
def checkedBox(self, state, p):
if state == core.Qt.Unchecked:
self.states[p] = 'closed'
else:
self.states[p] = 'open'
self.update_labels()
def update_labels(self):
for i in range(self.nCheckBoxes):
if self.states[i] == 'closed':
text = "closed"
styleSheet = "color: rgb(255,0,0)"
else:
text = "open"
styleSheet = "color: rgb(0,255,0)"
self.l[i].setText(text)
self.l[i].setStyleSheet(styleSheet)
I am using Python & GTK3 under mac OS X Sierra in order to display a database of nodes as a Diagram. It is a MVP architecture.
I use matplotlib to display my figure. As my display is interactive so I have to receive signals. I decided to receive signals directly from my matplotlib canvas instead using GTK GUI, as GTK event do not have enough parameters. Actually I want to receive signals on my canvas using matplotlib and show a popup menu of GTK.
The function that you have to look is MainFigurePressed. The idea is I received signals on my canvas with the program DrawChronoMap using the function Detecte() than I popup a menu of my GTK GUI calling View by connecting Viewhandlers. In order to launch my program, my controller will call my view.I am wondering how can I pass in argument my class Viewhandler in my class DrawChronoMap?
How should I do? How should I change my architecture to do that?
I have three classes actually :
class View():
def __init__(self, MainController):
#set windows
self.window = Gtk.Window()
self.window.connect("delete-event", Gtk.main_quit)
self.window.set_default_size(10000, 10000)
self.window.set_title('ChronoMap')
#Init Glade file # Get windows from glade
self.interface = Gtk.Builder()
self.interface.add_from_file("interface1.glade")
self.mainWindow = self.interface.get_object("mainWindow")
self.aboutchronomap = self.interface.get_object("aboutchronomap")
self.fichierdialogue=self.interface.get_object("fichierdialogue")
self.sw=self.interface.get_object("mainFigure")
self.toolbar=self.interface.get_object("MatplotlibToolbar")
self.sw3=self.interface.get_object("scrolledwindow1")
self.sw4=self.interface.get_object("scrolledwindow2")
self.add_node_window=self.interface.get_object("add_node_window")
self.add_edge_window=self.interface.get_object("add_edge_window")
self.modify_edge_window=self.interface.get_object("modify_edge_window")
self.modify_node_window=self.interface.get_object("modify_node_window")
self.add_reference_node_edge=self.interface.get_object("add_reference_node_edge")
self.popupmenuCartoNode=self.interface.get_object("popupmenuCartoNode")
self.popupmenuCartoEdge=self.interface.get_object("popupmenuCartoEdge")
self.popupmenuCartoOtherplace=self.interface.get_object("popupmenuCartoOtherplace")
self.popupmenuChronoNode=self.interface.get_object("popupmenuChronoNode")
self.popupmenuChronoZoneBC=self.interface.get_object("popupmenuChronoZoneBC")
self.popupmenuChronoCursor=self.interface.get_object("popupmenuChronoCursor")
#Global controller
self.controller=MainController
#Init CartoCanvas
self.figCarto = Figure(figsize=(20,20), dpi=80)
self.axCarto = self.figCarto.add_subplot(111)
self.canvasCarto = FigureCanvas(self.figCarto)
# Init ChronoCanvas
self.figChrono = Figure(figsize=(20,20), dpi=80)
self.axChrono = self.figChrono.add_subplot(111)
self.canvasChrono = FigureCanvas(self.figChrono)
#Create a New graph on the controller
self.controller.create_new_graph("CartoChronomap")
#add node & edges
nodeA=self.controller.create_evenement("outdated research material", "01-01-2016 00:00:00", "01-02-2016 00:00:00", 1, "BLAH BLAH BLAH", "http://")
nodeB= self.controller.create_evenement("Projected tsunami frequency too low", "08-08-2016 00:00:00", "09-10-2016 00:00:00", 1, "OK", "http://")
nodeC=self.controller.create_evenement("EV C", "08-07-2016 00:00:00", "09-08-2016 00:00:00", 1, "HOOOOO", "http://")
nodeD=self.controller.create_evenement("Accident", "08-10-2016 00:00:00", "09-11-2016 00:00:00", 1, "HOOOOO", "http://")
self.controller.create_edge(nodeA,nodeB, "LeLien", "Une mega explosion", "[]")
self.controller.create_edge(nodeB,nodeA, "InverseLien", "Une giga explosion", "[]")
self.controller.create_edge(nodeC,nodeD, "LienTest", "Ceci est un lien test", "[]")
self.controller.calculate_position('spring_layout');
#Connect to draw chronograph
self.FdessinChrono=Draw_chrono.DrawChronoMap(self.axChrono,self.controller)
#Connect to draw Cartograph
self.FdessinCarto = Draw_cartograph.DrawCartoMap(self.axCarto, self.controller)
#draw
self.FdessinCarto.draw_cartograph()
self.FdessinChrono.draw_chronograph()
#MouseFunction Carto
self.FdessinCarto.zoom_wheel()
self.FdessinCarto.pan_drag()
#self.FdessinCarto.drag_node()
self.FdessinCarto.ChangeNodeColor()
self.FdessinCarto.node_popup_mouse_over()
self.FdessinCarto.edge_popup_mouse_over()
#Global carto event & chrono event
self.CartoEvent = self.FdessinCarto.Detect()
self.ChronoEvent = self.FdessinChrono.Detect()
print(self.CartoEvent,self.ChronoEvent)
#MouseFunction Chrono
self.FdessinChrono.cursor()
self.FdessinChrono.ChangeColor()
#self.FdessinChrono.pan_drag()
self.FdessinChrono.node_popup_mouse_over()
#Display Mode
self.display_Mode = None
#Creating the ListStore model
#node_liststore
self.node_liststore = Gtk.ListStore(str, str, str,str,str,str)
if len(self.FdessinCarto.pos) != 0:
for i,node in enumerate(self.FdessinCarto.pos):
self.node_liststore.append([str(node.title),str(node.start_time),str(node.end_time),str(node.node_group),str(node.description),str(node.attachment_list)])
#edge_liststore
self.edge_liststore = Gtk.ListStore(str, str, str,str,str)
if len(self.FdessinCarto.edgelist) !=0:
edge_prop=self.FdessinCarto.controller.edge_data(nodeA,nodeB)
edge_prop1=self.FdessinCarto.controller.edge_data(nodeB,nodeA)
self.edge_liststore.append([edge_prop['label'],str(nodeA.title),str(nodeB.title),edge_prop['description'],edge_prop['attachment_list']])
self.edge_liststore.append([edge_prop1['label'],str(nodeA.title),str(nodeB.title),edge_prop1['description'],edge_prop1['attachment_list']])
#creating the filtre
self.node_filter = self.node_liststore.filter_new()
self.edge_filter = self.edge_liststore.filter_new()
#setting the filter function, note that we're not using the
self.node_filter.set_visible_func(ViewHandler.node_filter_func)
self.edge_filter.set_visible_func(ViewHandler.edge_filter_func)
#creating the treeview for Node, making it use the filter as a model, and adding the columns
self.treeviewNode = Gtk.TreeView.new_with_model(self.node_liststore)
for i, column_title in enumerate(["Nom", "Date début", "Date fin", "Type de noeud", "Description du noeud","fichier"]):
self.Noderenderer = Gtk.CellRendererText()
self.Noderenderer.set_property("editable", True)
column = Gtk.TreeViewColumn(column_title, self.Noderenderer, text=i)
self.treeviewNode.append_column(column)
#self.Noderenderer.connect("edited", self.onButtonCreateNode)
#creating the treeview for edge
self.treeviewEdge = Gtk.TreeView.new_with_model(self.edge_liststore)
for i, column_title in enumerate(["Nom", "Noeud 1", "Noeud 2", "Description du lien","fichier"]):
self.Edgerenderer = Gtk.CellRendererText()
self.Edgerenderer.set_property("editable", True)
column = Gtk.TreeViewColumn(column_title, self.Edgerenderer, text=i)
self.treeviewEdge.append_column(column)
# Connect with signals
self.interface.connect_signals(ViewHandler(self))
#setting up the layout, putting the treeview in a scrollwindow
self.sw3.add(self.treeviewNode)
self.sw4.add(self.treeviewEdge)
self.sw3.show_all()
self.sw4.show_all()
self.window.add(self.sw)
self.sw.show_all()
# All ready - open interface
Gtk.main()
def update_data(self):
self.CartoEvent = self.FdessinCarto.Detect()
self.ChronoEvent = self.FdessinChrono.Detect()
print(self.CartoEvent,self.ChronoEvent)
class ViewHandler():
def __init__(self, ViewConnection):
self.View = ViewConnection
def resetplot(self):
axCarto.cla()
axCarto.set_xlim(0,10)
axCarto.set_ylim(0,10)
axCarto.grid(True)
axChrono.cla()
axChrono.set_xlim(0,10)
axChrono.set_ylim(0,10)
axChrono.grid(True)
# All button signals of GTK
#Signal to open windows "creation of node"
def create_node_button_press_event(self,widget):
self.View.add_node_window.show_all()
#Signal to open window "creation of link"
def create_link_button_press_event(self,widget):
self.View.add_edge_window.show_all()
def onButtonCreateNode(self,widget):
self.resetplot()
nom=self.View.interface.get_object('name_node1').get_text()
node_type=self.View.interface.get_object('node_type1').get_text()
start_time_node=self.View.interface.get_object('start_time_node1').get_text()
end_time_node=self.View.interface.get_object('end_time_node1').get_text()
#print(nom,node_type,start_time_node,end_time_node)
self.View.node_liststore.append([nom, start_time_node, end_time_node, node_type, "BLAH BLAH BLAH", "http://"])
self.View.FdessinCarto.controller.create_evenement(nom, start_time_node, end_time_node, node_type, "BLAH BLAH BLAH", "http://")
self.View.FdessinChrono.controller.create_evenement(nom, start_time_node, end_time_node, node_type, "BLAH BLAH BLAH", "http://")
self.View.canvasCarto.draw()
self.View.canvasChrono.draw()
self.View.add_node_window.destroy()
self.View.sw.show_all()
self.View.sw3.show_all()
def onButtonAddFileEdge(self,widget):
pass
def onButtonCreateEdge(self,widget):
nom=self.View.interface.get_object('name_edge2').get_text()
edge_description=self.View.interface.get_object('edge_type2').get_text()
node1_edge=self.View.interface.get_object('node1_edge_entry').get_text()
node2_edge=self.View.interface.get_object('node2_edge_entry2').get_text()
#create signal with liststore
self.View.edge_liststore.append([node1_edge,node2_edge, nom, edge_description, "[]"])
#create link with canvas
self.View.FdessinCarto.controller.create_edge(node1_edge,node2_edge, nom, edge_description, "[]")
self.View.FdessinChrono.controller.create_edge(node1_edge,node2_edge, nom, edge_description, "[]")
self.View.canvasCarto.draw()
self.View.canvasChrono.draw()
#register it in the treeStore
edge_prop=list(self.View.FdessinCarto.controller.edge_data(node1_edge,node2_edge))
self.View.sw.show_all()
self.View.add_edge_window.destroy()
#Signal to contextual menu
def onMainFigurePressed(self,widget,event):
print(event.type, event.button, event.window, event.x, event.y, event.time,event.get_state(),event.time)
#update event
self.View.update_data()
self.CartoEvent = self.View.FdessinCarto.Detect()
self.ChronoEvent = self.View.FdessinChrono.Detect()
print(self.View.CartoEvent,self.View.ChronoEvent,self.CartoEvent,self.ChronoEvent)
if event.type == Gdk.EventType.ENTER_NOTIFY:
print("yes, enter")
if event.type == Gdk.EventType.LEAVE_NOTIFY:
print("No, out")
if event.type == Gdk.EventType.BUTTON_PRESS and event.button == 3:
if self.display_Mode == "carto" :
print("oui, carto " )
self.View.popupmenuCartoNode.popup(None, None, None, None, event.button, event.time)
elif self.display_Mode == "chrono" :
print( "oui chrono")
self.View.popupmenuChronoNode.popup(None, None, None, None, event.button, event.time)
else:
return None
def onButtonModifyNode(self,widget,event):
#print("modify windows show all")
self.View.modify_node_window.show_all()
def onButtonDeleteNode(self,widget,event):
#print("button pressed delete")
self.View.FdessinCarto.deleteNode()
def onButtonLinkNode(self,widget,event):
#print("hello")
self.View.add_edge_window.show_all()
def onButtonCopyNode(self,widget,event):
#print("copy")
self.View.FdessinCarto.copynode()
#print("copy it")
def onButtonOtherplaceCreateNode(self,widget,event):
self.View.add_node_window.show_all()
def onButtonAttributsNode(self,widget,event):
self.View.FdessinCarto.display_node_attributs()
#Signal of menubars
def on_node_file_button_press_event(self,widget):
self.View.add_node_window.show_all()
def on_create_edge_button_press_event(self,widget):
self.View.add_edge_window.show_all()
def on_Open_button_press_event(self,widget,event):
self.View.fichierdialogue.show_all()
#signal of about
def on_gtk_about_button_release_event(self,widget,event):
self.View.aboutchronomap.show_all()
# close window
def on_close_button_press_event(self,widget,event):
self.View.on_quit_button_press_event (widget,event)
def on_quit_button_press_event (self,widget,event):
#pop up menu
dialog = Gtk.MessageDialog(None, 0, Gtk.MessageType.INFO,Gtk.ButtonsType.OK_CANCEL, "Vous partez?")
dialog.format_secondary_text("Voulez vous toujours partir?")
response=dialog.run()
if response == Gtk.ResponseType.OK:
Gtk.main_quit()
elif response == Gtk.ResponseType.CANCEL:
dialog.destroy()
dialog.destroy()
return True
def on_confirmation_deletenode_button_press_event (self,widget,event):
#pop up menu
dialog = Gtk.MessageDialog(None, 0, Gtk.MessageType.INFO,Gtk.ButtonsType.OK_CANCEL, "Suppression du noeud?")
dialog.format_secondary_text("Voulez vous vraiment supprimer le noeud?")
response=dialog.run()
if response == Gtk.ResponseType.OK:
Gtk.main_quit()
elif response == Gtk.ResponseType.CANCEL:
dialog.destroy()
dialog.destroy()
return True
def on_mainWindow_destroy(self, widget):
Gtk.main_quit()
def on_carto_display_button_press_event(self,widget,event):
self.display_Mode = "carto"
child=self.View.sw.get_child()
child1 = self.View.toolbar.get_child()
#print(child)
if child != None:
self.View.toolbar.remove(child1)
self.View.sw.remove(child)
self.box.remove(self.View.canvasChrono)
self.box=Gtk.Box()
self.View.sw.add(self.box)
self.box.pack_start(self.View.canvasCarto, True, True, 0)
#Add toolbar
toolbar = NavigationToolbar(self.View.canvasCarto, self.View.window)
self.View.toolbar.add_with_viewport(toolbar)
self.View.sw.show_all()
def on_chrono_display_button_press_event(self,widget,event):
self.display_Mode= "chrono"
child = self.View.sw.get_child()
child1 = self.View.toolbar.get_child()
if child != None:
self.View.toolbar.remove(child1)
self.View.sw.remove(child)
self.box.remove(self.View.canvasCarto)
self.View.FdessinChrono.draw_chronograph()
self.box=Gtk.Box()
self.View.sw.add(self.box)
self.box.pack_start(self.View.canvasChrono, True, True, 0)
#Add toolbar
toolbar = NavigationToolbar(self.View.canvasChrono, self.View.window)
self.View.toolbar.add_with_viewport(toolbar)
self.View.sw.show_all()
class DrawChronoMap:
def __init__(self,ax, controller):
#Global controller
self.controller = controller
#Global graph
self.G = self.controller.total_graph()
#Global model
self.model=self.controller.model
#Global Axis
self.ax = ax
#Global figure
self.fig = self.ax.get_figure()
#Gloal canvas
self.canvas = self.ax.get_figure().canvas
#Global list
self.nodelist = self.controller.get_node_list()
self.edgelist=self.controller.get_edge_list()
#Global empty collection
#Global nodecollection
self.nodecollection=None
#Gloabl datanode
self.datanode = []
#Global empty list
self.eventnode_with_rectangle=[]
self.start_date=[]
self.end_date=[]
self.event_name=[]
#Global data axes
self.axis_x=[]
#Global label axis y
self.yticks = None
# Drag time
self.drag_time=0
self.press = []
self.drag_press = []
self.xdrag=0
self.ydrag=0
#event data
self.xdata=0
self.ydata=0
#event if we selecte edge
self.node1=None
self.node2=None
#cursor
self.ly = self.ax.axvline(color='k') # the vert line
self.txt = self.ax.text(0.7, 0.9, '', transform=self.ax.transAxes)
#Node attibute popup
self.popup = self.ax.text(0, 0, '', style='italic',bbox = {'facecolor':'y', 'alpha':0.5, 'pad':10})
#Edge attribute popup
self.popupedge = self.ax.text(0, 0, '', style='italic',bbox = {'facecolor':'y', 'alpha':0.5, 'pad':10})
def draw_chronograph(self):
#update graph
self.G = self.controller.total_graph()
#update data of nodecollection
self.nodelist = self.controller.get_node_list()
for i in range(len(self.nodelist)):
self.event_name.append(self.nodelist[i].title)
bottom = ((i-1)*0.5) + 1.0
width = self.nodelist[i].end_time - self.nodelist[i].start_time
left=self.nodelist[i].start_time
height=0.3
rectangle = self.ax.bar(left,height,width,bottom)
rectangle.bottom = bottom
rectangle.i = i
self.eventnode_with_rectangle.append([self.nodelist[i],rectangle])
self.nodelist[i].pos=i
self.datanode.append(self.nodelist[i].start_time)
self.datanode.append(self.nodelist[i].end_time)
#pos of i in the dictionnary
taille=len(self.event_name)
pos=arange(0.5,(taille+2)*0.5+0.5,0.5)
self.yticks=yticks(pos,self.event_name)
locsy,labelsy=self.yticks
self.ax.set_yticks(pos)
self.ax.set_yticklabels(labelsy, size='small')
self.ax.axis('tight')
self.ax.set_ylim(0, taille*0.5+0.5)
self.ax.grid(color = 'g', linestyle = ':')
font = font_manager.FontProperties(size='small')
self.ax.legend(loc=1,prop=font)
#format the x-axis
self.ax.set_xlim(min(self.datanode), max(self.datanode))
self.ax.xaxis.tick_top()
# Finish up
self.ax.invert_yaxis()
self.fig.autofmt_xdate()
#init cursor
self.ly.set_xdata((min(self.datanode)+ max(self.datanode))/2)
self.txt.set_text('y=%s' % ((min(self.datanode)+ max(self.datanode))/2))
self.canvas.draw()
def ChangeColor(self):
def on_press(event):
#update self.press
self.press=[]
x=event.xdata
y=event.ydata
self.press=[x,y]
#print(event.button)
if event.button == 1:
for i,rectangle in self.eventnode_with_rectangle:
if (i.start_time< self.press[0] <i.end_time) and ((i.pos-1)*0.5+1-0.15< self.press[1] <(i.pos-1)*0.5+1+0.15) :
rectangle=self.ax.barh(((i.pos-1)*0.5)+1.0, i.end_time - i.start_time, left=i.start_time, height=0.3, align='center', color='red', alpha = 0.75)
rectangle=self.ax.barh(((i.pos-1)*0.5)+1.0, i.end_time - i.start_time, left=i.start_time, height=0.3, align='center', color='blue', alpha = 0.75)
self.canvas.draw()
else :
return None
def on_release(event):
self.press = []
self.canvas.draw()
self.canvas.mpl_connect('button_press_event', on_press)
self.canvas.mpl_connect('button_release_event', on_release)
def Detect(self):
def event(event):
actual_node = None
x=event.xdata
y=event.ydata
for i,rectangle in self.eventnode_with_rectangle:
if (i.start_time<x<i.end_time) and ((i.pos-1)*0.5+1-0.15<y<(i.pos-1)*0.5+1+0.15) :
actual_node = i
break
print("function drawchrono %s" %actual_node)
return actual_node
self.canvas.mpl_connect('button_press_event', event)
import BasicModel as md
import View as vw
import networkx as nx
import matplotlib.pyplot as plt
import forceatlas.forceatlas as nxfa2
import undo
import re
import copy
class Controller:
def __init__(self, model):
"""
Initialization: Gets the model
:param model: Model class to use
"""
# Loads the model
self.model = model
if __name__ == '__main__':
# Init Model
MainModel = md.Model()
# Init Controller
MainController = Controller(MainModel)
# Init View
MainView = vw.View(MainController)
# Program Running.
I am going to add some pictures in order to better explain my problem.
Canvas embedded in my GTK environment
This is my GTK environment with the canvas (in white) using matplotlib to display things. My canvas is displayed with matplotlib. The canvas receives mouse events and it wil change the display of the canvas.
Display of the popup menu
We can see a popup menu here. So what is tricky here, it is NO LONGER my canvas which receives the event, but it is actually my GTK environment.
So the problem is here :
I want to display different menu depending on the type of object that I click on. I can do it easily if it is the canvas which receive event as I can directly calculate if the event is on a node or an edge, but if it is a GTK event, I can no longer detect if the event is on a node or on an edge.
So what should I do? should I build a new class in order to connect GTK event and canvas event?
Thanks
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...
I have to make several buttons, and later need to access them, so i tried as bellow but it does not work that way. How can i make buttons like array/lists?
""" Make 100 gtk.Button as array / list """
r = self.db_Query("http://pbx/a/endpoints")
if r:
for row in r:
lab1 = row['username']
self.button[lab1]= gtk.Button()
self.button[lab1].set_tooltip_text(lab1)
self.button[lab1].connect("clicked" , self.button1_action, None)
window.add(self.button[lab1] )
then search them as
self.button['STACKOVERFLOW'].set_text("how!")
it Works.
import sys,os
import pygtk, gtk, gobject
class GTK_Main:
def __init__(self):
""" Window """
button_rc = """
style "deButton" {
# Default - Yellow
bg[NORMAL] = "yellow"
fg[NORMAL] = "#000000"
# Mouse-Over - Blue
fg[PRELIGHT] = "blue"
bg[PRELIGHT] = "#000000"
# Click - RED
bg[ACTIVE] = "red"
fg[ACTIVE] = "#000000"
}
widget "*.deButton" style "deButton"
"""
window = gtk.Window(gtk.WINDOW_TOPLEVEL)
window.set_title("Test")
window.set_default_size(100, 100)
window.connect("destroy", gtk.main_quit, "WM destroy")
gtk.rc_parse_string(button_rc)
hbox_eq = gtk.HBox()
buttonlist = []
for i in [1,2,3,4,5,6,7,8,9]:
b = gtk.Button( str(i) )
b.set_name("deButton")
buttonlist.append(b)
hbox_eq.pack_start(b, True, True, 0)
for button in buttonlist:
print button.get_label()
window.add(hbox_eq)
window.show_all()
def exit(self, widget, data=None):
gtk.main_quit()
GTK_Main()
gtk.gdk.threads_init()
gtk.main()
Is there any way to use ttk Treeview with editable rows?
I mean it should work more like a table. For example on double click on the item make the #0 column 'editable'.
If this isn't possible, any way to allow mouse selecting on the item would be just fine. I haven't found any mention of this in tkdocs or other documents.
After long research I haven't found such feature so I guess there's any. Tk is very simple interface, which allows programmer to build 'high-level' features from the basics. So my desired behaviour this way.
def onDoubleClick(self, event):
''' Executed, when a row is double-clicked. Opens
read-only EntryPopup above the item's column, so it is possible
to select text '''
# close previous popups
# self.destroyPopups()
# what row and column was clicked on
rowid = self._tree.identify_row(event.y)
column = self._tree.identify_column(event.x)
# get column position info
x,y,width,height = self._tree.bbox(rowid, column)
# y-axis offset
# pady = height // 2
pady = 0
# place Entry popup properly
text = self._tree.item(rowid, 'text')
self.entryPopup = EntryPopup(self._tree, rowid, text)
self.entryPopup.place( x=0, y=y+pady, anchor=W, relwidth=1)
This is method within a class which composes ttk.Treeview as self._tree
And EntryPopup is then very simple sub-class of Entry:
class EntryPopup(Entry):
def __init__(self, parent, iid, text, **kw):
''' If relwidth is set, then width is ignored '''
super().__init__(parent, **kw)
self.tv = parent
self.iid = iid
self.insert(0, text)
# self['state'] = 'readonly'
# self['readonlybackground'] = 'white'
# self['selectbackground'] = '#1BA1E2'
self['exportselection'] = False
self.focus_force()
self.bind("<Return>", self.on_return)
self.bind("<Control-a>", self.select_all)
self.bind("<Escape>", lambda *ignore: self.destroy())
def on_return(self, event):
self.tv.item(self.iid, text=self.get())
self.destroy()
def select_all(self, *ignore):
''' Set selection on the whole text '''
self.selection_range(0, 'end')
# returns 'break' to interrupt default key-bindings
return 'break'
You could also pop up a tool window with the editable fields listed with Entries to update the values. This example has a treeview with three columns, and does not use subclasses.
Bind your double click to this:
def OnDoubleClick(self, treeView):
# First check if a blank space was selected
entryIndex = treeView.focus()
if '' == entryIndex: return
# Set up window
win = Toplevel()
win.title("Edit Entry")
win.attributes("-toolwindow", True)
####
# Set up the window's other attributes and geometry
####
# Grab the entry's values
for child in treeView.get_children():
if child == entryIndex:
values = treeView.item(child)["values"]
break
col1Lbl = Label(win, text = "Value 1: ")
col1Ent = Entry(win)
col1Ent.insert(0, values[0]) # Default is column 1's current value
col1Lbl.grid(row = 0, column = 0)
col1Ent.grid(row = 0, column = 1)
col2Lbl = Label(win, text = "Value 2: ")
col2Ent = Entry(win)
col2Ent.insert(0, values[1]) # Default is column 2's current value
col2Lbl.grid(row = 0, column = 2)
col2Ent.grid(row = 0, column = 3)
col3Lbl = Label(win, text = "Value 3: ")
col3Ent = Entry(win)
col3Ent.insert(0, values[2]) # Default is column 3's current value
col3Lbl.grid(row = 0, column = 4)
col3Ent.grid(row = 0, column = 5)
def UpdateThenDestroy():
if ConfirmEntry(treeView, col1Ent.get(), col2Ent.get(), col3Ent.get()):
win.destroy()
okButt = Button(win, text = "Ok")
okButt.bind("<Button-1>", lambda e: UpdateThenDestroy())
okButt.grid(row = 1, column = 4)
canButt = Button(win, text = "Cancel")
canButt.bind("<Button-1>", lambda c: win.destroy())
canButt.grid(row = 1, column = 5)
Then confirm the changes:
def ConfirmEntry(self, treeView, entry1, entry2, entry3):
####
# Whatever validation you need
####
# Grab the current index in the tree
currInd = treeView.index(treeView.focus())
# Remove it from the tree
DeleteCurrentEntry(treeView)
# Put it back in with the upated values
treeView.insert('', currInd, values = (entry1, entry2, entry3))
return True
Here's how to delete an entry:
def DeleteCurrentEntry(self, treeView):
curr = treeView.focus()
if '' == curr: return
treeView.delete(curr)
I have tried #dakov solution but it did not work for me since my treeView has multiple columns and for few more reasons. I made some changes that enhanced it so here is my version
class Tableview(ttk.Treeview):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
tv.bind("<Double-1>", lambda event: self.onDoubleClick(event))
def onDoubleClick(self, event):
''' Executed, when a row is double-clicked. Opens
read-only EntryPopup above the item's column, so it is possible
to select text '''
# close previous popups
try: # in case there was no previous popup
self.entryPopup.destroy()
except AttributeError:
pass
# what row and column was clicked on
rowid = self.identify_row(event.y)
column = self.identify_column(event.x)
# handle exception when header is double click
if not rowid:
return
# get column position info
x,y,width,height = self.bbox(rowid, column)
# y-axis offset
pady = height // 2
# place Entry popup properly
text = self.item(rowid, 'values')[int(column[1:])-1]
self.entryPopup = EntryPopup(self, rowid, int(column[1:])-1, text)
self.entryPopup.place(x=x, y=y+pady, width=width, height=height, anchor='w')
The EntryPopup class
class EntryPopup(ttk.Entry):
def __init__(self, parent, iid, column, text, **kw):
ttk.Style().configure('pad.TEntry', padding='1 1 1 1')
super().__init__(parent, style='pad.TEntry', **kw)
self.tv = parent
self.iid = iid
self.column = column
self.insert(0, text)
# self['state'] = 'readonly'
# self['readonlybackground'] = 'white'
# self['selectbackground'] = '#1BA1E2'
self['exportselection'] = False
self.focus_force()
self.select_all()
self.bind("<Return>", self.on_return)
self.bind("<Control-a>", self.select_all)
self.bind("<Escape>", lambda *ignore: self.destroy())
def on_return(self, event):
rowid = self.tv.focus()
vals = self.tv.item(rowid, 'values')
vals = list(vals)
vals[self.column] = self.get()
self.tv.item(rowid, values=vals)
self.destroy()
def select_all(self, *ignore):
''' Set selection on the whole text '''
self.selection_range(0, 'end')
# returns 'break' to interrupt default key-bindings
return 'break'
from tkinter import ttk
from tkinter import *
root = Tk()
columns = ("Items", "Values")
Treeview = ttk.Treeview(root, height=18, show="headings", columns=columns) #
Treeview.column("Items", width=200, anchor='center')
Treeview.column("Values", width=200, anchor='center')
Treeview.heading("Items", text="Items")
Treeview.heading("Values", text="Values")
Treeview.pack(side=LEFT, fill=BOTH)
name = ['Item1', 'Item2', 'Item3']
ipcode = ['10', '25', '163']
for i in range(min(len(name), len(ipcode))):
Treeview.insert('', i, values=(name[i], ipcode[i]))
def treeview_sort_column(tv, col, reverse):
l = [(tv.set(k, col), k) for k in tv.get_children('')]
l.sort(reverse=reverse)
for index, (val, k) in enumerate(l):
tv.move(k, '', index)
tv.heading(col, command=lambda: treeview_sort_column(tv, col, not reverse))
def set_cell_value(event):
for item in Treeview.selection():
item_text = Treeview.item(item, "values")
column = Treeview.identify_column(event.x)
row = Treeview.identify_row(event.y)
cn = int(str(column).replace('#', ''))
rn = int(str(row).replace('I', ''))
entryedit = Text(root, width=10 + (cn - 1) * 16, height=1)
entryedit.place(x=16 + (cn - 1) * 130, y=6 + rn * 20)
def saveedit():
Treeview.set(item, column=column, value=entryedit.get(0.0, "end"))
entryedit.destroy()
okb.destroy()
okb = ttk.Button(root, text='OK', width=4, command=saveedit)
okb.place(x=90 + (cn - 1) * 242, y=2 + rn * 20)
def newrow():
name.append('to be named')
ipcode.append('value')
Treeview.insert('', len(name) - 1, values=(name[len(name) - 1], ipcode[len(name) - 1]))
Treeview.update()
newb.place(x=120, y=(len(name) - 1) * 20 + 45)
newb.update()
Treeview.bind('<Double-1>', set_cell_value)
newb = ttk.Button(root, text='new item', width=20, command=newrow)
newb.place(x=120, y=(len(name) - 1) * 20 + 45)
for col in columns:
Treeview.heading(col, text=col, command=lambda _col=col: treeview_sort_column(Treeview, _col, False))
root.mainloop()
After so much research while doing my project got this code, it helped me a lot.
Double click on the element you want to edit, make the required change and click 'OK' button
I think this is what exactly you wanted
#python #tkinter #treeview #editablerow
New row
Editable row
This is just for creating a tree for the specified path that is set in the constructor. you can bind your event to your item on that tree. The event function is left in a way that the item could be used in many ways. In this case, it will show the name of the item when double clicked on it. Hope this helps somebody.
import ttk
from Tkinter import*
import os*
class Tree(Frame):
def __init__(self, parent):
Frame.__init__(self, parent)
self.parent = parent
path = "/home/...."
self.initUI(path)
def initUI(self, path):
self.parent.title("Tree")
self.tree = ttk.Treeview(self.parent)
self.tree.bind("<Double-1>", self.itemEvent)
yScr = ttk.Scrollbar(self.tree, orient = "vertical", command = self.tree.yview)
xScr = ttk.Scrollbar(self.tree, orient = "horizontal", command = self.tree.xview)
self.tree.configure(yscroll = yScr.set, xScroll = xScr.set)
self.tree.heading("#0", text = "My Tree", anchor = 'w')
yScr.pack(side = RIGHT, fill = Y)
pathy = os.path.abspath(path)
rootNode = self.tree.insert('', 'end', text = pathy, open = True)
self.createTree(rootNode, pathy)
self.tree.pack(side = LEFT, fill = BOTH, expand = 1, padx = 2, pady = 2)
self.pack(fill= BOTH, expand = 1)
def createTree(self, parent, path)
for p in os.listdir(path)
pathy = os.path.join(path, p)
isdir = os.path.isdir(pathy)
oid = self.tree.insert(parent, 'end' text = p, open = False)
if isdir:
self.createTree(oid, pathy)
def itemEvent(self, event):
item = self.tree.selection()[0] # now you got the item on that tree
print "you clicked on", self.tree.item(item,"text")
def main():
root = Tk.Tk()
app = Tree(root)
root.mainloop()
if __name__ == '__main__'
main()
You should not do this manually
there are ready to use pack that have this Feature and many more such as
tkintertable
it have some insane features
there is also pygubu-editable-treeview
if you are intrested in pygubu,
as for the the reason you shouldnt code your own ,
in order to do a good treeview you will need to build more Feature that make your gui easier to use
however such Feature takes hundred lines of code to create.(takes a long time to get right)
unless you are making a custom TREE-View-widget,it doesnot worth the effort.
I don't know about making the row editable, but to capture clicking on a row, you use the <<TreeviewSelect>> virtual event. This gets bound to a routine with the bind() method, then you use the selection() method to get the ids of the items selected.
These are snippets from an existing program, but show the basic sequence of calls:
# in Treeview setup routine
self.tview.tree.bind("<<TreeviewSelect>>", self.TableItemClick)
# in TableItemClick()
selitems = self.tview.tree.selection()
if selitems:
selitem = selitems[0]
text = self.tview.tree.item(selitem, "text") # get value in col #0