can openpyxl amend chart data labels to values in selected cells? - openpyxl

I have an excel spreadsheet which contains values across three columns. A = Task, B = Date, and C = Team. I've written some simple code to build a excel scatter chart, place the values in column C on the y axis and values in column B on the x axis. However, I would like the chart data labels to be taken from column A. Is there and way of doing this?
At the moment I'm getting the data labels to be column B via using chart.dataLabels.showCatName = True ,which is useful but not what I need. I think it might be possible by specifying the labels via the chart.dataLabels Parameter dLbl which stands for datalabellist. the source code for which is here http://openpyxl.readthedocs.io/en/2.3.5/_modules/openpyxl/chart/label.html .
The most useful posts I found on this issue are here https://groups.google.com/forum/#!topic/openpyxl-users/jB0pPVUtUsM, and here Adding labels to ScatterChart in openpyxl
At the moment the chart looks like this.
chart = ScatterChart()
chart.title = "Team's Schedule"
chart.style = 7
chart.x_axis.title = 'Days'
chart.y_axis.title = 'Team'
xvalues = Reference(chart_sheet, min_col=2, min_row=2, max_row=20)
values = Reference(chart_sheet, min_col=3, min_row=1, max_row=20)
series = Series(values, xvalues, title_from_data=True)
chart.series.append(series)
s1 = chart.series[0]
s1.marker.symbol = "triangle"
s1.marker.graphicalProperties.solidFill = "FF0000" # Marker filling
s1.marker.graphicalProperties.line.solidFill = "FF0000" # Marker outline
s1.graphicalProperties.line.noFill = True
chart.dataLabels = DataLabelList()
chart.dataLabels.showCatName = True
chart_sheet.add_chart(chart, "F2")

Related

openpyxl: Is it possible to load a workbook (with data_only=False), work on it, save it and re open the saved vile with (data_only= True)?

Basically the title. The thing is that I got an Excel file already (with a lot of formulas) and I have to use it as a template, but I have to copy certain column and paste it in another column.
Since I have to make some graphs in between I need the numeric data of the excel file so my plan is the following:
1.- load the file with data_only = False.
2.- Make the for loops needed to copy and paste info from one worksheet to another.
3.- Save the copied data as another Excel file.
4.- Open the new Excel created file, this time with data_only = True, so I can work with the numeric values.
The problem is that after doing this, it's like after putting data_only on the new created file it doesn't work, because when I made a list that filters NoneType values and strings in a column that have actual numerical values it gives me an empty list.
#I made the following
wb = load_workbook('file_name.xlsx', data_only = True)
S1 = wb['Sheet 1']
S2 = wb['Sheet 2']
#Determination of min and max cols and rows
col_min = S1.min_column
col_max = S1.max_column
row_min = S1.min_row
row_max = S1.max_row
for i in range(row_min + 2, row_max + 1):
for j in range(col_min + Value, Value + 2):
S2.cell(row = i+6, column = j+10-Value).value = S1.cell(row = i, column = j).value
Transition_file = wb.save('transition.xlsx')
wb1 = load_workbook('transition.xlsx', data_only = True) #To obtain only numerical values
S2 = wb1['Sheet 2'] #Re define my Sheet 2 values

How to plot a polyline between multiple points with vba

I am trying to plot a polyline between multiple points in vba into autocad. I have almost finished my code, but the thing is that the points may repeat themselves, as 2 lines can have the same starting point, and the points are nor in a sorted way.
I need to be able to addd all points even if they aren't sorted, cause I have to keep the order of the points I am tryint to plot.
I am getting this error:
Invalid Procedure or argument call
Set acadPol = acadDoc.ModelSpace.AddLightWeightPolyline(Points)
This is my code:
Points(1)=9736.242889: Points(2)=9954.553808
Points(3)=9718.429708: Points(4)=9936.874562
If acadDoc.ActiveSpace = acModelSpace Then
Set acadPol = acadDoc.ModelSpace.AddLightWeightPolyline(Points)
Else
Set acadPol = acadDoc.PaperSpace.AddLightWeightPolyline(Points)
End If
acadPol.Closed = False
acadPol.Update
End If
End If
Your code is incomplete but I notice you have started your coordinates list at index 1.
There are plenty of examples on the internet
Sub Example_AddLightWeightPolyline()
' This example creates a lightweight polyline in model space.
Dim plineObj As AcadLWPolyline
Dim points(0 To 9) As Double
' Define the 2D polyline points
points(0) = 1: points(1) = 1
points(2) = 1: points(3) = 2
points(4) = 2: points(5) = 2
points(6) = 3: points(7) = 2
points(8) = 4: points(9) = 4
' Create a lightweight Polyline object in model space
Set plineObj = ThisDrawing.ModelSpace.AddLightWeightPolyline(points)
ZoomAll
End Sub
As you can see, you need to start your coordinates array at index 0 and not 1.
Does this help?

