Python Win32 hide subtotals of pivot table - python

I've got the following code:
def pivot_table(wb: object, ws1: object, pt_ws: object, ws_name: str, pt_name: str, pt_rows: list, pt_filters: list, pt_fields: list):
"""
wb = workbook1 reference
ws1 = worksheet1 that contain the data
pt_ws = pivot table worksheet number
ws_name = pivot table worksheet name
pt_name = name given to pivot table
pt_rows, pt_cols, pt_filters, pt_fields: values selected for filling the pivot tables
"""
# pivot table location
pt_loc = len(pt_filters) + 2
# grab the pivot table source data
#to edit with new data
pc = wb.PivotCaches().Create(SourceType=win32c.xlDatabase, SourceData=ws1.Range("A9:Q10807"))
# create the pivot table object
pc.CreatePivotTable(TableDestination=f'{ws_name}!R{pt_loc}C1', TableName=pt_name)
# selecte the pivot table work sheet and location to create the pivot table
pt_ws.Select()
pt_ws.Cells(pt_loc, 1).Select()
# Visiblity True or False
pt_ws.PivotTables(pt_name).ShowValuesRow = False
pt_ws.PivotTables(pt_name).RowGrand = False
pt_ws.PivotTables(pt_name).ColumnGrand = False
pt_ws.PivotTables(pt_name).RowAxisLayout(win32c.xlTabularRow)
Now I would like to hide subtotals for all columns with python. Anyone know how to do this?
I've tried the following:
#pt_ws.PivotTables("User ID").RowGrand = False
pt_ws.PivotTables("User ID").ColumnGrand = False
pt_ws.PivotTables("User Group").RowGrand = False
pt_ws.PivotTables("User Group").ColumnGrand = False
pt_ws.PivotTables("Risk Description").RowGrand = False
pt_ws.PivotTables("Risk Description").ColumnGrand = False
Hpwever this gives an error..
com_error: (-2147352567, 'Exception occurred.', (0, 'Microsoft Excel', 'PivotTables method of Worksheet class failed', 'xlmain11.chm', 0, -2146827284), None)
Please help!

As defined in the Subtotals documentation at the end of the Remarks part, you need to set all values of Subtotals tuple to be False.
To hide all subtotals your code should look like that
for field in fields:
field.Subtotals = tuple(False for _ in range(12))
here field should be an item from the PivotFields collection. For example, to disable subtotals for User Id
pt.PivotFields('User Id').Subtotals = tuple(False for _ in range(12))

Related

Pandas table customization

This is a section of my python code, this class retrieves a sheet from smartsheet and turns it into a df, which later uses to display on a table on my GUI. I've been trying to change row colors but cant seem to get it right, in the code I managed to change the color of columns, however when I pass self.table.rowcolors... it does nothing. Anyone has an idea of what else I could try?
class SmartsheetFrame(Frame):
def __init__(self, master):
super().__init__(master)
self.configure(bg='white')
# Get the sheet
sheet_id = 3839061875025796
ss_client = smartsheet.Smartsheet(access_token=os.environ.get('SMARTSHEET_ACCESS_TOKEN'))
sheet = ss_client.Sheets.get_sheet(sheet_id)
rows = sheet.rows
columns = sheet.columns
#Creating a dataframe from the sheet
df = pd.DataFrame(columns=[col.title for col in columns])
for row in rows:
df.loc[row.id] = [col.value for col in row.cells]
last_4_rows = df.tail(4)
# Create a Table widget and set its properties
self.table = pt.Table(self, dataframe=df, showstatusbar=True, height=600, width=1200)
# Specify the row index you want to color
self.table.columncolors['AEM ID'] = 'forestgreen'
self.table.columncolors['Intel ID'] = 'midnightblue'
self.table.columncolors['Site'] = 'darkslategrey'
self.table.columncolors['Mode'] = 'firebrick'
self.table.redraw()
self.table.show()
I've tried many methods that I found online, so far I've managed to chage the color of columns, but havent been able to do the same for rows

Is there a possibility to skip rows and set column headers for the first n rows?

