在gtk3/python中创建打印任务

2 投票
1 回答
1138 浏览
提问于 2025-04-18 10:06

我有一些信息(一个活动参与者的名单),我想简单地打印出来。没必要搞得太花哨,只要一个有几列的表格,如果能在每行文字之间加条线,那就更好了,这样看起来更清晰。为了让所有内容都能放下,我需要横向打印(可以通过GtkPageSetup来实现)。

我在用Python,操作系统是Linux,所以我得用GtkPrintUnixDialog这个接口。我在网上找了很久,但就是找不到任何关于怎么实现这个的例子。

为了简化问题:这只是我自己用,所以纸张大小是固定的(A4)。

我面临的主要问题有两个:1)创建一个格式正确的文本,适合打印,2)把这个文本发送到打印机。

有没有什么建议可以让我开始?或者更好的是,能不能给我一些完整的例子?

1 个回答

5

我在找我以前的打印示例,先说说基本的:

你可以把内容写到一个PDF表面上,然后打印这个PDF,或者把绘图代码放在一个叫on_print的函数里。需要注意的是,它打印的不是你看到的内容,而是你在打印表面上绘制的内容。你可以像使用普通的cairo上下文那样绘制,但有一些方法是不可用的(不适合打印上下文),而其他一些方法,比如新建页面,是可以用的。如果我找到示例代码,我会再给出一个更容易理解的答案。

编辑:找到了一个示例:

def on_button_clicked(self, widget):
    ps = Gtk.PaperSize.new_custom("cc", "cc", 210, 297, Gtk.Unit.MM)
    st = Gtk.PrintSettings()
    s = Gtk.PageSetup()
    s.set_paper_size(ps)
    s.set_bottom_margin(4.3, Gtk.Unit.MM)
    s.set_left_margin(4.3, Gtk.Unit.MM)
    s.set_right_margin(4.3, Gtk.Unit.MM)
    s.set_top_margin(4.3, Gtk.Unit.MM)
    s.set_orientation(Gtk.PageOrientation.LANDSCAPE)
    # ret =  Gtk.print_run_page_setup_dialog(self, s, st)
    pd = Gtk.PrintOperation()
    pd.set_n_pages(1)
    pd.set_default_page_setup(s)
    pd.connect("begin_print", self.bg)
    pd.connect("draw_page", self.draw_page)
    # print(ret, s, st)
    pd.set_export_filename("test.pdf")
    result = pd.run(Gtk.PrintOperationAction.EXPORT, None) #play with action, but for test export first; if it's ok, then action.PRINT
    print (result)  # handle errors etc.
    # Gtk.PaperSize.free(ps) - not needed in py

上面的代码可以在按钮按下时触发,或者其他事件。

def draw_page (self, operation, context, page_number):
    end = self.layout.get_line_count()
    cr = context.get_cairo_context()
    cr.set_source_rgb(0, 0, 0)
    i = 0
    start = 0
    start_pos = 0
    iter = self.layout.get_iter()
    while 1:
        if i >= start:
            line = iter.get_line()
            print(line)
            _, logical_rect = iter.get_line_extents()
            # x_bearing, y_bearing, lwidth, lheight = logical_rect
            baseline = iter.get_baseline()
            if i == start:
                start_pos = 12000 / 1024.0  # 1024.0 is float(pango.SCALE)
            cr.move_to(0 / 1024.0, baseline / 1024.0 - start_pos)
            PangoCairo.show_layout_line(cr, line)
        i += 1
        if not (i < end and iter.next_line()):
            break

这只是一个基本示例。要注意,布局是一个pango布局:

self.layout = cx.create_pango_layout()
    self.layout.set_width(int(w / 4 * Pango.SCALE))
    self.layout.set_text(text, len(text))
    num_lines = self.layout.get_line_count()
    page_height = 0
    self.layout.set_font_description(Pango.FontDescription("Georgia Bold 12"))
    k = 0
    for line in range(num_lines):
        if k == 4:
            self.layout.set_font_description(Pango.FontDescription("Georgia 10"))
        layout_line = self.layout.get_line(line)
        ink_rect, logical_rect = layout_line.get_extents()
        lheight = 1200
        line_height = lheight / 1024.0  # 1024.0 is float(pango.SCALE)
        page_height += line_height
        k += 1
        print ("page_height ", page_height)

复制/粘贴的功能示例:

    #!/usr/bin/python
from gi.repository import Gtk, Pango, PangoCairo
import cairo

text = '''
Text.
I have some information (a list of participants to an event) which I 
want to print out easily.
No need for fancy layout,
just a table with several columns,
and if possible a drawn line in between the lines of
 text for better readability.
'''


