Python-pptx formatting datalabels after adding text - python

As I needed to add custom text to a chart data labels in python-pptx I used
for point in plot.series[1].points:
frame = point.data_label.text_frame
frame.text = "Test "
for run in frame.paragraphs[0].runs:
run.font.size = Pt(10)
run.font.color.rgb = RGBColor(0x0A, 0x42, 0x80)
This allowed me to change the font to the labels but I would need to rotate them.
I saw the solution from This other thread but it does not work. Any ideas?

I believe you need to change the txPr (as in the thread you mention) but on the respective element child:
for all_series in range(0, len(plot.series)):
for i in range(0, len(plot.series[all_series]._element)):
if re.sub("{.*}", '', plot.series[all_series]._element[i].tag) == >"dLbls":
txPr = plot.series[all_series]._element[i].get_or_add_txPr()
txPr.bodyPr.set('rot', '-5400000')
So you need to access the "dLbls" child within the correct element index, the "[i]" is the difference to the mentioned thread. I use regular expressions to derive the name from the ".tag"-method. There is probably a better way to find the correct index, but this is at least one ;)
This solution iterates over all series, if you do not need this you can skip the first loop (assuming you want only the 1-series):
for i in range(0, len(plot.series[1]._element)):
if re.sub("{.*}", '', plot.series[1]._element[i].tag) == "dLbls":
txPr = plot.series[1]._element[i].get_or_add_txPr()
txPr.bodyPr.set('rot', '-5400000')
Note, that this only applies to data labels which were set on the single point, so e.g. through
plot.series[1].points[0].data_label.text_frame.text = "Foo"
Hopefully this helps :)

You can try this custom function, feed in your plot variable, and other style properties as you needed. If we go at point level for data label then this wont work as we think, and below can help to achieve.
def apply_data_labels(plot,font,fontsize,numformat,rotate = False):
plot.has_data_labels = True
data_labels = plot.data_labels
data_labels.font.size = Pt(fontsize)
data_labels.font.name = font
if rotate == True:
data_labels._element.get_or_add_txPr().bodyPr.set('rot','-5400000')
data_labels.number_format = numformat
and call this function with your plot variable for example.
apply_data_labels(chart.plots[0],'Invention',10,'#,##0;[Red]#,##0',rotate = True)

Related

Changing row-height and column-width in LibreOffice Calc using Python3

