How to catch printer event in python - events

I want to catch the signal when the printer started to print. That would be fine if you tell me how to get the path of document that will print.
pywin32print looks like useful but I don't know how to use.

To be notified of new print jobs, you can use FindFirstPrinterChangeNotification, FindNextPrinterChangeNotification, and a wait function from kernel32 such as WaitForSingleObject.
Here's an example to set a filter that waits for a new print job on the local print server. There's much more work to be done if you want the JOB_NOTIFY_FIELD_DOCUMENT value out of a PRINTER_NOTIFY_INFO_DATA structure.
Edit:
Malik Brahimi requested information about obtaining the print job ID, so I've decided to flesh this out as a more complete example. The new code adds a wait_for_print_job_info function that waits for notifications specified using the PRINTER_NOTIFY_OPTIONS structure. Windows returns the data in an array of the above-mentioned PRINTER_NOTIFY_INFO_DATA structures, each of which has the print job Id for the given data.
The data has to be freed by calling FreePrinterNotifyInfo, which I handle automatically in the __del__ finalizer of the PPRINTER_NOTIFY_INFO_GC class.
import ctypes
from ctypes import wintypes
kernel32 = ctypes.WinDLL('kernel32.dll', use_last_error=True)
winspool = ctypes.WinDLL('winspool.drv', use_last_error=True)
# define LPHANDLE, PDWORD, and PWORD for Python 2
if not hasattr(wintypes, 'LPHANDLE'):
setattr(wintypes, 'LPHANDLE', ctypes.POINTER(wintypes.HANDLE))
if not hasattr(wintypes, 'PDWORD'):
setattr(wintypes, 'PDWORD', ctypes.POINTER(wintypes.DWORD))
if not hasattr(wintypes, 'PWORD'):
setattr(wintypes, 'PWORD', ctypes.POINTER(wintypes.WORD))
INFINITE = -1
WAIT_OBJECT_0 = 0x00000000
WAIT_TIMEOUT = 0x00000102
WAIT_FAILED = 0xFFFFFFFF
INVALID_HANDLE_VALUE = wintypes.HANDLE(-1).value
PRINTER_CHANGE_ADD_PRINTER = 0x00000001
PRINTER_CHANGE_SET_PRINTER = 0x00000002
PRINTER_CHANGE_DELETE_PRINTER = 0x00000004
PRINTER_CHANGE_FAILED_CONNECTION_PRINTER = 0x00000008
PRINTER_CHANGE_PRINTER = 0x000000FF
PRINTER_CHANGE_ADD_JOB = 0x00000100
PRINTER_CHANGE_SET_JOB = 0x00000200
PRINTER_CHANGE_DELETE_JOB = 0x00000400
PRINTER_CHANGE_WRITE_JOB = 0x00000800
PRINTER_CHANGE_JOB = 0x0000FF00
PRINTER_CHANGE_ADD_FORM = 0x00010000
PRINTER_CHANGE_SET_FORM = 0x00020000
PRINTER_CHANGE_DELETE_FORM = 0x00040000
PRINTER_CHANGE_FORM = 0x00070000
PRINTER_CHANGE_ADD_PORT = 0x00100000
PRINTER_CHANGE_CONFIGURE_PORT = 0x00200000
PRINTER_CHANGE_DELETE_PORT = 0x00400000
PRINTER_CHANGE_PORT = 0x00700000
PRINTER_CHANGE_ADD_PRINT_PROCESSOR = 0x01000000
PRINTER_CHANGE_DELETE_PRINT_PROCESSOR = 0x04000000
PRINTER_CHANGE_PRINT_PROCESSOR = 0x07000000
PRINTER_CHANGE_SERVER = 0x08000000 # NT 6.1+
PRINTER_CHANGE_ADD_PRINTER_DRIVER = 0x10000000
PRINTER_CHANGE_SET_PRINTER_DRIVER = 0x20000000
PRINTER_CHANGE_DELETE_PRINTER_DRIVER = 0x40000000
PRINTER_CHANGE_PRINTER_DRIVER = 0x70000000
PRINTER_CHANGE_ALL = 0x7F77FFFF
PRINTER_CHANGE_TIMEOUT = 0x80000000
PRINTER_NOTIFY_CATEGORY_ALL = 0x00
PRINTER_NOTIFY_CATEGORY_3D = 0x01
PRINTER_NOTIFY_TYPE = 0x00
JOB_NOTIFY_TYPE = 0x01
PRINTER_NOTIFY_FIELD_SERVER_NAME = 0x00 # not supported
PRINTER_NOTIFY_FIELD_PRINTER_NAME = 0x01
PRINTER_NOTIFY_FIELD_SHARE_NAME = 0x02
PRINTER_NOTIFY_FIELD_PORT_NAME = 0x03
PRINTER_NOTIFY_FIELD_DRIVER_NAME = 0x04
PRINTER_NOTIFY_FIELD_COMMENT = 0x05
PRINTER_NOTIFY_FIELD_LOCATION = 0x06
PRINTER_NOTIFY_FIELD_DEVMODE = 0x07
PRINTER_NOTIFY_FIELD_SEPFILE = 0x08
PRINTER_NOTIFY_FIELD_PRINT_PROCESSOR = 0x09
PRINTER_NOTIFY_FIELD_PARAMETERS = 0x0A
PRINTER_NOTIFY_FIELD_DATATYPE = 0x0B
PRINTER_NOTIFY_FIELD_SECURITY_DESCRIPTOR = 0x0C
PRINTER_NOTIFY_FIELD_ATTRIBUTES = 0x0D
PRINTER_NOTIFY_FIELD_PRIORITY = 0x0E
PRINTER_NOTIFY_FIELD_DEFAULT_PRIORITY = 0x0F
PRINTER_NOTIFY_FIELD_START_TIME = 0x10
PRINTER_NOTIFY_FIELD_UNTIL_TIME = 0x11
PRINTER_NOTIFY_FIELD_STATUS = 0x12
PRINTER_NOTIFY_FIELD_STATUS_STRING = 0x13 # not supported
PRINTER_NOTIFY_FIELD_CJOBS = 0x14
PRINTER_NOTIFY_FIELD_AVERAGE_PPM = 0x15
PRINTER_NOTIFY_FIELD_TOTAL_PAGES = 0x16 # not supported
PRINTER_NOTIFY_FIELD_PAGES_PRINTED = 0x17 # not supported
PRINTER_NOTIFY_FIELD_TOTAL_BYTES = 0x18 # not supported
PRINTER_NOTIFY_FIELD_BYTES_PRINTED = 0x19 # not supported
PRINTER_NOTIFY_FIELD_OBJECT_GUID = 0x1A
PRINTER_NOTIFY_FIELD_FRIENDLY_NAME = 0x1B # NT 6.0+
PRINTER_NOTIFY_FIELD_BRANCH_OFFICE_PRINTING = 0x1C # NT 6.2+
JOB_NOTIFY_FIELD_PRINTER_NAME = 0x00
JOB_NOTIFY_FIELD_MACHINE_NAME = 0x01
JOB_NOTIFY_FIELD_PORT_NAME = 0x02
JOB_NOTIFY_FIELD_USER_NAME = 0x03
JOB_NOTIFY_FIELD_NOTIFY_NAME = 0x04
JOB_NOTIFY_FIELD_DATATYPE = 0x05
JOB_NOTIFY_FIELD_PRINT_PROCESSOR = 0x06
JOB_NOTIFY_FIELD_PARAMETERS = 0x07
JOB_NOTIFY_FIELD_DRIVER_NAME = 0x08
JOB_NOTIFY_FIELD_DEVMODE = 0x09
JOB_NOTIFY_FIELD_STATUS = 0x0A
JOB_NOTIFY_FIELD_STATUS_STRING = 0x0B
JOB_NOTIFY_FIELD_SECURITY_DESCRIPTOR = 0x0C # not supported
JOB_NOTIFY_FIELD_DOCUMENT = 0x0D
JOB_NOTIFY_FIELD_PRIORITY = 0x0E
JOB_NOTIFY_FIELD_POSITION = 0x0F
JOB_NOTIFY_FIELD_SUBMITTED = 0x10
JOB_NOTIFY_FIELD_START_TIME = 0x11
JOB_NOTIFY_FIELD_UNTIL_TIME = 0x12
JOB_NOTIFY_FIELD_TIME = 0x13
JOB_NOTIFY_FIELD_TOTAL_PAGES = 0x14
JOB_NOTIFY_FIELD_PAGES_PRINTED = 0x15
JOB_NOTIFY_FIELD_TOTAL_BYTES = 0x16
JOB_NOTIFY_FIELD_BYTES_PRINTED = 0x17
JOB_NOTIFY_FIELD_REMOTE_JOB_ID = 0x18
PRINTER_NOTIFY_OPTIONS_REFRESH = 0x01
PRINTER_NOTIFY_INFO_DISCARDED = 0x01
JOB_STATUS_PAUSED = 0x00000001
JOB_STATUS_ERROR = 0x00000002
JOB_STATUS_DELETING = 0x00000004
JOB_STATUS_SPOOLING = 0x00000008
JOB_STATUS_PRINTING = 0x00000010
JOB_STATUS_OFFLINE = 0x00000020
JOB_STATUS_PAPEROUT = 0x00000040
JOB_STATUS_PRINTED = 0x00000080
JOB_STATUS_DELETED = 0x00000100
JOB_STATUS_BLOCKED_DEVQ = 0x00000200
JOB_STATUS_USER_INTERVENTION = 0x00000400
JOB_STATUS_RESTART = 0x00000800
JOB_STATUS_COMPLETE = 0x00001000
JOB_STATUS_RETAINED = 0x00002000
JOB_STATUS_RENDERING_LOCALLY = 0x00004000
JOB_STATUS_ALL = 0x00007FFF
JOB_STATUS_STRING = {
JOB_STATUS_PAUSED: 'PAUSED',
JOB_STATUS_ERROR: 'ERROR',
JOB_STATUS_DELETING: 'DELETING',
JOB_STATUS_SPOOLING: 'SPOOLING',
JOB_STATUS_PRINTING: 'PRINTING',
JOB_STATUS_OFFLINE: 'OFFLINE',
JOB_STATUS_PAPEROUT: 'PAPEROUT',
JOB_STATUS_PRINTED: 'PRINTED',
JOB_STATUS_DELETED: 'DELETED',
JOB_STATUS_BLOCKED_DEVQ: 'BLOCKED_DEVQ',
JOB_STATUS_USER_INTERVENTION: 'USER_INTERVENTION',
JOB_STATUS_RESTART: 'RESTART',
JOB_STATUS_COMPLETE: 'COMPLETE',
JOB_STATUS_RETAINED: 'RETAINED',
JOB_STATUS_RENDERING_LOCALLY: 'RENDERING_LOCALLY'}
class SYSTEMTIME(ctypes.Structure):
_fields_ = (('wYear', wintypes.WORD),
('wMonth', wintypes.WORD),
('wDayOfWeek', wintypes.WORD),
('wDay', wintypes.WORD),
('wHour', wintypes.WORD),
('wMinute', wintypes.WORD),
('wSecond', wintypes.WORD),
('wMilliseconds', wintypes.WORD))
#property
def as_datetime(self):
from datetime import datetime
return datetime(self.wYear, self.wMonth, self.wDay,
self.wHour, self.wMinute, self.wSecond,
self.wMilliseconds * 1000)
class PRINTER_NOTIFY_OPTIONS_TYPE(ctypes.Structure):
_fields_ = (('Type', wintypes.WORD),
('Reserved0', wintypes.WORD),
('Reserved1', wintypes.DWORD),
('Reserved2', wintypes.DWORD),
('Count', wintypes.DWORD),
('_pFields', wintypes.PWORD))
def __init__(self, Type=JOB_NOTIFY_TYPE, pFields=None):
super(PRINTER_NOTIFY_OPTIONS_TYPE, self).__init__(Type)
if pFields is not None:
self.pFields = pFields
#property
def pFields(self):
ptr_t = ctypes.POINTER(wintypes.WORD * self.Count)
return ptr_t(self._pFields.contents)[0]
#pFields.setter
def pFields(self, pFields):
self.Count = len(pFields)
self._pFields = pFields
PPRINTER_NOTIFY_OPTIONS_TYPE = ctypes.POINTER(PRINTER_NOTIFY_OPTIONS_TYPE)
class PRINTER_NOTIFY_OPTIONS(ctypes.Structure):
_fields_ = (('Version', wintypes.DWORD),
('Flags', wintypes.DWORD),
('Count', wintypes.DWORD),
('_pTypes', PPRINTER_NOTIFY_OPTIONS_TYPE))
def __init__(self, Flags=0, pTypes=None):
super(PRINTER_NOTIFY_OPTIONS, self).__init__(2, Flags)
if pTypes is not None:
self.pTypes = pTypes
#property
def pTypes(self):
ptr_t = ctypes.POINTER(PRINTER_NOTIFY_OPTIONS_TYPE * self.Count)
return ptr_t(self._pTypes.contents)[0]
#pTypes.setter
def pTypes(self, types):
if isinstance(types, PRINTER_NOTIFY_OPTIONS_TYPE):
self.Count = 1
self._pTypes = ctypes.pointer(types)
else:
self.Count = len(types)
self._pTypes = types
PPRINTER_NOTIFY_OPTIONS = ctypes.POINTER(PRINTER_NOTIFY_OPTIONS)
class PRINTER_NOTIFY_INFO_DATA(ctypes.Structure):
class _NOTIFY_DATA(ctypes.Union):
class _DATA(ctypes.Structure):
_fields_ = (('cbBuf', wintypes.DWORD),
('pBuf', wintypes.LPVOID))
_fields_ = (('adwData', wintypes.DWORD * 2),
('Data', _DATA))
_fields_ = (('Type', wintypes.WORD),
('Field', wintypes.WORD),
('Reserved', wintypes.DWORD),
('Id', wintypes.DWORD),
('_NotifyData', _NOTIFY_DATA))
#property
def _data_as_string(self):
if self._NotifyData.Data.pBuf:
return ctypes.c_wchar_p(self._NotifyData.Data.pBuf).value
return u""
#property
def _data_as_datetime(self):
if self._NotifyData.Data.pBuf:
t = SYSTEMTIME.from_address(self._NotifyData.Data.pBuf)
else:
t = SYSTEMTIME()
return t.as_datetime
#property
def NotifyData(self):
if self.Type == JOB_NOTIFY_TYPE:
if self.Field == JOB_NOTIFY_FIELD_PRINTER_NAME:
return 'job_printer_name', self._data_as_string
if self.Field == JOB_NOTIFY_FIELD_MACHINE_NAME:
return 'job_machine_name', self._data_as_string
if self.Field == JOB_NOTIFY_FIELD_USER_NAME:
return 'job_user_name', self._data_as_string
elif self.Field == JOB_NOTIFY_FIELD_STATUS:
return 'job_status', self._NotifyData.adwData[0]
elif self.Field == JOB_NOTIFY_FIELD_DOCUMENT:
return 'job_document', self._data_as_string
elif self.Field == JOB_NOTIFY_FIELD_PRIORITY:
return 'job_priority', self._NotifyData.adwData[0]
elif self.Field == JOB_NOTIFY_FIELD_POSITION:
return 'job_position', self._NotifyData.adwData[0]
elif self.Field == JOB_NOTIFY_FIELD_SUBMITTED:
return 'job_submitted', self._data_as_datetime
elif self.Field == JOB_NOTIFY_FIELD_PAGES_PRINTED:
return 'job_pages_printed', self._NotifyData.adwData[0]
elif self.Field == JOB_NOTIFY_FIELD_BYTES_PRINTED:
return 'job_bytes_printed', self._NotifyData.adwData[0]
# else return a copy of NotifyData
data = self._NOTIFY_DATA.from_buffer_copy(self._NotifyData)
if data.Data.pBuf:
buf_t = ctypes.c_char * data.Data.cbBuf
buf_src = buf_t.from_address(data.Data.pBuf)
buf_cpy = buf_t.from_buffer_copy(buf_src)
buf_ptr = ctypes.c_void_p(ctypes.addressof(buf_cpy))
data.Data.pBuf = buf_ptr
return (self.Type, self.Field), data
class PRINTER_NOTIFY_INFO(ctypes.Structure):
_fields_ = (('Version', wintypes.DWORD),
('Flags', wintypes.DWORD),
('Count', wintypes.DWORD),
('_aData', PRINTER_NOTIFY_INFO_DATA * 1))
#property
def aData(self):
ptr_t = ctypes.POINTER(PRINTER_NOTIFY_INFO_DATA * self.Count)
return ptr_t(self._aData[0])[0]
PPRINTER_NOTIFY_INFO = ctypes.POINTER(PRINTER_NOTIFY_INFO)
PPPRINTER_NOTIFY_INFO = ctypes.POINTER(PPRINTER_NOTIFY_INFO)
class PPRINTER_NOTIFY_INFO_GC(PPRINTER_NOTIFY_INFO):
"""PRINTER_NOTIFY_INFO * that Windows deallocates"""
_type_ = PRINTER_NOTIFY_INFO
_freed = False
def __del__(self,
FreePrinterNotifyInfo=winspool.FreePrinterNotifyInfo):
if self and not self._freed:
FreePrinterNotifyInfo(self)
self._freed = True
def check_bool(result, func, args):
if not result:
raise ctypes.WinError(ctypes.get_last_error())
return args
def check_ihv(result, func, args):
if result == INVALID_HANDLE_VALUE:
raise ctypes.WinError(ctypes.get_last_error())
return args
def check_idv(result, func, args):
if result == WAIT_FAILED:
raise ctypes.WinError(ctypes.get_last_error())
return args
winspool.OpenPrinterW.errcheck = check_bool
winspool.OpenPrinterW.argtypes = (
wintypes.LPWSTR, # _In_ pPrinterName
wintypes.LPHANDLE, # _Out_ phPrinter
wintypes.LPVOID) # _In_ pDefault
winspool.ClosePrinter.errcheck = check_bool
winspool.ClosePrinter.argtypes = (
wintypes.HANDLE,) # _In_ hPrinter
winspool.FindFirstPrinterChangeNotification.errcheck = check_ihv
winspool.FindFirstPrinterChangeNotification.restype = wintypes.HANDLE
winspool.FindFirstPrinterChangeNotification.argtypes = (
wintypes.HANDLE, # _In_ hPrinter
wintypes.DWORD, # fdwFilter
wintypes.DWORD, # fdwOptions
PPRINTER_NOTIFY_OPTIONS) # _In_opt_ pPrinterNotifyOptions
winspool.FindNextPrinterChangeNotification.errcheck = check_bool
winspool.FindNextPrinterChangeNotification.argtypes = (
wintypes.HANDLE, # _In_ hChange
wintypes.PDWORD, # _Out_opt_ pdwChange
PPRINTER_NOTIFY_OPTIONS, # _In_opt_ pPrinterNotifyOptions
PPPRINTER_NOTIFY_INFO) # _Out_opt_ ppPrinterNotifyInfo
winspool.FindClosePrinterChangeNotification.errcheck = check_bool
winspool.FindClosePrinterChangeNotification.argtypes = (
wintypes.HANDLE,) # _In_ hChange
winspool.FreePrinterNotifyInfo.errcheck = check_bool
winspool.FreePrinterNotifyInfo.argtypes = (
PPRINTER_NOTIFY_INFO,) # _In_ pPrinterNotifyInfo
kernel32.WaitForSingleObject.errcheck = check_idv
kernel32.WaitForSingleObject.restype = wintypes.DWORD
kernel32.WaitForSingleObject.argtypes = (
wintypes.HANDLE, # _In_ hHandle
wintypes.DWORD) # _In_ dwMilliseconds
def wait_for_print_job(filter=PRINTER_CHANGE_ADD_JOB,
timeout=INFINITE,
printer_name=None):
if timeout != INFINITE:
timeout = int(timeout * 1000)
hPrinter = wintypes.HANDLE()
dwChange = wintypes.DWORD()
winspool.OpenPrinterW(printer_name, ctypes.byref(hPrinter), None)
try:
hChange = winspool.FindFirstPrinterChangeNotification(
hPrinter, filter, 0, None)
try:
if (kernel32.WaitForSingleObject(hChange, timeout) !=
WAIT_OBJECT_0): return
winspool.FindNextPrinterChangeNotification(
hChange, ctypes.byref(dwChange), None, None)
return dwChange.value
finally:
winspool.FindClosePrinterChangeNotification(hChange)
finally:
winspool.ClosePrinter(hPrinter)
DEFAULT_FIELDS = (
JOB_NOTIFY_FIELD_PRINTER_NAME,
JOB_NOTIFY_FIELD_STATUS,
JOB_NOTIFY_FIELD_DOCUMENT,
JOB_NOTIFY_FIELD_PRIORITY,
JOB_NOTIFY_FIELD_POSITION,
JOB_NOTIFY_FIELD_SUBMITTED)
def wait_for_print_job_info(fields=DEFAULT_FIELDS,
timeout=INFINITE,
printer_name=None):
if timeout != INFINITE:
timeout = int(timeout * 1000)
hPrinter = wintypes.HANDLE()
fields = (wintypes.WORD * len(fields))(*fields)
opt = PRINTER_NOTIFY_OPTIONS(
pTypes=PRINTER_NOTIFY_OPTIONS_TYPE(
Type=JOB_NOTIFY_TYPE, pFields=fields))
pinfo = PPRINTER_NOTIFY_INFO_GC() # note: GC subclass
result = []
winspool.OpenPrinterW(printer_name, ctypes.byref(hPrinter), None)
try:
hChange = winspool.FindFirstPrinterChangeNotification(
hPrinter, 0, 0, ctypes.byref(opt))
try:
if (kernel32.WaitForSingleObject(hChange, timeout) !=
WAIT_OBJECT_0): return result
winspool.FindNextPrinterChangeNotification(
hChange, None, None, ctypes.byref(pinfo))
for data in pinfo[0].aData:
if data.Type != JOB_NOTIFY_TYPE:
continue
nd = (data.Id,) + data.NotifyData
result.append(nd)
return result
finally:
winspool.FindClosePrinterChangeNotification(hChange)
finally:
winspool.ClosePrinter(hPrinter)
def job_status_string(status, nfmt='%#010x'):
if status == 0:
return nfmt % 0
strings = []
for state, string in JOB_STATUS_STRING.items():
if status & state:
strings.append(string)
status &= ~state
if not status:
break
if status:
strings.append(nfmt % status)
return ','.join(strings)
Example usage:
if __name__ == '__main__':
import time
print('Type Ctrl+C to exit')
try:
while True:
info = wait_for_print_job_info(timeout=0.25)
if not info:
continue
for nd in info:
job_id, key, value = nd
if key == 'job_status':
status_string = job_status_string(value)
value = '%#010x (%s)' % (value, status_string)
print('[%08x] %s: %s' % (job_id, key, value))
time.sleep(.05)
print('')
time.sleep(.05)
except KeyboardInterrupt:
pass
Example output from printing a test page two times in a row:
Type Ctrl+C to exit
[00000001] job_printer_name: Larry (Network)
[00000001] job_status: 0x00000008 (SPOOLING)
[00000001] job_document: Test Page
[00000001] job_priority: 1
[00000001] job_position: 1
[00000001] job_submitted: 2016-02-06 13:14:14.139000
[00000001] job_position: 1
[00000002] job_printer_name: Larry (Network)
[00000002] job_status: 0x00000008 (SPOOLING)
[00000002] job_document: Test Page
[00000002] job_priority: 1
[00000002] job_position: 2
[00000002] job_submitted: 2016-02-06 13:14:15.186000
[00000001] job_position: 1
[00000001] job_position: 1
[00000001] job_position: 1
[00000001] job_position: 1
[00000001] job_position: 1
[00000001] job_position: 1
[00000001] job_status: 0x00002010 (PRINTING,RETAINED)
[00000001] job_position: 1
[00000001] job_status: 0x00000010 (PRINTING)
[00000001] job_status: 0x00000094 (DELETING,PRINTED,PRINTING)
[00000001] job_status: 0x00000094 (DELETING,PRINTED,PRINTING)
[00000002] job_position: 1
[00000002] job_position: 1
[00000002] job_position: 1
[00000002] job_position: 1
[00000002] job_position: 1
[00000002] job_position: 1
[00000002] job_position: 1
[00000002] job_position: 1
[00000002] job_status: 0x00002010 (PRINTING,RETAINED)
[00000002] job_position: 1
[00000002] job_status: 0x00000010 (PRINTING)
[00000002] job_status: 0x00000094 (DELETING,PRINTED,PRINTING)
[00000002] job_status: 0x00000094 (DELETING,PRINTED,PRINTING)
[00000002] job_status: 0x00000084 (DELETING,PRINTED)
[00000002] job_status: 0x00000184 (DELETING,PRINTED,DELETED)

