保存/恢复打印机 DevModes - wxPython / win32print
到目前为止,我发现了两种不同的方法来访问我认为是等效的打印机 DevMode 版本,这些都是通过 wxPython 用户界面实现的:
window = wx.GetTopLevelWindows()[0].GetHandle()
name = self.itemMap['device'].GetValue() # returns a valid printer name.
handle = win32print.OpenPrinter(name)
dmin = None
dmout = pywintypes.DEVMODEType()
mode = DM_IN_BUFFER | DM_OUT_BUFFER | DM_IN_PROMPT
res = win32print.DocumentProperties(window, handle, name, dmout, dmin, mode)
if res == 1:
print dmout.DriverData
还有一种方法是
dlg = wx.PrintDialog(self, dgData)
res = dlg.ShowModal()
if res == wx.ID_OK:
print dlg.GetPrintDialogData().PrintData.GetPrivData()
这两种二进制结构似乎包含了控制设备输出行为所需的信息。这很好,但问题是,不能直接用这些存储的 devmode 数据来重新加载打印设置对话框。在第一种情况下,PyDEVMODE 对象包含了几十个需要手动设置的属性(PyDEVMODE 参考)。而在第二种情况下,有一些 Getter / Setter 方法可以控制部分属性,但并不是所有的属性都能控制(wxPrintData 参考)。有没有人知道如何从实际的 DevMode(二进制数据)创建一个 Python Devmode 对象?我对这两种方法都可以接受,差别不大。我希望能避免每次都手动存储或重置每个属性,以便对话框能够在正确的状态下重新打开。
2 个回答
你还可以通过一个新的命名对象来编辑 pDevMode
这个对象,方法是在 win32print.GetPrinter()
中进行操作。
import win32print, os
name = win32print.GetDefaultPrinter()
printdefaults = {"DesiredAccess": win32print.PRINTER_ACCESS_USE}
handle = win32print.OpenPrinter(name, printdefaults)
level = 2
attributes = win32print.GetPrinter(handle, level)
# http://timgolden.me.uk/pywin32-docs/PyDEVMODE.html
# Note: All pDevMode settings are int() variables
attributes['pDevMode'].Copies = 2 # Num of copies
#attributes['pDevMode'].Color = 1 # Color
attributes['pDevMode'].Color = 2 # Monochrome
attributes['pDevMode'].Collate = 1 # Collate TRUE
#attributes['pDevMode'].Collate = 2 # Collate FALSE
我在类似的问题中扩展了 Yuri Gendelman 提供的“属性”命名结构:通过 Python 打印 PDF 文件的双面模式
下面是我在代码中使用它的一个示例。
import win32print, os
def autoprint(user):
name = win32print.GetDefaultPrinter()
printdefaults = {"DesiredAccess": win32print.PRINTER_ACCESS_USE}
handle = win32print.OpenPrinter(name, printdefaults)
level = 2
attributes = win32print.GetPrinter(handle, level) # http://timgolden.me.uk/pywin32-docs/PyDEVMODE.html
# All are int() variables
attributes['pDevMode'].Duplex = 1 # no flip
#attributes['pDevMode'].Duplex = 2 # flip up
#attributes['pDevMode'].Duplex = 3 # flip over
attributes['pDevMode'].Copies = 2 # Num of copies
#attributes['pDevMode'].Color = 1 # Color
attributes['pDevMode'].Color = 2 # Monochrome
attributes['pDevMode'].Collate = 1 # Collate TRUE
#attributes['pDevMode'].Collate = 2 # Collate FALSE
try:
win32print.SetPrinter(handle, level, attributes, 0)
except:
print("win32print.SetPrinter: settings could not be changed")
try:
newfile_name = max([downloadPath + "\\" + user["FULL_NAME"] + "PDFToBePrinted.pdf"])
Print2Copies = win32api.ShellExecute(0, 'print', newfile_name, None, '.', 0)
time.sleep(1)
Print2Copies
print("Printing now...")
win32print.ClosePrinter(handle)
final_filename = max([downloadPath + "\\" + user["FULL_NAME"] + "Printed.pdf"])
os.rename(newfile_name, final_filename)
except Exception as e:
print(str(e))
print("--Failed to print--")
time.sleep(5)
这是检查默认设置的代码:
win32print.GetPrinter(handle, level)['pDevMode'].Copies
win32print.GetPrinter(handle, level)['pDevMode'].Duplex
In[115]: print(win32print.GetPrinter(handle, level)['pDevMode'].Copies)
Out[115]: 1
In[116]:win32print.GetPrinter(handle, level)['pDevMode'].Duplex
Out[116]: 1
In[117]:win32print.GetPrinter(handle, level)['pDevMode'].Color
Out[117]: 1
这里是你可以通过 pDevMode
更改的其他打印机设置:http://timgolden.me.uk/pywin32-docs/PyDEVMODE.html
看起来目前在Python中没有简单优雅的方法可以直接做到这一点。我能想到的最接近的办法是用C++写了一个dll文件,我可以调用它。这样我就能得到完整的二进制DevMode结构,并且可以从中重新加载。那个C++ dll的代码大概是这样的:
#include "stdafx.h"
#include <iobind/base64_policy.hpp>
#include <string>
#ifdef _UNICODE
typedef std::wstring string_t;
#else
typedef std::string string_t;
#endif
typedef std::string cstring;
extern "C" BOOL WINAPI DllMain( HMODULE hModule, DWORD dwReason, LPVOID lpReserved ) {
switch ( dwReason ){
case DLL_PROCESS_ATTACH:
DisableThreadLibraryCalls( hModule );
break;
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}
extern "C" DOEXPORT int CleanupA( char *output ) {
if ( output ) {
free( output );
output = NULL;
}
return 0;
}
extern "C" DOEXPORT int CleanupW( wchar_t *output ) {
if ( output ) {
free( output );
output = NULL;
}
return 0;
}
extern "C" DOEXPORT int printer_setup(
void *handle, const TCHAR *printer_in, const char *input,
int local_only, TCHAR **printer, char **output )
{
HWND hwnd = (HWND)handle;
HRESULT hResult = 0;
LPPRINTDLG pPD = NULL;
LPPRINTPAGERANGE pPageRanges = NULL;
// Allocate structure.
pPD = (LPPRINTDLG)GlobalAlloc(GPTR, sizeof(PRINTDLG));
if (!pPD) return E_OUTOFMEMORY;
// Initialize structure.
pPD->lStructSize = sizeof(PRINTDLG);
pPD->hwndOwner = hwnd;
pPD->hDevMode = NULL;
if ( input ){
std::string dec = iobind::encode( input, iobind::from_base64_p );
if ( !dec.empty() ) {
HGLOBAL devmode = pPD->hDevMode = ::GlobalAlloc(GPTR, dec.size());
if ( devmode ){
LPDEVMODE src = (LPDEVMODE)&dec[0];
memcpy( devmode, src, dec.size() );
}
}
}
pPD->hDevNames = NULL;
if ( printer_in ){
HGLOBAL printer = pPD->hDevNames = ::GlobalAlloc(GPTR, sizeof(DEVNAMES)+_tcslen(printer_in)*sizeof(TCHAR)+sizeof(TCHAR));
if ( printer ){
LPDEVNAMES dv = (LPDEVNAMES)printer;
dv->wDefault = 0;
dv->wDriverOffset = 0;
dv->wOutputOffset = 0;
dv->wDeviceOffset = sizeof(DEVNAMES)/sizeof(TCHAR);
TCHAR *dest = (TCHAR *)(unsigned long)dv + dv->wDeviceOffset;
_tcscpy( dest, printer_in );
}
}
pPD->hDC = NULL;
pPD->Flags = PD_PRINTSETUP;
if ( local_only ) {
pPD->Flags |= /*PD_ENABLESETUPHOOK |*/ PD_NONETWORKBUTTON;
}
pPD->nMinPage = 1;
pPD->nMaxPage = 1000;
pPD->nCopies = 1;
pPD->hInstance = 0;
pPD->lpPrintTemplateName = NULL;
// Invoke the Print property sheet.
hResult = PrintDlg(pPD);
if ( hResult != 0 ) {
if ( pPD->hDevMode ) {
LPDEVMODE devmode = (LPDEVMODE)::GlobalLock( pPD->hDevMode );
size_t size = devmode->dmSize + devmode->dmDriverExtra;
if ( output ) {
std::string tmp;
tmp.resize( size );
memcpy( &tmp[0], devmode, tmp.size() );
std::string enc = iobind::encode( tmp, iobind::to_base64_p );
*output = _strdup( enc.c_str() );
}
::GlobalUnlock( pPD->hDevMode );
}
if ( pPD->hDevNames ) {
LPDEVNAMES devnames = (LPDEVNAMES)::GlobalLock( pPD->hDevNames );
TCHAR *device = (TCHAR *)(unsigned long)devnames + devnames->wDeviceOffset;
*printer = _tcsdup(device);
::GlobalUnlock( pPD->hDevNames );
}
}
else {
DWORD dlgerr = ::CommDlgExtendedError();
hResult = dlgerr;
}
if (pPD->hDC != NULL) {
DeleteDC( pPD->hDC );
}
if (pPD->hDevMode != NULL) {
GlobalFree( pPD->hDevMode );
}
if (pPD->hDevNames != NULL) {
GlobalFree( pPD->hDevNames );
}
return hResult;
}
在Python中调用它的方式是这样的:
client = ctypes.cdll.LoadLibrary(os.path.join(myDir, 'rpmclient.dll'))
client.printer_setup.argtypes = [ctypes.c_void_p,
ctypes.c_wchar_p,
ctypes.c_char_p,
ctypes.c_int32,
ctypes.POINTER(ctypes.c_wchar_p),
ctypes.POINTER(ctypes.c_char_p)]
client.printer_setup.restype = ctypes.c_int32
client.CleanupA.argtypes = [ctypes.c_char_p]
client.CleanupA.restype = ctypes.c_int32
client.CleanupW.argtypes = [ctypes.c_wchar_p]
client.CleanupW.restype = ctypes.c_int32
p_in = ctypes.c_wchar_p(self.itemMap['device'].GetValue())
p_out = ctypes.c_wchar_p()
d_in = ctypes.c_char_p(getattr(self, 'devmode', ''))
d_out = ctypes.c_char_p()
res = client.printer_setup(self.GetHandle(),
p_in,
d_in,
False,
p_out,
d_out)
if res == 0:
return
if res > 1:
# Error display code here.
return
self.devmode = d_out.value