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

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)

Related

How do I have my code recognize cells with a specific string STRUCTURE, while ignoring others?

So, here's what I'm facing.
I have a bit of vba code, that will find every cell in a single column containing a specific string. The words it looks for are in an array called totArray. This works well.
Problem is, when a word in the cell contains "SUBTOTAL" or something similar, it will still find it and copy it. Also, there will be words where TOTAL or TOTAAL aren't followed by numbers of unknown length.
Please see my code attached. How do I get it so that it will find every case where the cell contains TOTAL or TOTAAL but followed always by a non-common structure of numbers only.
Sub CopyValues()
Dim totArray As Variant
Dim wsSource As Worksheet
Dim wsDest As Worksheet
Dim NoRows As Long
Dim DestNoRows As Long
Dim I As Long
Dim J As Integer
Dim rngCells As Range
Dim rngFind As Range
Dim Found As Boolean
totArray = Array("TOTAAL ", "TOTAL")
Set wsSource = Worksheets("Input")
Set wsDest = Worksheets("Output")
NoRows = wsSource.Range("A65536").End(xlUp).Row
DestNoRows = 2
For I = 1 To NoRows
Set rngCells = wsSource.Range("A" & I)
Found = False
For J = 0 To UBound(totArray)
Found = Found Or Not (rngCells.Find(totArray(J)) Is Nothing)
Next J
If Found Then
rngCells.Copy wsDest.Range("B" & DestNoRows)
DestNoRows = DestNoRows + 1
End If
Next I
End Sub
To find a string with characters before/after the specified string:
You could put wildcards into the string, see this documentation.
"*TOTAL" would find Subtotal, Grandtotal etc as the asterisk wildcards any number of characters.
"*TOTAL????? would find any word with total at the end (like the example above) and with up to 5 characters after the word (as there are 5 question marks). For example: Subtotal123 or Subtotal54321
*TOTAL ????? would find Subtotal 123 or Subtotal 54321 (notice the space can be used in the string between characters/wildcards).
Using this info you should be able to adjust your Array strings to work in your situation.
To find an exact match to the specified string:
You should specify the LookAt parameter in your .find method.
e.g. rngCells.Find(totArray(J), , , LookAt:= xlWhole).
Using the LookAt function is pretty straightforward.
xlWhole means the search value must match the entire cell contents.
xlPart means the search value only has to match part of the cell
Note: In that example the After and LookIn parameters are omitted.
The LookAt parameter is the 4th parameter which is why there are blanks between the commas.
If you don't specify your parameters, Excel uses the parameters from the last search you ran either from using the .Find method via VBA or the GUI find window.
You can read more about each parameter, including the LookAt parameter in this article about the vba find method.

Multiply each value in a range by a constant, but skip blank cells