I want to write a LibreOffice Calc document from within a Python3 program. Using pyoo I can do almost everything I want, including formatting and merging cells. But I cannot adjust row heights and column widths.
I found Change the column width and row height very helpful, and have been experimenting with it, but I can't seem to get quite the result I want. My present test file, based on the answer mentioned above, looks like this:
#! /usr/bin/python3
import os, pyoo, time, uno
s = '-'
while s != 'Y':
s = input("Have you remembered to start Calc? ").upper()
os.popen("soffice --accept=\"socket,host=localhost,port=2002;urp;\" --norestore --nologo --nodefault")
time.sleep(2)
desktop = pyoo.Desktop('localhost', 2002)
doc = desktop.create_spreadsheet()
class ofic:
sheet_idx = 0
row_num = 0
sheet = None
o = ofic()
uno_localContext = uno.getComponentContext()
uno_resolver = uno_localContext.ServiceManager.createInstanceWithContext("com.sun.star.bridge.UnoUrlResolver", uno_localContext )
uno_ctx = uno_resolver.resolve( "uno:socket,host=localhost,port=2002;urp;StarOffice.ComponentContext" )
uno_smgr = uno_ctx.ServiceManager
uno_desktop = uno_smgr.createInstanceWithContext( "com.sun.star.frame.Desktop", uno_ctx)
uno_model = uno_desktop.getCurrentComponent()
uno_controller = uno_model.getCurrentController()
uno_sheet_count = 0
doc.sheets.create("Page {}".format(1), index=o.sheet_idx)
o.sheet = doc.sheets[o.sheet_idx]
o.sheet[0, 0].value = "The quick brown fox jumps over the lazy dog"
o.sheet[1, 1].value = o.sheet_idx
uno_controller.setActiveSheet(uno_model.Sheets.getByIndex(uno_sheet_count))
uno_sheet_count += 1
uno_active_sheet = uno_model.CurrentController.ActiveSheet
uno_columns = uno_active_sheet.getColumns()
uno_column = uno_columns.getByName("B")
uno_column.Width = 1000
The main problem with the above is that I have 2 Calc documents on the screen, one of which is created before the Python program gets going; the other is created from Python with a pyoo function. The first document gets the column width change, and the second receives the text input etc. I want just the second document, and of course I want the column width change applied to it.
I am sure the answer must be fairly straightforward, but after hours of experimentation I still can't find it. Could someone point me in the right direction, please?
Your code alternates between pyoo and straight Python-UNO, so it's no wonder that it's giving messy results. Pick one or the other. Personally, I use straight Python-UNO and don't see the benefit of adding the extra pyoo library.
the other is created from Python with a pyoo function
Do you mean this line of code from your question, and is this the "second document" that you want the column change applied to?
doc = desktop.create_spreadsheet()
If so, then get objects from that document instead of whichever window the desktop happens to have selected.
controller = doc.getCurrentController()
sheets = doc.getSheets()
Or perhaps you want the other document, the one that didn't get created from Python. In that case, grab a reference to that document before creating the second one.
first_doc = uno_desktop.getCurrentComponent()
second_doc = desktop.create_spreadsheet()
controller = first_doc.getCurrentController()
sheets = first_doc.getSheets()
If you don't have a reference to the document, you can find it by iterating through the open windows.
oComponents = desktop.getComponents()
oDocs = oComponents.createEnumeration()
Finally, how to resize a column. The link in your question is for Excel and VBA (both from Microsoft), so I'm not sure why you think that would be relevant. Here is a Python-UNO example of resizing columns.
oColumns = oSheet.getColumns()
oColumn = oColumns.getByName("A")
oColumn.Width = 7000
oColumn = oColumns.getByName("B")
oColumn.OptimalWidth = True

execute multiple variable functions(var_1,var_2,var_3)

I got another little question...
I want to make multiple variables which I create with 'setattr'
That works quite fine. It creates these variables:
self.sectionButton_1 = Button(text=x)
self.sectionButton_2 = Button(text=x)
self.sectionButton_3 = Button(text=x)
Now I want them to get displayed on the window with tkinter so that this should happen:
self.sectionButton_1.grid(row=i, column=0)
self.sectionButton_2.grid(row=i, column=0)
and so on..
But how do I have to edit the loop that the sectionButtons gonna be created with .grid from tkinter in a loop without writing the above ten times.
# Display Section selection
def checkSection(self):
# Read all sections from config
self.sections = config.sections()
self.sectionsCount = str(len(self.sections))
self.i = 0
self.text = Label(text="Choose Section:" + self.sectionsCount)
self.text.grid(row=1, column=0)
for x in self.sections:
i = +1
setattr(self, 'sectionButton_' + str(i), Button(text=x))
I'm not that good at explaining but hopefully its enough to understand my problem ^^
If not, just comment, I will try to answer it
If you have a group of related variables of the same type and you're doing the same operations to each one then that's a natural place to switch to using a list instead of individual variables.
Your code would become more like:
self.sectionButtons = []
for i, x in enumerate(self.sections):
button = Button(text=x)
button.grid(row=i+1, column=0)
self.sectionButtons.append(button)
This also has the advantage of no longer needing to construct the variable names as strings and use setattr, which is often a sign there's a better way.

Python GUI creating a list item with Text and checkboxes