Related

micropython temp sensor gives CRC error when in larger code file

I have a temp sensor taht i need to use for a school project it works perfectly when i dont put it together with the rest of the code but when I do it gives a CRC error after giving me the temp 3 or 4 times.
import onewire
import time, ds18x20
from machine import Pin
ow = onewire.OneWire(Pin(26))
ds = ds18x20.DS18X20(ow)
roms = ds.scan()
print("roms=",roms) # roms= [bytearray(b'(\x19\x1e\xbf\x0b\x00\x00\xc7'), bytearray(b'(\xd2*y\x97\x13\x03\xc3')]
# https://web.alfredstate.edu/faculty/weimandn/miscellaneous/ascii/ascii_index.html
last_reading_time = 0
while(1):
ds.convert_temp()
for rom in roms:
temp=ds.read_temp(rom)
current_time = time.time() # get the current time
if current_time - last_reading_time >= 10:
last_reading_time = current_time
print(temp)
this works without a problem.
from wifi import ssid, password, mqtt_server, client_id
from machine import Pin, I2C
from time import sleep, ticks_ms
from sh1106 import SH1106_I2C
import utime
import machine
import network
from umqttsimple import MQTTClient
from var import *
import time
import urequests
import json
from Pins import *
import onewire
import time, ds18x20
buzzer.freq(500)
buzzer.duty(0)
ow = onewire.OneWire(Pin(26))
ds = ds18x20.DS18X20(ow)
roms = ds.scan()
print("roms=",roms)
topic_sub = b'CVOFOCUS/RP3/*******'
topic_pub = b'CVOFOCUS/RP3/*******'
topic_pub2 = b'CVOFOCUS/RP3/******'
station = network.WLAN(network.STA_IF)
station.active(True)
station.connect(ssid, password)
while station.isconnected() == False:
pass
print('Connection successful')
print(station.ifconfig())
WIDTH = 128
HEIGHT = 64
i2c = I2C(scl=Pin(22), sda=Pin(27)) #Init i2c
oled= SH1106_I2C(128, 64, i2c)
# CONSTANTS
KEY_UP = const(0)
KEY_DOWN = const(1)
gewenste_code = "7ADC"
ingegeven_code = ""
keys = [['1', '2', '3', 'A'], ['4', '5', '6', 'B'], ['7', '8', '9', 'C'], ['*', '0', '#', 'D']]
# Pin names for Pico
cols = [19,16,15,23]
rows = [2,4,5,18]
# set pins for rows as outputs
row_pins = [Pin(pin_name, mode=Pin.OUT) for pin_name in rows]
# set pins for cols as inputs
col_pins = [Pin(pin_name, mode=Pin.IN, pull=Pin.PULL_DOWN) for pin_name in cols]
def init():
for row in range(0,4):
for col in range(0,4):
row_pins[row].value(0)
def scan(row, col):
""" scan the keypad """
# set the current column to high
row_pins[row].value(1)
key = None
# check for keypressed events
if col_pins[col].value() == KEY_DOWN:
key = KEY_DOWN
if col_pins[col].value() == KEY_UP:
key = KEY_UP
row_pins[row].value(0)
# return the key state
return key
print("starting")
# set all the columns to low
init()
def oled_text(text):
oled.fill(0)
oled.text(text, 10, 10)
oled.show()
def trigger_alarm(trig_by = ""):
global is_alarm_on, is_alarm_trigered, alarm_timer
if is_alarm_on and not is_alarm_trigered:
print("Alarm trigered by", trig_by)
is_alarm_trigered = 1
alarm_timer = ticks_ms()
def subscribe_callback(topic, msg): #b'cvofocus/wimverlinden/msg_for_esp'
print((topic, msg))
global is_alarm_on
if topic == b'CVOFOCUS/RP3/Matteo_Alarm':
if msg== b'Alarm aan':
if is_alarm_on == 0:
is_alarm_on = not is_alarm_on
print("alarm_on =" , str(is_alarm_on))
msg = b'Alarm aan'
client.publish(topic_pub, msg)
oled.text('van afstand', 10, 30)
oled.text('bediend', 10, 38)
oled.show()
elif msg ==b'Alarm off':
print("correct")
if is_alarm_on == 1:
is_alarm_on = not is_alarm_on
print("alarm_on =" , str(is_alarm_on))
if is_alarm_on == 0:
# alles uitschakelen
is_alarm_trigered = 0
led_red.value(0)
buzzer.duty(0)
oled.text('van afstand', 10, 30)
oled.text('bediend', 10, 38)
oled.show()
msg = b'Alarm uit'
client.publish(topic_pub, msg)
led_green.value(1)
sleep(0.2)
led_green.value(0)
sleep(0.2)
led_green.value(1)
sleep(0.2)
led_green.value(0)
ingegeven_code = ""
def connect_and_subscribe():
global client_id, mqtt_server, topic_sub
client = MQTTClient(client_id, mqtt_server)
client.set_callback(subscribe_callback)
client.connect()
client.subscribe(topic_sub)
print('Connected to %s MQTT broker, subscribed to %s topic' % (mqtt_server, topic_sub))
return client
def restart_and_reconnect():
print('Failed to connect to MQTT broker. Reconnecting...')
time.sleep(10)
machine.reset()
try:
client = connect_and_subscribe()
except OSError as e:
restart_and_reconnect()
###############################
###############################
while True:
ds.convert_temp()
for rom in roms:
temp= ds.read_temp(rom)
current_time = time.time() # get the current time
if current_time - last_reading_time >= 60:
last_reading_time = current_time
msg = b"%d" % temp
client.publish(topic_pub2, msg)
print(f"temp send ({temp})")
try:
client.check_msg() # needed in loop?
status_button2 = button2.value()
if status_button_before2 != status_button2:
status_button_before2 = status_button2
if status_button2 == 0:
msg = b'PANIC PANIC PANIC PANIC!'
client.publish(topic_pub, msg)
response = urequests.post(api_endpoint, data=json.dumps({
"token": app_token,
"user": user_key,
"message": message,
"sound": sound,
"priority": priority,
"retry": retry,
"expire": expire,
}), headers={"Content-type": "application/json"})
print(response.text)
except OSError as e:
restart_and_reconnect()
status_button = button.value()
if status_button_before != status_button:
status_button_before = status_button
if status_button == 0:
if is_alarm_on == 0:
is_alarm_on = not is_alarm_on
print("alarm_on =" , str(is_alarm_on))
msg = b'Alarm aan'
client.publish(topic_pub, msg)
sleep(0.05)
if is_alarm_on:
if ticks_ms() - timestamp_led > 200:
timestamp_led = ticks_ms()
is_led_on = not is_led_on
#print("is_led_on", is_led_on)
pir_status = pir.value()
if pir_status != status_pir_before:
status_pir_before = pir_status
print("PIR=" , pir_status)
if pir_status == 1:
trigger_alarm("PIR")
if is_alarm_trigered:
if ticks_ms() - timestamp_buzzer > 500:
timestamp_buzzer = ticks_ms()
is_buzzer_on = not is_buzzer_on
#print("is_buzzer_on", is_buzzer_on)
buzzer.duty(512 * is_buzzer_on)
if is_alarm_on == True:
led_red.value(1)
oled.fill(0)
for row in range(4):
for col in range(4):
key = scan(row, col)
if key == KEY_DOWN:
oled.text("Key Pressed: ",10,0)
print("Key Pressed", keys[row][col])
oled.text(keys[row][col], 55, 30)
oled.show()
##MATTEO
ingegeven_code += keys[row][col]
print(ingegeven_code)
if len(ingegeven_code) > 3:
oled.fill(0)
oled.show()
if ingegeven_code == gewenste_code:
oled.text("correct",35,0)
print("correct")
if is_alarm_on == 1:
is_alarm_on = not is_alarm_on
print("alarm_on =" , str(is_alarm_on))
if is_alarm_on == 0:
# alles uitschakelen
is_alarm_trigered = 0
led_red.value(0)
buzzer.duty(0)
oled.text(ingegeven_code, 45, 30)
oled.show()
led_green.value(1)
sleep(0.2)
led_green.value(0)
sleep(0.2)
led_green.value(1)
sleep(0.2)
led_green.value(0)
msg = b'Alarm uit'
client.publish(topic_pub, msg)
oled.fill(0)
oled.show()
ingegeven_code = ""
else:
oled.text("incorrect", 29,0)
print("incorrect")
oled.text(ingegeven_code, 45, 30)
oled.show()
oled.show()
ingegeven_code = ""
sleep(0.3)
last_key_press = keys[row][col]
And here it doesn't.
(I know some of the code might be mesy but the stuf about the temp sensor is on line 20-24 and 165-175)
Traceback (most recent call last):
File "main.py", line 169, in <module>
File "ds18x20.py", line 40, in read_temp
File "ds18x20.py", line 30, in read_scratch
Exception: CRC error
and this is the error i get.
i have tried connecting it to a different ESP32 and it gives the same.
I'm mostly confused beacouse it does work apart but not toggeter with the rest of my code.

