PyGtk Segmentation with signal "activate link" in a markup-text label - python-3.x

The MWE below simply creates a label with a hyper-text in it. Most of the time (but not every time) it causes a Segmentation fault when I click on that link.
I assume that I missunderstand something about PyGtk and I use it the wrong way?
That is the error output:
Window._on_activate_link()
Fatal Python error: Segmentation fault
Current thread 0x00007fa2718a7740 (most recent call first):
File "/usr/lib/python3/dist-packages/gi/overrides/Gtk.py", line 1641 in main
File "./window.py", line 45 in <module>
Speicherzugriffsfehler
Here are informations about my system and versions
Linux-4.19.0-14-amd64-x86_64-with-debian-10.8
Python 3.7.3 CPython
Gtk 3.0
GIO 3.30.4
Cairo 1.16.2
This is the MWE
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import faulthandler; faulthandler.enable()
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk
class FeedybusWindow(Gtk.Window):
def __init__(self):
Gtk.Window.__init__(self)
hbox = Gtk.Box(spacing=5)
self.add(hbox)
self._feed_label = Gtk.Label(label='')
self._feed_label.set_use_markup(True)
self._feed_label.set_markup('Click me')
self._feed_label.connect('activate-link', self._on_activate_link)
hbox.pack_start(self._feed_label, True, True, 20)
# EVENT: destroy
self.connect('delete-event', self._on_delete_event)
self.connect('destroy', self._on_destroy)
def _on_activate_link(self, label, uri):
print('Window._on_activate_link()')
if uri == 'renamelabel':
self._feed_label.set_markup('Click me AGAIN')
return True
return False
def _on_delete_event(self, window, event=None):
self.destroy()
def _on_destroy(self, caller):
Gtk.main_quit()
if __name__ == '__main__':
window = FeedybusWindow()
window.show_all()
Gtk.main()
EDIT: Of course I can use other GUI elements instead of a Gtk.Label. But this would be a workaround not a solution. The focus of my question is if I am using the Gtk package the wrong way or that there maybe is a bug in Gtk that I should report.