class MyWindow(Gtk.Window):

    def __init__(self):
        Gtk.Window.__init__(self, title="Hello World Printing")
        self.button = Gtk.Button(label="Print A Rectangle")
        self.button.connect("clicked", self.on_button_clicked)
        self.add(self.button)

    def on_button_clicked(self, widget):
        ps = Gtk.PaperSize.new_custom("cc", "cc", 210, 297, Gtk.Unit.MM)
        st = Gtk.PrintSettings()
        s = Gtk.PageSetup()
        s.set_paper_size(ps)
        s.set_bottom_margin(4.3, Gtk.Unit.MM)
        s.set_left_margin(4.3, Gtk.Unit.MM)
        s.set_right_margin(4.3, Gtk.Unit.MM)
        s.set_top_margin(4.3, Gtk.Unit.MM)
        s.set_orientation(Gtk.PageOrientation.LANDSCAPE)
        # ret =  Gtk.print_run_page_setup_dialog(self, s, st)
        pd = Gtk.PrintOperation()
        pd.set_n_pages(1)
        pd.set_default_page_setup(s)
        pd.connect("begin_print", self.bg)
        pd.connect("draw_page", self.draw_page)
        # print(ret, s, st)
        pd.set_export_filename("test.pdf")
        result = pd.run(Gtk.PrintOperationAction.EXPORT, None)
        print (result)  # handle errors etc.
        # Gtk.PaperSize.free(ps)

    def bg(self, op, cx):
        w = cx.get_width()
        h = cx.get_height()
        self.layout = cx.create_pango_layout()
        self.layout.set_width(int(w / 4 * Pango.SCALE))
        self.layout.set_text(text, len(text))
        num_lines = self.layout.get_line_count()
        page_height = 0
        self.layout.set_font_description(Pango.FontDescription("Georgia Bold 12"))
        k = 0
        for line in range(num_lines):
            if k == 4:
                self.layout.set_font_description(Pango.FontDescription("Georgia 10"))
            layout_line = self.layout.get_line(line)
            ink_rect, logical_rect = layout_line.get_extents()
            # print(logical_rect, ink_rect)
            # x_bearing, y_bearing, lwidth, lheight = logical_rect
            lheight = 1200
            line_height = lheight / 1024.0  # 1024.0 is float(pango.SCALE)
            page_height += line_height
            # page_height is the current location on a page.
            # It adds the the line height on each pass through the loop
            # Once it is greater then the height supplied by context.get_height
            # it marks the line and sets the current page height back to 0
            k += 1
            print ("page_height ", page_height)


    def box(self, w, h, x, y, cx):
        w, h = int(w), int(h)
        cx.set_font_size(100)
        cx.set_source_rgb(0, 0, 0)
        cx.rectangle(x, y, w, h)
        cx.stroke()
        yy = 120
        cx.select_font_face("Times", 0, 1)
        ex = cx.text_extents("TEGOLA ROMÂNIA SRL")[2]
        cx.move_to(w / 2 - ex / 2 + x, 105 + y)
        cx.show_text("TEGOLA ROMÂNIA SRL")
        ex = cx.text_extents("Str. Plevnei, nr. 5, Buzău")[2]
        cx.move_to(w / 2 - ex / 2 + x, 210 + y)
        cx.show_text("Str. Plevnei, nr. 5, Buzău")
        ex = cx.text_extents("Tel.: 0238/710.280")[2]
        cx.move_to(w / 2 - ex / 2 + x, 320 + y)
        cx.show_text("Tel.: 0238/710.280")
        ex = cx.text_extents("Fax : 0238/710021")[2]
        cx.move_to(w / 2 - ex / 2 + x, 415 + y)
        cx.show_text("Fax : 0238/710021")
        cx.set_font_size(90)
        cx.move_to(x + 120, 520 + y)
        ex = cx.text_extents("Compoziție:")[2]
        cx.show_text("Compoziție:")
        cx.select_font_face("Times", 0, 0)
        cx.move_to(x + 125 + ex, 520 + y)
        cx.show_text("Polimer bituminos, liant și")
        cx.move_to(x + 5, 620 + y)
        cx.show_text("material de umplutură de înaltă calitate.")
        cx.move_to(x + 5, 720 + y)
        cx.show_text("Nu conține gudron.")
        cx.move_to(x + 5, 800 + y)
        cx.select_font_face("Times", 0, 1)
        ex = cx.text_extents("Instrucțiuni de utilizare:")[2]
        cx.show_text("Instrucțiuni de utilizare:")
        cx.select_font_face("Times", 0, 0)
        cx.move_to(x + 10 + ex, 800 + y)
        cx.show_text("Suprafețele se")


    def draw_page1(self, operation, context, page_nr=None):
        ctx = context.get_cairo_context()
        w = context.get_width()
        h = context.get_height()
        ww, hh = int(w / 4), int(h / 2)
        self.k = 0
        for x in range(2):
            for y in range(4):
                self.box(ww, hh, y * ww, x * hh, ctx)
                self.k += 1
        print(ctx.get_font_matrix())

    def draw_page (self, operation, context, page_number):
        end = self.layout.get_line_count()
        cr = context.get_cairo_context()
        cr.set_source_rgb(0, 0, 0)
        i = 0
        start = 0
        start_pos = 0
        iter = self.layout.get_iter()
        while 1:
            if i >= start:
                line = iter.get_line()
                print(line)
                _, logical_rect = iter.get_line_extents()
                # x_bearing, y_bearing, lwidth, lheight = logical_rect
                baseline = iter.get_baseline()
                if i == start:
                    start_pos = 12000 / 1024.0  # 1024.0 is float(pango.SCALE)
                cr.move_to(0 / 1024.0, baseline / 1024.0 - start_pos)
                PangoCairo.show_layout_line(cr, line)
            i += 1
            if not (i < end and iter.next_line()):
                break



win = MyWindow()
win.connect("delete-event", Gtk.main_quit)
win.show_all()
Gtk.main()

别忘了把操作改成Gtk.PrintOperationAction.PRINT,这样才能真正进行打印。

撰写回答