ARM DTS query for TI based SoC

I am going through TI based evm dts file.
http://elixir.free-electrons.com/linux/latest/source/arch/arm/boot/dts/dra7-evm.dts#L686
&atl {
assigned-clocks = <&abe_dpll_sys_clk_mux>,
<&atl_gfclk_mux>,
<&dpll_abe_ck>,
<&dpll_abe_m2x2_ck>,
<&atl_clkin2_ck>;
assigned-clock-parents = <&sys_clkin2>, <&dpll_abe_m2_ck>;
assigned-clock-rates = <0>, <0>, <180633600>, <361267200>, <5644800>;
status = "okay";
atl2 {
bws = <DRA7_ATL_WS_MCASP2_FSX>;
aws = <DRA7_ATL_WS_MCASP3_FSX>;
};
};
&mcasp3 {
#sound-dai-cells = <0>;
assigned-clocks = <&mcasp3_ahclkx_mux>;
assigned-clock-parents = <&atl_clkin2_ck>;
status = "okay";
op-mode = <0>; /* MCASP_IIS_MODE */
tdm-slots = <2>;
/* 4 serializer */
serial-dir = < /* 0: INACTIVE, 1: TX, 2: RX */
1 2 0 0
>;
tx-num-evt = <32>;
rx-num-evt = <32>;
};
assigned-clocks contains a handle to <&mcasp3_ahclkx_mux> and this clock "&mcasp3_ahclkx_mux>" is defined in "dra7xx-clocks.dtsi"
http://elixir.free-electrons.com/linux/latest/source/arch/arm/boot/dts/dra7xx-clocks.dtsi#L1814
But could not find how this" dra7xx-clocks.dtsi" has included into "dra7-evm.dtsi" ?
Can any please point me out how definition of mcasp3_ahclkx_mux is provided into dra7-evm.dts ?
But could not find how this" dra7xx-clocks.dtsi" has included into "dra7-evm.dtsi" ?
dra7-evm.dts has included dra74x.dtsi which includes dra7.dtsi, and dra7xx-clocks.dtsi is included in dra7.dtsi at the end.