I have the following function using the following dependencies
import win32com.client as win32
import pandas as pd
import numpy as np
from pathlib import Path
import re
import sys
win32c = win32.constants
def run_excel(f_path: Path, f_name: str, sheet_name: str):
filename = f_path / f_name
# create excel object
excel = win32.gencache.EnsureDispatch('Excel.Application')
# excel can be visible or not
excel.Visible = True # False
# try except for file / path
try:
wb = excel.Workbooks.Open(filename)
except com_error as e:
if e.excepinfo[5] == -2146827284:
print(f'Failed to open spreadsheet. Invalid filename or location: {filename}')
else:
raise e
sys.exit(1)
# set worksheet
ws1 = wb.Sheets('GRC User Data Clean Up')
However, my ws1 has headers that start at row 9. How do I specify this with win32?
I want to skip the first eight rows
Please help!
I can't use pandas here unfortunately
Then I want to create a pivot table from the eight row down until 12000;
def run_excel(f_path: Path, f_name: str, sheet_name: str):
filename = f_path / f_name
# create excel object
excel = win32.gencache.EnsureDispatch('Excel.Application')
# excel can be visible or not
excel.Visible = True # False
# try except for file / path
try:
wb = excel.Workbooks.Open(filename)
except com_error as e:
if e.excepinfo[5] == -2146827284:
print(f'Failed to open spreadsheet. Invalid filename or location: {filename}')
else:
raise e
sys.exit(1)
# set worksheet
ws1 = wb.Sheets('GRC User Data Clean Up')
# Setup and call pivot_table
ws2_name = 'pivot_table'
wb.Sheets.Add().Name = ws2_name
ws2 = wb.Sheets(ws2_name)
# update the pt_name, pt_rows, pt_cols, pt_filters, pt_fields at your preference
pt_name = 'example' # pivot table name, must be a string
pt_rows = ['Access Risk ID', 'User ID','User Group','Execution Hit on Both Sides?', 'Risk Description'] # rows of pivot table, must be a list
# pt_cols = [] # columns of pivot table, must be a list
pt_filters = ['Final verdict','Execution Hit on Both Sides?' ] # filter to be applied on pivot table, must be a list
# [0]: field name [1]: pivot table column name [3]: calulation method [4]: number format (explain the list item of pt_fields below)
pt_fields = [['Access Risk ID', 'Access Risk ID', win32c.xlCount, '0'], # must be a list of lists
['User ID', 'User ID', win32c.xlCount, '0'],
['User Group', 'User Group', win32c.xlCount, '0'],
['Execution Hit on Both Sides?', 'Execution Hit on Both Sides?', win32c.xlCount, '0'],
['Risk Description', 'Risk Description', win32c.xlCount, '0']]
# calculation method: xlAverage, xlSum, xlCount
pivot_table(wb, ws1, ws2, ws2_name, pt_name, pt_rows, pt_filters, pt_fields)
wb.Save() # save the pivot table created
# wb.Close(True)
# excel.Quit()
def pivot_table(wb: object, ws1: object, pt_ws: object, ws_name: str, pt_name: str, pt_rows: list, pt_filters: list, pt_fields: list):
"""
wb = workbook1 reference
ws1 = worksheet1 that contain the data
pt_ws = pivot table worksheet number
ws_name = pivot table worksheet name
pt_name = name given to pivot table
pt_rows, pt_cols, pt_filters, pt_fields: values selected for filling the pivot tables
"""
# pivot table location
pt_loc = len(pt_filters) + 2
# grab the pivot table source data
pc = wb.PivotCaches().Create(SourceType=win32c.xlDatabase, SourceData=ws1.UsedRange)
# create the pivot table object
pc.CreatePivotTable(TableDestination=f'{ws_name}!R{pt_loc}C1', TableName=pt_name)
# selecte the pivot table work sheet and location to create the pivot table
pt_ws.Select()
pt_ws.Cells(pt_loc, 1).Select()
# Sets the rows, columns and filters of the pivot table
for field_list, field_r in ((pt_filters, win32c.xlPageField),
(pt_rows, win32c.xlRowField)):
for i, value in enumerate(field_list):
pt_ws.PivotTables(pt_name).PivotFields(value).Orientation = field_r
pt_ws.PivotTables(pt_name).PivotFields(value).Position = i + 1
# Sets the Values of the pivot table
for field in pt_fields:
pt_ws.PivotTables(pt_name).AddDataField(pt_ws.PivotTables(pt_name).PivotFields(field[0]), field[1], field[2]).NumberFormat = field[3]
# Visiblity True or False
pt_ws.PivotTables(pt_name).ShowValuesRow = True
pt_ws.PivotTables(pt_name).ColumnGrand = True

Modify Named Table in Excel File with Python openpyxl

