如何在pywin32中排列return内容

How to arrange return content in pywin32

我只有几行代码可以使用 pywin32 将作业发送到我的打印机,但是当我发送此命令时,新行的内容不会出现在纸上的行中 (newline) 打印后继续跟随第一行的内容。 当我将其打印到终端时,它会像我想要的那样打印,但当我将作业发送到打印机时却不会。

一直在该网站上搜索如何在发送打印内容时安排我的内容,但无济于事。

import win32con
import win32print
import win32ui


def text():
    rows = (("PETER PAUL", "MALE", "100000"), ("MARGARET ", "FEMALE", "1000"), ("MICHAEL JORDAN", "MALE", "1"),("AGNES", "FEMALE", "200"))
    return '\r\n'.join('{:20} {:8} {}'.format(*row) for row in rows)


print(text())


def printer():
    dc = win32ui.CreateDC()
    printername = win32print.GetDefaultPrinter()
    dc.CreatePrinterDC(printername)
    dc.SetMapMode(win32con.MM_TWIPS)
    scale_factor = 20
    dc.StartDoc('Win32print ')
    pen = win32ui.CreatePen(0, int(scale_factor), 0)
    dc.SelectObject(pen)
    font = win32ui.CreateFont({
    "name": "Lucida Console",
    "height": int(scale_factor * 10),
    "weight": 400,
})
    dc.SelectObject(font)
    dc.TextOut(scale_factor * 72, -1 * scale_factor * 72, text())
    dc.EndDoc()


printer()

:[GitHub]: mhammond/pywin32 - Python for Windows (pywin32) Extensions doesn't have an official doc (or at least I couldn't find any), so I'll be using the 2nd best thing available: ActiveState(只能找到古文Python2.4的参考,但是通常他们没问题)

[ActiveState.Docs]: PyCDC.TextOut wraps [MS.Docs]: TextOutW function。该函数不处理 \r\n(当然还有其他特殊的 chars),例如print 确实如此(文档对此没有任何说明),但它只是忽略了它们(它没有 line 的概念).这意味着为了实现 print - 类似的功能,用户负责单独输出每一行(当然在不同的 Y 坐标 -以避免在前一个之上输出)。

为了更好地说明行为,我创建了一个示例(基于您的代码)。

code.py:

#!/usr/bin/env python3

import sys
import time
import win32ui
import win32con
import win32print


def get_data_strings():
    rows = (("PETER PAUL", "MALE", "100000"), ("MARGARET ", "FEMALE", "1000"), ("MICHAEL JORDAN", "MALE", "1"),("AGNES", "FEMALE", "200"))
    return ["{:20} {:8} {}".format(*row) for row in rows]


def text():
    return "\r\n".join(get_data_strings())


def paint_dc(dc, printer_dc, paint_each_string=True):
    scale_factor = 20
    if printer_dc:
        x_y = 100, 0  # TopLeft of the page. In order to move away from the point, X increases to positives, while Y to negatives
        font_scale = 10
        y_direction_scale = -1  # For printers, the Y axis is "reversed"
        y_ellipsis = -100
    else:
        x_y = 100, 150  # TopLeft from wnd's client area
        font_scale = 1
        y_direction_scale = 1
        y_ellipsis = 100

    font0 = win32ui.CreateFont(
        {
            "name": "Lucida Console",
            "height": scale_factor * font_scale,
            "weight": 400,
        })
    font1 = win32ui.CreateFont(
        {
            "name": "algerian",
            "height": scale_factor * font_scale,
            "weight": 400,
        })
    fonts = [font0, font1]
    dc.SelectObject(font0)
    dc.SetTextColor(0x0000FF00) # 0BGR
    #dc.SetBkColor(0x000000FF)
    dc.SetBkMode(win32con.TRANSPARENT)
    if paint_each_string:
        for idx, txt in enumerate(get_data_strings()):
            dc.SelectObject(fonts[idx % len(fonts)])
            dc.TextOut(x_y[0], x_y[1] + idx * scale_factor * font_scale * y_direction_scale, txt)
    else:
        dc.TextOut(*x_y, text())
    pen = win32ui.CreatePen(0, 0, 0)
    dc.SelectObject(pen)
    dc.Ellipse((50, y_ellipsis, *x_y))


