Hi everyone. I’m new to Python and wxPython. I’ve got a form I use to calculate the Sq In of a leather project.
I’m using python 3.9.13 and wxPython 4.20
This is being developed on a Windows 10 but will be used on both Mac and Windows machines once the project is complete.
I’m having the following issues:
- When I come into the form, no grid cell has the focus set - I start typing and nothing happens. I have to click the cell.
If I hit Tab or Enter, the OnKeyDown fires, but does not move to the appropriate cell - it does nothing but run the update and move off of the current cell.
The action I’m trying to make is this
ENTER KEY: Always go down 1 row and to col 0
TAB, if Col 0 Move to Col 1 on same row, if Col 1 go to Row +1, Col 0
I also need to have what3ever cell it is supposed to land on to get the focus so I can just type.
Currently I have to click in each cell I want/need to add.
There could be up to 20 pieces of leather with differing sizes, so in order to accurately calculate the Sq In, I need to get all the measurements in.
The form, one of several tabs, comes up and does everything else I’ve coded for great. Just no navigation.
Can anyone assist? Here is the module with the form
***********************************************************************
'''
Module Name : alwsqin.py
Author : Chris Anderson
Create Date : 03/10/2023
Description : This module contains the Sq In/MM/CM of leather used
This file is Copyright Anderson Leather Works (c) 2023
'''
########################################################################
#
# File Last Update and Person
#
#***********************************************************************
# Imports
import wx
#from Imports
from alwlogic import leather_sqin
class LeatherSqInPanel(wx.Panel):
'''
Name : LeatherSqInPanel
Author : Chris Anderson
Create Date : 02/23/2023
Description : Panel for the 'Leather Sq In Calculator
in Leatherworking Cost Estimator app
'''
dbTableName = 'None'
def __init__(self, parent):
wx.Panel.__init__(self, parent)
self.ls = leather_sqin
self.grid = wx.grid.Grid(self, size=(600, 515))
self.grid.CreateGrid(30, 6)
# Set column labels
self.grid.SetColLabelValue(0, "Length")
self.grid.SetColLabelValue(1, "Width")
self.grid.SetColLabelValue(2, "Total")
self.grid.SetColLabelValue(3, "Grand Total")
self.grid.SetColLabelValue(4, "Type")
self.grid.SetColLabelValue(5, "Select Calc Method")
for col in range(self.grid.GetNumberCols()):
self.grid.AutoSizeColumn(col)
self.grid.EnableEditing(True)
# Set dropdown choices for column 5, row 0
types = ["Sq In", "Sq Cm", "Sq Mm"]
self.type_dropdown = wx.ComboBox(self.grid, choices=types, style=wx.CB_DROPDOWN|wx.CB_READONLY)
self.type_editor = wx.grid.GridCellChoiceEditor(choices=types)
self.grid.SetCellEditor(0, 5, self.type_editor)
self.grid.SetCellRenderer(0, 5, wx.grid.GridCellAutoWrapStringRenderer())
# Set initial value for Type column
self.grid.SetCellValue(0, 5, types[0])
# Make Total and Grand Total cells read-only
for i in range(self.grid.GetNumberRows()):
self.grid.SetReadOnly(i, 2)
self.grid.SetReadOnly(i, 3)
# Set Type column values
self.grid.SetCellValue(0, 4, "Sq In")
self.grid.SetCellValue(1, 4, "Sq Cm")
self.grid.SetCellValue(2, 4, "Sq Mm")
# Populate grid with data from LeatherSqIn object
for i, row in enumerate(self.ls.get_data()):
for j, val in enumerate(row):
self.grid.SetCellValue(i, j, str(val))
if j == 0: # Check if first column
self.grid.SetCellValue(i, j+1, "Sq In") # Set default value for column 2
if i == 0 and j == 5:
self.grid.SetCellEditor(i, j, wx.grid.GridCellChoiceEditor(choices=["Sq In", "Sq Cm", "Sq Mm"]))
else:
self.grid.SetCellValue(i, j, str(val))
# Calculate totals and grand total
for i, row in enumerate(self.ls.get_data()):
self.ls.calculate_total(row)
grandTotal = 0.0
total = 0.0
self.ls.calculate_grand_total(grandTotal, total)
# Bind events
self.grid.Bind(wx.grid.EVT_GRID_CELL_CHANGED, self.OnCellChange)
self.grid.Bind(wx.EVT_KEY_DOWN, self.OnKeyDown) # Bind the key down event
# Add grid and button sizers to top sizer
sizer = wx.BoxSizer(wx.VERTICAL)
sizer.Add(self.grid, 1, wx.ALL)
self.SetSizer(sizer)
# Set grid line width for last column to 0
self.grid.SetCellHighlightPenWidth(0)
# Set grid cursor and focus
# Select cell R0C0 and set focus
wx.CallAfter(self.grid.SetGridCursor, 0, 0)
wx.CallAfter(self.grid.MakeCellVisible, 0, 0)
wx.CallAfter(self.grid.SetFocus)
def OnCellChange(self, event):
print("OnCellChange called")
row, col = event.GetRow(), event.GetCol()
value = self.grid.GetCellValue(row, col)
# update total for the row
if col == 0 or col == 1:
try:
self.grid.SetCellValue(row, 2, f'{self.ls.calc_sqin(float(self.grid.GetCellValue(row, 0)), float(self.grid.GetCellValue(row, 1)))}')
except ValueError:
pass
# update grand total
grandTotal = 0.0
for row in range(1, self.grid.GetNumberRows()):
try:
grandTotal = self.ls.calculate_grand_total(grandTotal, self.grid.GetCellValue(row, 2))
except ValueError:
pass
self.grid.SetCellValue(0, 3, f'{grandTotal:.2f}')
# handle key events
if isinstance(event, wx.grid.GridEvent) and event.GetEventType() == wx.EVT_KEY_DOWN:
print("Key event captured")
print(event)
keycode = event.GetKeyCode()
if keycode == wx.WXK_TAB:
print("OnCellChange:TAB called")
if col == 0:
if event.ShiftDown():
self.grid.SetGridCursor(row, col-1)
else:
self.grid.SetGridCursor(row, col+1)
else:
self.grid.SetGridCursor(row+1, 0)
elif keycode == wx.WXK_RETURN:
print("OnCellChange:Enter called")
self.grid.SetGridCursor(row+1, 0)
# update dropdown options for type column
if col == 5 and row == 0:
# update the total and grand total when the dropdown is changed
try:
length = float(self.grid.GetCellValue(row, 0))
width = float(self.grid.GetCellValue(row, 1))
type_ = self.grid.GetCellValue(row, 5)
sqin = self.ls.calc_sqin(length, width, type_)
self.grid.SetCellValue(row, 2, f'{sqin:.2f}')
grandTotal = 0.0
for row in range(1, self.grid.GetNumberRows()):
try:
grandTotal = self.ls.calculate_grand_total(grandTotal, self.grid.GetCellValue(row, 2))
except ValueError:
pass
self.grid.SetCellValue(0, 3, f'{grandTotal:.2f}')
except ValueError:
pass
def OnClearButtonClick(self, event):
self.grid.ClearGrid()
def OnKeyDown(self, event):
print ("Key was hit")
keyCode = event.GetKeyCode()
row, col = self.grid.GetGridCursorRow(), self.grid.GetGridCursorCol()
if keyCode == wx.WXK_RETURN:
# If ENTER is pressed, move to next row, column 0
if row < self.grid.GetNumberRows() - 1:
self.grid.SetGridCursor(row + 1, 0)
elif keyCode == wx.WXK_TAB:
# If TAB is pressed, move to the next column or row
if col == 0:
# If on column 0, move to column 1
self.grid.SetGridCursor(row, col + 1)
elif col == 1:
# If on column 1, move to next row, column 0
if row < self.grid.GetNumberRows() - 1:
self.grid.SetGridCursor(row + 1, 0)
else:
# If on any other column, move to next row, column 0
if row < self.grid.GetNumberRows() - 1:
self.grid.SetGridCursor(row + 1, 0)
def OnQuitButtonClick(self, event):
self.GetParent().Close()
Here is the alwlogic.py
#***********************************************************************
'''
Module Name : alwlogic.py
Author : Chris Anderson
Create Date : 02/23/2023
Description : Contains the business logic for the Leatherworking Cost Estimator application
This file is Copyright Anderson Leather Works (c) 2023
'''
# Imports
import sqlite3
# From Imports
from commons.dbCommon import DBCommon
#***********************************************************************
# File Last Update and Person
# 02/23/2023 Chris Anderson Initial file
# 03/01/2023 Chris Anderson - Added class getLabels
# 03/02/2003 Chris Anderson - Added class dbDataPull
#***********************************************************************
# Instatiate the DB call before any classes so I can use them in all classes
dbName ='./database/alw-lwc.db'
db = DBCommon(dbName)
class dbDataPull:
def __init__(self):
dbName ='./database/alw-lwc.db'
def _getFormattedTableFieldNames(self, dbTableName):
field_names = db._dbGetTableFieldNames(dbTableName)
field_names = [name.replace('_', ' ').title() for name in field_names]
return field_names
def _getRawTableFieldNames(self, dbTableName):
field_names = db._dbGetTableFieldNames(dbTableName)
return field_names
def _GetTableFieldNameAndType(self, dbTableName):
field_data = db._dbGetTableFieldNamesAndTypes(dbTableName)
return field_data
def _GetTableFieldData(self, dbTableName):
field_data = db._dbGetTableFieldAllData(dbTableName)
return field_data
def _GetTableFieldData(self, dbTableName):
field_data = db._dbGetTableFieldAllData(dbTableName)
return field_data
class General:
def __init__(self):
pass
'''
Function Name : getOffset
Author : Chris Anderson
Create Date : 02/23/2023
Description : returns a tuple containing the cell_name and new_cell_label, where
cell_name is the cell label with spaces replaced by underscores and
new_cell_label is the cell label with added offset spaces and a colon.
offsetlen is an optional parameter with a default value of 10.
This file is Copyright Anderson Leather Works (c) 2023
'''
def getOffset(cell_label, offsetlen=10):
offset = (offsetlen - len(cell_label)) * ' '
new_cell_label = cell_label + offset + " : "
cell_name = cell_label.replace(" ", "_")
return cell_name, new_cell_label
class LeatherSqIn:
def __init__(self):
self.rows = [[0, 0, 0, 0] for _ in range(20)]
'''
Function Name : calc_sqin
Author : Chris Anderson
Create Date : 02/23/2023
Description : calculates the square inches for a given length and width.
This file is Copyright Anderson Leather Works (c) 2023
'''
def calc_square(self, length, width):
return round(length * width,2)
'''
Function Name : sqin2sqcm
Author : Chris Anderson
Create Date : 02/23/2023
Description : Converts a value from square inches to square centimeters.
Parameters :
- squaredNumber: The value to be converted in square inches.
Returns : The value in square centimeters.
This file is Copyright Anderson Leather Works (c) 2023
'''
def sqin2sqcm(self, squaredNumber):
return round(squaredNumber * 6.4516,2)
'''
Function Name : sqin2sqmm
Author : Chris Anderson
Create Date : 02/23/2023
Description : Converts a value from square inches to square millimeters.
Parameters :
- squaredNumber: The value to be converted in square inches.
Returns : The value in square millimeters.
This file is Copyright Anderson Leather Works (c) 2023
'''
def sqin2sqmm(self, squaredNumber):
return round(squaredNumber * 645.16,2)
'''
Function Name : sqcm2sqin
Author : Chris Anderson
Create Date : 02/23/2023
Description : Converts a value from square centimeters to square inches.
Parameters :
- squaredNumber: The value to be converted in square centimeters.
Returns : The value in square inches.
This file is Copyright Anderson Leather Works (c) 2023
'''
def sqcm2sqin(self, squaredNumber):
return round(squaredNumber / 6.4516,2)
'''
Function Name : sqcm2sqmm
Author : Chris Anderson
Create Date : 02/23/2023
Description : Converts a value from square centimeters to square millimeters.
Parameters :
- squaredNumber: The value to be converted in square centimeters.
Returns : The value in square millimeters.
This file is Copyright Anderson Leather Works (c) 2023
'''
def sqcm2sqmm(self, squaredNumber):
return round(squaredNumber / 100,2)
'''
Function Name : sqmm2sqin
Author : Chris Anderson
Create Date : 02/23/2023
Description : Converts a value from square millimeters to square inches.
Parameters :
- squaredNumber: The value to be converted in square millimeters.
Returns : The value in square inches.
This file is Copyright Anderson Leather Works (c) 2023
'''
def sqmm2sqin(self, squaredNumber):
return round(squaredNumber / 645.16,2)
'''
Function Name : sqmm2sqcm
Author : Chris Anderson
Create Date : 02/23/2023
Description : Converts a value from square millimeters to square centimeters.
Parameters :
- squaredNumber: The value to be converted in square millimeters.
Returns : The value in square centimeters.
This file is Copyright Anderson Leather Works (c) 2023
'''
def sqmm2sqcm(self, squaredNumber):
return round(squaredNumber / 1000,2)
'''
Function Name : calculate_grand_total
Author : Chris Anderson
Create Date : 02/23/2023
Description : calculates the grand total for a given grandtotal and total. If the total
is None, it is set to 0.0 before calculating the grand total.
This file is Copyright Anderson Leather Works (c) 2023
'''
def calculate_grand_total(self, grandtotal, total):
if total is None:
total = 0.0
return round(float(grandtotal) + float(total),2)
'''
Function Name : calculate_total
Author : Chris Anderson
Create Date : 02/23/2023
Description : calculates the total square inches for a given row in the table by multiplying
the length and width columns. If either length or width is 0 or empty, the total
for that row is set to 0.
This file is Copyright Anderson Leather Works (c) 2023
'''
def calculate_total(self, row):
length, width = row[0], row[1]
if length and width:
total = length * width
else:
total = 0
row[2] = total
'''
Function Name : get_data
Author : Chris Anderson
Create Date : 02/23/2023
Description : returns the current rows data of the LeatherSqIn instance.
This file is Copyright Anderson Leather Works (c) 2023
'''
def get_data(self):
return self.rows
'''
Function Name : get_grand_total_row
Author : Chris Anderson
Create Date : 02/23/2023
Description : returns the grand total row of the LeatherSqIn instance.
This file is Copyright Anderson Leather Works (c) 2023
'''
def get_grand_total_row(self):
return self.rows[0]
leather_sqin = LeatherSqIn()