I am trying to create a list like in outlook. With list items with an layout like this:
Don't get me wrong this isn't a "give me the full answer" question. I just have the problem of the right naming. I would appreciate it a lot if some could throw in the right words and I will look for them by my own.
I used tkinter at the moment but in that it seems like there isn't a solution for that.
Kind regards.
I think Tkinter can do this by using a bit of object oriented programming you could define how one list element should look like and then with static variables you could define a position of a new line relative to the position of the previous line. Something like:
from tkinter import *
class Line:
y = 0 # static variable for the y position
def __init__(self):
self.y_local = Line.y
Line.y = Line.y + 40 # increase the static variable for the next line
print(self.y_local)
def draw(self, tk, h_line="Headline", a_info="Addtional Information", date="01/01/1970"):
self.label_head = Label(text=h_line)
self.label_head.place(x=20, y=self.y)
self.label_info = Label(text=a_info)
self.label_info.place(x=20, y=self.y+20)
self.label_date = Label(text='Date')
self.label_date.place(x=200, y=self.y)
self.label_ndate = Label(text=date)
self.label_ndate.place(x=200, y=self.y+20)
self.chkbtn = Checkbutton(tk, text="Checkbox")
self.chkbtn.place(x=300, y=self.y+20)
tk = Tk()
data = [
["News", "WWIII has been avoided", "02/04/2018"],
["Python", "Python solves 42 riddles", "02/04/2018"]
]
for line in data:
temp = Line()
temp.draw(tk, line[0], line[1], line[2])
mainloop()
Hope I understood your question well. That you have a list with information and want to display that in an easy and scalable way. I have not looked to add the lines around the information as I've never done that before I know there are separators I've used vertical once before but I wouldn't be surprised if you can draw a box around each line either.

Create multiple Entry boxes in a loop tkinter

I was making an application using tkinter and came across an error. I wanted people to input a variable, which I have made, and then have that many Entry boxes popup on the screen for input. I was wondering what is wrong with my code, if it is possible, or if there is a better way. Thanks in advance!
p.s. the NoOfBoxes has been predefined
int(NoOfBoxes)
x = 1
while(NoOfBoxes>=x):
a = a + 50
fill_empty(a)
x = x + 1
def fill_empty():
empty = tk.Entry(self)
empty.grid(row=200,column=a)
return empty
In first line of shown code, you are converting NoOfBoxes to an integer but you are not assigning back it to NoOfBoxes hence, when while line comes, NoOfBoxes is still not an integer. Also, there is no parameter on your fill_empty definition.
Most likely you will need those Entry widgets at some point in your code, so it'll be much better if you keep references.
listOfEntries = [fill_empty(idx) for idx in range(int(NoOfBoxes))]
def fill_empty(a):
empty = tk.Entry(self)
empty.grid(row=200,column=a)
return empty
When you want to make any operation on those, you can easily do something like:
listOfEntries[0].get()

win32com moving between Styles in a Selectoin

I'm attempting to programmatically write a Word file and am struggling to alternate styles on a selection object. Here is the relevant bit of code:
objWord = win32com.client.Dispatch("Word.Application")
objSel = objWord.Selection
writestuff = "\r\nSome Heading"
objSel.Style = objWord.ActiveDocument.Styles("Heading 3")
objSel.TypeText(writestuff)
#This works so far, we have a heading and some text, now we want to write data below the heading
objSel.Style = objWord.ActiveDocument.Styles("Normal") #Setting style back to 'Normal' for next section
writestuff = "\r\nSome data about the heading we just wrote")
objSel.TypeText(writestuff)
#At this point the heading and new text both go to the 'Normal' style.
It appears that my 'selection' is affecting all of the document, however, when I make my initial objSel.Style assignment to 'Heading 3' previous lines aren't affected. When I switch back to normal everything else is.
It appears that what I needed to do (or at least one of the things which will solve this problem) is to add a TypeParagraph() method
Corrected code looks like this:
objWord = win32com.client.Dispatch("Word.Application")
objSel = objWord.Selection
writestuff = "\r\nSome Heading"
objSel.Style = objWord.ActiveDocument.Styles("Heading 3")
objSel.TypeText(writestuff)
objSel.TypeParagraph ##THIS IS WHAT I ADDED, NO NEED TO READJUST STYLE##
writestuff = "\r\nSome data about the heading we just wrote")
objSel.TypeText(writestuff)
This would present data with the correct heading and then normal-type text on the following line.

Categories

Resources