def paint_wnd(wnd, paint_each_string=True):
    dc = wnd.GetWindowDC()
    paint_dc(dc, False, paint_each_string=paint_each_string)
    wnd.ReleaseDC(dc)


def paint_prn(printer_name, paint_each_string=True):
    printer_name = printer_name or win32print.GetDefaultPrinter()
    dc = win32ui.CreateDC()
    dc.CreatePrinterDC(printer_name)
    dc.SetMapMode(win32con.MM_TWIPS)
    dc.StartDoc("Win32print")
    #dc.StartPage()
    paint_dc(dc, True, paint_each_string=paint_each_string)
    #dc.EndPage()
    dc.EndDoc()


def main():
    print("Python {:s} on {:s}\n".format(sys.version, sys.platform))
    print(text())
    time.sleep(0.1)
    if len(sys.argv) > 1:
        if sys.argv[1] == "window":
            paint_func = paint_wnd
            paint_func_dc_arg = win32ui.GetForegroundWindow()
        else:
            paint_func = paint_prn
            paint_func_dc_arg = sys.argv[1]
    else:
        paint_func = paint_prn
        paint_func_dc_arg = None
    paint_func(paint_func_dc_arg, paint_each_string=True)


if __name__ == "__main__":
    main()

备注:

  • 我没有连接打印机(实际上我有,但我不想每次 运行 程序都打印 smth),所以我使用当前 window (cmd) HDC 输出数据(因此,我删除了打印机特定代码)
  • 我对代码进行了一些结构化,添加了函数,使其模块化
  • 我将你的 text 功能分成两部分:
    • get_data_strings - returns 一个字符串列表,其中每个字符串都是 rows[=116] 中一行的文本表示=](让它成为一个 生成器 会更好,但我不想让事情过于复杂)
    • text - 简单地连接它们(与现有界面一致)
  • 关于图形(GDI):
    • TextOut 不关心笔,它只使用选定的字体、背景颜色(dc.SetBkColor)和文本颜色(dc.SetTextColor ), 但我把它留在那里画了一个椭圆 (只是为了好玩)
    • 整数参数(基于 scale_factor)太过分了(太大了 - 至少对我的 HDC), 所以我将它们减少到更合适的值
    • 如您所见,我正在单独输出每个字符串(并且还将其 Y 增加 scale_factor -这也是字体高度)。我还保留了旧方法(打印整个字符串),你只需要将 print_each_string 参数设置为 False 即可实现结果和你一样
  • time.sleep 是必需的,因为输出到 HDCprint 快很多(因为缓冲),所以即使根据代码它发生在 print 之后,实际上它的效果发生在 print "pushes" window 之前(包括我们的图形输出)向上,所以当图形输出超出可见区域时,它将失效并且该区域将被重新绘制,使其消失。
    我不确定我是否说清楚了,但是一旦你玩过代码(注释行),你就会明白我的意思
  • 代码中的某些内容可能不适用于打印机(或工作方式不同),因为它是不同类型的设备
  • 还有一个替代方案:使用[ActiveState.Docs]: PyCDC.DrawText (wrapper over [MS.Docs]: DrawText function),它可以处理多行字符串,但您仍然需要做一些计算才能调整绘图RECT(我也不想玩那个功能)

输出:

@EDIT0:

添加了一些打印机特定的功能。此外,更改了行为:

  • 没有参数,应用程序打印到默认打印机
  • 1st 参数(如果给定)可以是打印机名称或“window”(用于初始行为)
  • HDC的工作方式不同:
    • 对于打印机,缩放比例要高得多(~10 倍)- 我认为这是因为 window HDC 直接使用像素,而对于打印机 HDC 还考虑了 DPI
    • 此外,从顶部到底部 Y 坐标的绝对值增加,但是 负值
    • 我输入了一些适用于 "Microsoft Print to PDF" 打印机 OK 的值,但我认为应该通过读取打印机属性
    • 相应地设置这些值

输出:

@EDIT1:

  • 已根据评论中的要求添加 "multiple font support"