I want to add columns to an existing table in an excel file.
Therefore I wan't to use python and the openpyxl library.
Right now I use a class when it is initialising, it is connecting to the file.
Afterwards I call the check_for_column function and when the column is not existing it should create it. And in the end of the script I save the file.
import os
from openpyxl import load_workbook
from openpyxl.worksheet.table import Table, TableColumn, range_boundaries
from openpyxl.utils.cell import get_column_letter
class ExcelHandler:
_wb_name = None
_table = None
_wb = None
def __init__(self):
self._wb_name = os.getenv('EXCEL_FULLPATH')
self._wb = load_workbook(filename=self._wb_name, keep_vba=True)
sheet = self._wb['DataInbox']
self._table = sheet.tables['WebPageForms']
return
def check_for_column(self, column_name):
if not column_name in self._table.column_names:
lst_ids = [my_object.id for my_object in self._table.tableColumns]
new_id = lst_ids[-1]+1
# change range of table
min_col, min_row, max_col, max_row = range_boundaries(
self._table.ref)
max_col += 1
mx = get_column_letter(max_col)
mn = get_column_letter(min_col)
self._table.ref = '{}{}:{}{}'.format(mn, min_row, mx, max_row)
# add column to table
tc = TableColumn(id=new_id, name=column_name)
self._table.tableColumns.append(tc)
return
def save_wb(self):
self._wb.save(self._wb_name)
return
The code runs fine as shown. Although when I then try to open the file with excel it gives me an alert saying:
We found a problem with some content in ’file.xlsm’. Do you want us to try to recover as much as we can? If you trust the source of this workbook, click Yes.
This is the repair result of excel when I press yes
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<recoveryLog xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main"><logFileName>Repair Result to file.xml</logFileName><summary>Errors were detected in file ’*path*/file.xlsm’</summary><repairedRecords summary="Following is a list of repairs:"><repairedRecord>Repaired Records: Table from /xl/tables/table1.xml part (Table)</repairedRecord></repairedRecords></recoveryLog>
I would highly appreciate If anyone could help me
Ok, I found the problem why the excel file is corrupt, my bad.
when I create the column in the table, I also have to write the name in the respective cell:
def check_for_column(self, column_name):
***
# write name in cell of new column header
self._ws.cell(row=min_row, column=max_col).value = column_name
***
return
If I add this to the code, my table is modified correctly

extend xlsxwriter.Worksheet() class

