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)
Related
I have problem with the results of my pop-up window. Below I have shown part of my code to understand the problem.
It's a kind of pop-up window where the user makes some choice in the GUI. After this it should show a window where there will be the question "Are you sure?", and two buttons "Yes" and "No".
The problem is that when I test the code below (before and after the msg.show()), I have the same value set as False.
Why doesnt it work like this:
Before function -> False
Show my window and wait to click the button
If I clicked button "Yes", then give True, else False
How I can handle this properly? Is there another approach?
from PyQt4 import QtCore, QtGui
from Message import Ui_Message
import sys
class MessageBox(QtGui.QDialog):
def __init__(self, parent=None):
QtGui.QWidget.__init__(self, parent=None)
self.msg = Ui_Message()
self.msg.setupUi(self)
self.confirmed=False
self.declined=False
QtCore.QObject.connect(self.msg.NoButton, QtCore.SIGNAL(("clicked()")), self.Declined)
QtCore.QObject.connect(self.msg.YesButton, QtCore.SIGNAL(("clicked()")), self.Confirmed)
def Confirmed(self):
self.confirmed = True
MessageBox.close(self)
return True
def Declined(self):
self.declined = True
MessageBox.close(self)
if __name__ == "__main__":
app = QtGui.QApplication(sys.argv)
msg = MessageBox()
print('Befor show window',msg.confirmed)
msg.show()
print('After show window', msg.confirmed)
sys.exit(app.exec_())
Your example doesn't work, because you are printing "After show window" before the window has closed. It is the exec() method that blocks, not the show() method, so your example would need to be written like this:
app = QtGui.QApplication(sys.argv)
msg = MessageBox()
print('Before show window', msg.confirmed)
msg.show()
app.exec_() # this blocks, waiting for close
print('After show window', msg.confirmed)
sys.exit()
However, a much more realistic example showing how to use a dialog to confirm an action would be something like this:
import sys
from PyQt4 import QtCore, QtGui
class MessageBox(QtGui.QDialog):
def __init__(self, parent=None):
super(MessageBox, self).__init__(parent)
self.yesButton = QtGui.QPushButton('Yes')
self.noButton = QtGui.QPushButton('No')
layout = QtGui.QGridLayout(self)
layout.addWidget(QtGui.QLabel('Are you sure?'), 0, 0)
layout.addWidget(self.yesButton, 1, 0)
layout.addWidget(self.noButton, 1, 1)
self.yesButton.clicked.connect(self.accept)
self.noButton.clicked.connect(self.reject)
class Window(QtGui.QWidget):
def __init__(self):
super(Window, self).__init__()
self.button = QtGui.QPushButton('Do Something')
self.button.clicked.connect(self.handleButton)
layout = QtGui.QVBoxLayout(self)
layout.addWidget(self.button)
def handleButton(self):
if self.confirmSomething():
print('Yes')
else:
print('No')
def confirmSomething(self):
msg = MessageBox(self)
result = msg.exec_() == QtGui.QDialog.Accepted
msg.deleteLater()
return result
if __name__ == "__main__":
app = QtGui.QApplication(sys.argv)
window = Window()
window.show()
app.exec_()
So I have the following code (python 2.7) that checks for a combination of keypresses and shows/hides a gtk window with a few gtk entry boxes. The show/hide works until the window locks up and stops responding, and the inner part of the window turns black. I have to kill the process and start it up again once that window turns black. I've tried all different combinations of show_all() and returning True after hiding the window to no avail. I'm not sure what I'm doing wrong here.
In the end I'd like to be able to press this key combination and show/hide this window without the contents going away.
#!/usr/bin/python
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk
import pyxhook
class MyWindow(Gtk.Window):
def __init__(self):
Gtk.Window.__init__(self, title="Configurator")
self.box = Gtk.Box(spacing=6)
self.add(self.box)
self.ipEntry = Gtk.Entry()
self.ipEntry.set_text("IP Address")
self.maskEntry = Gtk.Entry()
self.maskEntry.set_text("NetMask")
self.gatewayEntry = Gtk.Entry()
self.gatewayEntry.set_text("gatewayEntry")
self.button1 = Gtk.Button(label="Save")
self.button1.connect("clicked", self.on_button1_clicked)
self.box.pack_start(self.ipEntry, True, True, 0)
self.box.pack_start(self.maskEntry, True, True, 0)
self.box.pack_start(self.gatewayEntry, True, True, 0)
self.box.pack_start(self.button1, True, True, 0)
#catch window destroy event and stop it from happening
self.connect('delete-event', self.on_destroy)
def on_button1_clicked(self, widget):
print("Hello")
def on_destroy(self, widget=None, *data):
print("tried to destroy")
self.hide()
return True
#list of ascii keypresses to test if a certain combination of keys is pressed
keypresses = []
win = MyWindow()
win.connect("delete-event", Gtk.main_quit)
win.show_all()
win.set_keep_above(True)
def OnKeyboardEvent(event):
#ascii 227 is l_control, ascii 225 is l_shift, ascii 120 is x
#bring the following external variables into the scope of this function
global keypresses
global win
#check if gtk window is hidden or visible
isVisible = win.get_property("visible")
#store our keypress if it is worthy of being stored (we started with a control character)
if event.Ascii == 227 or ( len(keypresses) >= 1 and keypresses[0] == 227 ):
print("here" + str(len(keypresses)))
keypresses.append(event.Ascii)
#check if we have three items in keypresses to compare, then see if it's the right combination
if len(keypresses) == 3 and keypresses[0] == 227 and keypresses[1] == 225 and keypresses[2] == 120:
#show/hide our window
if isVisible:
win.hide()
del keypresses[:]
keypresses = []
else:
win.show_all()
#clear out our list to compare again
del keypresses[:]
keypresses = []
elif len(keypresses) >= 3:
del keypresses[:]
keypresses = []
#create instance of hook manager
HookManager = pyxhook.HookManager()
#define our callback to fire when a key is pressed
HookManager.KeyDown = OnKeyboardEvent
#hook the keyboard
HookManager.HookKeyboard()
#start our listener
HookManager.start()
Gtk.main()
#close the hook listener when gtk main loop exits
HookManager.cancel()
Here is a small hack that tests the window show/hide when pressing F5. When you press it a second window will show or hide and i've tested for sometime without problems. Maybe it's the HookManager that somehow is messing with the Gtk/Glib mainloop. Nonetheless my method only works if there is a window to grab the key presses, so you will need, indeed, some form of grabbing key presses if your goal is to have no GUI:
#!/usr/bin/python
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk
from gi.repository import Gdk
#import pyxhook
class MyWindow(Gtk.Window):
def __init__(self):
Gtk.Window.__init__(self, title="Configurator")
self.box = Gtk.Box(spacing=6)
self.add(self.box)
self.ipEntry = Gtk.Entry()
self.ipEntry.set_text("IP Address")
self.maskEntry = Gtk.Entry()
self.maskEntry.set_text("NetMask")
self.gatewayEntry = Gtk.Entry()
self.gatewayEntry.set_text("gatewayEntry")
self.button1 = Gtk.Button(label="Save")
self.button1.connect("clicked", self.on_button1_clicked)
self.box.pack_start(self.ipEntry, True, True, 0)
self.box.pack_start(self.maskEntry, True, True, 0)
self.box.pack_start(self.gatewayEntry, True, True, 0)
self.box.pack_start(self.button1, True, True, 0)
#catch window destroy event and stop it from happening
self.connect('delete-event', self.on_destroy)
self.connect('key-press-event', self.on_keypress)
def on_keypress(self, widget, event):
global w
if event.keyval == Gdk.KEY_F5:
isVisible = w.get_property("visible")
if (isVisible):
w.hide()
else:
w.show()
print("Keypress")
def on_button1_clicked(self, widget):
print("Hello")
def on_destroy(self, widget=None, *data):
print("tried to destroy")
self.hide()
return False
w = MyWindow()
win = MyWindow()
win.connect("delete-event", Gtk.main_quit)
win.show_all()
win.set_keep_above(True)
Gtk.main()
I developed a GUI program based on python Gtk3. In the Main Window there is a button and a progressbar, my purpose is when pressing the button there is another thread running to do some work finally another GTK window shows up to display the result, in the meantime the preogress could update the progress in the main Window correctly.
But I always got a error as below:
[xcb] Unknown sequence number while processing queue
[xcb] Most likely this is a multi-threaded client and XInitThreads has not been called
[xcb] Aborting, sorry about that.
python: xcb_io.c:274: poll_for_event: Assertion `!xcb_xlib_threads_sequence_lost' failed.
Aborted (core dumped)
I really don't know what is going wrong there and have no idea about it, could anyone help taking look at it and provide a example here ?
Thanks a lot !
I developed a test program here as below, I always got the follow errors under Linux platform:
(fileChooser.py:40834): Gtk-CRITICAL **: gtk_text_attributes_ref: assertion 'values != NULL' failed
(fileChooser.py:40834): Gtk-CRITICAL **: gtk_text_attributes_ref: assertion 'values != NULL' failed
(fileChooser.py:40834): Pango-CRITICAL **: pango_layout_new: assertion 'context != NULL' failed
Segmentation fault (core dumped)
#!/usr/bin/env python
import os
import re
import multiprocessing
import threading
import platform
from gi.repository import Gtk, GLib, Gdk, GObject
class ANA(Gtk.Window):
def __init__(self):
Gtk.Window.__init__(self)
self.tree_store = dict()
self.COL_NAME = 0
self.COL_COLOR = 1
self.window = Gtk.Window()
self.window.set_title("Analysing Results")
self.window.set_size_request(1000, 750)
self.connect('destroy', lambda *w: Gtk.main_quit())
self.main_vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=1)
self.window.add(self.main_vbox)
self.hpaned = Gtk.HPaned()
self.hpaned.set_position(295)
self.main_vbox.pack_start(self.hpaned, True, True, 0)
self.notebook = Gtk.Notebook()
for tab in ['A', 'B']:
scrolled_window = Gtk.ScrolledWindow()
scrolled_window.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC)
scrolled_window.set_shadow_type(Gtk.ShadowType.IN)
self._new_notebook_page(self.notebook, scrolled_window, tab)
treeview = self.__create_treeview(tab)
scrolled_window.add(treeview)
self.hpaned.add(self.notebook)
self.notebook1 = Gtk.Notebook()
self.hpaned.add(self.notebook1)
self.scrolled_window2, self.info_buffer, self.info_text_view = self.__create_text(True)
self._new_notebook_page(self.notebook1, self.scrolled_window2, '_Info')
info_text_view = self.info_text_view
self.window.show_all()
def _new_notebook_page(self, notebook, widget, label):
l = Gtk.Label(label='')
l.set_text_with_mnemonic(label)
notebook.append_page(widget, l)
def __create_text(self, is_source=False):
scrolled_window = Gtk.ScrolledWindow()
scrolled_window.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC)
scrolled_window.set_shadow_type(Gtk.ShadowType.IN)
text_view = Gtk.TextView()
scrolled_window.add(text_view)
buffer = Gtk.TextBuffer()
text_view.set_buffer(buffer)
text_view.set_editable(False)
text_view.set_cursor_visible(True)
return scrolled_window, buffer, text_view
def __create_treeview(self, tab_name):
treestore = Gtk.TreeStore(str, str)
self.tree_store[tab_name] = treestore
treeview = Gtk.TreeView(treestore)
selection = treeview.get_selection()
selection.set_mode(Gtk.SelectionMode.BROWSE)
treeview.set_size_request(200, -1)
cell = Gtk.CellRendererText()
column = Gtk.TreeViewColumn(tab_name, cell, foreground=self.COL_COLOR)
column.add_attribute(cell, "text", self.COL_NAME)
treeview.append_column(column)
if tab_name == "A":
selection.connect('changed', self.selection_changed_A)
elif tab_name == "B":
selection.connect('changed', self.selection_changed_B)
treeview.expand_all()
return treeview
def selection_changed_A(self):
print "A"
def selection_changed_B(self):
print "B"
class ANALYSING_PROCESS(multiprocessing.Process):
def __init__(self):
super(ANALYSING_PROCESS, self).__init__()
def run(self):
import time
time.sleep(5)
ANA()
class ANALYSING_THREAD(threading.Thread):
def __init__(self, pbar, timer):
super(ANALYSING_THREAD, self).__init__()
self.pbar = pbar
self.timer = timer
def run(self):
Gdk.threads_init()
Gdk.threads_enter()
import time
time.sleep(5)
ANA()
Gdk.threads_leave()
self.pbar.set_text("Done")
self.pbar.set_show_text(True)
GObject.source_remove(self.timer)
class File_Chooser(Gtk.Window):
def __init__(self):
Gtk.Window.__init__(self)
self.connect('destroy', lambda *w: Gtk.main_quit())
self.set_title("Test")
self.set_border_width(8)
frame = Gtk.Frame()
self.add(frame)
self.vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=8)
self.vbox.set_border_width(8)
frame.add(self.vbox)
label = Gtk.Label()
label.set_markup("<span font_desc=\"Serif 25\" foreground=\"#015F85\" size=\"x-large\"> Test</span>")
self.vbox.pack_start(label, False, False, 0)
self.button_entry = dict()
self.entry_name = dict()
hbox = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=8)
button = Gtk.Button("Browse Files")
button.connect('clicked', self.browse_files)
hbox.pack_start(button, False, False, 0)
label = Gtk.Label()
label.set_markup("<span foreground=\"#015F85\"> files </span>")
label.set_use_underline(True)
hbox.pack_start(label, False, False, 0)
self.entry1 = Gtk.Entry()
hbox.pack_start(self.entry1, True, True, 0)
label.set_mnemonic_widget(self.entry1)
self.button_entry[button] = self.entry1
self.entry_name['files'] = self.entry1
self.vbox.pack_start(hbox, False, False, 0)
separator = Gtk.HSeparator()
self.vbox.pack_start(separator, False, False, 1)
alignment1 = Gtk.Alignment()
alignment1.set_halign(Gtk.Align.CENTER)
self.vbox.pack_start(alignment1, False, False, 1)
self.pbar = Gtk.ProgressBar()
alignment1.add(self.pbar)
self.pbar.set_text("Not Run")
self.pbar.set_show_text(True)
hbox2 = Gtk.HBox(False, 2)
self.vbox.pack_start(hbox2, False, False, 1)
button3 = Gtk.Button("Analyze")
button3.connect('clicked', self.tar_File_analyze)
hbox2.pack_end(button3, False, False, 1)
button4 = Gtk.Button("close")
button4.connect('clicked', self.__quit)
hbox2.pack_end(button4, False, False, 1)
self.show_all()
def browse_files(self, button):
dialog = Gtk.FileChooserDialog("Please choose a file", self,
Gtk.FileChooserAction.OPEN,
(Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL,
Gtk.STOCK_OPEN, Gtk.ResponseType.OK))
filter = Gtk.FileFilter()
filter.set_name("Tar files")
filter.add_pattern("*.tar")
filter.add_pattern("*.rar")
filter.add_pattern("*.tar.gz")
dialog.add_filter(filter)
filter = Gtk.FileFilter()
filter.set_name("All files")
filter.add_pattern("*")
dialog.add_filter(filter)
filter = Gtk.FileFilter()
filter.set_name("Images")
filter.add_mime_type("image/png")
filter.add_mime_type("image/jpeg")
filter.add_mime_type("image/gif")
filter.add_pattern("*.png")
filter.add_pattern("*.jpg")
filter.add_pattern("*.gif")
filter.add_pattern("*.tif")
filter.add_pattern("*.xpm")
dialog.add_filter(filter)
response = dialog.run()
if response == Gtk.ResponseType.OK:
print dialog.get_filename(), 'selected'
self.button_entry[button].set_text(dialog.get_filename())
#entry.set_text(dialog.get_filename())
elif response == Gtk.ResponseType.CANCEL:
print 'Closed, no files selected'
dialog.destroy()
def tar_File_analyze(self, button):
#PROGRESS_THREAD(self.pbar).start()
self.pbar.set_text("Running")
self.pbar.set_show_text(True)
####self.timer = GObject.timeout_add (100, self.progress_timeout, self)
self.timer = GLib.timeout_add(100, self.progress_timeout)
t = ANALYSING_THREAD(self.pbar, self.timer)
#t = ANALYSING_PROCESS()
t.start()
def progress_timeout(self):
self.pbar.pulse()
return True
def warning(self, warnings):
dialog = Gtk.MessageDialog(self, 0, Gtk.MessageType.WARNING,
Gtk.ButtonsType.OK, "Warning!")
dialog.format_secondary_text(warnings)
response = dialog.run()
if response == Gtk.ResponseType.OK:
dialog.destroy()
def __quit(self, button):
self.destroy()
def run(self):
Gtk.main()
def main():
File_Chooser().run()
if __name__ == '__main__':
main()
Could anyone take a look at it ?
In general, GUI toolkits do not support multiple threads making toolkit calls. Usually only the initial thread is allowed to do that. Making a GUI toolkit really thread-safe is a hard problem and this is a way to avoid it.
A possible solution is to have the main thread use an idle function or timer callback to check the progress in the worker thread.
Another solution is to to the work in an external process instead of a thread. This has a number of advantages;
It makes communication explicit and tracable. GUI toolkits can generally watch e.g. a file descriptor or a socket, processing the info from it in a timeout or idle callback.
Keeping the GUI single-threaded makes locking mostly unnecessary.
And on CPython it can actually make use of multi-core machines by side-stepping the GIL.
I've been trying to sort this problem for hours now, scouring the web and reading the documentation hasn't helped. I'm trying to perform a long running task in a separate thread and display the progress in a progress bar in the UI. The new thread is started but is not given any time until the main loop quits, I have called Gdk.threads_init() before Gtk.main and I have surrounded UI calls with Gdk.threads_enter() and Gdk.threads_leave().
The following code reproduces the problem, clicking on the 'start' button has no effect, but once the window is closed and the main loop quits I see the second thread doing work (i.e I see the print statements half a second apart)
class App(Gtk.Window):
def __init__(self):
super(App, self).__init__()
self.connect("destroy", self.on_destroy)
self.layout = Gtk.VBox()
self.progress = Gtk.ProgressBar()
self.layout.pack_start(self.progress, False, False, 0)
self.set_size_request(100,100)
self.go_button = Gtk.Button("Start")
self.go_button.connect("clicked", self.do_work_subclass)
self.layout.pack_start(self.go_button, False, False, 0)
self.add(self.layout)
self.show_all()
def on_destroy(widget, event):
Gtk.main_quit()
def do_work(self, widget):
def worker_thread():
so_far = 0
while so_far < 10:
time.sleep(0.5)
print("work so far: %s" % so_far)
Gdk.threads_enter()
try:
if so_far > 0:
self.progress.set_fraction(so_far / 10.0)
finally:
Gdk.threads_leave()
so_far += 1
threading.Thread(target=worker_thread).start()
if __name__ == "__main__":
app = App()
Gdk.threads_init()
Gdk.threads_enter()
Gtk.main()
Gdk.threads_leave()
Could this be related to the fact that I am using Gtk3?
And having spent hours trying to find an answer before posting a question I find the solution minutes after posting. Turns out (from here) that for GTK3 you need to call GLib.threads_init(), the code works with an initial from gi.repository import GLib and a GLib.threads_init() before the call to Gtk.main(), so a working version of the code looks like this:
from gi.repository import Gtk,Gdk, GLib
import threading
import time
class App(Gtk.Window):
def __init__(self):
super(App, self).__init__()
self.connect("destroy", self.on_destroy)
self.layout = Gtk.VBox()
self.progress = Gtk.ProgressBar()
self.layout.pack_start(self.progress, False, False, 0)
self.set_size_request(100,100)
self.go_button = Gtk.Button("Start")
self.go_button.connect("clicked", self.do_work)
self.layout.pack_start(self.go_button, False, False, 0)
self.add(self.layout)
self.show_all()
def on_destroy(widget, event):
Gtk.main_quit()
def do_work(self, widget):
def worker_thread():
so_far = 0
while so_far < 10:
time.sleep(0.5)
print("work so far: %s" % so_far)
Gdk.threads_enter()
try:
if so_far > 0:
self.progress.set_fraction(so_far / 10.0)
finally:
Gdk.threads_leave()
so_far += 1
threading.Thread(target=worker_thread).start()
if __name__ == "__main__":
app = App()
GLib.threads_init()
Gdk.threads_init()
Gdk.threads_enter()
Gtk.main()
Gdk.threads_leave()
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)