im trying to get drag and drop working well in pygobject, but it is slow and unresponsive, 90% of the time i have to wave the item i am dragging around before i can drop it successfully, can anyone see if i am doing it incorrectly or is this a bug with pygobject? here is my code
from gi.repository import Gtk, GdkPixbuf, Gdk
import os
def got_data_cb(windowid, context, x, y, data, info, time):
# Got data.
tempArray = data.get_text().splitlines()
for i in tempArray:
i = i.replace('file://','')
print i
windowid.get_model().append([i])
context.finish(True, False, time)
def drop_cb(windowid, context, x, y, time):
# Some data was dropped, get the data
windowid.drag_get_data(context, context.list_targets()[-1], time)
return True
def main():
win = Gtk.Window()
win.connect('destroy', lambda x: Gtk.main_quit())
win.set_default_size(450, 400)
amodel = Gtk.ListStore(str)
column = Gtk.TreeViewColumn()
title = Gtk.CellRendererText()
column.pack_start(title, True)
column.add_attribute(title, "text", 0)
atree = Gtk.TreeView(amodel)
atree.append_column(column)
builder = Gtk.Builder()
filename = os.path.join('', 'treeview.ui')
builder.add_from_file(filename)
abox = builder.get_object('treeview1')
atree.drag_dest_set(0, [], 0)
atree.connect('drag_motion', lambda v,w,x,y,z: True)
atree.connect('drag_drop', drop_cb)
atree.connect('drag_data_received', got_data_cb)
win.add(atree)
win.show_all()
if __name__ == '__main__':
Gtk.main()
main()
Because I haven't "treeview.ui" file, you referenced, I port the drag'n'drop treeview example of pygtk.
This might be also an example on how to convert old pygtk code to pygi. I only test this code with python3.
#!/usr/bin/env python
# example treeviewdnd.py
from gi.repository import Gtk, Gdk, Pango, GObject
class TreeViewDnDExample:
TARGETS = [
('MY_TREE_MODEL_ROW', Gtk.TargetFlags.SAME_WIDGET, 0),
('text/plain', 0, 1),
('TEXT', 0, 2),
('STRING', 0, 3),
]
# close the window and quit
def delete_event(self, widget, event, data=None):
Gtk.main_quit()
return False
def clear_selected(self, button):
selection = self.treeview.get_selection()
model, iter = selection.get_selected()
if iter:
model.remove(iter)
return
def __init__(self):
# Create a new window
self.window = Gtk.Window()
self.window.set_title("URL Cache")
self.window.set_size_request(200, 200)
self.window.connect("delete_event", self.delete_event)
self.scrolledwindow = Gtk.ScrolledWindow()
self.vbox = Gtk.VBox()
self.hbox = Gtk.HButtonBox()
self.vbox.pack_start(self.scrolledwindow, True, True, 0)
self.vbox.pack_start(self.hbox, False, True, 0)
self.b0 = Gtk.Button('Clear All')
self.b1 = Gtk.Button('Clear Selected')
self.hbox.pack_start(self.b0, True, True, 0)
self.hbox.pack_start(self.b1, True, True, 0)
# create a liststore with one string column to use as the model
self.liststore = Gtk.ListStore(str)
# create the TreeView using liststore
self.treeview = Gtk.TreeView(self.liststore)
# create a CellRenderer to render the data
self.cell = Gtk.CellRendererText()
# create the TreeViewColumns to display the data
self.tvcolumn = Gtk.TreeViewColumn('URL', self.cell, text=0)
# add columns to treeview
self.treeview.append_column(self.tvcolumn)
self.b0.connect_object('clicked', Gtk.ListStore.clear, self.liststore)
self.b1.connect('clicked', self.clear_selected)
# make treeview searchable
self.treeview.set_search_column(0)
# Allow sorting on the column
self.tvcolumn.set_sort_column_id(0)
# Allow enable drag and drop of rows including row move
self.treeview.enable_model_drag_source( Gdk.ModifierType.BUTTON1_MASK,
self.TARGETS,
Gdk.DragAction.DEFAULT|
Gdk.DragAction.MOVE)
self.treeview.enable_model_drag_dest(self.TARGETS,
Gdk.DragAction.DEFAULT)
self.treeview.drag_dest_add_text_targets()
self.treeview.drag_source_add_text_targets()
self.treeview.connect("drag_data_get", self.drag_data_get_data)
self.treeview.connect("drag_data_received",
self.drag_data_received_data)
self.scrolledwindow.add(self.treeview)
self.window.add(self.vbox)
self.window.show_all()
def drag_data_get_data(self, treeview, context, selection, target_id,
etime):
treeselection = treeview.get_selection()
model, iter = treeselection.get_selected()
data = bytes(model.get_value(iter, 0), "utf-8")
selection.set(selection.get_target(), 8, data)
def drag_data_received_data(self, treeview, context, x, y, selection,
info, etime):
model = treeview.get_model()
data = selection.get_data().decode("utf-8")
drop_info = treeview.get_dest_row_at_pos(x, y)
if drop_info:
path, position = drop_info
iter = model.get_iter(path)
if (position == Gtk.TreeViewDropPosition.BEFORE
or position == Gtk.TreeViewDropPosition.BEFORE):
model.insert_before(iter, [data])
else:
model.insert_after(iter, [data])
else:
model.append([data])
if context.get_actions() == Gdk.DragAction.MOVE:
context.finish(True, True, etime)
return
def main():
Gtk.main()
if __name__ == "__main__":
treeviewdndex = TreeViewDnDExample()
main()
In order for me to get drag n drop working from another desktop app to my app, I had to add a drag_motion_callback to copy the data into the context:
Class Window(Gtk.Window):
def __init__(self):
#other stuff before here
#Set up dragNdrop to add URIs to open draw
self.connect("drag-motion", self.motion_cb)
self.connect("drag-drop", self.drop_cb)
self.connect("drag-data-received", self.got_data_cb)
self.drag_dest_set(0, [], 0)
def motion_cb(self, wid, context, x, y, time):
Gdk.drag_status(context,Gdk.DragAction.COPY, time)
# Returning True which means "I accept this data".
return True
def drop_cb(self, wid, context, x, y, time):
wid.drag_get_data(context, context.list_targets()[-1], time)
def got_data_cb(self, wid, context, x, y, data, info, time):
#Need to do something here
files=data.get_text().rstrip('\n').split('\n')
for fn in files:
fn= fn.split('file://',1)[-1]
self.add_file(fn)
self.place_items()
print files
context.finish(True, False, time)
Related
I want to draw some points at the mouse position (on the click of the mouse)
I use Python with Cairo
I've written some code but it doesn't work very well (I see the points when I click on the buttons).
I made the GUI using Glade which is also linked at the end of question.
import os,sys,platform
from os.path import join
import cairo
import gi
class Interface:
__gtype_name__ = "MonNom"
def __init__(self):
file_path = os.path.join('Interface.glade')
interface = Gtk.Builder()
interface.add_from_file(file_path)
self.double_buffer = None
interface.connect_signals(self)
window = interface.get_object("window")
window.show_all()
def on_draw(self, widget, cr):
if self.double_buffer is not None:
cr.set_source_surface(self.double_buffer, 0, 0)
cr.paint()
else:
print('Invalid double buffer')
return False
def on_configure(self, widget, event, data=None):
if self.double_buffer is not None:
self.double_buffer.finish()
self.double_buffer = None
self.double_buffer = cairo.ImageSurface(cairo.FORMAT_ARGB32, 600,400)
db = self.double_buffer
cc = cairo.Context(db)
cc.set_source_rgb(0,0, 0)
cc.rectangle(0, 0, 600, 400)
cc.set_source_rgb(1, 0, 0)
cc.stroke()
db.flush()
return False
def on_da_button_press(self, widget, event):
print ("Mouse clicked... at ", event.x, ", ", event.y)
# self.widget.queue_draw()
db = self.double_buffer
if db is not None:
cc = cairo.Context(db)
cc.move_to(event.x-5,event.y)
cc.line_to(event.x+5,event.y)
cc.move_to(event.x, event.y-5)
cc.line_to(event.x, event.y+5)
cc.set_source_rgb(0, 1, 0)
cc.stroke()
# db.flush()
return True
def on_destroy(self, widget):
Gtk.main_quit()
if __name__ == "__main__":
app = Interface()
Gtk.main()
The glade file
I have a program written in Python 3 and using Drag and Drop. I want to make Video tutorials. But when the Video capture is working, Drop fails. The capture program seems to intercept some signal.
I have tried several programs (Jing and others) with the same result. I am testing with a very simple Python script. When the capture is working, the lines which normally indicate the drop location do not appear. The get_data and the received_data functions are not launched.
I am in Windows 8. I have not tested in Linux.
Below, my python3 test code.
#!/usr/bin/env python
# example treeviewdnd.py modified
from gi.repository import Gtk, Gdk, Pango, GObject
class TreeViewDnDExample:
TARGETS = [
('MY_TREE_MODEL_ROW', Gtk.TargetFlags.SAME_WIDGET, 0),
('text/plain', 0, 1),
('TEXT', 0, 2),
('STRING', 0, 3),
]
# close the window and quit
def delete_event(self, widget, event, data=None):
Gtk.main_quit()
return False
def clear_selected(self, button):
selection = self.treeview.get_selection()
model, iter = selection.get_selected()
if iter:
model.remove(iter)
return
def __init__(self):
# Create a new window
self.window = Gtk.Window()
self.window.set_title("URL Cache")
self.window.set_size_request(200, 200)
self.window.connect("delete_event", self.delete_event)
self.scrolledwindow = Gtk.ScrolledWindow()
self.vbox = Gtk.VBox()
self.hbox = Gtk.HButtonBox()
self.vbox.pack_start(self.scrolledwindow, True, True, 0)
self.vbox.pack_start(self.hbox, False, True, 0)
self.b0 = Gtk.Button('Clear All')
self.b1 = Gtk.Button('Clear Selected')
self.hbox.pack_start(self.b0, True, True, 0)
self.hbox.pack_start(self.b1, True, True, 0)
# create a liststore with one string column to use as the model
self.liststore = Gtk.ListStore(str)
for a in "abcd" :
self.liststore.append([a])
# create the TreeView using liststore
self.treeview = Gtk.TreeView(self.liststore)
# create a CellRenderer to render the data
self.cell = Gtk.CellRendererText()
# create the TreeViewColumns to display the data
self.tvcolumn = Gtk.TreeViewColumn('URL', self.cell, text=0)
# add columns to treeview
self.treeview.append_column(self.tvcolumn)
self.b0.connect_object('clicked', Gtk.ListStore.clear, self.liststore)
self.b1.connect('clicked', self.clear_selected)
# make treeview searchable
self.treeview.set_search_column(0)
# Allow sorting on the column
self.tvcolumn.set_sort_column_id(0)
# Allow enable drag and drop of rows including row move
self.treeview.enable_model_drag_source( Gdk.ModifierType.BUTTON1_MASK,
self.TARGETS,
Gdk.DragAction.DEFAULT|
Gdk.DragAction.MOVE)
self.treeview.enable_model_drag_dest(self.TARGETS,
Gdk.DragAction.DEFAULT)
self.treeview.drag_dest_add_text_targets()
self.treeview.drag_source_add_text_targets()
self.treeview.connect("drag_data_get", self.drag_data_get_data)
self.treeview.connect("drag_data_received",
self.drag_data_received_data)
self.scrolledwindow.add(self.treeview)
self.window.add(self.vbox)
self.window.show_all()
def drag_data_get_data(self, treeview, context, selection, target_id,
etime):
print("drag get data")
treeselection = treeview.get_selection()
model, iter = treeselection.get_selected()
data = bytes(model.get_value(iter, 0), "utf-8")
selection.set(selection.get_target(), 8, data)
def drag_data_received_data(self, treeview, context, x, y, selection,
info, etime):
print("received")
def main():
Gtk.main()
if __name__ == "__main__":
treeviewdndex = TreeViewDnDExample()
main()
Since I didn't find an answer to the problem in Windows, the solution was to launch the program on a Linux machine where video capture programs does not present the same effect. It was even possible to be connected to the Linux machine with VNCViewer, and make the capture in the Windows box. For demonstration purposes, it is sufficient.
But I am still wondering what prevents Gtk-Windows to receive the drop event.
I'm having problems to understand why my custom Gtk.CellRenderer is only rendering the first row of a Gtk.ListStore.
I've been reading many docs and trying stuff like cellrenderer.set_visible(True) but I still have no idea of why this is happening.
Here is a full example:
from gi.repository import Gtk, Gdk, cairo, Pango, PangoCairo, GObject
import time
class CellRenderer5Stars(Gtk.CellRenderer):
__gproperties__ = {
'rating': ( int, # type
"integer prop", # nick
"A property that contains an integer", # blurb
0, # min
5, # max
0, # default
GObject.PARAM_READWRITE # flags
),
}
def __init__(self):
super().__init__()
self.font_size=15
self.font="Sans Bold {}".format(self.font_size)
self.rating = 5
def do_set_property(self, pspec, value):
setattr(self, pspec.name, value)
def do_get_property(self, pspec):
return getattr(self, pspec.name)
def do_get_size(self, widget, cell_area):
return (0, 0, self.font_size*5, self.font_size+5)
def do_render(self, cr, widget, background_area, cell_area, flags):
cr.translate (0, 0)
layout = PangoCairo.create_layout(cr)
desc = Pango.font_description_from_string (self.font)
layout.set_font_description(desc)
stars_var = self.rating
for i in range(5):
if i < stars_var:
layout.set_text("★", -1)
else:
layout.set_text("☆", -1)
cr.save()
PangoCairo.update_layout (cr, layout)
cr.move_to (i*(self.font_size+1), 0)
PangoCairo.show_layout (cr, layout)
cr.restore()
GObject.type_register(CellRenderer5Stars)
class Window(Gtk.Window):
def __init__(self):
Gtk.Window.__init__(self)
self.connect('destroy', self.on_quit)
liststore = Gtk.ListStore(int)
liststore.append([3])
liststore.append([2])
liststore.append([1])
treeview = Gtk.TreeView(liststore)
treeviewcolumn = Gtk.TreeViewColumn("Rating")
treeview.append_column(treeviewcolumn)
cellrenderer = CellRenderer5Stars()
treeviewcolumn.pack_start(cellrenderer, True)
treeviewcolumn.add_attribute(cellrenderer, "rating", 0)
self.add(treeview)
self.show_all()
def on_quit(self):
Gtk.main_quit()
w = Window()
Gtk.main()
You're rendering your stars to the same place of the cairo surface for every item you pass and don't obey the cell_area you should use. Replacing
cr.move_to (i*(self.font_size+1), cell_area.y)
in your code will yield a result you'd expect. But the documentation for do_render() gives you a little more info about the spacing to use:
Invokes the virtual render function of the Gtk.CellRenderer. The three passed-in rectangles are areas in cr. Most renderers will draw within cell_area; the xalign, yalign, xpad, and ypad fields of the Gtk.CellRenderer should be honored with respect to cell_area. background_area includes the blank space around the cell, and also the area containing the tree expander; so the background_area rectangles for all cells tile to cover the entire window.
Additionally the 'destroy' signal has a window argument, so you should define on_quit(self, window) or similar instead.
In below picture you can see the 3 parent child window. actually I made one code which will show all child according to parent. that is showing correctly but i want username & password column editable for database parent only(like as 3rd window). other should not be editable.
In my case all child of parent is editable.
here is my code
#!/usr/bin/env python
import pygtk
pygtk.require('2.0')
import gtk, gobject
import os
import cairo, gio, pango, pangocairo, atk
class BasicTreeViewExample:
# close the window and quit
def delete_event(self, widget, event, data=None):
gtk.main_quit()
return False
def col1_toggled_cb( self, cell, path, model ):
model[path][1] = not model[path][1]
def on_cell_edited(self, cell, path_string, new_text, model):
print "hi"
#cell.set_property('editable', False)
iter = model.get_iter_from_string(new_text)
path = model.get_path(iter)[0]
column = cell.get_data("username")
def __init__(self):
# Create a new window
self.window = gtk.Window(gtk.WINDOW_TOPLEVEL)
self.window.set_title("Basic TreeView Example")
self.window.set_size_request(400, 400)
self.window.connect("delete_event", self.delete_event)
self.treestore = gtk.TreeStore(gobject.TYPE_STRING, gobject.TYPE_BOOLEAN,gobject.TYPE_BOOLEAN, gobject.TYPE_STRING, gobject.TYPE_STRING)
for parent in range(2):
piter = self.treestore.append(None, ['Computer %i' % parent, False,False, '', ''])
pit = self.treestore.append(piter, ['Drive', False,False, '', ''])
pith = self.treestore.append(piter, ['Database', False,False, '', ''])
for child in range(2):
self.treestore.append(pit, ['child', True,True, '', ''])
for child in range(2):
self.treestore.append(pith, ['child', True,True, 'user', 'psw'])
self.treeview = gtk.TreeView(self.treestore)
self.cell = gtk.CellRendererText()
self.tvcolumn = gtk.TreeViewColumn("Computer Name", self.cell, text=0)
self.cell.set_property( 'editable', False )
self.cell0 = gtk.CellRendererToggle()
self.tvcolumn0 = gtk.TreeViewColumn("Select Drive", self.cell0 , active=1, visible=2)
self.cell0.set_property('activatable', True)
self.cell0.connect( 'toggled', self.col1_toggled_cb, self.treestore )
self.cell1 = gtk.CellRendererText()
self.tvcolumn1 = gtk.TreeViewColumn("username", self.cell1, text=3, visible=2)
self.cell1.set_property( 'editable', True )
self.cell1.connect("editing-started", self.on_cell_edited, self.treestore)
self.cell2 = gtk.CellRendererText()
self.tvcolumn2 = gtk.TreeViewColumn("password", self.cell2, text=4, visible=2)
self.cell2.set_property( 'editable', True )
self.treeview.append_column(self.tvcolumn)
self.treeview.append_column(self.tvcolumn0)
self.treeview.append_column(self.tvcolumn1)
self.treeview.append_column(self.tvcolumn2)
self.treeview.set_reorderable(True)
self.window.add(self.treeview)
self.window.show_all()
def main():
gtk.main()
if __name__ == "__main__":
tvexample = BasicTreeViewExample()
main()
Anybody help me for this problem.
Thanks...
The answer here is similar to the answer to your previous question: you need to extend your tree store with an additional column that specifies the attribute you care about — in this case, editability of username/password cells for a particular row, and connect that store column with the tree view columns that display and edit the username and password.
The provided code simply sets the editable property to true, which means the property is editable everywhere.
There are tutorials that explain the GTK tree view in some detail. Here is a version of the code from the question, modified to match your requirements as I understood them.
#!/usr/bin/env python
import pygtk
pygtk.require('2.0')
import gtk, gobject
import os
import cairo, gio, pango, pangocairo, atk
class BasicTreeViewExample:
# close the window and quit
def delete_event(self, widget, event, data=None):
gtk.main_quit()
return False
def col1_toggled_cb( self, cell, path, model ):
model[path][1] = not model[path][1]
def on_cell_edited(self, cell, path_string, new_text, model):
print "hi"
#cell.set_property('editable', False)
iter = model.get_iter_from_string(new_text)
path = model.get_path(iter)[0]
column = cell.get_data("username")
def __init__(self):
# Create a new window
self.window = gtk.Window(gtk.WINDOW_TOPLEVEL)
self.window.set_title("Basic TreeView Example")
self.window.set_size_request(400, 400)
self.window.connect("delete_event", self.delete_event)
self.treestore = gtk.TreeStore(gobject.TYPE_STRING, gobject.TYPE_BOOLEAN,gobject.TYPE_BOOLEAN, gobject.TYPE_STRING, gobject.TYPE_STRING, gobject.TYPE_BOOLEAN)
for parent in range(2):
piter = self.treestore.append(None, ['Computer %i' % parent, False,False, '', '', False])
pit = self.treestore.append(piter, ['Drive', False,False, '', '', False])
pith = self.treestore.append(piter, ['Database', False,False, 'user', 'psw', True])
for child in range(2):
self.treestore.append(pit, ['child', True,True, '', '', False])
for child in range(2):
self.treestore.append(pith, ['child', True,True, '', '', False])
self.treeview = gtk.TreeView(self.treestore)
self.cell = gtk.CellRendererText()
self.tvcolumn = gtk.TreeViewColumn("Computer Name", self.cell, text=0)
self.cell.set_property( 'editable', False )
self.cell0 = gtk.CellRendererToggle()
self.tvcolumn0 = gtk.TreeViewColumn("Select Drive", self.cell0 , active=1, visible=2)
self.cell0.set_property('activatable', True)
self.cell0.connect( 'toggled', self.col1_toggled_cb, self.treestore )
self.cell1 = gtk.CellRendererText()
self.tvcolumn1 = gtk.TreeViewColumn("username", self.cell1, text=3, visible=5, editable=5)
self.cell1.connect("editing-started", self.on_cell_edited, self.treestore)
self.cell2 = gtk.CellRendererText()
self.tvcolumn2 = gtk.TreeViewColumn("password", self.cell2, text=4, visible=5, editable=5)
self.treeview.append_column(self.tvcolumn)
self.treeview.append_column(self.tvcolumn0)
self.treeview.append_column(self.tvcolumn1)
self.treeview.append_column(self.tvcolumn2)
self.treeview.set_reorderable(True)
self.window.add(self.treeview)
self.window.show_all()
def main():
gtk.main()
if __name__ == "__main__":
tvexample = BasicTreeViewExample()
main()
Imagine I want to make a simple program with GUI like this.
this is my code:
from gi.repository import Gtk
import gobject
class gui():
def __init__(self):
self.window = Gtk.Window()
self.window.connect('delete-event', Gtk.main_quit)
self.box = Gtk.Box()
self.window.add(self.box)
self.progressbar = Gtk.ProgressBar()
self.box.pack_start(self.progressbar, True, True, 0)
self.button = Gtk.Button(label='Start')
self.button.connect('clicked', self.on_button_clicked)
self.box.pack_start(self.button, True, True, 0)
self.window.show_all()
Gtk.main()
def on_button_clicked(self, widget):
self.task = self.iters(100000)
gobject.idle_add(self.task.next)
def iters(self, j):
i = j
while i > 0 :
i -= 1
print i
self.progressbar.set_fraction((j - float(i)) / j)
yield True
yield False
if __name__ == '__main__':
gui = gui()
I know how to make progressbar work if I put everything in one script like that.
But, what if I want to separate gui part from logic part? So, it would be like this:
GUI.py
from gi.repository import Gtk
class gui():
def __init__(self):
self.window = Gtk.Window()
self.window.connect('delete-event', Gtk.main_quit)
self.box = Gtk.Box()
self.window.add(self.box)
self.progressbar = Gtk.ProgressBar()
self.box.pack_start(self.progressbar, True, True, 0)
self.button = Gtk.Button(label='Start')
self.button.connect('clicked', self.on_button_clicked)
self.box.pack_start(self.button, True, True, 0)
self.window.show_all()
Gtk.main()
def on_button_clicked(self, widget):
//how to implement it
if __name__ == '__main__':
gui = gui()
Iteration.py
class Iteration():
def iters(self, j):
i = j
while i > 0 :
i -= 1
print i
EDIT:
One more thing, the first code above works, but why do I get this message:
/usr/lib/python2.7/dist-packages/gobject/constants.py:24: Warning: g_boxed_type_register_static: assertion `g_type_from_name (name) == 0' failed
import gobject._gobject
EDIT:
Okay, imagine I have one module like this one:
Works.py
def getImageFromInternet(uri):
#some code to get image from internet
this getImageFromInternet function is not iterable. What I want to do is processing this function in the background while updating the progressbar and doing other thing with gui. Do I need a thread for this?
Create a generator that updates the GUI and drives the underlying data model generator, and pass that to idle_add. For example (untested):
def on_button_clicked(self, widget):
cnt = 10000
task = self.iters(cnt)
def gui_iter():
for i, ignore in enumerate(task, 1):
self.progressbar.set_fraction(i / cnt)
yield True
yield False
gobject.idle_add(gui_iter().next)