Using variables as function parameters in VBA, specifically Cells() Function - python

I am working with loops & arrays in VBA.
I watched a bunch of videos online and none of the instructors have gone over this.
In Python, you can dynamically access values from an array by setting the index of my variable:
array1 = [1,2,3,4,5]
b = 0
while(b < len(array1)):
print(array1[b])
b = b + 1
You can do something similar with pandas indexing iloc capability....this way I can change the variable _currentRow7 to whatever I want....I just need to do _currentRow7 = _currentrow7+1
Array.iloc[_currentRow7, 11])
With the VBA Cells function, I get an error. Essentially I want to access values from Excel dynamically. For example - Cells(variable, 1)....then as the variable changes, I am accessing the value in the next cell.
Sub HomeRunCounterFNCTN()
Dim HomeRuns(27) As Integer
Dim HRCounter As Variant
Worksheets("Baseball").Activate
Range("L3").Activate
For HRCounter = 0 To 27
HomeRuns(HRCounter) = ActiveCell.Offset(HRCounter, 0).Value
If (HomeRuns(HRCounter) >= 45) Then MsgBox (HomeRuns(HRCounter))
MsgBox (Cells(HRCounter & 1))
Next HRCounter
End Sub
Essentially I would like the MsgBox (Cells(HRCounter & 1)) to update dynamically as the variable HRCounter changes.

I was able to run your code without error, so as #Tim Williams says, it would be helpful to describe the error you are getting.
I did spot one non-fatal flaw in this line:
MsgBox (Cells(HRCounter & 1)
You probably meant Cells(HRCounter, 1). The typo doesn't cause a program error, but the line fails to capture what you intended to capture.
Aside from the typo, the code seems to do what you intended it to do. However, to answer your question more directly, you can certainly work with variable cell references. In basic pattern, simply use SomeWorksheet.Cells(MyVar,1).Value, where MyVar is the variable holding your row number.
To write efficient VBA code, learn to avoid Activate and Select whenever possible. Think of them as code imitating the actions of a human user, which is rarely required. Instead, work directly with Excel objects and their properties. Your code can be recast as follows, which will also make the use of a variable row reference more visible:
Sub HomeRunCounterFNCTN()
Dim HomeRuns(27) As Integer, ReadRow As Integer
Dim HRCounter As Variant
With Worksheets("Baseball")
For HRCounter = 0 To 27
ReadRow = HRCounter + 3
HomeRuns(HRCounter) = .Cells(ReadRow, 12).Value
If (HomeRuns(HRCounter) >= 45) Then MsgBox HomeRuns(HRCounter)
Next HRCounter
End With
End Sub

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

Applying variable in html tag with python code

Is it possible to insert variable in html's tag?
For example:
def colour():
c = np.random.uniform(0, 1)
c1 = np.random.uniform(0, 1)
c2 = np.random.uniform(0, 1)
r = hex(int(c*255))[2:]
g = hex(int(c1*255))[2:]
b = hex(int(c2*255))[2:]
return "#" + r + g + b
code = colour()
print(code)
dis = HTML(r'<h3 style="color:{code};">This is the colour you feel like today. Like it? </h3>')
display(dis)
I randomly generate three colors and i want to print my sentence three time with colors generated above.I want to make a list and put three color's string form in it, then use for loop to output my answer.
Hope I explain my question cleary, and if there are any problem, i will give another explanation, thank you!
Thanks Robert 830213. I have been searching for the same answer and I could not find it. I am replying here to let people know that you have to be careful where you put the f-string. It has to encompass the entire tag (here ) and also, if you have a placeholder for data such as {0}, it must be treated as an f-string variable by putting it in another set of curly braces. Otherwise, instead of your value, it prints "0" in the table. Also make sure to end the f-string before any format commands.
table += f" <td bgcolor={row_color1} style='text-align:left'>{{0}}</td>\n".format(column.strip())
Simply use an f-string, as in
rf'<h3 style="color:{code};">'

Python: Change variable suffix with for loop

I know this was asked a lot but I can not work with/understand the answers so far.
I want to change the suffix of variables in a for loop.
I tried all answers the stackoverflow search provides. But it is difficult to understand specific codes the questioner often presents.
So for clarification I use an easy example. This is not meant as application-oriented. I just want to understand how I can change the suffix.
var_1 = 10
var_2 = 100
var_3 = 1000
for i in range(1,4):
test_i = var_i + 1
print(test_i)
Expected result:
creating and printing variables:
test_1 = 11
test_2 = 101
test_3 = 1001
Expected Output
11
101
1001
Error: var_i is read as a variable name without the changes for i.
I would advise against using eval in 99.99% of all cases. What you could do is use the built-in getattr function:
import sys
var_1 = 10
var_2 = 100
var_3 = 1000
for i in range(1,4):
test_i = getattr(sys.modules[__name__], f"var_{i}") + 1
print(test_i)
Instead of doing a convoluted naming convention, try to conceive of your problem using a data structure like dictionaries, for example.
var={}
var[1] = 10
var[2] = 100
var[3] = 1000
test={}
for i in range(1,4):
test[i] = var[i] +1
print(test)
If somehow you are given var_1 etc as input, maybe use .split("_") to retrieve the index number and use that as the dictionary keys (they can be strings or values).
Small explanation about using indexing variable names. If you are starting out learning to program, there are many reasons not to use the eval, exec, or getattr methods. Most simply, it is inefficient, not scalable, and is extremely hard to use anywhere else in the script.
I am not one to insist on "best practices" if there is an easier way to do something, but this is something you will want to learn to avoid. We write programs to avoid having to type things like this.
If you are given that var_2 text as a starting point, then I would use string parsing tools to split and convert the string to values and variable names.
By using a dictionary, you can have 1000 non-consecutive variables and simply loop through them or assign new associations. If you are doing an experiment, for example, and call your values tree_1, tree_10 etc, then you will always be stuck typing out the full variable names in your code rather than simply looping through all the entries in a container called tree.
This is a little related to using a bunch of if:else statements to assign values:
# inefficient way -- avoid
if name == 'var_1' then:
test_1=11
elif name == 'var_2' then:
test_2=101
It is so much easier just to say:
test[i]= var[i]+1
and that one line will work for any number of values.
for i in range(1, 4):
print(eval('var_' + str(i)))
Step by step:
1) Make your variables strings:
stringified_number = str(i)
2) evaluate your expression during runtime:
evaluated_variable = eval('var_' + stringified_number)

