0

我正在使用(主要是)Eclipse Indigo 和 PyDev 插件,为一些带有 Gtk3 UI 的自制 Python3 代码添加打印机接口。

在开发 PrintOperation 回调时,我发现了一个问题,显然 gi-introspection 无法为 Cairo Context 找到正确的底层库结构。控制台报错是:

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()

PrintOperation draw_page这两个脚本都会生成一个内部 pdf 文档,该文档用于通过回调根据请求呈现每个页面。

现在,脚本是否以及如何成功或失败取决于它们运行的​​上下文。脚本 1 始终有效,除非它在空闲时脚本 2 失败后运行。在 Eclipse 中运行时,脚本 2 总是生成如上报告的错误消息。在空闲时,脚本 2 的行为很复杂。有时它会由于第二个问题(标记)而失败,并且没有表现出第一个失败。但是,由于我尚未确定的原因,它每隔一段时间就会生成原始错误,并且当它发生时,它会继续这样做,并且脚本 1 也会显示错误,直到重新启动空闲。直接从命令行运行与 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 产生自省错误?

如果您能就出了什么问题提供任何建议,我将不胜感激。如果您能提出解决方案,我会很高兴!

4

2 回答 2

0

鉴于缺乏响应,我提出了以下解决方法,它使用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()

认为正在发生的是 weasyprint 以某种方式干扰了内省过程。我在github上的 weasyprint 主页上将此作为错误提出。

于 2013-08-25T12:46:45.627 回答
0

以防万一它有助于我正在寻找开罗错误的解决方案。

如果使用 Pandas 和 Matplotlib 在我的 RPi3 上发生此 cairo 错误。绘图窗口显示为空白。我不得不运行sudo apt-get install python-gobject-cairo 基于此:https ://github.com/rbgirshick/py-faster-rcnn/issues/221

于 2017-03-31T22:15:06.867 回答