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

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

Related

Python Serial "ClearCommError failed (OSError(9, 'The handle is invalid.', None, 6))

I am new to python and multiprocessing (Electronics background). I am writing an GUI application to communicate with micro controller over serial port. The interaction with user is through some buttons on the GUI, which sends some command on serial port to the MCU and displays data received from MCU in the same GUI(there can be data from MCU without any command being sent). I am using PyQt5 module and QT designer to design the UI. I converted the UI to py code using uic.compileUi. I am then calling this python file in my main code.
After reading through several documents and stack overflow posts I decided to use to QThreads for the application.
I am however facing issue with serial communication when being used inside thread (It works fine if used in a single main loop without threads).
My code is:
from PyQt5 import uic
from PyQt5.QtWidgets import QApplication,QWidget,QMainWindow #
from PyQt5.QtCore import QCoreApplication, QObject, pyqtSignal, pyqtSlot, QThread
import serial
from GUI import Ui_MainGUIWindow
import sys
serialString = ""
def ClosePort():
serialPort.close()
# print("port closed")
def OpenPort():
serialPort.open()
#print("port open")
def sendFV():
serialPort.write('fv\r'.encode())
def configSerial(port,baudRate):
global serialPort
serialPort=serial.Serial(port = port, baudrate=baudRate, bytesize=8, timeout=None, stopbits=serial.STOPBITS_ONE)
if not serialPort.isOpen():
serialPort.open()
class Worker(QObject):
finished = pyqtSignal()
dataReady = pyqtSignal(['QString'])#[int], ['Qstring']
print("workerinit")
def run(self):
print ("worker run")
while(1):
if(serialPort.in_waiting > 0):
serialString = serialPort.read()
self.dataReady.emit(str(serialString))
self.finished.emit()
class GUI(QWidget):
def __init__(self):
self.obj = Worker()
self.thread = QThread()
self.obj.dataReady.connect(self.onDataReady)
self.obj.moveToThread(self.thread)
self.obj.finished.connect(self.thread.quit)
self.thread.started.connect(self.obj.run)
self.thread.finished.connect(app.exit)
self.thread.start()
self.initUI()
def initUI(self):
#Form, Window = uic.loadUiType("TestGUI.ui")
#print(Form,Window)
#self.window = Window()
#self.form=Form()
self.window = QMainWindow()
self.form = Ui_MainGUIWindow()
self.form.setupUi(self.window)
self.form.FVButton.clicked.connect(sendFV)
print("guiinit")
self.window.show()
def onDataReady(self, serialString):
print("here")
print(serialString.decode('Ascii'))
self.form.plainTextOutput.insertPlainText((serialString.decode('Ascii')))
self.form.plainTextOutput.ensureCursorVisible()
if __name__ == '__main__':
configSerial("COM20",9600)
"""
app = QCoreApplication.instance()
if app is None:
app = QCoreApplication(sys.argv)
"""
app = QApplication(sys.argv)
form=GUI()
app.exec_()
sys.exit(ClosePort())
I get following error on line if(serialPort.in_waiting > 0):
File "C:\Users\...\Anaconda3\lib\site-packages\serial\serialwin32.py", line 259, in in_waiting
raise SerialException("ClearCommError failed ({!r})".format(ctypes.WinError()))
SerialException: ClearCommError failed (OSError(9, 'The handle is invalid.', None, 6))
I found this post talking about similar issue, but do know how exactly to implement the suggested solution in my code.
Also I get "kernel died" error multiple times when I run the code on spyder IDE. I added a check if I am creating multiple instances of QT application but that did not help.Running the code from anaconda command prompt works fine.

wx.DirDialog not closing

