Using ffmpeg with Python 2.7 - ffmpeg
I have been trying to install pyffmpeg in Python 2.7 unsuccessfully. I found a package for Python 2.6, but I can't get it to work. So, I have been mulling around with 2.7. I've seen previous post from others on this site, but they have not helped. Does anyone have experience with this. Ultimately, I want to develop an wxPython app that converts video formats. Thanks
Code that I ultimately wrote that worked for me (very rudimentary, but it works ....):
import wx
import os
import sys
import time
import datetime
from wx.lib.delayedresult import startWorker
class dConvert(wx.Frame):
def __init__(self):
wx.Frame.__init__(self, None, -1, 'd-Converter', size=(500, 310))
panel = wx.Panel(self, wx.ID_ANY)#Creates a panel over the widget
toolbar = self.CreateToolBar()
toolbar.Realize()
#Setup Menu
#Setting up Menu
menuFile = wx.Menu()
menuFile.Append(1, "&About...")
menuFile.AppendSeparator()
menuFile.Append(2, "E&xit")
menuBar = wx.MenuBar()
menuBar.Append(menuFile, "&File")
panel.SetBackgroundColour('WHITE')
menu2 = wx.Menu()
menu2.Append(5, "&.mpg to dvd", ".mpg to dvd")
menu2.AppendSeparator()
menu2.Append(wx.NewId(), "&Options...", "Options...")
menuBar.Append(menu2, "&DVD")
menu3 = wx.Menu()
menu3.Append(7, "&Audio/Video Trim")
#menu3.AppendSeparator()
menuBar.Append(menu3, "Media")
self.SetMenuBar(menuBar)
self.Bind(wx.EVT_MENU, self.OnAbout, id=1)
self.Bind(wx.EVT_MENU, self.OnQuit, id=2)
self.Bind(wx.EVT_MENU, self.OnDVD, id=5)
self.Bind(wx.EVT_MENU, self.OnMedia, id=7)
#Add icon to frame
iconFile = "dconverter_image.jpg"
icon1 = wx.Icon(iconFile, wx.BITMAP_TYPE_JPEG)
self.SetIcon(icon1)
self.statusbar = self.CreateStatusBar()
self.statusbar.SetStatusText("Convert Audio & Video")
self.statusbar.SetFieldsCount(3)
self.statusbar.SetStatusWidths([200, -2, -2])
#Panel Text
font = wx.Font(10, wx.DEFAULT, wx.NORMAL, wx.BOLD)
font2 = wx.Font(7, wx.DEFAULT, wx.NORMAL, wx.BOLD)
directory = wx.StaticText(panel, -1, 'Path: c:\\ffmpeg\\bin', (300, 13))
directory.SetFont(font2)
convertfile = wx.StaticText(panel, -1, 'File:', (270, 53))
convertfile.SetFont(font)
convertfile2 = wx.StaticText(panel, -1, 'Format:', (245, 83))
convertfile2.SetFont(font)
convertfile3 = wx.StaticText(panel, -1, 'Quality:', (244, 113))
convertfile3.SetFont(font)
convertfile4 = wx.StaticText(panel, -1, 'Presets:', (239, 143))
convertfile4.SetFont(font)
image_file = 'cd_rom.gif'
bmp1 = wx.Image(image_file, wx.BITMAP_TYPE_ANY).ConvertToBitmap()
panel.bitmap1 = wx.StaticBitmap(panel, -1, bmp1, (50, 30))
self.formats1 = []
#Select Media path
os.chdir("c:\\ffmpeg\\bin")
wrkdir = os.getcwd()
filelist = os.listdir(wrkdir)
#self.formats1 = []
for filename in filelist:
(head, filename) = os.path.split(filename)
if filename.endswith(".avi") or filename.endswith(".mp4") or filename.endswith(".mpg") or filename.endswith(".m4A") or filename.endswith(".MTS") or filename.endswith(".flv") or filename.endswith(".mov") or filename.endswith(".mpeg4") or filename.endswith(".mpeg") or filename.endswith(".mpg2") or filename.endswith(".mkv") or filename.endswith(".m4v") or filename.endswith(".wav") or filename.endswith(".mp3"):
self.formats1.append(filename)
self.format_combo1=wx.ComboBox(panel, size=(140, -1),value='Select Media', choices=self.formats1, style=wx.CB_DROPDOWN, pos=(300,50))
self.Bind(wx.EVT_COMBOBOX, self.fileFormats, self.format_combo1)
#Media Formats
self.formats2 = ['Select Format', '.avi','.mpeg','.mp4','.flv','.mov','.m4a','.m4v','.mkv','.mpeg4','.mpg','.mpg2','.mp3','.ogg','.wav','.wma']
self.format_combo2=wx.ComboBox(panel, size=(100, -1),value='Select Format', choices=self.formats2, style=wx.CB_SORT, pos=(300,81))
#Media Quality
self.formats3 = ['-sameq','-qmax']
self.format_combo3=wx.ComboBox(panel, size=(100, -1),value='Select Quality', choices=self.formats3, style=wx.CB_DROPDOWN, pos=(300,111))
#-qmax settings
self.formats4 = ['1','2','3','4','5','6','7','8']
self.format_combo4=wx.ComboBox(panel, size=(30, -1),value='0', choices=self.formats4, style=wx.CB_DROPDOWN, pos=(405,111))
self.format_combo4.Disable()
#Media Quality
self.formats5 = ['Select Preset','video to mp3']
self.format_combo5=wx.ComboBox(panel, size=(100, -1),value='Select Preset', choices=self.formats5, style=wx.CB_DROPDOWN, pos=(300,141))
#Bit rate
self.formats6 = ['128000', '160000', '180000', '192000']
self.format_combo6=wx.ComboBox(panel, size=(47, -1),value='k/bs', choices=self.formats6, style=wx.CB_DROPDOWN, pos=(405,141))
self.format_combo6.Disable()
#Convert Button
self.button = wx.Button(panel, label="Convert", pos=(300, 171), size=(80, 20))
self.Bind(wx.EVT_BUTTON, self.convertButton, self.button)
#Abort Button
self.button2 = wx.Button(panel, label="Abort", pos=(385, 171), size=(80, 20))
self.Bind(wx.EVT_BUTTON, self.OnAbortButton, self.button2)
self.button2.Disable()
#Refresh Button
self.button3 = wx.Button(panel, label="Refresh", pos=(215, 171), size=(80, 20))
self.Bind(wx.EVT_BUTTON, self.file_refresh, self.button3)
#ComboBox Event
self.Bind(wx.EVT_COMBOBOX, self.OncomboBox, self.format_combo3)
self.Bind(wx.EVT_COMBOBOX, self.OncomboBox2, self.format_combo5)
self.Bind(wx.EVT_COMBOBOX, self.OncomboBox3, self.format_combo2)
def file_refresh(self, e):
self.format_combo1.Clear()
os.chdir("c:\\ffmpeg\\bin")
wrkdir = os.getcwd()
filelist = os.listdir(wrkdir)
for m_files in filelist:
if m_files.endswith(".avi") or m_files.endswith(".mp4") or m_files.endswith(".mpg") or m_files.endswith(".m4A") or m_files.endswith(".MTS") or m_files.endswith(".flv") or m_files.endswith(".mov") or m_files.endswith(".mpeg4") or m_files.endswith(".mpeg") or m_files.endswith(".mpg2") or m_files.endswith(".mkv") or m_files.endswith(".m4v") or m_files.endswith(".wav") or m_files.endswith(".mp3"):
self.format_combo1.Append(m_files)
def file_rename(self, f_name):
ts = time.time()
#Capture readable timestamp
st = datetime.datetime.fromtimestamp(ts).strftime('%Y-%m-%d_%H-%M-%S')
os.chdir("c:\\ffmpeg\\bin")
wrkdir = os.getcwd()
#get file extenstion from original file
#fileName, fileExtension = os.path.splitext(wrkdir + '\\' + f_name)
#add file extension to timestamp
new_file = st
return new_file
def fileFormats(self, e):
myFormats = {'audio': ('Select Format', '.m4a', '.mp3', '.ogg', '.wav', '.wma'), 'video': ('Select Format', '.avi', '.flv', '.mkv', '.m4v', '.mov', '.mpg', '.mpg2', '.mpeg4', '.mp4', '.mpeg')}
bad_file = ['Media not supported']
myFile = self.format_combo1.GetValue()
f_exten = (x for x in myFormats['audio'] + myFormats['video'] if myFile.endswith(x))
extension = f_exten.next()
if extension in myFormats['audio']:
self.format_combo2.SetItems(myFormats['audio'])
elif extension in myFormats['video']:
self.format_combo2.SetItems(myFormats['video'])
else:
self.format_combo2.SetItems(bad_file)
def OnQuit(self, event):
self.Close(True)
def OnAbout(self, event):
wx.MessageBox("d-Converter 1.0\n\n Developer: D.Monroe\n\nCopyright 2012",
"About d-Converter", wx.OK | wx.ICON_INFORMATION, self)
def OncomboBox(self, e):
quality=self.format_combo3.GetValue()
if quality == '-qmax':
self.format_combo4.Enable()
else:
self.format_combo4.Disable()
def OncomboBox2(self, e):
quality=self.format_combo5.GetValue()
if quality != 'Select Preset':
self.format_combo1.Enable()
self.format_combo2.Disable()
self.format_combo3.Disable()
self.format_combo4.Disable()
self.format_combo6.Enable()
elif quality == 'Select Preset':
self.format_combo1.Enable()
self.format_combo2.Enable()
self.format_combo3.Enable()
self.format_combo4.Disable()
self.format_combo5.Enable()
self.format_combo6.Disable()
elif quality == 'video to mp3':
self.format_combo6.Enable()
self.format_combo2.Disable()
self.format_combo3.Disable()
self.format_combo4.Disable()
def OncomboBox3(self, e):
v_format=self.format_combo2.GetValue()
if v_format != 'Select Format':
self.format_combo1.Enable()
self.format_combo2.Enable()
self.format_combo3.Enable()
self.format_combo4.Enable()
self.format_combo5.Disable()
self.format_combo6.Disable()
elif v_format == 'Select Format':
self.format_combo1.Enable()
self.format_combo2.Enable()
self.format_combo3.Enable()
self.format_combo4.Disable()
self.format_combo5.Enable()
self.format_combo6.Disable()
def OnMedia(self, e):
pass
def OnDVD(self, e):
""" Select a directory to search"""
os.chdir("c:\\ffmpeg\\bin")
wrkdir = os.getcwd()
filelist = os.listdir(wrkdir)
progdir = 'c:\\ffmpeg\\bin\\ffmpeg.exe' + ' -i '
prog_dir = ' -target '
progdir3 = '-dvd -ac 2 '
vid_format = '.mpg'
sampleList = []
for filename in filelist:
(head, filename) = os.path.split(filename)
if filename.endswith(".avi") or filename.endswith(".flv") or filename.endswith(".mpeg") or filename.endswith(".mp4") or filename.endswith(".mov") or filename.endswith(".mpg2"):
sampleList.append(filename)
dlg = wx.SingleChoiceDialog(
self, "Files in c:\\ffmpeg\\bin", 'Select video to convert',
sampleList,
wx.CHOICEDLG_STYLE
)
if dlg.ShowModal() == wx.ID_OK:
cur_item = dlg.GetStringSelection()
s_string = cur_item
#f_string = self.file_rename(s_string)
f_string = s_string.replace(' ', '')
dlg.Destroy()
dlg2 = wx.SingleChoiceDialog(
self, "Files in c:\\ffmpeg\\bin", 'Select video standard ',
["pal", "ntsc"],
wx.CHOICEDLG_STYLE
)
if dlg2.ShowModal() == wx.ID_OK:
cur_item2 = dlg2.GetStringSelection()
s_string2 = cur_item2
self.button.Disable()
self.button2.Enable()
self.format_combo1.Disable()
self.format_combo2.Disable()
self.format_combo3.Disable()
self.format_combo4.Disable()
self.format_combo5.Disable()
self.format_combo6.Disable()
startWorker(self.LongTaskDone, self.LongTask4, wargs=(progdir, wrkdir, prog_dir, progdir3, f_string, s_string2, vid_format))
dlg2.Destroy()
def convertButton(self, e):
unit1 = self.format_combo1.GetValue()
unit2 = self.format_combo2.GetValue()
unit3 = self.format_combo3.GetValue()
unit4 = None
unit5 = self.format_combo5.GetValue()
bitRate = self.format_combo6.GetValue()
unit6 = bitRate
if unit3 == '-qmax':
unit4 = self.format_combo4.GetValue()
else:
pass
os.chdir("c:\\ffmpeg\\bin")
wrkdir = os.getcwd()
newfile = unit1
#stripped = os.path.splitext(newfile)[0] # Updated 9/26/2013 to strip extension.
#stripped = newfile.strip('mpeg3kaviovfl4w2c.') #Strips the extension from the original file name
stripped = self.file_rename(newfile)
#os.rename(newfile, newfile_f)
progname='c:\\ffmpeg\\bin\\ffmpeg.exe' + ' -i '
preset1_a='-vn -ar 44100 -ac 2 -ab'
preset1_b='-f mp3 '
preset_mp3='.mp3'
if (unit1 == 'Select Media' or unit1 == ''):
amsg = wx.MessageDialog(None, 'You must select a media file!', 'Media Converter', wx.ICON_INFORMATION)
amsg.ShowModal()
amsg.Destroy()
elif (unit1 != 'Select Media' or unit1 != '') and (unit5 == 'Select Preset'):
if (unit2 == 'Select Format' or unit2 == ''):
amsg = wx.MessageDialog(None, 'You must select a format', 'Media Converter', wx.ICON_INFORMATION)
amsg.ShowModal()
amsg.Destroy()
self.format_combo3.Enable()
self.format_combo4.Enable()
self.format_combo5.Enable()
else:
pass
if (unit3 == 'Select Quality' or unit3 == ''):
amsg = wx.MessageDialog(None, 'You must select quality', 'Media Converter', wx.ICON_INFORMATION)
amsg.ShowModal()
amsg.Destroy()
elif (unit3 == '-qmax'):
if (unit4 == '0' or unit4 == ''):
amsg = wx.MessageDialog(None, 'You must select number between 1-8.', 'Media Converter', wx.ICON_INFORMATION)
amsg.ShowModal()
amsg.Destroy()
else:
self.button.Disable()
self.button2.Enable()
pass
else:
self.button.Disable()
self.button2.Enable()
self.format_combo1.Disable()
self.format_combo2.Disable()
self.format_combo3.Disable()
self.format_combo4.Disable()
startWorker(self.LongTaskDone, self.LongTask, wargs=(progname,wrkdir,unit1,unit3,stripped,unit2))
elif (unit1 != 'Select Media' or unit1 != '') and (unit5 == 'video to mp3'):
if unit6 == 'k/bs' or unit6 == None:
amsg = wx.MessageDialog(None, 'You must select a bit rate.', 'Media Converter', wx.ICON_INFORMATION)
amsg.ShowModal()
amsg.Destroy()
else:
self.button.Disable()
self.button2.Enable()
self.format_combo1.Disable()
self.format_combo2.Disable()
self.format_combo3.Disable()
self.format_combo4.Disable()
self.format_combo5.Disable()
self.format_combo6.Disable()
startWorker(self.LongTaskDone, self.LongTask3, wargs=(progname, wrkdir, unit1, preset1_a, unit6, preset1_b, stripped, preset_mp3))
def LongTask(self, progname, wrkdir, unit1, unit3, stripped, unit2):
convert_file1 = progname + wrkdir + '\\' + unit1 + ' ' + unit3 + ' ' + stripped + unit2
self.statusbar.SetStatusText("Converting: " + unit1 + "...")
os.system(convert_file1)
print convert_file1
def LongTask2(self, progname, wrkdir, unit1, unit3, unit4, stripped, unit2):
convert_file2 = progname + wrkdir + '\\' + unit1 + ' ' + unit3 + ' ' + unit4 + ' ' + stripped + unit2
self.statusbar.SetStatusText("Converting: " + unit1 + "...")
os.system(convert_file2)
def LongTask3(self, progname, wrkdir, unit1, preset1_a, unit6, preset1_b, stripped, preset_mp3):
convert_file3 = progname + wrkdir + '\\' + unit1 + ' ' + preset1_a + ' ' + unit6 + ' ' + preset1_b + stripped + preset_mp3
self.statusbar.SetStatusText("Converting: " + unit1 + "...")
os.system(convert_file3)
def LongTask4(self, progdir, wrkdir, prog_dir, progdir3, f_string, s_string2, vid_format):
#convert_file4 = progdir + wrkdir + '\\' + s_string + prog_dir + s_string2 + progdir3 + s_string.strip('mpegaviw24ofl.') + vid_format
convert_file4 = progdir + f_string + prog_dir + s_string2 + progdir3 + f_string.strip('mpegaviw24ofl.') + vid_format
self.statusbar.SetStatusText("Converting: " + f_string + "...")
os.system(convert_file4)
print convert_file4
def LongTaskDone(self, result):
r = result.get()
if r:
amsg = wx.MessageDialog(None, 'Aborted!', 'Media Converter', wx.ICON_INFORMATION)
self.statusbar.SetStatusText("Convert Aborted ...")
amsg.ShowModal()
amsg.Destroy()
self.LongTask.terminate()
else:
self.statusbar.SetStatusText("Done ...")
emsg = wx.MessageDialog(None, 'Finished Converting!', 'Media Converter', wx.ICON_INFORMATION)
emsg.ShowModal()
emsg.Destroy()
self.format_combo1.Enable()
self.format_combo2.Enable()
self.format_combo3.Enable()
self.format_combo5.Enable()
self.format_combo4.Disable()
self.format_combo6.Disable()
self.button.Enable()
self.button2.Disable()
self.shouldAbort = False
"""self.progress_bar.SetValue(0)
self.progress_bar.Hide()"""
def OnAbortButton(self, e):
endprogram = 'c:\\Windows\\System32\\taskkill /IM cmd.exe'
os.system(endprogram)
self.format_combo1.Enable()
self.format_combo2.Enable()
self.format_combo3.Enable()
self.format_combo5.Enable()
self.button.Enable()
if __name__ == '__main__':
app = wx.PySimpleApp()
frame = dConvert()
frame.SetSizeHints(500,310,500,310)
frame.Show()
app.MainLoop()
Since this has no proper answer, I'd like to form one. Unfortunately pyffmpeg wrapper was not able to perform video encoding. So as a solution I suggest you some alternative python wrappers which is capable of our need to encode the videos.
Python Video Converter
FFmpegWrapper
FFVideo
Apart from these you can use full FFmpeg functionality by using python subprocess module to execute full FFmpeg commands. Also with that you can create your own wrapper if you need to. Here Zulko describes a nice way of using subprocess with FFmpeg. Sometimes you will get some confusing events like this when using subprocess. But always there is a solution!
Hope this helps some one with same problem using pyffmpeg to encode videos!
Related
Slow filtering of tableview - QSortFilterProxyModel - QAbstractTableModel
I'm working on an app which displays some member info in a table view. I have a filter function to search in the table: ![image of the ui file] The content of the table loads really fast but the filtering is extremely slow. Here is the code: class PandasTableModel(QtCore.QAbstractTableModel): def __init__(self, data): super(PandasTableModel, self).__init__() self._data = data a=os.getcwd() self.icons_folder = a + "/assets/icons/" def data(self, index, role): # value = self._data.iloc[index.row()][index.column()] column_value = index.column() if role == Qt.DisplayRole: return self._data.iloc[index.row()][index.column()] return str(value) if role == Qt.DisplayRole: if isinstance(value, datetime): return value.strftime("%d-%m-%Y") else: return str(value) if role == Qt.DecorationRole: # if isinstance(value, datetime): if column_value == 12 or column_value == 23 or column_value == 25: return QtGui.QIcon(self.icons_folder + "calendar.png") elif column_value == 14: return QtGui.QIcon(self.icons_folder + "iphone-icon.png") elif column_value == 13 or column_value == 15: return QtGui.QIcon(self.icons_folder + "icon-phone.png") elif column_value == 16: return QtGui.QIcon(self.icons_folder + "email-icon.png") def rowCount(self, index): return self._data.shape[0] def columnCount(self, index): return self._data.shape[1] def headerData(self, section, orientation, role): # section is the index of the column/row. if role == Qt.DisplayRole: if orientation == Qt.Horizontal: return str(self._data.columns[section]) if orientation == Qt.Vertical: return str(self._data.index[section]) class MembersAdminWindow(QtWidgets.QMainWindow): def __init__(self): super(MembersAdminWindow, self).__init__() self.selectionModel = None self.ui = loadUi("uidesign/members/members2.ui", self) self.default_pic = str(os.getcwd()) + "/assets/clipart/muaythai.png" def LoadMemberdata(self): self.ui.lblMemberID.clear() self.ui.txtNickName.clear() self.ui.txtName.clear() self.ui.txtFirstName.clear() self.ui.txtStreet.clear() self.ui.txtHouseNumber.clear() self.ui.txtCity.clear() self.ui.txtPostalCode.clear() self.ui.txtCountry.clear() self.ui.txtNationality.clear() self.ui.txtLanguage.clear() self.ui.txtGender.clear() self.ui.txtBirthDate.clear() self.ui.txtFixedNumber.clear() self.ui.txtMobileNumber.clear() self.ui.txtBusinessNumber.clear() self.ui.txtEmail.clear() self.ui.txtPassword.clear() self.ui.scrbMemberLevel.setValue(0) self.ui.txtSportsType.clear() self.ui.txtStatusMember.clear() self.ui.txtDoctor.clear() self.ui.txtSubscriptionType.clear() self.ui.txtMemberStart.clear() self.ui.txtInsuranceStart.clear() self.ui.txtMemberInfo.clear() self.ui.txtSaldo.clear() self.ui.txtSaldo.hide() self.ui.lblSaldo.hide() self.ui.lblMemberCard.setPixmap(QtGui.QPixmap(self.default_pic)) start = timeit.default_timer() print('Start LoadMemberdata\n') self.ui.tableMembers.setEnabled(True) folder = os.getcwd() master_file = str(folder) + "/modules/ssh/downloads/master.pkl" Member_data = pd.read_pickle(master_file) df = pd.DataFrame(Member_data) df.fillna('',inplace=True) aantal_gebruikers = df.shape[0] print(aantal_gebruikers, 'gebruikers zijn ingelezen') self.model = PandasTableModel(df) self.proxy_model = QSortFilterProxyModel() self.proxy_model.setSourceModel(self.model) self.ui.inputFilter.setText("") self.ui.inputFilter.textChanged.connect(self.SetFilteredView) filter_result = self.proxy_model.rowCount() self.ui.lblTotalMembers.setText("(" + str(filter_result) + " gebruikers)") self.ui.cmdNewMember.setEnabled(True) self.ui.cmdSaveMember.setEnabled(False) self.ui.cmdCancel.setEnabled(False) self.ui.cmdChangeMember.setEnabled(False) self.ui.cmdTakeMemberPicture.setEnabled(False) self.ui.cmdMemberFaceId.setEnabled(False) self.ui.lblSaldo.setEnabled(False) self.ui.txtSaldo.setEnabled(False) self.ui.lblSaldo.hide() self.ui.txtSaldo.hide() self.ui.lblMemberCard.setPixmap(QtGui.QPixmap(self.default_pic)) self.ui.groupMembersData.setEnabled(False) print('\nEnd LoadMemberdata') end = timeit.default_timer() print("Process Time: ", (end - start)) def SetFilteredView(self): print("Start set_filter") filter_text = self.ui.inputFilter.text() rows = self.ui.tableMembers.model().rowCount() for row in range(rows): self.filter_row(row, filter_text) # self.proxy_model.setFilterFixedString(filter_text) # filter_result = self.proxy_model.rowCount() self.ui.lblTotalMembers.setText("(" + str(rows) + " gebruikers)") print("Stop set_filter") def filter_row(self, row, pattern): print("Start filter_row") if not pattern: self.ui.tableMembers.setRowHidden(row, False) return model = self.ui.tableMembers.model() columns = model.columnCount() stringlist = [] # collect text from all columns into single string for searching for c in range(columns): mdx = model.index(row, c) if mdx.isValid(): val = str(mdx.data(role=QtCore.Qt.DisplayRole)).lower() stringlist.append(val) # search for string patterns = filter(None, [x.lower() for x in pattern.split(' ')]) results = all(any(x in y for y in stringlist) for x in patterns) if results: self.ui.tableMembers.setRowHidden(row, False) else: self.ui.tableMembers.setRowHidden(row, True) print("Stop filter_row") i noticed when i type in the filter text field that the function 'filter_row' is called about 2000 times so i think i have to look for the solution here ;-) Can anyone give a hand ? Cheers John
ZPL print Italic Bold and Underline
I need to print ZPL with dynamic content. I Need your help. I am dynamic content , please help me. is this word possible to print. Note the content is dynamic. ZPL Code please.
If you want to type bold you can use this ^FO340,128^FDbold^FS ^FO339,128^FDbold^FS Another option (External fonts usage for underline, italic and bold) http://labelary.com/docs.html
There is no simple way to bold or italicize text withing ZPL. The fonts the printer has are very basic and can't be changed like that.
Complex font settings (italic, bold, serif ) are actually sent as compressed images to ZPL printers (you can check this with ZebraDesigner). The format is called Z64, which is based on LZ77. These two pages contain interesting code in Java to write a converter : http://www.jcgonzalez.com/img-to-zpl-online https://gist.github.com/trevarj/1255e5cbc08fb3f79c3f255e25989a18 ...still I'm not sure whether the CRC part of the conversion will remain the same in the future, as this is probably vendor-dependent. Here is a Python port of the first script : import cv2 import base64 import matplotlib.pyplot as plt import io import numpy blacklimit=int(50* 768/100) compress=False total=0 width_byte=0 mapCode = dict() LOCAL_PATH="C://DEV//printer//zebra_debug.txt" ''' class StringBuilder(object): def __init__(self): self._stringio = io.StringIO() def __str__(self): return self._stringio.getvalue() def getvalue(self): return self._stringio.getvalue() def append(self, *objects, sep=' ', end=''): print(*objects, sep=sep, end=end, file=self._stringio) ''' def init_map_code(): global mapCode mapCode[1] = "G" mapCode[2] = "H" mapCode[3] = "I" mapCode[4] = "J" mapCode[5] = "K" mapCode[6] = "L" mapCode[7] = "M" mapCode[8] = "N" mapCode[9] = "O" mapCode[10] = "P" mapCode[11] = "Q" mapCode[12] = "R" mapCode[13] = "S" mapCode[14] = "T" mapCode[15] = "U" mapCode[16] = "V" mapCode[17] = "W" mapCode[18] = "X" mapCode[19] = "Y" mapCode[20] = "g" mapCode[40] = "h" mapCode[60] = "i" mapCode[80] = "j" mapCode[100] = "k" mapCode[120] = "l" mapCode[140] = "m" mapCode[160] = "n" mapCode[180] = "o" mapCode[200] = "p" mapCode[220] = "q" mapCode[240] = "r" mapCode[260] = "s" mapCode[280] = "t" mapCode[300] = "u" mapCode[320] = "v" mapCode[340] = "w" mapCode[360] = "x" mapCode[380] = "y" mapCode[400] = "z" def numberToBase(n, b): if n == 0: return [0] digits = [] while n: digits.append(int(n % b)) n //= b return digits[::-1] def four_byte_binary(binary_str): decimal=int(binary_str, 2) if decimal>15: returned=hex(decimal).upper() returned=returned[2:] else: #returned=hex(decimal).upper()+"0" returned=hex(decimal).upper() if binary_str!="00000000": print("cut="+returned) returned=returned[2:] returned="0"+returned if binary_str!="00000000": print("low10="+returned) # if binary_str!="00000000": print(binary_str+"\t"+str(decimal)+"\t"+returned+"\t") return returned def createBody(img): global blacklimit global width_byte global total height, width, colmap = img.shape print(height) print(width) print(colmap) rgb = 0 index=0 aux_binary_char=['0', '0', '0', '0', '0', '0', '0', '0'] sb=[] if(width%8>0): width_byte=int((width/8)+1) else: width_byte=width/8 total=width_byte*height print(height) print("\n") print(width) print("\n") i=0 for h in range(0, height): for w in range(0, width): color = img[h,w] #print(color) #print(w) blue=color[0] green=color[1] red=color[2] blue=blue & 0xFF green=green & 0xFF red=red & 0xFF """ blue=np.uint8(blue) green=np.unit8(green) red=np.unit8(red) """ #print(bin(blue)) auxchar='1' total_color=red+green+blue if(total_color> blacklimit): #print('above_black_limit') auxchar='0' aux_binary_char[index]=auxchar index=index+1 if(index==8 or w==(width-1)): if "".join(aux_binary_char) !="00000000": print(i) sb.append(four_byte_binary("".join(aux_binary_char))) i=i+1 aux_binary_char=['0', '0', '0', '0', '0', '0', '0', '0'] index=0 #print(h) sb.append("\n") #print(sb) print(blacklimit) return ''.join(sb) def encode_hex_ascii(code): global width_byte global mapCode max_linea=width_byte*2 sb_code=[] sb_linea=[] previous_line=1 counter=1 aux = code[0] first_char=False for i in range(1, len(code)): if(first_char): aux=code[i] first_char=False continue if(code[i]=="\n"): if(counter>= max_linea and aux=='0'): sb_linea.append(",") elif(counter>= max_linea and aux=='F'): sb_linea.append("!") elif(counter>20): multi20=int((counter/20))*20 resto20=counter%20 sb_linea.append(mapCode[multi20]) if(resto20!=0): sb_linea.append(mapCode[resto20] +aux) else: sb_linea.append(aux) else: sb_linea.append(mapCode[counter] +aux) counter=1 first_char=True if(''.join(sb_linea)==previous_line): sb_code.append(":") else: sb_code.append(''.join(sb_linea)) previous_line=''.join(sb_linea) sb_linea=[] continue if aux==code[i]: counter=counter+1 else: if counter>20: multi20=int((counter/20))*20 resto20=counter%20 sb_linea.append(mapCode[multi20]) if resto20!=0: sb_linea.append(mapCode[resto20] + aux) else: sb_linea.append(aux) else: sb_linea.append(mapCode[counter] + aux) counter=1 aux=code[i] return ''.join(sb_code) def head_doc(): global total global width_byte return "^XA " + "^FO0,0^GFA,"+ str(int(total)) + ","+ str(int(total)) + "," + str(int(width_byte)) +", " def foot_doc(): return "^FS"+ "^XZ" def process(img): global compress init_map_code() cuerpo=createBody(img) print("CUERPO\n") print(cuerpo) print("\n") if compress: cuerpo=encode_hex_ascii(cuerpo) print("COMPRESS\n") print(cuerpo) print("\n") return head_doc() + cuerpo + foot_doc() img = cv2.imread("C:\\Users\\ftheeten\\Pictures\\out.jpg", cv2.IMREAD_COLOR ) compress=True blacklimit ==int(50* 768/100) test=process(img) file=open(LOCAL_PATH, 'w') file.write(test) file.close()
The Image will not Show in a Loop, Why not?
(Skip to hash-tags if in a hurry) This program will only work if it ends on the image showing. I want to use it as a function inside another looping program, but it will not work. It will display the stats of the Pokemon(p.whatever), but the image will not show. The image will show in IDLE Python 3.4, but not the terminal. I've been stuck on this for months. Here is the program that works(in IDLE Python 3.4, not the terminal): import pykemon print('What are you looking for?') askedpokemon = input() pokemonInDatabase = False while pokemonInDatabase == False: pokemonInDatabase = True try: if ('1' in askedpokemon) or ('2' in askedpokemon) or ('3' in askedpokemon) or ('4' in askedpokemon) or ('5' in askedpokemon) or ('6' in askedpokemon) or ('7' in askedpokemon) or ('8' in askedpokemon) or ('9' in askedpokemon): p = (pykemon.get(pokemon_id = askedpokemon)) else: askedpokemon = askedpokemon.lower() p = (pykemon.get(pokemon = askedpokemon)) #Turns askedpokemon into number askedpokemon = p.resource_uri askedpokemon = askedpokemon.replace('/api/v1/pokemon/',' ') askedpokemon = askedpokemon.replace('/',' ') askedpokemon = askedpokemon.strip() except pykemon.exceptions.ResourceNotFoundError: print(askedpokemon + " is not a valid Pokemon name or id number.") print('Try another') askedpokemon = input() pokemonInDatabase = False print (p) pTypes = (p.types) for key, value in pTypes.items() : pTypes = str(key) print (' Type: ' + pTypes) print (' HP: ' + str(p.hp)) print (' Attack: ' + str(p.attack)) print ('Defense: ' + str(p.defense)) print (' Sp Atk: ' + str(p.sp_atk)) print (' Sp Def: ' + str(p.sp_def)) print (' Speed: ' + str(p.speed)) print ('Exp Yield: ' + str(p.exp)) ####################################################### import time import urllib import urllib.request import tkinter as tk root = tk.Tk() url = "http://assets22.pokemon.com/assets/cms2/img/pokedex/full/526.png" if len(askedpokemon) < 3: if len(askedpokemon) == 2: askedpokemon = ('0' + askedpokemon) if len(askedpokemon) == 1: askedpokemon = ('00' + askedpokemon) url = url.replace('526', askedpokemon) u = urllib.request.urlopen(url) raw_data = u.read() u.close() import base64 b64_data = base64.encodestring(raw_data) image = tk.PhotoImage(data=b64_data) label = tk.Label(image=image) label.pack() ########################################################## Below is the working program with its modules. https://drive.google.com/file/d/0B3Q4wQpL0nDUYWFFSjV3cUhXVWc/view?usp=sharing
Here is an mcve that illustrates the problem. Call the file tem.py. import tkinter as tk root = tk.Tk() image = tk.PhotoImage(file='python.png') label = tk.Label(image=image) label.pack() When you run in a terminal, this runs, but the root window closes after label.pack(), before you can see it. Either put root.mainloop() at the end of the code or run with python -i tem.py (as IDLE, in effect, does). The -i says to switch from batch to interactive mode after the end of the program instead of closing. IDLE does this so one can interact with the live program before it is closed.
Python compiled code crashes
I made this game in python 2.7 and it worked in .py format but when I compiled it with py2exe it suddenly broke. It gave me this error: "Microsoft Visuak C++ Runtime Library Runtime Error! Program C:\Python27\Helmetdodger\Stuffz\main.exe This application has requested the Runtime to terminate it in an unusual way. Please contact the program's support team." The code: import pygame import random, sys, os from pygame.locals import * WINDOWWIDTH = 1200 WINDOWHEIGHT = 900 TEXTCOLOR = (255, 255, 255) BACKGROUNDCOLOR = (0, 0, 0) FPS = 40 BADDIEMINSIZE = 10 BADDIEMAXSIZE = 40 BADDIEMINSPEED = 1 BADDIEMAXSPEED = 8 ADDNEWBADDIERATE = 6 PLAYERMOVERATE = 5 def terminate(): pygame.quit() os.exit(1) def waitForPlayerToPressKey(): while True: for event in pygame.event.get(): if event.type == QUIT: terminate() if event.type == KEYDOWN: if event.key == K_ESCAPE: # pressing escape quits terminate() return def playerHasHitBaddie(playerRect, baddies): for b in baddies: if playerRect.colliderect(b['rect']): return True return False def drawText(text, font, surface, x, y): textobj = font.render(text, 1, TEXTCOLOR) textrect = textobj.get_rect() textrect.topleft = (x, y) surface.blit(textobj, textrect) # set up pygame, the window, and the mouse cursor pygame.init() mainClock = pygame.time.Clock() windowSurface = pygame.display.set_mode((WINDOWWIDTH, WINDOWHEIGHT)) pygame.display.set_caption('Helmetdodger') pygame.mouse.set_visible(False) # set up fonts font = pygame.font.SysFont(None, 48) # set up sounds gameOverSound = pygame.mixer.Sound('gameover.wav') pygame.mixer.music.load('background.mid') # set up images playerImage1 = pygame.image.load('player1.png') powerImage = pygame.image.load('power.png') playerRect = playerImage1.get_rect() baddieImage1 = pygame.image.load('baddie.png') baddieImage2 = pygame.image.load('baddie1.png') baddieImage3 = pygame.image.load('baddie2.png') baddieImage4 = pygame.image.load('baddie3.png') baddieImage5 = pygame.image.load('baddie4.png') baddieImage6 = pygame.image.load('baddie5.png') baddieImage7 = pygame.image.load('baddie6.png') baddieImage8 = pygame.image.load('baddie7.png') baddieImage9 = pygame.image.load('baddie8.png') baddieImage10 = pygame.image.load('baddie9.png') baddieImage11 = pygame.image.load('baddie10.png') baddieImage12 = pygame.image.load('baddie11.png') baddieImage13 = pygame.image.load('baddie12.png') baddieImage14 = pygame.image.load('baddie13.png') baddieImage15 = pygame.image.load('baddie14.png') baddieImage16 = pygame.image.load('baddie15.png') baddieImage17 = pygame.image.load('baddie16.png') baddieImage18 = pygame.image.load('baddie17.png') baddieImage19 = pygame.image.load('baddie18.png') baddieImage20 = pygame.image.load('baddie19.png') baddieImage21 = pygame.image.load('baddie20.png') baddieImage22 = pygame.image.load('baddie21.png') baddieImage23 = pygame.image.load('baddie22.png') baddieImage24 = pygame.image.load('baddie23.png') baddieImage25 = pygame.image.load('baddie24.png') baddieImage26 = pygame.image.load('baddie25.png') baddieImage27 = pygame.image.load('baddie26.png') baddieImage28 = pygame.image.load('baddie27.png') baddieImage29 = pygame.image.load('baddie28.png') baddieImage30 = pygame.image.load('baddie29.png') baddieImages = [baddieImage1, baddieImage2, baddieImage3, baddieImage4, baddieImage5, baddieImage6, baddieImage7, baddieImage8, baddieImage9, baddieImage10, baddieImage11, baddieImage12, baddieImage13, baddieImage14, baddieImage15, baddieImage16, baddieImage17, baddieImage18, baddieImage19, baddieImage20, baddieImage21, baddieImage22, baddieImage23, baddieImage24, baddieImage25, baddieImage26, baddieImage27, baddieImage28, baddieImage29, baddieImage20] # show the "Start" screen drawText('Helmetdodger', font, windowSurface, (WINDOWWIDTH / 3), (WINDOWHEIGHT / 3)) drawText('Press a key to start.', font, windowSurface, (WINDOWWIDTH / 3) - 30, (WINDOWHEIGHT / 3) + 50) pygame.display.update() waitForPlayerToPressKey() #Get highscore topScore = 0 try: file = open('hs.txt', "r") topScore = file.read() topScore = int(topScore) file.close() except: topScore = 0 while True: # set up the start of the game baddies = [] score = 0 playerRect.topleft = (WINDOWWIDTH / 2, WINDOWHEIGHT - 50) moveLeft = moveRight = moveUp = moveDown = False reverseCheat = slowCheat = False baddieAddCounter = 0 pygame.mixer.music.play(-1, 0.0) powerCount = 0 while True: # the game loop runs while the game part is playing score += 1 # increase score for event in pygame.event.get(): if event.type == QUIT: terminate() if event.type == KEYDOWN: if event.key == ord('z'): reverseCheat = True if event.key == ord('x'): slowCheat = True if event.key == K_LEFT or event.key == ord('a'): moveRight = False moveLeft = True if event.key == K_RIGHT or event.key == ord('d'): moveLeft = False moveRight = True if event.key == K_UP or event.key == ord('w'): moveDown = False moveUp = True if event.key == K_DOWN or event.key == ord('s'): moveUp = False moveDown = True if event.type == KEYUP: if event.key == ord('z'): reverseCheat = False score = 0 if event.key == ord('x'): slowCheat = False score = 0 if event.key == K_ESCAPE: terminate() if event.key == K_LEFT or event.key == ord('a'): moveLeft = False if event.key == K_RIGHT or event.key == ord('d'): moveRight = False if event.key == K_UP or event.key == ord('w'): moveUp = False if event.key == K_DOWN or event.key == ord('s'): moveDown = False if event.type == MOUSEMOTION: # If the mouse moves, move the player where the cursor is. playerRect.move_ip(event.pos[0] - playerRect.centerx, event.pos[1] - playerRect.centery) # Add new baddies at the top of the screen, if needed. if not reverseCheat and not slowCheat: baddieAddCounter += 1 if baddieAddCounter == ADDNEWBADDIERATE: baddieCount = random.randrange(len(baddieImages)) baddieAddCounter = 0 baddieSize = random.randint(BADDIEMINSIZE, BADDIEMAXSIZE) newBaddie = {'rect': pygame.Rect(random.randint(0, WINDOWWIDTH-baddieSize), 0 - baddieSize, baddieSize, baddieSize), 'speed': random.randint(BADDIEMINSPEED, BADDIEMAXSPEED), 'surface':pygame.transform.scale(baddieImages[baddieCount], (baddieSize, baddieSize)), } baddies.append(newBaddie) # Move the player around. if moveLeft and playerRect.left > 0: playerRect.move_ip(-1 * PLAYERMOVERATE, 0) if moveRight and playerRect.right < WINDOWWIDTH: playerRect.move_ip(PLAYERMOVERATE, 0) if moveUp and playerRect.top > 0: playerRect.move_ip(0, -1 * PLAYERMOVERATE) if moveDown and playerRect.bottom < WINDOWHEIGHT: playerRect.move_ip(0, PLAYERMOVERATE) # Move the mouse cursor to match the player. pygame.mouse.set_pos(playerRect.centerx, playerRect.centery) # Move the baddies down. for b in baddies: if not reverseCheat and not slowCheat: b['rect'].move_ip(0, b['speed']) elif reverseCheat: b['rect'].move_ip(0, -5) elif slowCheat: b['rect'].move_ip(0, 1) # Delete baddies that have fallen past the bottom. for b in baddies[:]: if b['rect'].top > WINDOWHEIGHT: baddies.remove(b) # Draw the game world on the window. windowSurface.fill(BACKGROUNDCOLOR) # Draw the score and top score. drawText('Score: %s' % (score), font, windowSurface, 10, 0) drawText('Top Score: %s' % (topScore), font, windowSurface, 10, 40) # Draw the player's rectangle windowSurface.blit(playerImage1, playerRect) # Draw each baddie for b in baddies: windowSurface.blit(b['surface'], b['rect']) pygame.display.update() # Check if any of the baddies have hit the player. if playerHasHitBaddie(playerRect, baddies): if score > topScore: topScore = score # set new top score file = open("hs.txt", "w") score = str(score) file.write(score) file.close() break mainClock.tick(FPS) # Stop the game and show the "Game Over" screen. pygame.mixer.music.stop() gameOverSound.play() drawText('GAME OVER', font, windowSurface, (WINDOWWIDTH / 3), (WINDOWHEIGHT / 3)) drawText('Press a key to play again.', font, windowSurface, (WINDOWWIDTH / 3) - 80, (WINDOWHEIGHT / 3) + 50) pygame.display.update() waitForPlayerToPressKey() gameOverSound.stop()
My buildscript was incorrect, I found a better buildscript online to use instead try: from distutils.core import setup import py2exe, pygame from modulefinder import Module import glob, fnmatch import sys, os, shutil import operator except ImportError, message: raise SystemExit, "Unable to load module. %s" % message #hack which fixes the pygame mixer and pygame font origIsSystemDLL = py2exe.build_exe.isSystemDLL # save the orginal before we edit it def isSystemDLL(pathname): # checks if the freetype and ogg dll files are being included if os.path.basename(pathname).lower() in ("libfreetype-6.dll", "libogg-0.dll","sdl_ttf.dll"): # "sdl_ttf.dll" added by arit. return 0 return origIsSystemDLL(pathname) # return the orginal function py2exe.build_exe.isSystemDLL = isSystemDLL # override the default function with this one class pygame2exe(py2exe.build_exe.py2exe): #This hack make sure that pygame default font is copied: no need to modify code for specifying default font def copy_extensions(self, extensions): #Get pygame default font pygamedir = os.path.split(pygame.base.__file__)[0] pygame_default_font = os.path.join(pygamedir, pygame.font.get_default_font()) #Add font to list of extension to be copied extensions.append(Module("pygame.font", pygame_default_font)) py2exe.build_exe.py2exe.copy_extensions(self, extensions) class BuildExe: def __init__(self): #Name of starting + .py self.script = "raw.py" #Name of program self.project_name = "main" #Project url self.project_url = "about:none" #Version of program self.project_version = "0.0" #License of the program self.license = "MyApps License" #Auhor of program self.author_name = "Me" self.author_email = "example#example.com" self.copyright = "Copyright (c) 2009 Me." #Description self.project_description = "MyApps Description" #Icon file (None will use pygame default icon) self.icon_file = None #Extra files/dirs copied to game self.extra_datas = [] #Extra/excludes python modules self.extra_modules = [] self.exclude_modules = [] #DLL Excludes self.exclude_dll = [''] #python scripts (strings) to be included, seperated by a comma self.extra_scripts = [] #Zip file name (None will bundle files in exe instead of zip file) self.zipfile_name = None #Dist directory self.dist_dir ='C:\Python27\dist' ## Code from DistUtils tutorial at http://wiki.python.org/moin/Distutils/Tutorial ## Originally borrowed from wxPython's setup and config files def opj(self, *args): path = os.path.join(*args) return os.path.normpath(path) def find_data_files(self, srcdir, *wildcards, **kw): # get a list of all files under the srcdir matching wildcards, # returned in a format to be used for install_data def walk_helper(arg, dirname, files): if '.svn' in dirname: return names = [] lst, wildcards = arg for wc in wildcards: wc_name = self.opj(dirname, wc) for f in files: filename = self.opj(dirname, f) if fnmatch.fnmatch(filename, wc_name) and not os.path.isdir(filename): names.append(filename) if names: lst.append( (dirname, names ) ) file_list = [] recursive = kw.get('recursive', True) if recursive: os.path.walk(srcdir, walk_helper, (file_list, wildcards)) else: walk_helper((file_list, wildcards), srcdir, [os.path.basename(f) for f in glob.glob(self.opj(srcdir, '*'))]) return file_list def run(self): if os.path.isdir(self.dist_dir): #Erase previous destination dir shutil.rmtree(self.dist_dir) #Use the default pygame icon, if none given if self.icon_file == None: path = os.path.split(pygame.__file__)[0] self.icon_file = os.path.join(path, 'pygame.ico') #List all data files to add extra_datas = [] for data in self.extra_datas: if os.path.isdir(data): extra_datas.extend(self.find_data_files(data, '*')) else: extra_datas.append(('.', [data])) setup( cmdclass = {'py2exe': pygame2exe}, version = self.project_version, description = self.project_description, name = self.project_name, url = self.project_url, author = self.author_name, author_email = self.author_email, license = self.license, # targets to build windows = [{ 'script': self.script, 'icon_resources': [(0, self.icon_file)], 'copyright': self.copyright }], options = {'py2exe': {'optimize': 2, 'bundle_files': 2, 'compressed': True, \ 'excludes': self.exclude_modules, 'packages': self.extra_modules, \ 'dll_excludes': self.exclude_dll, 'includes': self.extra_scripts} }, zipfile = self.zipfile_name, data_files = extra_datas, dist_dir = self.dist_dir ) if os.path.isdir('build'): #Clean up build dir shutil.rmtree('build') if __name__ == '__main__': if operator.lt(len(sys.argv), 2): sys.argv.append('py2exe') BuildExe().run() #Run generation raw_input("Press any key to continue") #Pause to let user see that things ends
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