Is there a way to plot a line graph with integer x values but label each point on the x axis with a string?

I'm producing a line graph and I'm plotting points according to x and y values using points.addxy(X, Y). However the xvalues are plotted through regular intervals which I want to have labeled below the axis. My code is below:
Dim AllRaceArray() As String = GetAllRaceArray() 'Array of The race names in date order
Dim RaceIDs() As Integer = GetAllRaceID() 'Array Of The Race IDs Paralell to the above array
Dim Xnum As Integer = AllRaceArray.Length
Dim Interval As Integer = Math.Floor(Chart1.Size.Width / Xnum)
Chart1.Series.Clear()
For i = 0 To 3
If CompareSlotEmpty(i) = False Then
Chart1.Series.Add(i & CompPaddler(i).Name)
Chart1.Series(i).ChartType = SeriesChartType.Line
Chart1.Series(i).XValueType = ChartValueType.String
Chart1.Series(i).BorderWidth = 2
Chart1.Series(i).MarkerStyle = DataVisualization.Charting.MarkerStyle.Circle
Chart1.Series(i).MarkerSize = 8
For p = 0 To Xnum - 1
For q = 0 To CompPaddler(i).RacePoints.Length - 1
If CompPaddler(i).RacePoints(q).RaceID = RaceIDs(p) Then
Chart1.Series(i).Points.AddXY(Interval * p, CompPaddler(i).RacePoints(q).Points) ' this point plot creates the correct ordering
Chart1.Series(i).Points.AddXY(AllRaceArray(p), CompPaddler(i).RacePoints(q).Points) ' this point plot creates the correct labels
End If
Next
Next
End If
' EDIT
' This below Section has now been added but nothing appears below the axis at all now
For t = 0 To Chart1.Series(i).Points.Count - 1
Chart1.Series(i).Points(t).AxisLabel = AllRaceArray(t)
Next
'EDIT
Next
As annotated above One chart series creates the correct looking graph (http://tinypic.com/r/sobwbc/9) but x axis labels. Then the second plotter plots the correct labels but the ordering and scale gets messed up (http://tinypic.com/r/2w2g5s4/9).
TLDR: How to I change the xAxis Labels to strings that line up with regularly interval points?
Edit: I have added a loop to change the .axislabel but now nothing shows below the axis

Dynamically add items from a file to a ComboBox

I am working on an application that allows the user to dynamically add to and remove items from an excel file. The quantity of items shall be unlimited.
I am looking for a way to grab the items from the excel file and transfer them to the ComboBox.
To make myself clearer: The problem is not iterating through cells, but getting cell values into the ComboBox. I need a method that captures the content of all cells with values in a given column, where the end of range is unknown and then transfer the values to a ComboBox.
The Combobox only accepts values, not any empty cells. I also don't want fields in the ComboBox that say "No Value".
I have tried itering through cells and range methods, but this doesn't get the values into the ComboBox.
What I have so far is:
wb = load_workbook (source_file)
ws = wb.active
self.value_1 = ws['B2'].value
self.value_2 = ws['B3'].value
self.value_3 = ws['B4'].value
self.value_4 = ws['B5'].value
self.value_5 = ws['B6'].value
self.value_6 = ws['B7'].value
self.value_7 = ws['B8'].value
self.value_8 = ws['B9'].value
self.value_9 = ws['B10'].value
self.value_10 = ws['B11'].value
stock_items = [ self.value_1 , self.value_2 , self.value_3 , self.value_4 , self.value_5 ,
self.value_6 , self.value_7 , self.value_8 , self.value_9 , self.value_10 ]
self.combo_items_list = [ ]
for stock_item in stock_items :
if stock_item != None :
self.combo_items_list.append (stock_item)
self.combo.addItems(self.combo_items_list)
This works as expected, but what troubles me is that I have to add a line of code for each item I grab from the excel file, besides having to put an extra entry into the stock_items list. If there were 5.000 items in the file, that would result in 5.000 lines of code and 5000 entries in the list.
Is there a more efficient and elegant way to handle the issue with "counter" or pandas?
Thanks in advance.
I found a way to do this nicely using Pandas, not opnpyxl :
import pandas as pd
import numpy as np
# get sheet and whole column
sales = pd.read_excel ("Inventory.xlsx")
# filter out any None Values
sales_article = sales ["Artigo"] .dropna()
# transform into list
sales_list = sales_article.values.tolist()
# add list to ComboBox
self.combo.addItems(sales_list)
In openpyxl 2.4 worksheets have an iter_cols method that allow you to select a range of cells and have them returned as columns. Just like iter_rows returns them as rows. This is the simplest and most efficient way to do what you want to do.
See https://openpyxl.readthedocs.io/en/default/tutorial.html#accessing-many-cells for details.
An example for your use case:
cells = [cell.value for cell in ws.iter_cols(min_col=2, max_col=2, min_row=2) if cell.value is not None]
wb = load_workbook(source_file)
ws = wb.active
lastrow = ws.UsedRange.Height # don't remember method name
for row in range(lastrow):
value = ws['B' + str(row + 2)].value
if value is not None:
self.combo_items_list.append (value)
self.combo.addItems(self.combo_items_list)
See worksheet docs for other ways to get range of excel rows.
wb = load_workbook (source_file)
ws = wb.active
self.combo_items_list = [ ]
// loop from 2(start)-11(end)
// check if ws['B'<counter>].value is available and not null
// add this value to your array of combo for each ittration.
self.combo_items_list.append (ws['B'<counter>].value)
self.combo.addItems(self.combo_items_list)
Sorry, if i am getting it wrong. I don't know the syntax of given language. Still saw a logical answer and decided to post.

Read Chart Properties using VBA

I'm using VBA to create some graphics I need to do.
Basically, what I want to do is to create the 1st series automatically and then, the 2nd series, copy the color and format from the 1st series.
I'm trying to do this code but with no success:
ActiveChart.SeriesCollection(a - 1).Select
ActiveChart.SeriesCollection(a).Border.ColorIndex = ActiveChart.SeriesCollection(a - 1).Border.ColorIndex
ActiveChart.SeriesCollection(a).MarkerBackgroundColorIndex = ActiveChart.SeriesCollection(a - 1).MarkerBackgroundColorIndex
ActiveChart.SeriesCollection(a).MarkerForegroundColorIndex = ActiveChart.SeriesCollection(a - 1).MarkerForegroundColorIndex
ActiveChart.SeriesCollection(a).MarkerStyle = ActiveChart.SeriesCollection(a - 1).MarkerStyle
Can someone please help me on how to read the properties from the previous series and apply them to the next?
Thanks.
In general something like this could work:
Sub Tester()
Dim Cht as Chart
Dim a As Series, b As Series, x As Long
Set cht = ActiveChart
Set a = cht.SeriesCollection(1)
For x = 2 To cht.SeriesCollection.Count
Set b = cht.SeriesCollection(x)
b.Border.Color = a.Border.Color
b.MarkerBackgroundColor = a.MarkerBackgroundColor
b.MarkerForegroundColor = a.MarkerForegroundColor
b.MarkerStyle = a.MarkerStyle
Next x
End Sub
Note however that some properties will not be read unless the first series was manually formatted, and is not just the "default" format.
Eg: see Fill and Border color property of data point marker (scatter or line) Excel VBA for similar problem.