Using wxPython version 4.0.1 (pheonix) with python 3.6.5
I use a wxPython DirDialog to allow my user to input a start directory. It correctly selects and populates my "working directory" variable (using GetPath()) but then doesn't ever close the directory dialog prompt box.
I read through the wxPython-user google pages and the only related question I found referred to this as being "intended behavior," implying it would happen later in execution (https://groups.google.com/forum/#!searchin/wxpython-users/close%7Csort:date/wxpython-users/ysEZK5PVBN4/ieLGEWc6AQAJ).
Mine, however, doesn't close until the entire script has completed running (which takes a fair amount of time), giving me the spinning wheel of death. I have tried a combination of calls to try to force the window to close.
app = wx.App()
openFileDialog = wx.DirDialog(None, "Select", curr, wx.DD_DIR_MUST_EXIST)
openFileDialog.ShowModal()
working_directory = openFileDialog.GetPath()
openFileDialog.EndModal(wx.CANCEL) #also wx.Close(True) and wx.Destroy()
openFileDialog.Destroy()
openFileDialog=None
I have also tried creating a window, passing it as the parent of the DirDialog, and then closing the window and it gives the same behavior.
You don't mention which operating system you are on or version of wx but in the partial code that you supplied there is no MainLoop, which is what was mentioned by Robin Dunn in his answer, in your link.
Try this and see if it works the way you would expect.
import wx
from os.path import expanduser
import time
class choose(wx.Frame):
def __init__(self, parent):
wx.Frame.__init__(self, parent, -1, "Dialog")
panel = wx.Panel(self,-1)
text = wx.StaticText(panel,-1, "Place holder for chosen directory")
self.Bind(wx.EVT_CLOSE, self.OnClose)
self.Show()
curr = expanduser("~")
dlg = wx.DirDialog(None, message="Choose a directory", defaultPath = curr,
style=wx.DD_DEFAULT_STYLE|wx.DD_DIR_MUST_EXIST)
if dlg.ShowModal() == wx.ID_OK:
text.SetLabel(dlg.GetPath())
dlg.Destroy()
b = wx.BusyInfo("I'm busy counting",parent=None)
wx.Yield()
for i in range(30):
time.sleep(1)
del b
def OnClose(self, event):
self.Destroy()
if __name__ == '__main__':
my_app = wx.App()
choose(None)
my_app.MainLoop()

How to create progressbar using python and glade for copying log files from one system to another system?

I am using this bash script for copying log files from one system to another system
#!/bin/bash s="/var/log"
d="/root/Directory" BACKUPFILE=scripts.backup.`date +%F`.tar.gz
scp -r root#$1:$s $2 rsync -chavzP --stats root#ipaddr
filename=ug-$(date +%-Y%-m%-d)-$(date +%-T).tgz
tar -czvf $2/$BACKUPFILE $s
tar --create --gzip --file=$d$filename $s
rm -rf /root/aaa/log
Also I have done progressbar code like this
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk, GObject
class ProgressBarWindow(Gtk.Window):
def __init__(self):
Gtk.Window.__init__(self, title="ProgressBar Demo")
self.set_border_width(10)
vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=6)
self.add(vbox)
self.progressbar = Gtk.ProgressBar()
vbox.pack_start(self.progressbar, True, True, 0)
button = Gtk.CheckButton("Show text")
button.connect("toggled", self.on_show_text_toggled)
vbox.pack_start(button, True, True, 0)
button = Gtk.CheckButton("Activity mode")
button.connect("toggled", self.on_activity_mode_toggled)
vbox.pack_start(button, True, True, 0)
button = Gtk.CheckButton("Right to Left")
button.connect("toggled", self.on_right_to_left_toggled)
vbox.pack_start(button, True, True, 0)
self.timeout_id = GObject.timeout_add(50, self.on_timeout, None)
self.activity_mode = False
def on_show_text_toggled(self, button):
show_text = button.get_active()
if show_text:
text = "some text"
else:
text = None
self.progressbar.set_text(text)
self.progressbar.set_show_text(show_text)
def on_activity_mode_toggled(self, button):
self.activity_mode = button.get_active()
if self.activity_mode:
self.progressbar.pulse()
else:
self.progressbar.set_fraction(0.0)
def on_right_to_left_toggled(self, button):
value = button.get_active()
self.progressbar.set_inverted(value)
def on_timeout(self, user_data):
"""
Update value on the progress bar
"""
if self.activity_mode:
self.progressbar.pulse()
else:
new_value = self.progressbar.get_fraction() + 0.01
if new_value > 1:
new_value = 0
self.progressbar.set_fraction(new_value)
# As this is a timeout function, return True so that it
# continues to get called
return True
win = ProgressBarWindow() win.connect("delete-event", Gtk.main_quit)
win.show_all() Gtk.main()
But I don't know how to embed my script with this progress bar code.
There are some approaches to this problem but main concern that you must care is that GUI and time consuming tasks (long tasks) are not good friends. Most GUI frameworks use their mainloop to take care of user input handling and draw the UI. This being said, you must separate those long tasks from the main UI and there are some ways to do it, be it threads, async methods, etc, it will all resume to how your language of choice deals with these problems.
Then there is the question of how to invoke the OS functions that you want to track. Probably the best way to do it would be by implementing them in your code, programmatically, but that would be a time consuming effort. So, using shell scripts will be your choice and doing so leads to the question: how to get the output of those commands? Well, there's popen and since you will be using python then you must spawn the shell scripts with popen calls. Best way to do it seems to be subprocess. An additional improvement would be a better tailored bash script that can get some previous analysis of command results (success or failure) and conditioned/formatted output.
Hope you can see where i'm going... You'll have to parse the data that normally goes to the console, interpret it and update the UI accordingly.
I give you a simple example of a Gtk Window with a progress bar that gets pulsed for every line of a shell command output (tree /):
import time
import threading
import subprocess
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import GLib, Gtk, GObject
def app_main():
win = Gtk.Window(default_height=50, default_width=300)
win.connect("delete-event", Gtk.main_quit)
win.connect("destroy", Gtk.main_quit)
progress = Gtk.ProgressBar(show_text=True)
win.add(progress)
def update_progress(i):
progress.pulse ()
#progress.set_fraction (i/100.0) # use this for percentage
#progress.set_text(str(i))
return False
def example_target():
i = 0 # can be used to pass percentage
proc = subprocess.Popen(['tree', '/'],stdout=subprocess.PIPE)
while True:
line = proc.stdout.readline()
if line != '':
time.sleep(0.1) # output is too quick, slow down a little :)
GLib.idle_add(update_progress, i)
else:
break
win.show_all()
thread = threading.Thread(target=example_target)
thread.daemon = True
thread.start()
if __name__ == "__main__":
app_main()
Gtk.main()
On the example above, we use threads. The shell command, that you can try on the console, dumps a tree of the folder structure on you disk. Can be anything but the goal was to have a long task. Since we can't track progress, the bar will be in activity mode and we pulse it for every line but if you can track progress you can use the set_fraction method instead.
Hope this will lead you on the right direction. GL

