Сохранение / восстановление DevModes принтера - wxPython / win32print - PullRequest
1 голос
/ 16 сентября 2011

До сих пор я нашел два разных способа доступа к, как мне кажется, эквивалентным версиям 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()

Эти двоичные структуры содержат необходимую информацию для управления поведением устройства.Это хорошо, за исключением того, что его нельзя напрямую использовать для перезагрузки диалогов PrintSetup с этими сохраненными данными devmode.В первом случае объект PyDEVMODE содержит десятки отдельных свойств, которые необходимо установить вручную ( Ссылка PyDEVMODE ).Во втором случае есть несколько методов Getter / Setter, которые контролируют некоторые свойства, но не все из них ( wxPrintData Reference ).Кто-нибудь знает способ создания объекта Python Devmode (я воспользуюсь любым подходом, различия тривиальны) из фактического DevMode (двоичные данные)?Я хотел бы избежать необходимости вручную сохранять / сбрасывать каждый отдельный атрибут, чтобы диалоги каждый раз открывались в правильном состоянии.

Ответы [ 2 ]

0 голосов
/ 17 июня 2019

Вы также можете редактировать 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

Я расширил структуру именования «атрибуты», предоставленную Юрием Гендельманом, в похожем вопросе: Печать PDF-файла в дуплексном режиме через Python

Вот пример того, как я использовал его в своем коде.

    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

0 голосов
/ 30 декабря 2011

Похоже, что на данный момент нет элегантного способа достичь этого непосредственно в Python.Самым близким, что я смог придумать, была dll, написанная на c ++, которую я могу вызвать.Я получаю полную бинарную структуру 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->lpfnSetupHook = SetupHookProc; //removed: doing things differently, don't like how this was working
    }

    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
...