I need a simple a fast solution for multiplying all values in a range by a numeric value in VBA code. I know about this solution: Multiply Entire Range By Value?
Set rngData = ThisWorkbook.Worksheets("Sheet1").Range("A1:B10")
rngData = Evaluate(rngData.Address & "*2")
But it has a big drawback - if the original cell was blank, it results in zero. How to force it skip blank values?
I want to avoid looping through the values because it is very slow.
You can use your existing approach with Evaluate but get a little smarter with it - it can take conditions etc, so just include a test for ISBLANK. This example is tested on a combination of blank and non-blank cells in the range A1:C3 - just update for your range and give it a try:
Option Explicit
Sub Test()
Dim rng As Range
Set rng = Sheet1.Range("A1:C3")
'give the name a range so we can refer to it in evaluate
rng.Name = "foo"
'using Evaluate
rng = Evaluate("IF(ISBLANK(foo),"""",foo*2)")
'using [] notation
'preferred IMO as dont need to escape "
rng = [IF(ISBLANK(foo),"",foo*2)]
End Sub
I know you have an accepted answer, but for whatever it's worth it turns out you don't have to name the range. And in case the cells in the range contain text, then this one-line code works fine
Sub MultiplyRangeByConstant()
[A1:C3] = [IF(ISBLANK(A1:C3),"",IF(ISTEXT(A1:C3),A1:C3,2*A1:C3))]
End Sub
if there are formulas or anything else in the range:
'[a1:b3] = [{"=1","a";2,"=0/0";"",3}]
[a1:b3] = [if(a1:b3="","",if(isNumber(a1:b3),a1:b3*2,a1:b3))]
or to ignore the formulas, the good old PasteSpecial
Set temp = [c1].EntireRow.Find("") ' any blank cell that is not in the range
temp.Value = 2
temp.Copy
[a1:b3].SpecialCells(xlCellTypeConstants).PasteSpecial , Operation:=xlMultiply
temp.Value = ""

VBA Range is Nothing

Running the code below and what I'm hoping to see as a result is to have all columns with 0s on row 27 hidden - but depending on the frequency of the data, the range for those columns to be hidden is different. Basically anything that is in a sheet that starts with Daily/monthly/weekly will have to have columns hidden, all the rest of the sheets should be left alone.
It worked on a simple workbook using an if statement (sheets starting with X vs. all other), but when I added the case statement it broke...
The line marked down with bold is the one where I get an error:
Run-time error '1004'
Application-defined or object-defined error
I'm new to this, so please feel free to suggest a good vba tutorials website/book.
Sub Hide_Zero_Columns()
Dim WS As Worksheet
Dim Col_to_hide As Range
Dim Range_to_hide As Range
Dim X As Integer
For Each WS In ThisWorkbook.Sheets
Worksheets(WS.Name).Activate
With WS
Select Case Data_Frequency_Sheets
Case Left(WS.Name, 5) = "Daily"
Set Range_to_hide = Range("BDV$27:CWH$27")
Case Left(WS.Name, 5) = "Month"
Set Range_to_hide = Range("AY$27:CO$27")
Case Left(WS.Name, 5) = "Weekl"
Set Range_to_hide = Range("HF$27:NN$27")
Case Else
Set Range_to_hide = Range("A1:B1")
End Select
Select Case Data_Condition
Case Left(WS.Name, 5) = "Daily"
X = 1
Case Left(WS.Name, 5) = "Month"
X = 30
Case Left(WS.Name, 5) = "Weekl"
X = 7
Case Else
X = 999
End Select
If X <> 999 Then
For Each Col_to_hide In ActiveSheet.Range(Range_to_hide) '<-- Error here
If UCase(Col_to_hide) = 0 Then
Col_to_hide.EntireColumn.Hidden = True
Else: Col_to_hide.EntireColumn.Hidden = False
End If
Next Col_to_hide
End If
End With
Next
ActiveWorkbook.Worksheets("Registrations").Activate
End Sub
Since you have already defined a Range, you the problem is you are trying to evaluate: Sheet.Range(Range) which throws the error you are getting.
Since it appears you are wanting to iterate across the columns, all you need to do is change the line to this:
' Iterate across the columns in the defined range.
For Each Col_to_hide In Range_to_hide.Columns
' Each "Col_to_hide" will represent all cells within the column.
MsgBox Col_to_hide.Address
The error you're getting is because you're passing a Range object as the argument to Activesheet.Range() and it doesn't accept that because of the Range object's default value. It would be like doing this in the Immediate Window
?Range(Range("A1")).Address
You'll get the same error. Whereas with
?Range("A1").Address
You don't. You could do this too
?Range(Range("A1").Address).Address
So the thing is that when you don't specify a property for an object, like Range("A1") instead of Range("A1").Address, then the default property is used. The Range object is a bit strange, but in this case its default property is the Value property. So Activesheet.Range(Range_to_hide) is the same as Activesheet.Range(Range_to_hide.Value). And if Range_to_hide is a multi-cell range, then it's Value property returns an array, which you definitely can't pass into a Range's argument.
That's the explanation, the short answer is simply to use Range_to_hide, like
For Each Col_to_hide in Range_to_hide.Columns
Why the .Columns. Remember when I said that the Range object was a little strange. Well, unlike most objects, it has two default properties based on the context you're using it in. Earlier, the default property was Value, but in the context of a For..Each, the default value is Cells. If you don't specify .Columns in your For..Each, then it will loop through every cell in the Range. In fact, I always specify how the range is aggregated, even if it's the default Cells.

VBA Conditional format cell based on whether value is in list of text

I have this code:
Sub Japan()
Set MyPlage = Range("A1:R1000")
For Each Cell In MyPlage
If Cell.Value = "A" Then
Rows(Cell.Row).Interior.ColorIndex = 3
End If
If Cell.Value = "B" Then
Rows(Cell.Row).Interior.ColorIndex = 3
End If
If Cell.Value = "C" Then
Rows(Cell.Row).Interior.ColorIndex = 3
End If
If Cell.Value = "D" Then
Rows(Cell.Row).Interior.ColorIndex = 3
End If
If Cell.Value = "E" Then
Rows(Cell.Row).Interior.ColorIndex = 3
End If
Next
End Sub
THis find any cells that have either A, B, C, D, E as the value and then colours the entire row red if so.
Basically, I have hundreds of more values that I want to lookup. I have them stored in another excel file (could just as easily be in a text file). How could I reference them? i.e, if cell value is in this list of text, do this.
Sounds like you want a Set datastructure that contains unique values and you can use an Exist method on it.
For example your desired usage is this.
Set MySet = LoadRedValueSet(???) ' explain later
Set MyPlage = Range("A1:R1000")
For Each Cell In MyPlage
If MySet.Exists(Cell.Value) Then
Rows(Cell.Row).Interior.ColorIndex = 3
End If
Next
Well too bad Set is a reserved keyword and VBA does not provide a Set object. However, it does provide a Dictionary object which can be abused like a Set would be. You will need to reference the Scripting Runtime Library to use it first through. The usage would be exactly as stated as above. But first we need to define LoadRedValueSet()
Lets assume that you are able to load whatever file you save these values as in as an Excel worksheet. I will not be explaining how to open various file types in Excel as there are many answers detailing that in more detail than I can. But once you have your range of values to add to the set we can add them to the dictionary.
Private Function LoadRedValueSet(valueRange As Range) As Dictionary
Dim result As New Dictionary
Dim cell As Range
For Each cell In valueRange.Cells
result(cell.value) = Nothing
Next cell
Set LoadRedValueSet = result
End Function
Dictionary are mapping objects that have key->value pairs. The key's are effectively a set, which is what we want. We don't care about the values and you can pass whatever you want to it. I used Nothing. If you use the .Add method the dictionary will throw an error if your list contains duplicate entries.
Assuming you have implemented some function that loads your file as a worksheet and returns that worksheet.
Dim valueSheet As Worksheet
Set valueSheet = LoadSomeFileTypeAsWorksheet("some file path")
Dim valueRange As Range
Set valueRange = valueSheet.??? 'column A or whatever
Dim MyDictAsSet As Dictionary
Set MyDictAsSet = LoadRedValueSet(valueRange)
Set MyPlage = Range("A1:R1000")
For Each Cell In MyPlage
If MyDictAsSet.Exists(Cell.Value) Then
Rows(Cell.Row).Interior.ColorIndex = 3
End If
Next
There are quite a few ways you could possibly do this but here's my approach. Application.WorksheetFunction.<function name> can be used to evaluate worksheet functions within VBA. This means we can use it to run a Match function. For the sake of a simple example let's assume your values to match are in Column A of a worksheet called Sheet2 (in the same workbook).
Dim MyPlage As Range, Cell As Range
Dim result as Variant
Set MyPlage = Range("A1:R1000") '<~~ NOTE: Sheets("<SheetName>").Range("A1:R1000") would be better
For Each Cell in MyPlage
result = Application.WorksheetFunction.Match(Cell.Value, Sheets("Sheet2").Range("A:A"), 0)
If Not IsError(result) Then
Rows(Cell.Row).Interior.ColorIndex = 3
End If
Next Cell
We only need to know whether or not the WorksheetFunction.Match function returned an error: If it didn't then Cell.Value was present in Column A of Sheet2 and we color the row red.
Paste your color value + index data to a new sheet called "Colors" in the following order;
Value ColorIndex
A 1
B 2
C 3
D 4
E 5
And update your method with the following code and update the range based your data;
Sub SetColors()
' DataCells: The cells that's going to be checked against the color values
Set DataCells = Range("A1:A15") ' Update this value according to your data cell range
' ColorValueCells: The cells that contain the values to be colored
Set ColorValueCells = Sheets("Colors").Range("A2:A6") ' Update this value according to your color value + index range
' Loop through data cells
For Each DataCell In DataCells
' Loop through color value cells
For Each ColorValueCell In ColorValueCells
' Search for a match
If DataCell.Value = ColorValueCell.Value Then
' If there is a match, find the color index
Set ColorIndexCell = Sheets("Colors").Range("B" & ColorValueCell.Row)
' Set data cell's background color with the color index
DataCell.Interior.ColorIndex = ColorIndexCell.Value
End If
Next
Next
End Sub

VBA Excel Range() with Cell argument

Why does the following not work:
Range(Cells(1,1)).Value = 3
Cells(1,1) should essentially be the same thing as using A1 right?
(I realize that I could just do Cells(1,1).Value = 3, but I'm just curious as to why it doesn't work.)
I read the MSDN entry and it shows that the first argument must be A1 style, yet something like this does work:
Range(Cells(1,1), Cells(2,3)).Value = 2
Totally confused.
When Range is used with a single parameter, the parameter is is interpreted as a range name.
Range(Cells(1,1))
is the same as using
Range(Cells(1,1).Value)
So you will get a result only is the value of Cells(1,1) is a valid range address in A1 style
Only when passed two range parameters are they interpreted as the corners of a range.
When you want to use the Cells property to specify the parameters of the range object (if I remember rightly - I've not been using VBA for some time), then you have to effectively supply two arguments.
So if you want to reference a range object that has only one cell, then you need to write:
Range(Cells(1, 1), Cells(1, 1)).value = "Hello World"
Instead of referring to a single cell like this:
Range(Cells(1,1), Cells(1,1))
You can write:
Range(Cells(1,1).Address)
For a single cell its much easier: Use the default Cells() function:
Cells(1,1) = "hello world"
or use a Sheet's Cells() function:
Dim sht as Worksheet
Set sht = Sheets("myworksheet") ' or: = Sheets(1)
sht.Cells(1,1) = "hello world"
For a range you'll have to use two params, as explained in the other answers given here. But the advantage is that you can set a whole range of fields to a value. And you can work on a sheet that isn't the 'Active one', behind the scenes. For example:
Const colRand = 4
Const colDiff = 5
Dim sht as Worksheet, rngHi As Range, rngRand As Range, rngDiff As Range
Set sht = Sheets("myworksheet") ' or: = Sheets(1)
Set rngHi = sht.Range(sht.Cells(1,1), sht.Cells(3,3)
rngHi = "hello world"
Set rngRand = sht.Range(sht.Cells(1,colRand), sht.Cells(8,colRand) ' column 4, rows 1-8
rngRand = "=RAND()"
Set rngDiff = sht.Range(sht.Cells(2,colDiff), sht.Cells(8,colDiff) ' column 5, rows 2-8
' using FormulaR1C1 in case the sheet isn't set to use that type of formula
Set rngDiff.FormulaR1C1="=RC[-1] - R[-1]C[-1]" ' on previous columnn, diff between this row and previous row
Explanation:
The Cells function receives either:
a string parameter - in which you specify the A1_And_Colon Style range
or two Cell parameters - the beginning cell of the range and the end cell.
So to set the range with 'cells' you need to give both cells divided by a comma:
Range(Cells(1,1), Cells(1,1)) = "hello world"
Range(Cells(2,2), Cells(3,4)) = "you cannot square around, but you can round a square"
Sheets(1).Cells(5,5) = "=Round(Sqrt(5))"
I'm writing this answer because I'm learning VBA and it took me the better part of three days to figure out what was happening here, and the official documentation does not discuss this topic at all. This QA is good but the information is a bit scattered, from my perspective today.
Here's what I know about using the Cells() property inside a Range() object to reference a single-cell range. Which I need to do all the time!
Given a valid ws object...
You think this will work:
ws.Range(ws.Cells(i,j))
It doesn't. You'll get Run-time error '1004': Method 'Range' of object'_Worksheet' failed.
The obvious fix, as described by #Woody_Pride is:
ws.Range(ws.Cells(i,j), ws.Cells(i,j))
Unfortunately, having to do this is absolutely infuriating, and is not actually strictly necessary.
What you actually need is, as asserted by #Willby, although the explanation as to why this is the case is actually in the answer by #chris_neilsen:
ws.Range(ws.Cells(i,j).Address)
This will also work, as suggested by #pashute (who is wrong in most parts of his explanation):
ws.Cells(i,j)
Thank you to everyone who contributed on this page; I feel like I now, finally, have the entire picture.
I know sometimes you need a range for other properties other than value. What i would do is make a function to help you:
Public Function cellRange(ws As Worksheet, rowNum As Integer, colNum As Integer) As Range
Set cellRange = ws.Range(ws.Cells(rowNum, colNum), ws.Cells(rowNum, colNum))
End Function
This way you can make cleaner code:
Set ws = ActiveWorkbook.Sheets("Sheet1")
cellRange(ws, 1, 3).Interior.Color = cellRange(ws, 1, 8).Interior.Color
When using "cells", it is required to formulate Object.cells ,
e.g. Application.cells(2,2) or activeWorksheet.cells