pyside signal not fired with pyinstaller when communicating between threads

I am trying to build an .app for my pyside gui and I'm having a problem when I use signals to communicate between the main thread and another one. When I run the python code, everything works fine. The problem only occurs when I use the .app built from pyinstaller.
The project is quite big, so it's hard to include any code, so i'll try to explain what I'm doing.
My second thread is used to call some functions from a dylib which controls our devices. The main thread uses a callback to tell the dylib to stop what it's doing. The dylib calls the callback function in a loop, and stops if the value returned from the main thread is different than 0.
As I mentioned, when I run the Python code (under Windows, Ubuntu, Mac), it works perfectly. But when using the .app built with pyinstaller, it looks like the signal is not sent from the main thread. While debugging, I printed stuff and eventually saw that the dylib doesn't receive the returned value from the callback. The dylib does use the callback, and I can see the expected data in the main thread.
So why would the signal not be fired with frozen python code?
Has anybody ever encounter a similar problem?
If you could give me some debugging advices, it would be really helpful?
EDIT
I managed to write a !little code which reproduces the problem. When I run the python code, pressing "ok" followed by "Cancel" sets the message "DLL got the value" in the widget. When freezing the code with Pyinstaller, under mac, the message never gets set on the widget. On Windows 7, everything is fine.
EDIT
Actually, no need for the shared library to cause the problem. The problem occurs only with the Python code.
Here is the Python code:
import sys
from PySide import QtGui
from PySide.QtCore import QThread, Signal, QObject
def functionInDLL(callback):
return_value = 0
while (return_value == 0):
return_value = callback(2)
class MyThread(QThread):
str_signal = Signal(str)
def __init__(self):
QThread.__init__(self)
self.return_value = 0
def returnValueToDll(self, data=0):
return self.return_value
def run(self):
if functionInDLL(self.returnValueToDll) == 42:
print"DLL got the value."
self.str_signal.emit("DLL got the value.")
else:
print"DLL did not get the value."
self.str_signal.emit("DLL did not get the value.")
self.exec_()
def setValueSentToDll(self, data):
self.return_value = data
class MainThreadClass(QObject):
int_signal = Signal(int)
def __init__(self):
super(MainThreadClass, self).__init__()
self.test_thread = MyThread()
self.int_signal.connect(self.test_thread.setValueSentToDll)
def startMyThread(self):
self.test_thread.start()
def sendStopValueToDLL(self):
self.int_signal.emit(42)
class MyGUI(QtGui.QWidget):
def __init__(self):
super(MyGUI, self).__init__()
self.text_information = QtGui.QLabel(text="No started yet...")
self.dll_information = QtGui.QLabel(text="")
self.ok_button = QtGui.QPushButton("OK")
self.cancel_button = QtGui.QPushButton("Cancel")
self.close_button = QtGui.QPushButton("Close")
label_hbox = QtGui.QHBoxLayout()
hbox = QtGui.QHBoxLayout()
hbox.addWidget(self.ok_button)
hbox.addWidget(self.cancel_button)
hbox.addWidget(self.close_button)
vbox = QtGui.QVBoxLayout()
label_hbox.addWidget(self.text_information)
label_hbox.addWidget(self.dll_information)
vbox.addLayout(label_hbox)
vbox.addLayout(hbox)
self.setLayout(vbox)
self.ok_button.clicked.connect(self.onOk)
self.cancel_button.clicked.connect(self.onCancel)
self.close_button.clicked.connect(self.onClose)
self.show()
self.main_thread = MainThreadClass()
self.main_thread.test_thread.str_signal.connect(lambda data: self.setDllStatusOnWidget(data))
def onOk(self):
self.main_thread.startMyThread()
self.text_information.setText("Started")
self.dll_information.setText("")
def onCancel(self):
self.main_thread.sendStopValueToDLL()
self.text_information.setText("Canceled")
def onClose(self):
self.main_thread.test_thread.exit()
self.close()
def setDllStatusOnWidget(self, text=""):
self.dll_information.setText(text)
if __name__ == '__main__':
app = QtGui.QApplication(sys.argv)
ex = MyGUI()
sys.exit(app.exec_())
Thanks
Using Python2.7, Mac Pro Yosemite
Thanks to codewarrior, my bug was solved.
When I replaced the line:
self.main_thread.test_thread.str_signal.connect(lambda data: self.setDllStatusOnWidget(data))
by this line, removing the lambda:
self.main_thread.test_thread.str_signal.connect(self.setDllStatusOnWidget)
See the link for more details. I hope this can help other people.
pyinstaller_issue