Using python win32com can't make two separate tables in MS Word 2007

I am trying to create multiple tables in a new Microsoft Word document using Python. I can create the first table okay. But I think I have the COM Range object configured wrong. It is not pointing to the end. The first table is put before "Hello I am a text!", the second table is put inside the first table's first cell. I thought that returning a Range from wordapp will return the full range, then collapse it using wdCollapseStart Enum which I think is 1. (I can't find the constants in Python win32com.). So adding a table to the end of the Range will add it to the end of the document but that is not happening.
Any ideas?
Thanks Tim
import win32com.client
wordapp = win32com.client.Dispatch("Word.Application")
wordapp.Visible = 1
worddoc = wordapp.Documents.Add()
worddoc.PageSetup.Orientation = 1
worddoc.PageSetup.BookFoldPrinting = 1
worddoc.Content.Font.Size = 11
worddoc.Content.Paragraphs.TabStops.Add (100)
worddoc.Content.Text = "Hello, I am a text!"
location = worddoc.Range()
location.Collapse(1)
location.Paragraphs.Add()
location.Collapse(1)
table = location.Tables.Add (location, 3, 4)
table.ApplyStyleHeadingRows = 1
table.AutoFormat(16)
table.Cell(1,1).Range.InsertAfter("Teacher")
location1 = worddoc.Range()
location1.Paragraphs.Add()
location1.Collapse(1)
table = location1.Tables.Add (location1, 3, 4)
table.ApplyStyleHeadingRows = 1
table.AutoFormat(16)
table.Cell(1,1).Range.InsertAfter("Teacher1")
worddoc.Content.MoveEnd
worddoc.Close() # Close the Word Document (a save-Dialog pops up)
wordapp.Quit() # Close the Word Application
The problem seems to be in the Range object that represents a part of the document. In my original code the Range object contains the first cell and starts at the first cell, where it will insert. Instead I want to insert at the end of the range. So I got the following code replacement to work. I moved the Collapse after the Add() call and gave it an argument of 0. Now there is only one Collapse call per Range object.
location = worddoc.Range()
location.Paragraphs.Add()
location.Collapse(0)
Now the code works, I can read from a database and populate new tables from each entry.
Tim