This must be a bug (maybe: https://gitlab.gnome.org/GNOME/gtk/-/issues/1498). I can't see any problems with your code, and I can reproduce the crash on my Ubuntu 20.04 system as well.
It seems like setting the label in the activate-link handler is the problem.
FWIW: A workaround is to set it outside the handler like this:
def _on_activate_link(self, label, uri):
print('Window._on_activate_link()')
if uri == 'renamelabel':
Gdk.threads_add_idle(GLib.PRIORITY_DEFAULT_IDLE, self._rename)
return False
def _rename(self):
self._feed_label.set_markup('Click me AGAIN')

Related

Error Cannot create instance of abstract (non-instantiable) type `GtkBox' python3

I have error when run this code:
from gi.repository import Gtk
class MainWindow(Gtk.Window):
def __init__(self):
Gtk.Window.__init__(self, title="")
# box
self.box = Gtk.Box(spacing=10)
self.add(self.box)
# bacon button
self.bacon_button = Gtk.Button(label="Bacon")
self.bacon_button.connect("clicked", self.bacon_clicked)
self.box.pack_start(self.bacon_button, True, True,0)
# tuna button
self.tuna_button = Gtk.Button(label="Tuna")
self.tuna_button.connect("clicked", self.tuna_clicked)
self.box.pack_start(self.tuna_button, True, True,0)
def bacon_clicked(self, widget):
print("You clicked Bacon")
def tuna_clicked(self, widget):
print("You clicked Tuna")
window = MainWindow()
window.connect("delete-event", Gtk.main_quit)
window.show_all()
Gtk.main()
And error at output:
File "/Users/*********/Documents/Program/Tutorial/venv/lib/python3.11/site-packages/gi/overrides/__init__.py", line 319, in new_init
return super_init_func(self, **new_kwargs)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
TypeError: cannot create instance of abstract (non-instantiable) type `GtkBox'
Process finished with exit code 1
I currently using python3 and already install PyGObject package in PyCharm
Your code is loading GTK2, instead of GTK3; in GTK 2.x, GtkBox is indeed an abstract type.
If you want to use GTK 3.x, you will need to add:
import gi
gi.require_version('Gtk', '3.0')
at the top, before importing the Gtk namespace.
Don't use Box directly. If you want a Box that grows horizontally with pack_start, use Gtk.HBox (documented here).
If you want it to grow vertically, use Gtk.VBox (documented here).
That being said, the more modern (GTK4) way of doing things is to use a Gtk.Grid for everything.

Monitor files for changes in a python3 Gtk application

I'm trying to monitor a directory, in order to detect when files are added to it and take action, in a Gtk application.
I've written the following Gio / Gtk snippet to experiment that, but no event get detected, if I create a file with something like echo tata > tutu or if I move a file, like mv tutu plop:
#!/usr/bin/env python3
import gi
gi.require_version("Gtk", "3.0")
from gi.repository import Gio, Gtk
def directory_changed(monitor, f1, f2, evt):
print("Changed:", f1, f2, evt)
def add_monitor(directory):
gdir = Gio.File.new_for_path(directory)
monitor = gdir.monitor_directory(Gio.FileMonitorFlags.NONE, None)
monitor.connect("changed", directory_changed)
win = Gtk.Window()
win.connect("destroy", Gtk.main_quit)
add_monitor('.')
win.show_all()
Gtk.main()
If it matters, I'm using python3.7 on debian 11 (bullseye) and the python3-gi package version is 3.30.4-1.
Does anyone have an idea of what I'm doing wrong?
Just from reading the code, I would suggest your first code fails because the add_monitor() de-allocates all its variables when the function exits, unlike the second which keeps them in the object. Although you might want to use self.gdir for the same reason. But perhaps it is not necessary.
I solved my problem with the following snippet which is basically the same, but with a custom class, subclassing Gtk.Window:
#!/usr/bin/env python3
import gi
gi.require_version("Gtk", "3.0")
from gi.repository import Gio, Gtk
class DocCliWindow(Gtk.Window):
def __init__(self):
Gtk.Window.__init__(self, title = "Document to clipboard")
def on_directory_changed(self, monitor, f1, f2, evt):
print("Changed", f1, f2, evt)
def add_monitor(self, directory):
gdir = Gio.File.new_for_path(directory)
self.monitor = gdir.monitor_directory(Gio.FileMonitorFlags.NONE, None)
self.monitor.connect("changed", self.on_directory_changed)
win = DocCliWindow()
win.connect("destroy", Gtk.main_quit)
win.add_monitor('.')
win.show_all()
Gtk.main()
But the problem is, I have absolutely no idea of why it works and the previous one doesn't :)

Tkinter AssertionError: Python3 Web App using selenium won't input search term in driver.title

I have been learning python for the past 10 months; I am also new to StackOverflow so hope you will forgive any errors in communicating here. My simple web application uses Selenium to automatically go to a specific website and enters a search term in the driver.title input box. Now all works but I get an AssertionError. I looked on StackOverflow and other websites but couldn't figure out how to solve this one. My goal was to teach myself how to use Selenium to automate. My app was built using Jupyter Notebook v6.0.1 which uses Python3.7.
I would be grateful for your help/tips. Many Thanks, Sean
Here is the error:
Exception in Tkinter callback
Traceback (most recent call last):
File "/Users/seankerr/opt/anaconda3/lib/python3.7/tkinter/__init__.py", line 1705, in __call__
return self.func(*args)
File "<ipython-input-9-c3e0a628cdec>", line 38, in hackathons
assert "hackathons" in driver.title
AssertionError
Here is the python code:
import selenium
import tkinter as tk
from tkinter import *
from tkinter import messagebox #SEAN NOTE: you don't need to import message box from
#tkinter separately because you have already imported '*' all tkinter tools/widgets
import os
import subprocess
import platform
from selenium import webdriver
from selenium.webdriver.common.keys import Keys
def ask_quit():
if messagebox.askokcancel("Quit","Are you sure you want to quit the application and close the window?"):
root.destroy()
root = tk.Tk()
root.title("Hackathon Fetcher")
root.geometry('200x200+200+200')
root.protocol("WM_DELETE_WINDOW", ask_quit)
def raise_app(root: Tk):
root.attributes("-topmost", True)
root.title("Test Title")
if platform.system() == 'Darwin':
tmpl = 'tell application "System Events" to set frontmost of every process whose unix id is {} to true'
script = tmpl.format(os.getpid())
output = subprocess.check_call(['/usr/bin/osascript', '-e', script])
root.after(0, lambda: root.attributes("-topmost", False))
def hackathons():
driver = webdriver.Chrome("./chromedriver")
driver.get("https://challengerocket.com/hackathons-and-challenges.html")
assert "hackathons" in driver.title
elem = driver.find_element_by_name("searchitems")
elem.send_keys("python")
elem.send_keys(Keys.RETURN)
Button(root, text= 'Fetch Python Hackathons', command = hackathons).pack(padx = 20)
#Button(root, text= "Quit", command = ask_quit).pack(expand=1, fill=tk.Y)
root.mainloop()

Pyqt5--Why QApplication.processEvents() failed to update GUI

I am learning to use PyQT5 and QtDesign. The workflow is :Click button->Change a image. But GUI failed to update until QMessege appeared. Here is a code. Firstly, I draw a button named pass_btn, a label named fds_img, by QtDesign.
Then, code as below:
from PyQt5 import QtWidgets, uic, QtGui
import os, sys
from PyQt5.QtWidgets import *
app = QtWidgets.QApplication(sys.argv)
dlg = uic.loadUi('grading_sys.ui')
stepper = 0
img_dir_list = [] #this is a image dir list
dlg.fds_img.setPixmap(QtGui.QPixmap(img_dir_list[stepper]))
def pass_btn():
global stepper
if stepper == len(img_dir_list) - 1:
QtWidgets.QMessageBox.information(None, 'Warning', 'Warning')
return
stepper += 1
dlg.fds_img.setPixmap(QtGui.QPixmap(img_dir_list[stepper]))
print('Try to refresh')
QApplication.processEvents()
dlg.pass_btn.clicked.connect(pass_btn)
dlg.show()
sys.exit(app.exec_())
So why I could not refresh the label? Or how could I refresh image when I click the button?
Finally, I solved this by using QTimer. I know it is a waste of CPU, may not efficient enough, but it finally works. But I still confusing about why it did't work before. I solved this as codes below:
First, put all the update codes in a def, then connected it with QTimer, and ran it every second:
def refresh_ui():
print('Try to refresh UI')
print('Prepare to change image' + str(img_dir_list[stepper]))
dlg.fds_img.setPixmap(QtGui.QPixmap(img_dir_list[stepper]))
timer = QTimer()
timer.timeout.connect(refresh_ui)
timer.start(1000)
My environment: MacOS 10.14.2, PyCharm 2018.3.3 edu edition, Python 3.6, PyQt5, QtDesign (through Anaconda). Dose this happened because of MacOS?

Cairo introspection errors in Eclipse with PyDev `TypeError: Couldn't find conversion for foreign struct 'cairo.Context'`

I am working on adding a printer interface to some home-brewed Python3 code with a Gtk3 UI, using (mostly) Eclipse Indigo with the PyDev plugin.
While developing the PrintOperation callbacks I found a problem where apparently the gi-introspection fails to find the right underlying library struct for the Cairo Context. The error reported in the console is:
Traceback (most recent call last):
File "/home/bob/Projects/MovieList/src/MovieList/MovieListIO.py", line 203, in on_printDialog_draw_page
cr = context.get_cairo_context()
File "/usr/lib/python3/dist-packages/gi/types.py", line 43, in function
return info.invoke(*args, **kwargs)
TypeError: Couldn't find conversion for foreign struct 'cairo.Context'
At first I thought this was something to do with Eclipse and/or PyDev, because I could run the program within Idle without any error messages. But then I found that when the program was packaged for deployment with the built-in command-line Python tools, the installed version also gave the error. So, I wrote a couple of test scripts abstracting the printer functionality to try to isolate what was going on. In both cases, the key line is in the on_printOperation_draw_page() callback (marked with comments).
Here is the first test script (Script 1, printTestPdf.py), which loads a pdf file using Poppler, and prints it using the system print dialog:
#!/usr/bin/env python3
import os
from gi.repository import Gtk, Poppler
testFile = 'file://' + os.path.join(os.getcwd(), 'printTestPdf.pdf')
pdfDocument = Poppler.Document.new_from_file(testFile, None)
class Example(Gtk.Window):
def __init__(self):
super(Example, self).__init__()
self.init_ui()
def init_ui(self):
self.set_title("Print Pdf Test")
self.resize(230, 150)
self.set_position(Gtk.WindowPosition.CENTER)
self.connect("delete-event", Gtk.main_quit)
printButton = Gtk.Button('Press Me')
self.add(printButton)
printButton.connect('clicked', self.on_printButton_clicked)
self.show_all()
def on_printButton_clicked(self, widget):
"""
Handler for the button click.
"""
printOperation = Gtk.PrintOperation()
printOperation.connect('draw-page', self.on_printOperation_draw_page)
printOperation.set_job_name('Print Pdf Test')
printOperation.set_n_pages(pdfDocument.get_n_pages())
printOperation.run(Gtk.PrintOperationAction.PRINT_DIALOG,
parent=self)
def on_printOperation_draw_page(self, printOperation, context, pageNo):
"""
Handler for the draw-page signal from the printOperation.
"""
cr = context.get_cairo_context() # <-- THIS IS THE LINE
page = pdfDocument.get_page(pageNo)
page.render_for_printing(cr)
def main():
app = Example()
Gtk.main()
if __name__ == "__main__":
main()
This is the second script (Script 2, printTestHtml.py), which is almost identical, except it loads an HTML file for printing using weasyprint:
#!/usr/bin/env python3
import os
from gi.repository import Gtk
from weasyprint import HTML
testFile = os.path.join(os.getcwd(), 'printTestHtml.html')
pdfDocument = HTML(filename=testFile).render()
class Example(Gtk.Window):
def __init__(self):
super(Example, self).__init__()
self.init_ui()
def init_ui(self):
self.set_title("Print Html Test")
self.resize(230, 150)
self.set_position(Gtk.WindowPosition.CENTER)
self.connect("delete-event", Gtk.main_quit)
printButton = Gtk.Button('Press Me')
self.add(printButton)
printButton.connect('clicked', self.on_printButton_clicked)
self.show_all()
def on_printButton_clicked(self, widget):
"""
Handler for the button click.
"""
printOperation = Gtk.PrintOperation()
printOperation.connect('begin-print', self.on_printOperation_begin_print)
printOperation.connect('draw-page', self.on_printOperation_draw_page)
printOperation.set_job_name('Print HTML Test')
printOperation.set_n_pages(len(pdfDocument.pages))
printOperation.run(Gtk.PrintOperationAction.PRINT_DIALOG,
parent=self)
def on_printOperation_draw_page(self, printOperation, context, pageNo):
"""
Handler for the draw-page signal from the printOperation.
"""
cr = context.get_cairo_context() # <-- THIS IS THE LINE
page = pdfDocument.pages[pageNo]
page.paint(cr) # <-- there is a separate issue here
def main():
app = Example()
Gtk.main()
if __name__ == "__main__":
main()
Both scripts generate an internal pdf document, which is used to render each page on request via the PrintOperation draw_page callback.
Now, whether and how the scripts succeed or fail depends on the context in which they are run. Script 1 always works, except if it is run after a failure of Script 2 in Idle. Script 2 always generates the error message as reported above when run in Eclipse. In Idle, Script 2's behaviour is complex. Sometimes it fails due to a second problem (marked), and does not exhibit the first failure. However, for reasons I have yet to establish, every so often it generates the original error, and when it does, it keeps doing it and Script 1 show the error too, until Idle is re-booted. Running directly from the command line matches the behaviour in Eclipse. I have tried to summarise this behaviour below:
* Eclipse
- Script 1: Always OK
- Script 2: Always Fails
* Command line
- Script 1: Always OK
- Script 2: Always Fails
* Idle
- Script 1: OK, except after failure of Script 2
- Script 2: Intermittent Fail. Knock-on to future runs (up to next error)
This pattern of failure may help determine what the root problem is, but it is beyond me to understand it.
Ignoring the bizarre behaviour in Idle, it is possible the difference between Script 1 and Script 2 holds a clue to my original problem. Why does Script 1 run successfully, while Script 2 generates the introspection error?
If you can offer any suggestions as to what is going wrong I would be most grateful. If you can come up with a solution I will be delighted!
In view of the lack of response I have come up with the following workaround, which uses WebKit instead of weasyprint to do the parsing, rendering and administration of the printing from html:
#!/usr/bin/env python3
import os
from gi.repository import Gtk, WebKit
testFile = 'file://' + os.path.join(os.getcwd(), 'printTestHtml.html')
class Example(Gtk.Window):
def __init__(self):
super(Example, self).__init__()
self.init_ui()
def init_ui(self):
self.set_title("Print Html WebKit Test")
self.resize(230, 150)
self.set_position(Gtk.WindowPosition.CENTER)
self.connect("delete-event", Gtk.main_quit)
printButton = Gtk.Button('Press Me')
self.add(printButton)
printButton.connect('clicked', self.on_printButton_clicked)
self.show_all()
def on_printButton_clicked(self, widget):
webView = WebKit.WebView()
webView.load_uri(testFile)
webFrame = webView.get_main_frame()
webFrame.print_full(Gtk.PrintOperation(),
Gtk.PrintOperationAction.PRINT_DIALOG)
def main():
app = Example()
Gtk.main()
if __name__ == "__main__":
main()
What I think is going on is that somehow weasyprint interferes with the introspection process. I have raised this as a bug on the weasyprint home page on github.
Just in case it helps I was looking for a solution to the cairo error.
Had this cairo error happen on my RPi3 with Pandas and Matplotlib. The plot window was showing up blank.
I had to run sudo apt-get install python-gobject-cairo
Based on this: https://github.com/rbgirshick/py-faster-rcnn/issues/221

Resources