I currently override the xlsxwriter.Workbook, called rpt.Workbook. Would like to add method to xlsxwriter.Worksheet(), but since xlsxwriter.Workbook() imports Worksheet,not sure how this can be done without major convulsions. Currently, I have to pass the worksheet object as an argument.
Seems like I need to write override methods for xlsxwriter.Workbook() to point to a custom class of xlsxwriter.Worksheet() , but can't figure out how.
Here is the current override rpt.Workbook() being used:
####################################################################
class Workbook(xlsxwriter.Workbook):
####################################################################
"""\nSpreadsheet class provides methods to build a spreadsheet.
"""
####################################################################
def __init__(self,filename=None, options={}):
####################################################################
try:
filename = rpt.fname(filename)
except FileNotFoundError as err:
log.error(err)
return False
log.info("Initializing excel file " + filename)
super().__init__(filename,options)
####################################################################
def add_tab(self,name=None,data=None,header=None,
srow=0,scol=0,autocol=True):
####################################################################
"""\nProvides method to add_worksheet and add_table in 1 call.
Required Attribute args:
name = TAB name
header = list of header names
data = list of lists for spreadsheet contents
Optional Attribute args:
srow = starting row for table, default 0
scol = starting col for table, default 0
autocol = True/False, auto set the column sizes
add_tab also adds the worksheet.header attribute to
allow the set_col_by_name function to work
"""
if not data:
log.warning("data=[][] required")
return None
if not header:
log.warning("header=[] required")
return False
columns = []
for field in header:
columns.append({ 'header' : field })
worksheet = self.add_worksheet(name)
worksheet.header = header
tableinfo= {
'data' : data,
'columns' : columns
}
lastcol = scol + (len(header) - 1)
lastrow = srow + (len(data) + 1)
worksheet.add_table(srow,scol,lastrow,lastcol,tableinfo)
#if autocol:
#self.auto_set_columns(worksheet=worksheet,data=data,scol=scol)
worksheet.freeze_panes(0,1)
return worksheet
####################################################################
def auto_set_columns(self,worksheet=None,data=None,header=None,scol=0):
####################################################################
"""\nDetermines the max length of each column and then set
that column width.
Required Attribute args:
worksheet = worksheet object
data = list of lists data
Optional Attribute args:
scol = Column start
header = row of headers for data list of lists.
If header not specified, worksheet
must have been created with self.add_tab()
"""
if not header and worksheet.header:
header = worksheet.header
## table = [] list of lists, combine header and data
table = []
table.append(header) for row in data:
table.append(row)
ziptable = list(zip (*table))
colsizes = []
for idx,val in enumerate(table[0]):
size = max(len(s) for s in ziptable[idx])
colnum = idx + scol
log.debug("Setting column => {} col size => {} => {}".format(colnum,val,size))
worksheet.set_column(colnum,colnum,size)
I want to add a method to xlsxwriter.Worksheet() called auto_set_columns(). Currently I have to pass the worksheet object (worksheet=worksheet) as an object to get this to work.I would like to utilize worksheet.auto_set_columns() instead. Currently auto_set_columns() is a method of rpt.Workbook.
Would like auto_set_columns() to be an extended method of xlsxwriter.Worksheet.
The script side utilization currently looks like this and works:
excelfile = nashomes + '/nas_homes.xlsx'
spreadsheet = rpt.Workbook(excelfile)
worksheet = spreadsheet.add_tab(name='Nas Homes',data=hrpt.data,header=hrpt.header)
spreadsheet.auto_set_columns(worksheet=worksheet,data=hrpt.data,scol=0)
What I desire, notice the last line changes:
excelfile = nashomes + '/nas_homes.xlsx'
spreadsheet = rpt.Workbook(excelfile)
worksheet = spreadsheet.add_tab(name='Nas Homes',data=hrpt.data,header=hrpt.header)
worksheet.auto_set_columns(data=hrpt.data,scol=0)
Goal desired here is that worksheet object (which is xlsxwriter.Worksheet() ) can have an extended "auto_set_columns" method. However, since the worksheet object is created from a add_worksheet() method in the xlsxwriter.Workbook() class, I can't figure out how to extend xlsxwriter.Worksheet() without major override methods to xlsxwriter.Workbook() also. How can I get xlsxwriter.Workbook() to reference my extended xlsxwriter.Worksheet() when Workbook.add_worksheet() creates the Worksheet object?
How about monkey patching the worksheet before returning it from add_tab?
First create the standalone function outside of any class definitions:
import types
def auto_set_columns(self,data=None,header=None,scol=0):
if not header and self.header:
header = self.header
## table = [] list of lists, combine header and data
table = []
table.append(header)
for row in data:
table.append(row)
ziptable = list(zip (*table))
colsizes = []
for idx,val in enumerate(table[0]):
size = max(len(s) for s in ziptable[idx])
colnum = idx + scol
print "Setting column => {} col size => {} => {}".format(colnum,val,size)
self.set_column(colnum,colnum,size)
And then inside your Worksheet.add_tab function, patch in the method before returning:
....
worksheet.freeze_panes(0,1)
worksheet.auto_set_columns = types.MethodType( auto_set_columns, worksheet )
return worksheet
Now you should be able to run:
worksheet = spreadsheet.add_tab(name='Nas Homes',data=hrpt.data,header=hrpt.header)
worksheet.auto_set_columns(data=hrpt.data,scol=0)

PyGTK TreeView showing blank rows from ListStore

I'm trying to show several rows from database in a TreeView but all I am getting are some dummy rows as you can see in the image below.
class SettingsDialog(gtk.Dialog):
def __init__(self):
gtk.Dialog.__init__(self, "Server Settings", self, gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT)
# Instantiate ServerManager
self.server_manager = ServerManager()
# Create TreeStore for Server list
self.liststore = gtk.ListStore(str, str)
self.treeview = gtk.TreeView(self.liststore)
# Create TreeViewColumns to display data
cell = gtk.CellRendererText()
col = gtk.TreeViewColumn("Name")
col.pack_start(cell, True)
self.treeview.append_column(col)
cell = gtk.CellRendererText()
col = gtk.TreeViewColumn("URL")
col.pack_start(cell, True)
self.treeview.append_column(col)
self.vbox.pack_start(self.treeview)
self.resize(500,350)
self.set_position(gtk.WIN_POS_CENTER)
self.show_all()
self.load_server_list()
def load_server_list(self):
self.liststore.clear()
servers = self.server_manager.list()
for name, url in servers.iteritems():
self.liststore.append([name, url])
self.show_all()
Data returned from self.server_manager.list() is valid an added to the list store perfectly. There seems to be something wrong with the CellRenderers but I wasn't able to find the error.
You have to set an attribute mapping on the column. For example, the cellrenderer's text attribute value will be displayed in the treeview cell. It is taken from the values on the data model (self.liststore). The column number on the model where the value is taken from is specified in the attribute mapping.
## Take value for *text* attribute of the cell renderer from the model's 3rd column
col = gtk.TreeViewColumn(title, cellrenderer, text=2)

Categories