Dynamically add items from a file to a ComboBox - pandas

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.

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

Nested dimensions in macro array argument

I am providing a multi-dimensional array to a macro in Word. Each element is an array, and each array element has 2 values, the name of an image and a short description. The macro is:
For i = LBound(figures, 1) To UBound(figures, 1)
cgmImage = "C:\path\to\images\" & figures(i, 0) & ".jpeg"
Selection.InlineShapes.AddPicture FileName:=cgmImage, LinkToFile:=False, SaveWithDocument:=True
Selection.TypeParagraph
With Selection
.Font.Size = 14
.Font.Bold = True
.Font.Underline = wdUnderlineSingle
.TypeText Text:=figures(i, 1)
End With
Next i
Sample input array would be:
[
['123','image 1'],
['456','image 2']
]
The macro works, inserting each image and its description. However I now want to add a third element, this element will itself be a 2D array representing a table. So input will look something like this:
[
[ '123','image 1', [['val1','val2'],['val3','val4']] ],
[ '456','image 2', [['val1','val2'],['val3','val4']] ]
]
In the macro I'll use this 3rd element to create a table. However, if I give input data like that into my original macro I now get a 'subscript out of range' error on this line:
cgmImage = "C:\path\to\images\" & figures(i, 0) & ".jpeg"
All I want to iterate is the first level of this whole data structure (e.g. for the sample data I provided above, 2 iterations). Even if I provide the 'dimension' attribute to LBound and UBound I still get the error, which shouldn't happen since the first element of the top level array elements are always just a string (the image file name). How can I achieve this? And will accessing the third element representing the table be along the lines of :figures(i,2,X,X)?
EDIT:
Array creation is in Python, just a standard list type. Calling of the macro is done using the mhammond pywin32 module here's a simplified version of the technique:
import win32com.client as win32
word = win32.gencache.EnsureDispatch("Word.Application")
template = word.Documents.Open("\path\to\file.docm")
word.Run("macroName",imagesArray)
Then I save and close it
if the inner most array is an array of array, then something like figures(i)(j)(k)(l) should be the way to go
you can use UBound(array,index) and LBound(array,index) functions to get the actual lower and upper array bounds of its index dimension
for example, along the lines of data you shown, you could consider the following code:
Option Explicit
Sub main()
Dim figures(1) As Variant
figures(0) = Array("123", "image 1", Array(Array("val1", "val2"), Array("val3", "val4"))) ' see how the innermost array is an array of arrays, too
figures(1) = Array("456", "image 2", Array(Array("val5", "val6"), Array("val7", "val8"))) ' see how the innermost array is an array of arrays, too
Dim i As Long, j As Long, k As Long, l As Long
For i = LBound(figures) To UBound(figures)
For j = LBound(figures(i)) To UBound(figures(i))
If IsArray(figures(i)(j)) Then
For k = LBound(figures(i)(j)) To UBound(figures(i)(j))
For l = LBound(figures(i)(j)(k)) To UBound(figures(i)(j)(k))
Debug.Print figures(i)(j)(k)(l)
Next
Next
Else
Debug.Print figures(i)(j)
End If
Next
Next
End Sub

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

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")

VBA for loop with VLookups avoiding duplicates

I am compiling a list of employees CPD classes and the hours they are, producing a printout essentially for each one. I am pulling data from multiple worksheets and am having trouble with VLOOKUP continually finding the same data. My code currently is:
result = lookup.Find(name) Is Nothing
If (result = False) Then
For Each cell In lookup
className = Application.VLookup(name, lookup, 7, True)
classHours = Application.VLookup(name, lookup, 9, True)
Sheets("employee output").Range("B" & counter).Formula = className
Sheets("employee output").Range("C" & counter).Formula = classHours
empHours = empHours + classHours
counter = counter + 1
numClasses = numClasses + 1
corporate = Range("B" & i) *** vba precompliles the loop so this doesn't work *
Set lookup = Range("corporate:K") ** doesn't work either***
Next
End If
So I need to limit the scope of the lookup range or somehow avoid finding the same data. Any help would be greatly appreciated...
I assume your lookup variable represents the range containing you data.
Your data duplication propably comes from your for each loop. Indeed, you are iterating trough each Cell of your range. That means that once the name is found, the code Inside the loop is executed as many times as there are cells in the range lookup.
Try changing your loop to the following:
Dim row as Range
for each row In lookup.Row
'your code goes here
Next
Whit this, the code will only run once for each rows.

How to get named range for a particular cell from excel using python

i need to get "name a range" value entered for a particular cell.
i entered "name a range" value for (A4,B4) cell in excel,to read cell value we use sh.cell(row,col) but how to get name range value for that cell.
i am not getting how to do...
help me.. thanks in advance
Unfortunately I don't have enough rep to comment, so I will try my best from what I can to understand from your question.
So if I got this right, you have a named range on a worksheet, where the name of the range, Referenced by sh.Range("A4:B4"), is 'name_a_range' (I included underscores because you can't have spaces in the name).
You can get the value from the Sheets.Cells(row, col).Value , but you want to do this through the named range.
Ok, to do this:
Dim valA as Variant, valB as Variant
Dim valArray() as Variant
'' To get the value in Cell "A4"
valA = sh.Range("name_a_range").Cells(1,1).Value
'' To get the value in Cell "B4"
valB = sh.Range("name_a_range").Cells(1,2).Value
'' Or if you have a Variant() array already Dim'ed (like above)
valArray = sh.Range("name_a_range").Value
The Worksheet's Range object can either take:
'' Expression for a range: "A4" to "B4" , using Cells
sh.Range(sh.Cells(4,1), sh.Cells(4,2))
'' Expression for a range: "A4" to "B4" , using xlA1 format
'' *Which is the most familiar
sh.Range("A4:B4")
'' Expression for a range: "A4" to "B4" , using name
sh.Range("PreviouslyNamedRange")
With any range, you can think of it as it's own array (starting from position 1).
And if the size (dimensions) of the range is unknown, you can use the built-in Range functions:
sh.Range("name_a_range").Rows.Count '' returns 1
sh.Range("name_a_range").Column.Count '' returns 2
As for the Python aspect from the title, you will use the same exact syntax:
Although, I don't use xlrd, and instead I drive Excel through python using win32com.client
from win32com.client import Dispatch
app_xl = Dispatch("Excel.Application")
wb = app_xl.Workbooks.Open("Path\\To\\Your\\Workbook.xlsx")
range = wb.Sheets(1).Range("name_a_range").Value
For example, I ran this on one of my Sheets:
>>> from win32com.client import Dispatch
>>> app_xl = Dispatch("Excel.Application")
>>> wb = app_xl.Workbooks.Open("C:\***\***\excel_vba_tests\getinfo.xlsx")
>>> rangeValues = wb.Sheets("Summary").Range("avgVolume").Value
>>> rangeValues
((u'Avg/Wk',), (Decimal('791517.25'),), (Decimal('796369'),), (Decimal('769922.8846'),), (Decimal('743311.549'),))
Here, I opened my Workbook, "getinfo.xlsx", and from my Sheet named "Summary" I was able to get all the values in my named range "avgVolume"
Is this helpful/what you were looking for? Since I wasn't too sure of what your question was exactly I figured I'd hit as many points as possible.
from xlsxwriter.utility import xl_rowcol_to_cell
cell = xl_rowcol_to_cell(1, 2) # C2
print(cell)