QFileDialog: no signals emitted, wrong starting directory

I'm trying to show a QFileDialog using the following piece of code:
import os, sys
from PyQt4.QtGui import *
class MainWindow(QWidget):
def __init__(self):
QWidget.__init__(self)
self._button = QPushButton('Test button')
self._button.clicked.connect(self._onButtonClicked)
self._layout = QHBoxLayout()
self._layout.addWidget(self._button)
self.setLayout(self._layout)
def _onButtonClicked(self):
self._dialog = QFileDialog(self, 'Select directory')
self._dialog.setDirectory(os.getenv('HOME'))
self._dialog.setFileMode(QFileDialog.Directory)
self._dialog.directoryEntered.connect(self._onDirEntered)
self._dialog.exec_()
def _onDirEntered(self, directory):
print("Entered directory: %s" % (directory))
if __name__ == "__main__":
app = QApplication(sys.argv)
mw = MainWindow()
mw.show()
app.exec_()
Two problems here:
The directoryEntered signal is never emitted, at least I don't get any output from the script (except for some KDE warnings about Samba support, etc.); actually no signal from the QFileDialog class I tried to connect to gets emitted in the example. What am I doing wrong?
In this example I set the starting directory to $HOME, but the dialog will start in /home instead and have my home directory selected in the listing instead of starting directly in my home directory. Can I change this behaviour somehow?
I'm using Python 3.4.0 with PyQt 4.10.4-2.
Both problems do not occur when using a non-native dialog as suggested by #ekhumoro.

Resources