converting UTF-8 string to ASCII in pure LUA

I have a question about sending and receiving data with special chars. (German Umlauts)
When I send the string "Café Zeezicht" with the code below, then on the server-side the string is oke.
But how can I receive and decode the receiving data that containing the same chars? Now it look likes "Caf? Zeezicht"
I am searching for a pure LUA function, because I have no ability to load libraries.
------------------------------------------------------------
-- Function voor converting ASCII naar UTF8
------------------------------------------------------------
-- return char as utf8 string
local function CodeToUTF8 (Unicode)
if (Unicode == nil) then
return ""
end
if (Unicode < 0x20) then return ' '; end;
if (Unicode <= 0x7F) then return string.char(Unicode); end;
if (Unicode <= 0x7FF) then
local Byte0 = 0xC0 + math.floor(Unicode / 0x40);
local Byte1 = 0x80 + (Unicode % 0x40);
return string.char(Byte0, Byte1);
end;
if (Unicode <= 0xFFFF) then
local Byte0 = 0xE0 + math.floor(Unicode / 0x1000);
local Byte1 = 0x80 + (math.floor(Unicode / 0x40) % 0x40);
local Byte2 = 0x80 + (Unicode % 0x40);
return string.char(Byte0, Byte1, Byte2);
end;
return ""; -- ignore UTF-32 for the moment
end;
-- convert ascii string to utf8 string
function AsciiToUTF8(str)
result = ""
for i = 1, #str do
result = result .. CodeToUTF8(string.byte(str, i, i+1))
end
return result
end
------------------------------------------------------------
-- Einde Function voor converting ASCII naar UTF8
------------------------------------------------------------
local char, byte, pairs, floor = string.char, string.byte, pairs, math.floor
local table_insert, table_concat = table.insert, table.concat
local unpack = table.unpack or unpack
local function unicode_to_utf8(code)
-- converts numeric UTF code (U+code) to UTF-8 string
local t, h = {}, 128
while code >= h do
t[#t+1] = 128 + code%64
code = floor(code/64)
h = h > 32 and 32 or h/2
end
t[#t+1] = 256 - 2*h + code
return char(unpack(t)):reverse()
end
local function utf8_to_unicode(utf8str, pos)
-- pos = starting byte position inside input string (default 1)
pos = pos or 1
local code, size = utf8str:byte(pos), 1
if code >= 0xC0 and code < 0xFE then
local mask = 64
code = code - 128
repeat
local next_byte = utf8str:byte(pos + size) or 0
if next_byte >= 0x80 and next_byte < 0xC0 then
code, size = (code - mask - 2) * 64 + next_byte, size + 1
else
code, size = utf8str:byte(pos), 1
end
mask = mask * 32
until code < mask
end
-- returns code, number of bytes in this utf8 char
return code, size
end
local map_1252_to_unicode = {
[0x80] = 0x20AC,
[0x81] = 0x81,
[0x82] = 0x201A,
[0x83] = 0x0192,
[0x84] = 0x201E,
[0x85] = 0x2026,
[0x86] = 0x2020,
[0x87] = 0x2021,
[0x88] = 0x02C6,
[0x89] = 0x2030,
[0x8A] = 0x0160,
[0x8B] = 0x2039,
[0x8C] = 0x0152,
[0x8D] = 0x8D,
[0x8E] = 0x017D,
[0x8F] = 0x8F,
[0x90] = 0x90,
[0x91] = 0x2018,
[0x92] = 0x2019,
[0x93] = 0x201C,
[0x94] = 0x201D,
[0x95] = 0x2022,
[0x96] = 0x2013,
[0x97] = 0x2014,
[0x98] = 0x02DC,
[0x99] = 0x2122,
[0x9A] = 0x0161,
[0x9B] = 0x203A,
[0x9C] = 0x0153,
[0x9D] = 0x9D,
[0x9E] = 0x017E,
[0x9F] = 0x0178,
[0xA0] = 0x00A0,
[0xA1] = 0x00A1,
[0xA2] = 0x00A2,
[0xA3] = 0x00A3,
[0xA4] = 0x00A4,
[0xA5] = 0x00A5,
[0xA6] = 0x00A6,
[0xA7] = 0x00A7,
[0xA8] = 0x00A8,
[0xA9] = 0x00A9,
[0xAA] = 0x00AA,
[0xAB] = 0x00AB,
[0xAC] = 0x00AC,
[0xAD] = 0x00AD,
[0xAE] = 0x00AE,
[0xAF] = 0x00AF,
[0xB0] = 0x00B0,
[0xB1] = 0x00B1,
[0xB2] = 0x00B2,
[0xB3] = 0x00B3,
[0xB4] = 0x00B4,
[0xB5] = 0x00B5,
[0xB6] = 0x00B6,
[0xB7] = 0x00B7,
[0xB8] = 0x00B8,
[0xB9] = 0x00B9,
[0xBA] = 0x00BA,
[0xBB] = 0x00BB,
[0xBC] = 0x00BC,
[0xBD] = 0x00BD,
[0xBE] = 0x00BE,
[0xBF] = 0x00BF,
[0xC0] = 0x00C0,
[0xC1] = 0x00C1,
[0xC2] = 0x00C2,
[0xC3] = 0x00C3,
[0xC4] = 0x00C4,
[0xC5] = 0x00C5,
[0xC6] = 0x00C6,
[0xC7] = 0x00C7,
[0xC8] = 0x00C8,
[0xC9] = 0x00C9,
[0xCA] = 0x00CA,
[0xCB] = 0x00CB,
[0xCC] = 0x00CC,
[0xCD] = 0x00CD,
[0xCE] = 0x00CE,
[0xCF] = 0x00CF,
[0xD0] = 0x00D0,
[0xD1] = 0x00D1,
[0xD2] = 0x00D2,
[0xD3] = 0x00D3,
[0xD4] = 0x00D4,
[0xD5] = 0x00D5,
[0xD6] = 0x00D6,
[0xD7] = 0x00D7,
[0xD8] = 0x00D8,
[0xD9] = 0x00D9,
[0xDA] = 0x00DA,
[0xDB] = 0x00DB,
[0xDC] = 0x00DC,
[0xDD] = 0x00DD,
[0xDE] = 0x00DE,
[0xDF] = 0x00DF,
[0xE0] = 0x00E0,
[0xE1] = 0x00E1,
[0xE2] = 0x00E2,
[0xE3] = 0x00E3,
[0xE4] = 0x00E4,
[0xE5] = 0x00E5,
[0xE6] = 0x00E6,
[0xE7] = 0x00E7,
[0xE8] = 0x00E8,
[0xE9] = 0x00E9,
[0xEA] = 0x00EA,
[0xEB] = 0x00EB,
[0xEC] = 0x00EC,
[0xED] = 0x00ED,
[0xEE] = 0x00EE,
[0xEF] = 0x00EF,
[0xF0] = 0x00F0,
[0xF1] = 0x00F1,
[0xF2] = 0x00F2,
[0xF3] = 0x00F3,
[0xF4] = 0x00F4,
[0xF5] = 0x00F5,
[0xF6] = 0x00F6,
[0xF7] = 0x00F7,
[0xF8] = 0x00F8,
[0xF9] = 0x00F9,
[0xFA] = 0x00FA,
[0xFB] = 0x00FB,
[0xFC] = 0x00FC,
[0xFD] = 0x00FD,
[0xFE] = 0x00FE,
[0xFF] = 0x00FF,
}
local map_unicode_to_1252 = {}
for code1252, code in pairs(map_1252_to_unicode) do
map_unicode_to_1252[code] = code1252
end
function string.fromutf8(utf8str)
local pos, result_1252 = 1, {}
while pos <= #utf8str do
local code, size = utf8_to_unicode(utf8str, pos)
pos = pos + size
code = code < 128 and code or map_unicode_to_1252[code] or ('?'):byte()
table_insert(result_1252, char(code))
end
return table_concat(result_1252)
end
function string.toutf8(str1252)
local result_utf8 = {}
for pos = 1, #str1252 do
local code = str1252:byte(pos)
table_insert(result_utf8, unicode_to_utf8(map_1252_to_unicode[code] or code))
end
return table_concat(result_utf8)
end
Usage:
local str1252 = "1\128" -- "one euro" in latin-1
local str_utf8 = str1252:toutf8() -- "1\226\130\172" -- one euro in utf-8
local str1252_2 = str_utf8:fromutf8()

Can I sort a QPainter to be drawn on top on other widgets in PyQt4?

I'm doing a basic node editor with PyQt4. However I'm stuck with this: The lines linking nodes are drawn behind of the widgets when it should be in the reverse order.
Is there a way to force this or should I restart from scratch and try another approach?
The attacahed code should work and give you 2 nodes that you can move. The line linking them is drawn behind of everything instead of on top.
Thank you
from PyQt4 import QtGui
from PyQt4.QtCore import *
from PyQt4.QtGui import *
from PyQt4.QtCore import Qt
import sys
class Connector():
def __init__(self, name="", id=id, data=None, connected=None, isInput=True, index=-1, parent=None):
self.id = id
self.name = name
self.data = data
self.parent = parent
self.isConnected = not connected
self.connectedTo = connected
self.isInput = isInput
self.index = index
self.width = 16
self.height = 16
self.upperMargin = 30
def getAbsPosition(self):
return QPoint(self.parent.pos().x()+self.getPosition().x(), self.parent.pos().y() + self.getPosition().y())
def getPosition(self):
if self.parent:
if self.isInput:
deltaX = 0 + self.width / 2
else:
deltaX = self.parent.width() - self.width
deltaY = self.upperMargin + (self.index * self.height*2)
pos = QPoint(deltaX, deltaY)
else:
pos = QPoint(0, 0)
return pos
class Node(QLineEdit ):
def __init__(self,parent=None, name=""):
QLineEdit.__init__(self,parent)
self.size = QSize(200, 300)
self.setText(name)
self.resize(self.size.width(), self.size.height())
self.connectors = []
self.createInputConnector()
self.createOutputConnector()
def getConnectors(self):
return self.connectors
def getNewConnectorIndex(self):
indexInputs = 0
indexOutputs = 0
for connector in self.connectors:
if connector.isInput:
indexInputs += 1
else:
indexOutputs += 1
return indexInputs, indexOutputs
def createConnector(self, name, isInput, index):
newConnector = Connector(name=name, isInput=isInput, index=index, parent=self)
self.connectors.append(newConnector)
return newConnector
def createInputConnector(self, name=""):
index, ignore = self.getNewConnectorIndex()
newConnector = self.createConnector(name, isInput=True, index=index)
return newConnector
def createOutputConnector(self, name=""):
ignore, index = self.getNewConnectorIndex()
newConnector = self.createConnector(name, isInput=False, index=index)
return newConnector
def paintEvent(self, event):
painter = QPainter()
painter.begin(self)
painter.path = QPainterPath()
# disabling next line "solves" the problem but not really.
painter.setBrush(QColor(122, 163, 39))
painter.path.addRoundRect(2,2, self.size.width()-6,self.size.height()-6,20,15)
painter.drawPath(painter.path)
for connector in self.connectors:
pos = connector.getPosition()
cx = pos.x()
cy = pos.y()
cw = connector.width
ch = connector.height
painter.drawEllipse(cx, cy, cw, ch)
painter.end()
def mousePressEvent(self, event):
self.__mousePressPos = None
self.__mouseMovePos = None
if event.button() == Qt.LeftButton:
self.__mousePressPos = event.globalPos()
self.__mouseMovePos = event.globalPos()
super(Node, self).mousePressEvent(event)
def mouseMoveEvent(self, event):
if event.buttons() == Qt.LeftButton:
currPos = self.mapToGlobal(self.pos())
globalPos = event.globalPos()
diff = globalPos - self.__mouseMovePos
newPos = self.mapFromGlobal(currPos + diff)
self.move(newPos)
self.__mouseMovePos = globalPos
super(Node, self).mouseMoveEvent(event)
def mouseReleaseEvent(self, event):
if self.__mousePressPos is not None:
moved = event.globalPos() - self.__mousePressPos
if moved.manhattanLength() > 3:
event.ignore()
return
super(Node, self).mouseReleaseEvent(event)
class Window(QMainWindow):
def __init__(self):
QMainWindow.__init__(self)
self.resize(640,480)
self.entry1 = Node(self, "NODE1")
self.entry2 = Node(self, "NODE2")
def paintEvent(self, e):
qp = QtGui.QPainter()
qp.begin(self)
qp.setClipping(False)
self.doDrawing(qp)
qp.end()
self.update()
def doDrawing(self, qp):
p1 = self.entry1.getConnectors()[0].getAbsPosition()
p2 = self.entry2.getConnectors()[0].getAbsPosition()
qp.drawLine(p1.x(),p1.y(), p2.x(), p2.y())
if __name__=="__main__":
app=QApplication(sys.argv)
win=Window()
win.show()
sys.exit(app.exec_())

What's the best way to extract Excel cell comments using Perl or Ruby?

I've been parsing Excel documents in Perl successfully with Spreadhsheet::ParseExcel (as recommended in What's the best way to parse Excel file in Perl?), but I can't figure out how to extract cell comments.
Any ideas? A solution in Perl or Ruby would be ideal.
The Python xlrd library will parse cell comments (if you turn on xlrd.sheet.OBJ_MSO_DEBUG, you'll see them), but it doesn't expose them from the API. You could either parse the dump or hack on it a bit so you can get to them programmatically. Here's a start (tested extremely minimally):
diff --git a/xlrd/sheet.py b/xlrd/sheet.py
--- a/xlrd/sheet.py
+++ b/xlrd/sheet.py
## -206,6 +206,7 ##
self._dimncols = 0
self._cell_values = []
self._cell_types = []
+ self._cell_notes = []
self._cell_xf_indexes = []
self._need_fix_ragged_rows = 0
self.defcolwidth = None
## -252,6 +253,7 ##
return Cell(
self._cell_types[rowx][colx],
self._cell_values[rowx][colx],
+ self._cell_notes[rowx][colx],
xfx,
)
## -422,12 +424,14 ##
if self.formatting_info:
self._cell_xf_indexes[nrx].extend(aa('h', [-1]) * nextra)
self._cell_values[nrx].extend([''] * nextra)
+ self._cell_notes[nrx].extend([None] * nextra)
if nc > self.ncols:
self.ncols = nc
self._need_fix_ragged_rows = 1
if nr > self.nrows:
scta = self._cell_types.append
scva = self._cell_values.append
+ scna = self._cell_notes.append
scxa = self._cell_xf_indexes.append
fmt_info = self.formatting_info
xce = XL_CELL_EMPTY
## -436,6 +440,7 ##
for _unused in xrange(self.nrows, nr):
scta([xce] * nc)
scva([''] * nc)
+ scna([None] * nc)
if fmt_info:
scxa([-1] * nc)
else:
## -443,6 +448,7 ##
for _unused in xrange(self.nrows, nr):
scta(aa('B', [xce]) * nc)
scva([''] * nc)
+ scna([None] * nc)
if fmt_info:
scxa(aa('h', [-1]) * nc)
self.nrows = nr
## -454,6 +460,7 ##
aa = array_array
s_cell_types = self._cell_types
s_cell_values = self._cell_values
+ s_cell_notes = self._cell_notes
s_cell_xf_indexes = self._cell_xf_indexes
s_dont_use_array = self.dont_use_array
s_fmt_info = self.formatting_info
## -465,6 +472,7 ##
nextra = ncols - rlen
if nextra > 0:
s_cell_values[rowx][rlen:] = [''] * nextra
+ s_cell_notes[rowx][rlen:] = [None] * nextra
if s_dont_use_array:
trow[rlen:] = [xce] * nextra
if s_fmt_info:
## -600,6 +608,7 ##
bk_get_record_parts = bk.get_record_parts
bv = self.biff_version
fmt_info = self.formatting_info
+ txos = {}
eof_found = 0
while 1:
# if DEBUG: print "SHEET.READ: about to read from position %d" % bk._position
## -877,13 +886,23 ##
break
elif rc == XL_OBJ:
# handle SHEET-level objects; note there's a separate Book.handle_obj
- self.handle_obj(data)
+ obj = self.handle_obj(data)
+ if obj:
+ obj_id = obj.id
+ else:
+ obj_id = None
elif rc == XL_MSO_DRAWING:
self.handle_msodrawingetc(rc, data_len, data)
elif rc == XL_TXO:
- self.handle_txo(data)
+ txo = self.handle_txo(data)
+ if txo and obj_id:
+ txos[obj_id] = txo
+ obj_id = None
elif rc == XL_NOTE:
- self.handle_note(data)
+ note = self.handle_note(data)
+ txo = txos.get(note.object_id)
+ if txo:
+ self._cell_notes[note.rowx][note.colx] = txo.text
elif rc == XL_FEAT11:
self.handle_feat11(data)
elif rc in bofcodes: ##### EMBEDDED BOF #####
## -1387,19 +1406,16 ##
def handle_obj(self, data):
- if not OBJ_MSO_DEBUG:
- return
- DEBUG = 1
if self.biff_version < 80:
return
o = MSObj()
data_len = len(data)
pos = 0
- if DEBUG:
+ if OBJ_MSO_DEBUG:
fprintf(self.logfile, "... OBJ record ...\n")
while pos < data_len:
ft, cb = unpack('<HH', data[pos:pos+4])
- if DEBUG:
+ if OBJ_MSO_DEBUG:
hex_char_dump(data, pos, cb, base=0, fout=self.logfile)
if ft == 0x15: # ftCmo ... s/b first
assert pos == 0
## -1430,16 +1446,14 ##
else:
# didn't break out of while loop
assert pos == data_len
- if DEBUG:
+ if OBJ_MSO_DEBUG:
o.dump(self.logfile, header="=== MSOBj ===", footer= " ")
+ return o
def handle_note(self, data):
- if not OBJ_MSO_DEBUG:
- return
- DEBUG = 1
if self.biff_version < 80:
return
- if DEBUG:
+ if OBJ_MSO_DEBUG:
fprintf(self.logfile, '... NOTE record ...\n')
hex_char_dump(data, 0, len(data), base=0, fout=self.logfile)
o = MSNote()
## -1453,13 +1467,11 ##
o.original_author, endpos = unpack_unicode_update_pos(data, 8, lenlen=2)
assert endpos == data_len - 1
o.last_byte = data[-1]
- if DEBUG:
+ if OBJ_MSO_DEBUG:
o.dump(self.logfile, header="=== MSNote ===", footer= " ")
+ return o
def handle_txo(self, data):
- if not OBJ_MSO_DEBUG:
- return
- DEBUG = 1
if self.biff_version < 80:
return
o = MSTxo()
## -1477,8 +1489,9 ##
rc3, data3_len, data3 = self.book.get_record_parts()
assert rc3 == XL_CONTINUE
# ignore the formatting runs for the moment
- if DEBUG:
+ if OBJ_MSO_DEBUG:
o.dump(self.logfile, header="=== MSTxo ===", footer= " ")
+ return o
def handle_feat11(self, data):
if not OBJ_MSO_DEBUG:
## -1638,11 +1651,12 ##
class Cell(BaseObject):
- __slots__ = ['ctype', 'value', 'xf_index']
+ __slots__ = ['ctype', 'value', 'note', 'xf_index']
- def __init__(self, ctype, value, xf_index=None):
+ def __init__(self, ctype, value, note=None, xf_index=None):
self.ctype = ctype
self.value = value
+ self.note = note
self.xf_index = xf_index
def __repr__(self):
Then you could write something like:
import xlrd
xlrd.sheet.OBJ_MSO_DEBUG = True
xls = xlrd.open_workbook('foo.xls')
for sheet in xls.sheets():
print 'sheet %s (%d x %d)' % (sheet.name, sheet.nrows, sheet.ncols)
for rownum in xrange(sheet.nrows):
for cell in sheet.row(rownum):
print cell, cell.note
One option is to use Ruby's win32ole library.
The following (somewhat verbose) example connects to an open Excel worksheet and gets the comment text from cell B2.
require 'win32ole'
xl = WIN32OLE.connect('Excel.Application')
ws = xl.ActiveSheet
cell = ws.Range('B2')
comment = cell.Comment
text = comment.Text
More info and examples of using Ruby's win32ole library to automate Excel can be found here:
http://rubyonwindows.blogspot.com/search/label/excel

Resources