What is the Pythonic way to implement a simple FSM?

Yesterday I had to parse a very simple binary data file - the rule is, look for two bytes in a row that are both 0xAA, then the next byte will be a length byte, then skip 9 bytes and output the given amount of data from there. Repeat to the end of the file.
My solution did work, and was very quick to put together (even though I am a C programmer at heart, I still think it was quicker for me to write this in Python than it would have been in C) - BUT, it is clearly not at all Pythonic and it reads like a C program (and not a very good one at that!)
What would be a better / more Pythonic approach to this? Is a simple FSM like this even still the right choice in Python?
My solution:
#! /usr/bin/python
import sys
f = open(sys.argv[1], "rb")
state = 0
if f:
for byte in f.read():
a = ord(byte)
if state == 0:
if a == 0xAA:
state = 1
elif state == 1:
if a == 0xAA:
state = 2
else:
state = 0
elif state == 2:
count = a;
skip = 9
state = 3
elif state == 3:
skip = skip -1
if skip == 0:
state = 4
elif state == 4:
print "%02x" %a
count = count -1
if count == 0:
state = 0
print "\r\n"
The coolest way I've seen to implement FSMs in Python has to be via generators and coroutines. See this Charming Python post for an example. Eli Bendersky also has an excellent treatment of the subject.
If coroutines aren't familiar territory, David Beazley's A Curious Course on Coroutines and Concurrency is a stellar introduction.
You could give your states constant names instead of using 0, 1, 2, etc. for improved readability.
You could use a dictionary to map (current_state, input) -> (next_state), but that doesn't really let you do any additional processing during the transitions. Unless you include some "transition function" too to do extra processing.
Or you could do a non-FSM approach. I think this will work as long as 0xAA 0xAA only appears when it indicates a "start" (doesn't appear in data).
with open(sys.argv[1], 'rb') as f:
contents = f.read()
for chunk in contents.split('\xaa\xaa')[1:]:
length = ord(chunk[0])
data = chunk[10:10+length]
print data
If it does appear in data, you can instead use string.find('\xaa\xaa', start) to scan through the string, setting the start argument to begin looking where the last data block ended. Repeat until it returns -1.
I am a little apprehensive about telling anyone what's Pythonic, but here goes. First, keep in mind that in python functions are just objects. Transitions can be defined with a dictionary that has the (input, current_state) as the key and the tuple (next_state, action) as the value. Action is just a function that does whatever is necessary to transition from the current state to the next state.
There's a nice looking example of doing this at http://code.activestate.com/recipes/146262-finite-state-machine-fsm. I haven't used it, but from a quick read it seems like it covers everything.
A similar question was asked/answered here a couple of months ago: Python state-machine design. You might find looking at those responses useful as well.
I think your solution looks fine, except you should replace count = count - 1 with count -= 1.
This is one of those times where fancy code-show-offs will come up ways of have dicts mapping states to callables, with a small driver function, but it isn't better, just fancier, and using more obscure language features.
I suggest checking out chapter 4 of Text Processing in Python by David Mertz. He implements a state machine class in Python that is very elegant.
I think the most pythonic way would by like what FogleBird suggested, but mapping from (current state, input) to a function which would handle the processing and transition.
You can use regexps. Something like this code will find the first block of data. Then it's just a case of starting the next search from after the previous match.
find_header = re.compile('\xaa\xaa(.).{9}', re.DOTALL)
m = find_header.search(input_text)
if m:
length = chr(find_header.group(1))
data = input_text[m.end():m.end() + length]

Categories

Resources