我正在使用(主要)Eclipse Indigo和PyDev插件,为一些自制的带有Gtk3 UI的Python3代码添加打印机接口。
在开发PrintOperation回调时,我发现了一个问题,显然gi内省无法为Cairo上下文找到正确的底层库结构。控制台中报告的错误为:
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'
起初,我认为这与Eclipse和/或PyDev有关,因为我可以在Idle中运行程序,而不会出现任何错误消息。但后来我发现,当使用内置的命令行Python工具打包部署程序时,安装的版本也出现了错误。因此,我编写了几个抽象打印机功能的测试脚本,试图隔离正在发生的事情。在这两种情况下,关键行都在on_printOperation_draw_page()
回调中(用注释标记)。
这是第一个测试脚本(脚本1,printTestPdf.py),它使用Poppler加载pdf文件,并使用系统打印对话框打印:
#!/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()
这是第二个脚本(脚本2,printTestHtml.py),它几乎完全相同,只是它加载了一个HTML文件以使用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()
两个脚本都生成一个内部pdf文档,用于通过PrintOperation
draw_page
回调根据请求呈现每个页面。
现在,脚本是成功还是失败以及如何成功取决于运行脚本的上下文。脚本1始终有效,除非它是在脚本2在空闲状态下失败后运行的。当在Eclipse中运行时,脚本2总是生成如上所述的错误消息。在"空闲"中,脚本2的行为很复杂。有时它由于第二个问题(标记)而失败,并且没有表现出第一个失败。然而,由于我尚未确定的原因,它每隔一段时间就会生成原始错误,当它生成原始错误时,它会继续执行,脚本1也会显示错误,直到Idle重新启动。直接从命令行运行与Eclipse中的行为相匹配。我试图将这种行为总结如下:
* 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)
这种故障模式可能有助于确定根本问题是什么,但我无法理解
忽略Idle中的怪异行为,脚本1和脚本2之间的差异可能为我最初的问题提供了线索。为什么脚本1成功运行,而脚本2生成内省错误?
如果你能对出了什么问题提出任何建议,我将不胜感激。如果你能想出一个解决方案,我会很高兴的!
鉴于缺乏响应,我想出了以下解决方法,使用WebKit
而不是weasyprint
来从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()
我认为正在发生的事情是,某种程度上,黄页干扰了内省过程。我在github的黄页主页上提出了这个bug。
为了以防万一,我一直在寻找cairo错误的解决方案。
如果这个cairo错误发生在我的带有Pandas和Matplotlib的RPi3上。绘图窗口显示为空白。我必须运行sudo apt-get install python-gobject-cairo
基于此:https://github.com/rbgirshick/py-faster-rcnn/issues/221