Inserting/overwriting a table after a paragraph (specific location) in a word file with docx python? - python
Does anybody know how to create/update a table with the docx library in a word document at a specific location.
So e.g. after a paragraph with text 'test1'?
The idea is to check if paragraph exists in the document, overwrite the existsing table underneath if not create a new paragraph and underneath at a certain location (at certain header level).
I managed to add a paragraph after a specific paragraph but it does not seem to work with tables in the same way.
I can not seem to find a link between the paragraph objects and creating a table object underneath that paragraph object or identifying the existing table object based on the paragraph.
A bit of context on the code, the code is first reading xaml files and writing this data in a word document. The first time the code is run it will create all subheadings and text/tables. With a second run the code will be updating the text/table values as the subheadings already exist.
e.g.
template:
02_BusinessProcess
00_Dispatcher
01_Initialization
after first run:
02_BusinessProcess
1.1 Xamlfilename
Text
Table
00_Dispatcher
2.1 Xamlfilenam
Text
01_Initialization
after second run:
02_BusinessProcess
1.1 Xamlfilename
Updated Text
Updated Table
00_Dispatcher
2.1 Xamlfilenam
Updated Text
01_Initialization
I want to add in the tables between these lines(newly created paragraphs).
paragraph1 = insert_paragraph_after(paragraph, xaml_obt.xamlfilename, style=document.styles['Heading 3'])
paragraph_annseq = insert_paragraph_after(paragraph1, xaml_obt.ann_seq, style=document.styles['No Spacing'])
paragraph_var = insert_paragraph_after(paragraph_annseq, "Variables - " + xaml_obt.xamlfilename,style=document.styles['Heading 4'])
paragraph_in_arg = insert_paragraph_after(paragraph_var, "In_Agruments - " + xaml_obt.xamlfilename, style=document.styles['Heading 4'])
paragraph_io_arg = insert_paragraph_after(paragraph_in_arg, "In_Out_Agruments - " + xaml_obt.xamlfilename, style=document.styles['Heading 4'])
insert_paragraph_after(paragraph_io_arg, "Out_Agruments - " + xaml_obt.xamlfilename, style=document.styles['Heading 4'])
Or update the table in this spot if the paragraph exists in the document:
if paragraph.style.name.startswith('Heading'):
if paragraph.text == xaml_obt.xamlfilename:
new_para = document.paragraphs[i + 1]
new_para.text = xaml_obt.ann_seq + "\n\n"
style = document.styles['No Spacing']
new_para.style = style
new_para.alignment = WD_ALIGN_PARAGRAPH.LEFT
Here is the complete code:
from tkinter import Tk, filedialog
import os
import json
from xml.etree import ElementTree as ET
import docx
from docx.oxml.xmlchemy import OxmlElement
from docx.text.paragraph import Paragraph
from docx.enum.text import WD_ALIGN_PARAGRAPH
import time
import win32com.client
import pandas as pd
class xamlinfo(object):
def __init__(self, name: object) -> object:
self.aut_block = str
self.xamlfilepath = str
self.xamlfilename = str
self.xaml_read = None
self.toplevelnaming = str
self.ann_seq = str
self.in_arguments = pd.DataFrame(columns=['Name', 'Type', 'Annotation'])
self.out_arguments = pd.DataFrame(columns=['Name', 'Type', 'Annotation'])
self.io_agruments = pd.DataFrame(columns=['Name', 'Type', 'Annotation'])
self.variables = pd.DataFrame(columns=['Name', 'Annotation'])
def selectfolder():
root = Tk() # pointing root to Tk() to use it as Tk() in program.
root.withdraw()
root.attributes('-topmost', True)
open_file = filedialog.askdirectory()
open_file = os.path.normpath(open_file)
print("Following filepath selected: ",open_file)
return open_file
def assignxamlobjects(listxamls, path):
if os.path.exists(path):
for root,dirs,files in os.walk(path):
for file in files:
xaml_obt = xamlinfo(os.path.basename(file))
xaml_obt.aut_block = os.path.basename(path)
xaml_obt.xamlfilename = file
xaml_obt.xamlfilepath = os.path.join(root,file)
tree = ET.parse(xaml_obt.xamlfilepath)
treeroot = tree.getroot()
xaml_obt.xaml_read = treeroot
top_sequence = treeroot.find(".//{*}Sequence")
xaml_obt.toplevelnaming = top_sequence.attrib["DisplayName"]
annotation = ""
annotationelements = [x for x in top_sequence.attrib if "annotationtext" in x.lower()]
if (len(annotationelements) > 0):
annotation = top_sequence.attrib[annotationelements[0]]
xaml_obt.ann_seq = annotation
listofelements = treeroot.findall(".//{*}Property")
for element in listofelements:
if "InArgument" in element.attrib["Type"]:
annotation = ""
annotationelements = [x for x in element.attrib if "annotationtext" in x.lower()]
if (len(annotationelements) > 0):
annotation = element.attrib[annotationelements[0]]
xaml_obt.in_arguments = xaml_obt.in_arguments.append({0:str(element.attrib["Name"]), 1:str(element.attrib["Type"]).replace("InArgument",""),2: annotation})
if "InOutArgument" in element.attrib["Type"]:
annotation = ""
annotationelements = [x for x in element.attrib if "annotationtext" in x.lower()]
if (len(annotationelements) > 0):
annotation = element.attrib[annotationelements[0]]
xaml_obt.io_agruments = xaml_obt.io_agruments.append({0:str(element.attrib["Name"]), 1:str(element.attrib["Type"]).replace("InOutArgument",""),2: annotation})
if "OutArgument" in element.attrib["Type"]:
annotation = ""
annotationelements = [x for x in element.attrib if "annotationtext" in x.lower()]
if (len(annotationelements) > 0):
annotation = element.attrib[annotationelements[0]]
xaml_obt.out_arguments = xaml_obt.out_arguments.append({0:str(element.attrib["Name"]), 1:str(element.attrib["Type"]).replace("OutArgument",""),2: annotation})
listofelements = treeroot.findall(".//{*}Variable")
for element in listofelements:
annotation = ""
annotationelements = [x for x in element.attrib if "annotationtext" in x.lower()]
if (len(annotationelements) > 0):
annotation = element.attrib[annotationelements[0]]
xaml_obt.variables = xaml_obt.variables.append({0:str(element.attrib["Name"]),1:annotation})
listxamls.append(xaml_obt)
else:
print("The following path does not exists, please amend your project structure: "+path)
return listxamls
def getworkflowinfo(openfile):
jsonpath = os.path.join(openfile,"project.json")
procestrans_path = os.path.join(openfile,"process","02_BusinessProcess")
dispatcher_path = os.path.join(openfile,"process","00_Dispatcher")
init_path = os.path.join(openfile,"process","01_Initialization")
process_path = os.path.join(openfile,"Process")
listxamls = []
listxamls = assignxamlobjects(listxamls, path=procestrans_path)
listxamls = assignxamlobjects(listxamls, path=dispatcher_path)
listxamls = assignxamlobjects(listxamls, path=init_path)
listxamls = assignxamlobjects(listxamls, path=process_path)
with open(jsonpath) as f:
uipathjson = json.load(f)
return uipathjson, listxamls
def insert_paragraph_after(paragraph, text, style):
new_p = OxmlElement('w:p')
paragraph._p.addnext(new_p)
new_para = Paragraph(new_p, paragraph._parent)
if text:
new_para.add_run(text)
if style is not None:
new_para.style = style
paragraph1 = new_para
return paragraph1
def fillxamldata(document, listofxamls):
print("Starting to update workflow information.")
for xaml_obt in listofxamls:
paraexists = False
for paragraph in document.paragraphs:
if paragraph.text == xaml_obt.xamlfilename:
paraexists = True
if paraexists is True:
for i, paragraph in enumerate(document.paragraphs):
# Check if the paragraph is a heading
if paragraph.style.name.startswith('Heading'):
if paragraph.text == xaml_obt.xamlfilename:
new_para = document.paragraphs[i + 1]
new_para.text = xaml_obt.ann_seq + "\n\n"
style = document.styles['No Spacing']
new_para.style = style
new_para.alignment = WD_ALIGN_PARAGRAPH.LEFT
else:
for paragraph in document.paragraphs:
# Check if the paragraph is a heading
if paragraph.style.name.startswith('Heading'):
if paragraph.text == xaml_obt.aut_block:
paragraph1 = insert_paragraph_after(paragraph, xaml_obt.xamlfilename, style=document.styles['Heading 3'])
paragraph_annseq = insert_paragraph_after(paragraph1, xaml_obt.ann_seq, style=document.styles['No Spacing'])
paragraph_var = insert_paragraph_after(paragraph_annseq, "Variables - " + xaml_obt.xamlfilename,style=document.styles['Heading 4'])
paragraph_in_arg = insert_paragraph_after(paragraph_var, "In_Agruments - " + xaml_obt.xamlfilename, style=document.styles['Heading 4'])
paragraph_io_arg = insert_paragraph_after(paragraph_in_arg, "In_Out_Agruments - " + xaml_obt.xamlfilename, style=document.styles['Heading 4'])
insert_paragraph_after(paragraph_io_arg, "Out_Agruments - " + xaml_obt.xamlfilename, style=document.styles['Heading 4'])
print("Workflow information updated successfully.\n")
return
def filldependencies(document, jsonUI):
print("Starting to fill dependencies.")
dict_depend = jsonUI['dependencies']
text = ""
for i in dict_depend:
text = text + i+": "+ dict_depend[i]+"\n"
for i, paragraph in enumerate(document.paragraphs): # Loop through all the paragraphs in the Word file
if paragraph.style.name.startswith('Heading'): # Check if the paragraph is a heading
if 'dependencies' == paragraph.text.lower():
new_para = document.paragraphs[i+1]
new_para.text = text
style = document.styles['No Spacing']
new_para.style = style
new_para.alignment = WD_ALIGN_PARAGRAPH.LEFT
print("Dependencies updated successfully.\n")
return
def fillgeneralinfo(document, jsonUI):
print("Starting to fill process info.")
text = ("Process name: "+"\t\t\t"+ jsonUI['name'] + "\n" +
"Process description:"+"\t\t" + jsonUI['description'] +"\n" +
"UIpath Studio version:"+"\t\t"+ jsonUI['studioVersion'] + "\n" +
"Project version:"+"\t\t\t" + jsonUI['projectVersion'] + "\n")
for i, paragraph in enumerate(document.paragraphs): # Loop through all the paragraphs in the Word file
if paragraph.style.name.startswith('Heading'): # Check if the paragraph is a heading
if 'general info' == paragraph.text.lower():
new_para = document.paragraphs[i+1]
new_para.text = text
style = document.styles['No Spacing']
new_para.style = style
new_para.alignment = WD_ALIGN_PARAGRAPH.LEFT
print("Process info successfully updated.\n")
return
def fillworddata(path, listofxamls):
print("You seleceted the following SDD file: "+path+"\n")
document = docx.Document(path)
with open(path, "w") as doc:
fillxamldata(document, listofxamls)
filldependencies(document, jsonUI)
fillgeneralinfo(document, jsonUI)
document.save(path)
return
def startmessage():
print("###############################################################\n"+
" SDD_AUT \n"+
"###############################################################\n")
starttimer = time.time()
startmessage()
openfile = selectfolder()
jsonUI, listxamls = getworkflowinfo(openfile)
correct_proc = input("The information for process | " + jsonUI['name'] + " | has been read.\n"+
"Do you want to continue? (y/n)\n")
if correct_proc.lower() == 'y':
sdd_doc = filedialog.askopenfilename(title='Select a file')
fillworddata(path=sdd_doc, listofxamls=listxamls)
print("Process has been executed successfully!")
else:
print("The process has been terminated as the incorrect project was selected.")
endtimer = time.time()
duration = endtimer - starttimer
print("Process took: " + str(duration))
Related
FPDF Python Package to Write PDF Files - Inserting extra line breaks. Does this for Multi_cell and cell. Want to remove extra line breaks
I have a python application that traverses directories on the file path passed in as a parameter. As each file is found, the file is opened and data is inserted into multi_cells. I have checked the TXT values provided to multi_cell and there are no extra line breaks. I have played around with different setting to no avail. When inspecting the PDF output, extra line breaks are inserted. Any help would be appreciated. Look at the end of the script --- the final multicell. import os,sys from fpdf import FPDF for i in range(1, len(sys.argv)): print('argument:', i, 'value:', sys.argv[i]) mypath = sys.argv[i]; mypath = "D:\\test\\test\\CrossingTrades\\DataDictionary\\testLib\\forms" class TOC(FPDF): def __init__(this, orientation='P',unit='mm',format='A4'): this._toc=[] this._numbering=0 this._numberingFooter=0 this._numPageNum=1 FPDF.__init__(this,orientation,unit,format) def header(self): self.set_font('Courier', 'B', 15); self.cell(0, 5, "Tradeflow Format and Rule Documentation", 0, 0, 'C') self.ln(20) def footer(self): self.set_y(-10) self.set_font('Arial', 'I', 8) # Add a page number page = 'Page ' + str(self.page_no()) + '/{nb}' self.cell(0, 10, page, 0, 0, 'C') def startPageNums(this): this._numbering=1 this._numberingFooter=1 def stopPageNums(this): this._numbering=0 def numPageNo(this): return this._numPageNum def TOC_Entry(this,txt,level,pageNumber): this._numPageNum=pageNumber this._toc+=[{'t':txt,'l':level,'p':this.numPageNo()}] #print("PAGE NO IS ",this.numPageNo()) def insertTOC(this,location=1,labelSize=20,entrySize=10,tocfont='Times',label='Table of Contents'): #make toc at end this.stopPageNums() #this.AddPage() this.add_page(); this._numPageNum+=1 tocstart=this.page this.set_font(tocfont,'B',labelSize) this.cell(0,5,label,0,1,'C') this.ln(10) for t in this._toc: #Offset level=t['l'] if(level>0): this.cell(level*8) weight='' if(level==0): weight='B' Str=t['t'] this.set_font(tocfont,weight,entrySize) strsize=this.get_string_width(Str) this.cell(strsize+2,this.font_size+2,Str) #Filling dots this.set_font(tocfont,'',entrySize) PageCellSize=this.get_string_width(str(t['p']))+2 w=this.w-this.l_margin-this.r_margin-PageCellSize-(level*8)-(strsize+2) #nb=w/this.get_string_width('.') dots = "........................................................................................................" #dots.replace('.', '.'*60, 1) this.cell(w,this.font_size+2,dots,0,0,'R') #Page number this.cell(PageCellSize,this.font_size+2,str(t['p']),0,1,'R') #grab it and move to selected location n=this.page n_toc = n - tocstart + 1 last = [] #store toc pages for i in range(tocstart,n+1): last+=[this.pages[i]] #move pages for i in range(tocstart-1,location-1,-1): #~ for(i=tocstart - 1;i>=location-1;i--) this.pages[i+n_toc]=this.pages[i] #Put toc pages at insert point for i in range(0,n_toc): this.pages[location + i]=last[i] #def Footer(this): # if(this._numberingFooter==0): # return # #Go to 1.5 cm from bottom # this.SetY(-15) # #Select Arial italic 8 # this.SetFont('Arial','I',8) # this.Cell(0,7,str(this.numPageNo()),0,0,'C'); # if(this._numbering==0): # this._numberingFooter=0 class DPLFormat: def __init__(self, name,arguments,contentsList,callerList,callingList): self.name = name self.arguments = arguments self.contentsList = contentsList self.callerList = callerList self.callingList = callingList class DPLRule: def __init__(self, name,arguments,contentsList,callerList,callingList): self.name = name self.arguments = arguments self.contentsList = contentsList self.callerList = callerList self.callingList = callingList def get_filepaths(directory): """ This function will generate the file names in a directory tree by walking the tree either top-down or bottom-up. For each directory in the tree rooted at directory top (including top itself), it yields a 3-tuple (dirpath, dirnames, filenames). """ file_paths = [] # List which will store all of the full filepaths. #print ("Opening:", directory) # Walk the tree. for root, directories, files in os.walk(directory): for filename in files: if ".cfformat" in filename and ".cfformatprop" not in filename: # Join the two strings in order to form the full filepath. filepath = os.path.join(root, filename) file_paths.append(filepath) # Add it to the list. #file_paths.append(filename) # Add it to the list. return file_paths # Self-explanatory. # PDF Handling # save FPDF() class into a # variable pdf pdf = TOC('P', 'mm', 'A4') pdf.alias_nb_pages() # Run the above function and store its results in a variable. full_file_paths = get_filepaths(mypath) formatList = [] tocList = [] print ("Beginning Processing ",mypath) for index, item in enumerate(full_file_paths): formatName = item.replace(".cfformat","") #print(index,"",formatName) formatArgs = "" ruleFlag = 1 contentsList = [] callerList = [] callingList = [] #Find format in files full_file_paths2 = get_filepaths(mypath) cnt = 0 for index2, item2 in enumerate(full_file_paths2): formatName2 = item2.replace(".cfformat","") #if cnt == 0: # print("Searching ",os.path.basename(formatName)," IN ",os.path.basename(formatName2)) with open(item2,'r') as fp2: line2 = fp2.readline() #print ("Opening ",os.path.basename(formatName2)) while line2: line2 = fp2.readline() if cnt == 0 : if os.path.basename(formatName)in line2 and os.path.basename(formatName) != os.path.basename(formatName2) : callerList.append(os.path.basename(formatName2)) cnt += 1 #---------------------END SEARCH FOR FORMAT IN OTHER FILES------------------ with open(item,'r') as fp: line = fp.readline() if "[" in line and "SQL" or ";" not in line: formatArgs = line ruleFlag = 0 cnt = 1 while line: line = fp.readline() #print("Line {}: {}".format(cnt, line.strip())) #if "!" in line: line = line.replace("–", "-") #res = bytes(line,'UTF-8') contentsList.append(line) if "#" in line: callingList.append(line) cnt += 1 if formatArgs != "": formatList.append( DPLFormat(os.path.basename(formatName),formatArgs,contentsList,callerList,callingList) ) #Now go through format files pdf.set_font("Courier", size = 8) formatList.sort(key=lambda x: x.name) pdf.startPageNums() for obj in formatList: #print( obj.name, obj.arguments,sep =' ' ) # Add a page pdf.add_page() pdf.TOC_Entry(obj.name,1,pdf.page_no()) # caller list pdf.set_font('Courier', 'B', size=13); pdf.cell(200, 10, txt = obj.name , ln = 1, align = 'C') ii = 0 pdf.set_font('Courier', 'I', size=10); pdf.cell(200, 10, txt = "Called By: " , ln = 1, align = 'L') callerStr="" #print ("Caller list length is ", len(obj.callerList)) while ii != len (obj.callerList): callerStr=callerStr+obj.callerList[ii] ii += 1 pdf.multi_cell(0, 8, callerStr, 1, 'J') #calling list pdf.set_font('Courier', 'I', size=10); ii = 0 pdf.cell(200, 10, txt = "Calling: " , ln = 1, align = 'L') callingStr="" #print ("Caller list length is ", len(obj.callerList)) while ii != len (obj.callingList): callingStr=callingStr+obj.callingList[ii] ii += 1 pdf.set_font('Courier',size=8); pdf.multi_cell(0, 8, callingStr, 1, 'J') #contents pdf.set_font('Courier',size=8); i = 0 codeStr = "" while i != len (obj.contentsList): codeStr=codeStr+obj.contentsList[i] i += 1 pdf.multi_cell(0, 8, codeStr, 1, 'J') # save the pdf with name .pdf print ("\nWriting PDF Documentation") pdf.insertTOC() pdf.output("D:\\DPLAutoDoc.pdf") print ("\nFinished")
The width in multi cell call, the first parameter is cell size or width. In portrait set that to something like 190. The second value actually controls spacing between lines so set to a value like 1 or 2. Set justification to left.
Python flask web API XML findall no attribute
import requests try: import xml.etree.cElementTree as et except ImportError: import xml.etree.ElementTree as et user_key = "CWB-22E1B400-6B6B-4E9E-8BBF-9D78666CF958" doc_name = "F-C0032-001" url = "https://opendata.cwb.gov.tw/api/v1/rest/datastore/%s?Authorization=%s&format=xml" % (doc_name,user_key) report = requests.get(url).text #print(report) xml_namespace = "{urn:cwb:gov:tw:cwbcommon:0.1}" root = et.fromstring(report) dataset = root.find(xml_namespace + 'dataset') locations_info = dataset.findall(xml_namespace + 'location') location = '高雄市' target_idx = -1 for idx,ele in enumerate(locations_info): locationName = ele[0].text if locationName == location: target_idx = idx break if target_idx != -1: show = '' tlist = ['天氣狀況', '最高溫', '最低溫', '舒適度', '降雨機率'] for i in range(5): element = locations_info[target_idx][i+1] timeblock = element[1] data = timeblock[2][0].text show = show + tlist[i] + ':' + data + '\n' print(show) else: print('無此縣市資料') in the Console it show File "D:\flask\weatherdata.py", line 16, in locations_info = dataset.findall(xml_namespace + 'location') AttributeError: 'NoneType' object has no attribute 'findall'
I can not iterate over Python-docx document in word file
LOOK AT THE PICTUREI have the following code that reads a word docx document and write a line on the top of the table in it. It iterates over the tables inside the document. This is the old version of the code. I want to iterate over all the elements (supposely paragraphs and tables together) and insert two pointer (one for paragraphs and one for tables) and iterate with pointers. I want to write something before the previous paragraph where the data came from every table. If we have 3 tables, I want to write a sentence for every table before the last seen paragraph while we traverse the all the word file. import docx import section as section from docx import Document from docx.section import _Header from docx.text.paragraph import Paragraph from docx.oxml.text.paragraph import CT_P from docx.enum.style import WD_STYLE_TYPE import re # Insert a empty line before the tables in the document. def insert_paragraph_before(item, text, style=None): """ Return a newly created paragraph, inserted directly before this item (Table, etc.). """ # CT_P.add_p_before(item._element) p = CT_P.add_p_before(item._element) # CT_P.add_p_before(item._element) p2 = Paragraph(p, item._parent) p2.text = text p2.style = styles["REQ"] return p2 FileName = 'Test' document = docx.Document(FileName + ".docx") styles = document.styles style = styles.add_style('REQ', WD_STYLE_TYPE.PARAGRAPH) # for table in document.tables: # # insert_paragraph_before(table, "" ) for table in document.tables: req_name = "" req_id = "" req_id_found = False counter = 0 for row in table.rows: for cell in row.cells: for paragraph in cell.paragraphs: syntax = re.compile(r'(UNIT_S-REQ|UNIT_S-DC|UNIT_S-DD|Information|MRS-REQ|MRS-DC|MRS-DD)') result = syntax.findall(paragraph.text) if result and req_id_found == False: req_id = table.cell(0, 1).text # req_id = str(result[0]) req_id_found = True txt = table.cell(2, 1).text # print(txt) if txt not in ["<name>"]: req_name = table.cell(2, 1).text for paragraph in document.paragraphs: if req_id_found == True: if counter != 0: continue if counter == 0: if req_id and req_name: document.add_heading(table, "ID-" + req_id + " - " + req_name) print(table, "ID-" + req_id + " - " + req_name) print(paragraph.text) elif req_id and not req_name: document.add_heading(table, "ID-" + req_id) print(table, "ID-" + req_id) print(paragraph.text) counter += 1 if counter == 0: continue if counter != 0: if req_id and req_name: document.add_paragraph(table, "ID-" + req_id + " - " + req_name) print(table, "ID-" + req_id + " - " + req_name) print(paragraph.text) elif req_id and not req_name: document.add_paragraph(table, "ID-" + req_id) print(table, "ID-" + req_id) print(paragraph.text) counter += 1 document.save(FileName + "_modified.docx")
python-docx how to merge row cells
I am creating a Word document from data using python-docx. I can create all the rows and cells with no problem but, in some cases, when the current record from the database has some content in field comment, I need to add a new line to display a long content. I tried by appending a paragraph, but the result is that the comment is appended after the table, and I need it to be added bellow the current table row. I think the solution is to append a table row with all cells merged, but I can't find documentation to do so. This is the code where I generate the docx file: class OperationDOCXView(viewsets.ViewSet): exclude_from_schema = True def list(self, request): from ReportsManagerApp.controllers import Operations2Controller self.profile_id = request.query_params['profile_id'] self.operation_date = request.query_params['operation_date'] self.operation_type = request.query_params['operation_type'] self.format = request.query_params['doc_format'] operation_report_controller = Operations2Controller(self.profile_id, self.operation_date, self.operation_type) context = operation_report_controller.get_context() if self.format == 'json': return Response(context) else: word_doc = self.get_operation_word_file(request, context) return Response("{}{}{}".format(request.get_host(), settings.MEDIA_URL, word_doc)) def get_operation_word_file(self, request, context): import unicodedata from django.core.files import File from django.urls import reverse from docx import Document from docx.shared import Inches, Pt operation_type = { 'arrival': 'Llegadas', 'departure': 'Salidas', 'hotel': 'Hotel-Hotel', 'tour': 'Tours', } weekdays = { '0': 'LUNES', '1': 'MARTES', '2': 'MIÉRCOLES', '3': 'JUEVES', '4': 'VIERNES', '5': 'SÁBADO', '6': 'DOMINGO', } titles = ['Booking', 'Nombre', '#', 'Vuelo', 'Hr', 'P Up', 'Traslado', 'Circuito', 'Priv?', 'Agencia', ''] widths = [Inches(1), Inches(2), Inches(0.5), Inches(1), Inches(1), Inches(1), Inches(2), Inches(3), Inches(0.5), Inches(3), Inches(0.5)] document = Document() section = document.sections[-1] section.top_margin = Inches(0.5) section.bottom_margin = Inches(0.5) section.left_margin = Inches(0.3) section.right_margin = Inches(0.2) style = document.styles['Normal'] font = style.font font.name ='Arial' font.size = Pt(10) company_paragraph = document.add_heading("XXXX TTOO INC") company_paragraph.alignment = WD_ALIGN_PARAGRAPH.CENTER description_paragraph = document.add_paragraph("Operación de {} del día {}".format(operation_type[self.operation_type], self.operation_date)) description_paragraph.alignment = WD_ALIGN_PARAGRAPH.CENTER operation_date = self.get_operation_date().date() operation_week_day = operation_date.weekday() day_paragraph = document.add_paragraph(weekdays[str(operation_week_day)]) day_paragraph.alignment = WD_ALIGN_PARAGRAPH.CENTER for provider_unit, transfers in context.items(): provider_unit_paragraph = document.add_paragraph(provider_unit) provider_unit_paragraph.style.font.size = Pt(10) provider_unit_paragraph.style.font.bold = False table = document.add_table(rows=1, cols=11) hdr_cells = table.rows[0].cells runs = [] for i in range(len(hdr_cells)): runs.append(self.get_hdr_cells_run(hdr_cells[i], titles[i])) for row in table.rows: for idx, width in enumerate(widths): row.cells[idx].width = width adults = 0 minors = 0 for transfer in transfers: # table = document.add_table(rows=1, cols=11) row_cells = table.add_row().cells row_cells[0].text = transfer['booking'] row_cells[1].text = transfer['people'] row_cells[2].text = transfer['pax'] flight = transfer.get("flight","") if transfer.get("flight","") is not None else "" row_cells[3].text = flight flight_time = self.get_flight_time(flight) if flight != '' else '' row_cells[4].text = flight_time row_cells[5].text = transfer['pickup_time'].strftime('%H:%M') if transfer['pickup_time'] is not None else '' row_cells[6].text = transfer['place'] row_cells[7].text = transfer['roundtrip'] row_cells[8].text = transfer['is_private'] row_cells[9].text = transfer['agency'] people = transfer['pax'].split('.') adults = adults + int(people[0]) minors = minors + int(people[1]) if transfer['comment'] is not None: document.add_paragraph("Comentarios: {}".format(transfer['comment'])) for row in table.rows: for idx, width in enumerate(widths): row.cells[idx].width = width for cell in row.cells: paragraphs = cell.paragraphs for paragraph in paragraphs: for run in paragraph.runs: font = run.font font.size = Pt(8) row_cells = table.add_row().cells row_cells[10].text = "{}.{}".format(adults, minors) current_directory = settings.MEDIA_DIR file_name = "Operaciones {} {}.docx".format(self.operation_type, self.operation_date) document.save("{}{}".format(current_directory, file_name)) return file_name def get_flight_time(self, flight): from OperationsManagerApp.models import Flight operation_types = { 'arrival': 'ARRIVAL', 'departure': 'DEPARTURE' } operation_date = datetime.strptime(self.operation_date, '%Y-%m-%d') try: flight = Flight.objects.get(flight_type=operation_types[self.operation_type], number=flight) except: return '' else: weekday_times = { '0': flight.time_monday, '1': flight.time_tuesday, '2': flight.time_wednesday, '3': flight.time_thursday, '4': flight.time_friday, '5': flight.time_saturday, '6': flight.time_sunday, } weekday_time = weekday_times[str(operation_date.weekday())] return weekday_time.strftime('%H:%M') if weekday_time is not None else '' def get_hdr_cells_run(self, hdr_cells, title): from docx.shared import Pt new_run = hdr_cells.paragraphs[0].add_run(title) new_run.bold = True new_run.font.size = Pt(8) return new_run def get_operation_date(self): date_array = self.operation_date.split('-') day = int(date_array[2]) month = int(date_array[1]) year = int(date_array[0]) operation_date = datetime(year, month, day) return operation_date
One approach is to add a paragraph to one of the cells: cell.add_paragraph(transfer['comment']) This will place it in the right position with respect to the row it belongs to, rather than after the table. If that would take too much room for a single cell that already has another data item in it and you want to add a row, you'll need to account for that when you allocate the table. But assuming you get that worked out, merging the cells is easy: row.cells[0].merge(row.cells[-1])
Python find function selects one match per line
I am trying to make a simple text editor using python. I am now trying to make a find function. This is what I've got: def Find(): text = textArea.get('1.0', END+'-1c').lower() input = simpledialog.askstring("Find", "Enter text to find...").lower() startindex = [] endindex = [] lines = 0 if input in text: text = textArea.get('1.0', END+'-1c').lower().splitlines() for var in text: character = text[lines].index(input) start = str(lines + 1) + '.' + str(character) startindex.append(start) end = str(lines + 1) + '.' + str(character + int(len(input))) endindex.append(end) textArea.tag_add('select', startindex[lines], endindex[lines]) lines += 1 textArea.tag_config('select', background = 'green') This will succesfully highlight words that match the users input with a green background. But the problem is, that it only highlights the first match every line, as you can see here. I want it to highlight all matches. Full code here: https://pastebin.com/BkuXN5pk
Recommend using the text widget's built-in search capability. Shown using python3. from tkinter import * root = Tk() textArea = Text(root) textArea.grid() textArea.tag_config('select', background = 'green') f = open('mouse.py', 'r') content = f.read() f.close() textArea.insert(END, content) def Find(input): start = 1.0 length = len(input) while 1: pos = textArea.search(input, start, END) if not pos: break end_tag = pos + '+' + str(length) + 'c' textArea.tag_add('select', pos, end_tag) start = pos + '+1c' Find('display') root.mainloop()