Displaying an multiple excel tables into different tabs (one table per tab) - python

I'm trying to make a widget with multiple tabs that are occupied with different excel tables per tab and can't figure why I cant populate each tab separately.
About the tables the number of columns remain the same for all Excel tables only the number of rows change.
import sys
from PyQt5 import QtCore , QtWidgets ,QtGui
import pandas as pd
import os
class MyApp(QtWidgets.QWidget):
def __init__(self):
super().__init__()
self.sizeHint().width(), self.sizeHint().height()
self.resize(self.width(), self.height())
layout = QtWidgets.QVBoxLayout()
self.setLayout(layout)
self.table = QtWidgets.QTableWidget()
self.tabs = QtWidgets.QTabWidget()
self.table.setSortingEnabled(True)
self.table.setEditTriggers(QtWidgets.QAbstractItemView.NoEditTriggers)
self.table.setSelectionBehavior(QtWidgets.QAbstractItemView.SelectRows)
self.table.selectionModel().selectionChanged.connect(self.selected)
# self.add_flight_tab()
layout.addWidget(self.tabs)
tab_list = self.add_flight_tab(path_xl)
for tables , names in zip(range(tab_list.__sizeof__()), tab_list):
self.tabs.setCurrentIndex(tables)
self.load_data(f"{path_xl}/{names}")
self.setLayout(layout)
def load_data(self, path_to_xl):
df = pd.read_excel(path_to_xl,sheet_name=0, usecols=["AWB", "Destn.", "SCC", "Agent Name",
"Stated Pcs/Wgt/Vol (kg/CBM)","Booked Flight"])
if df.size == 0:
return
df.fillna('', inplace=True)
self.table.setRowCount(df.shape[0])
self.table.setColumnCount(df.shape[1])
self.table.setHorizontalHeaderLabels(df.columns)
self.table.insertColumn(self.table.columnCount())
self.table.setHorizontalHeaderItem(6 , QtWidgets.QTableWidgetItem("Offload"))
self.table.horizontalHeader().setSectionResizeMode(QtWidgets.QHeaderView.ResizeToContents)
for row in df.iterrows():
values = row[1]
for i in range(df.shape[0]):
check_box = QtWidgets.QTableWidgetItem()
check_box.setFlags(QtCore.Qt.ItemIsUserCheckable | QtCore.Qt.ItemIsEnabled)
check_box.setCheckState(QtCore.Qt.CheckState())
self.table.setItem(i, 6, check_box)
for col_index, value in enumerate(values):
if isinstance(value, (float, int)):
value = '{0:0,.0f}'.format(value)
tableItem = QtWidgets.QTableWidgetItem(str(value))
self.table.setItem(row[0], col_index, tableItem)
def add_flight_tab(self, path_files):
files = os.listdir(path_files)
new_ls = []
for xl, tab_ind in zip(files, range(files.__sizeof__())):
if xl.endswith(".xlsx") or xl.endswith(".XLSX"):
new_ls.append(xl)
self.tabs.insertTab(tab_ind,QtWidgets.QTableWidget(), xl)
self.tabs.setCurrentIndex(tab_ind)
return new_ls
if __name__ == '__main__':
path_xl = "path to excel"
app = QtWidgets.QApplication(sys.argv)
myApp = MyApp()
myApp.show()
# print(myApp.add_flight_tab())
try:
sys.exit(app.exec_())
except SystemExit:
print("Closing window...")
Everything I have tried gives two results(there must be something I'm missing out in here) :
The Widget is populated but the are no other tabs only thing changed is the following lines:
layout.addWidget(self.tabs)
tab_list = self.add_flight_tab(path_xl)
for tables , names in zip(range(tab_list.__sizeof__()), tab_list):
self.tabs.setCurrentIndex(tables)
self.load_data(f"{path_xl}/{names}")
layout.addChildWidget(self.table)
For now the names of the tabs is irrelevant , I only need to have number of tabs according to number of Excel files and each tab is filled with table accordingly.

Related

How to iterate over QSqlTableModel and export to .csv

I'm trying to export data from SQlite3 database to .csv, using pyqt5, QSqlTableModel and csv. I can export all the data from my database, I'm trying to export only the selected rows from QSqlTableModel, I really liked to mess around QSqlTableModel class, the write/read interaction is really amazing.
I got to this stage by following a couple of examples from others users.
def on_Click(self):
with open("cadastro.csv", 'w', newline='') as csvfile:
csvwriter = csv.writer(csvfile, delimiter=',', quotechar='"', quoting=csv.QUOTE_MINIMAL)
if self.tableView.selectionModel().hasSelection():
names = []
rows = self.tableView.selectionModel().selectedIndexes()
for i in rows:
name = self.tableView.model().index(i.row(), i.column()).data()
names.append(name)
print(name)
csvwriter.writerow(
['id', 'instituicao', 'crianca', 'data_de_nascimento', 'manequim', 'calcado', 'responsavel'])
csvwriter.writerow(names)
# csvwriter.writerows(map(lambda x: [x], names))
# print(i.row())
else:
print('No row selected!')
So I'm trying to select rows and save that selection on a csv file like that.
enter image description here
The result is:
enter image description here
and I need something like that :
enter image description here
Also here is the rest of the code.
class MainWindow(QWidget):
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent)
self.db = QSqlDatabase.addDatabase("QSQLITE")
self.db.setDatabaseName("bolsa_natal.db")
self.db.open()
self.model = QSqlTableModel()
self.initializedModel()
self.tableView = QTableView()
# turn off row numbers
self.tableView.verticalHeader().setVisible(False)
# turn off horizontal headers
# self.tableView.horizontalHeader().setVisible(False)
self.tableView.resizeColumnsToContents()
self.tableView.resizeRowsToContents()
self.tableView.horizontalHeader().setDefaultAlignment(Qt.AlignLeft)
self.source_model = self.model
# self.initializedModel()
self.proxy_model = QSortFilterProxyModel(self.source_model)
self.searchcommands = QLineEdit("")
self.searchcommands.setObjectName(u"searchcommands")
self.searchcommands.setAlignment(Qt.AlignLeft)
self.buttonCadastro = QPushButton('to .csv', self)
self.buttonCadastro.clicked.connect(self.exportCadastro)
self.proxy_model.setSourceModel(self.source_model)
self.tableView.setModel(self.proxy_model)
# hide columns from the maintableview
# self.tableView.hideColumn(0)
# self.tableView.hideColumn(3)
# self.tableView.hideColumn(4)
# self.tableView.hideColumn(5)
# self.tableView.hideColumn(6)
# self.tableView.hideColumn(7)
self.proxy_model.setFilterRegExp(QRegExp(self.searchcommands.text(), Qt.CaseInsensitive,
QRegExp.FixedString))
# search all columns
self.proxy_model.setFilterKeyColumn(-1)
# enable sorting by columns
self.tableView.setSortingEnabled(True)
# set editing disabled for my use
# self.tableView.setEditTriggers(QAbstractItemView.NoEditTriggers)
self.tableView.setWordWrap(True)
self.layout = QVBoxLayout()
self.command_description = QLabel()
self.command_description.setWordWrap(True)
# self.command_requires_label = QLabel("Command Requires:")
# self.command_requires_label.setWordWrap(True)
hLayout = QHBoxLayout()
# hLayout.addWidget(self.command_requires_label)
self.layout.addWidget(self.tableView)
self.layout.addWidget(self.searchcommands)
self.layout.addWidget(self.buttonCadastro)
self.layout.addWidget(self.command_description)
self.layout.addLayout(hLayout)
self.setLayout(self.layout)
self.resize(730, 600)
self.searchcommands.textChanged.connect(self.searchcommands.update)
self.searchcommands.textChanged.connect(self.proxy_model.setFilterRegExp)
self.searchcommands.setText("")
self.searchcommands.setPlaceholderText(u"buscar")
# self.tableView.clicked.connect(self.listclicked)
# self.startDate.dateChanged.connect(self.startDate.update)
# self.startDate.dateChanged.connect(self.FilterBetweenDates)
# self.endDate.dateChanged.connect(self.endDate.update)
# self.endDate.dateChanged.connect(self.FilterBetweenDates)
# Save Button
def exportCadastro(self):
with open('cadastro.csv', 'w') as stream: # 'w'
writer = csv.writer(stream, lineterminator='\n')
for rowNumber in range(self.model.rowCount()):
fields = [
self.model.data(
# self.tableView.selectionModel().selectedRows()
self.model.index(rowNumber, columnNumber),
Qt.DisplayRole
)
for columnNumber in range(self.model.columnCount())
]
print(fields)
writer.writerow(fields)
def initializedModel(self):
self.model.setTable("cadastro")
self.model.setEditStrategy(QSqlTableModel.OnFieldChange)
# self.model.setEditStrategy(QSqlTableModel.OnManualSubmit)
self.model.select()
self.model.setHeaderData(0, Qt.Horizontal, "ID")
self.model.setHeaderData(1, Qt.Horizontal, "instituicao")
self.model.setHeaderData(2, Qt.Horizontal, "crianca")
self.model.setHeaderData(3, Qt.Horizontal, "data_de_nascimento")
self.model.setHeaderData(4, Qt.Horizontal, "manequim")
self.model.setHeaderData(5, Qt.Horizontal, "calcado")
self.model.setHeaderData(6, Qt.Horizontal, "responsavel")
def onAddRow(self):
self.model.insertRows(self.model.rowCount(), 1)
self.model.submit()
def onDeleteRow(self):
self.model.removeRow(self.tableView.currentIndex().row())
self.model.submit()
self.model.select()
def closeEvent(self, event):
self.db.close()
# def listclicked(self, index):
# row = index.row()
# cmd = self.proxy_model.data(self.proxy_model.index(row, 3))
# cmd_requires = self.proxy_model.data(self.proxy_model.index(row, 4))
# cmd_description = self.proxy_model.data(self.proxy_model.index(row, 5))
# print(cmd_description)
# self.command_description.setText(cmd_description)
# self.command_requires_label.setText('This command requires being executed via: ' + cmd_requires.upper())
if __name__ == "__main__":
app = QApplication(sys.argv)
window = MainWindow()
window.show()
sys.exit(app.exec_())
You have to iterate over the selected rows:
def exportCadastro(self):
with open("cadastro.csv", "w") as stream:
writer = csv.writer(stream, lineterminator="\n")
for row in self.tableView.selectionModel().selectedRows():
fields = []
for col in range(self.tableView.model().columnCount()):
field = self.tableView.model().index(row, col).data()
fields.append(field)
writer.writerow(fields)

Displaying an excel table into PyQt gui window

I need bit of assistance with my python code. I am a newbie to python so not very good at it.
I have an excel spreadsheet with a bunch of lecture times and I am using the code below;
df = pd.read_excel('/home/pi/timetable1.xlsx')
df['Date'] = pd.to_datetime(df['Date']).dt.strftime("%d-%m-%Y")
now = pd.to_datetime('today').strftime("%d-%m-%Y")
print(df[df['Date'] == now])
which displays a simple line of timetable text such as below [xx:xx is time in 24 hr format];
Lesson 1: Lesson 2: Lesson 3: Lession 4: Lesson 5:
xx:xx xx:xx xx:xx xx:xx xx:xx
I have configured it so that the above displayed times only shows the times for the "Current Date".
What I am trying to acheive is that I want to use PyQt4 to display this information on a graphical window.
So for example, the below displays "HELLO WORLD" text in the gui window;
def wxfinished():
attribution3.setText("HELLO WORLD")
attribution3 = QtGui.QLabel(foreGround)
attribution3.setObjectName("attribution3")
attribution3.setStyleSheet("#attribution3 { " +
"background-color: transparent; color: " +
Config.textcolor +
"; font-size: " +
str(int(50 * xscale)) + #50 is the size of the text
"px; " +
Config.fontattr +
"}")
attribution3.setAlignment(Qt.AlignTop)
attribution3.setGeometry(450 * xscale, 670 * yscale, 1000 * xscale, 1000)
w.show()
w.showFullScreen()
sys.exit(app.exec_())
How can I change it so that instead of "HELLO WORLD" I can print out the timetable output?
While I'm not that familiar with Pandas, here's an MRE to help you. In this, a table is made, the labels are set to the excel's labels (lesson x), and the singular row is filled out with the dates.
from PyQt4.QtGui import QApplication, QTableWidget, QLabel, QFont
from PyQt4.QtCore import Qt
import sys
import pandas
CELL_FONT = QFont()
CELL_FONT.setPointSize(8)
def getLessions(): # assuming the code you provided works
"""
Returns Pandas DataFrame object w/ today's date info
:return:
"""
data = pandas.read_excel("path")
date = pandas.to_datatime(data["Date"]).dtftime("%d-%m-%Y")
now = pandas.to_datetime('today').strftime("%d-%m-%Y")
return data[date == now]
def makeWidget():
datum = getLessions()
columns = len(datum.columns)
# Use table to display information
table = QTableWidget()
table.setRowCount(1)
table.setColumnCount(columns)
table.setHorizontalHeaderLabels(datum.columns) # I think datum.columns returns [str], I could be wrong
table.setVerticalHeaderLabels([""])
# Row will be filled with Labels w/ times
for i in range(columns):
lession_time = str(datum.iloc[0,i]) # edit: iloc returns a Timestamp object
label = QLabel()
label.setFont(CELL_FONT)
label.setText(lession_time)
label.setAlignment(Qt.AlignHCenter | Qt.AlignBottom)
table.setCellWidget(0, i, label)
return table
def makeWidgetDummy(): # Doesn't use Pandas
columns = 5
table = QTableWidget()
table.setRowCount(1)
table.setColumnCount(columns)
horizontal_lbls = ["Lesson {}".format(i + 1) for i in range(columns)]
table.setHorizontalHeaderLabels(horizontal_lbls)
table.setVerticalHeaderLabels([""])
for i in range(columns):
lession_time = "XX:XX"
label = QLabel()
label.setFont(CELL_FONT)
label.setText(lession_time)
label.setAlignment(Qt.AlignHCenter | Qt.AlignBottom)
table.setCellWidget(0, i, label)
def main():
app = QApplication(sys.argv)
widget = makeWidget()
# widget = makeWidgetDummy()
widget.show()
app.exec_()
if __name__ == "__main__":
main()

How to properly order columns in a QTreeView based on a list as input

Basically I am displaying a QTreeView with 20 columns. I want the user to re-arrange the columns and push a save button to store the ordering as a list in an ini-file. On starting up the program, I want to re-order the columns based on the settings from the ini-file.
I am storing the original colum orders as list in "list_origin".
The desired order is in "list_custom".
E.g.
list_origin=['From', 'Subject', 'Date']
list_custom=['Date', 'Subject', 'From']
Now the problem is, when I move columns with the model headers moveSection() command, the original indexes are sometimes not correct anymore, because the columns might get inserted in between and thus lose their origin position index.
See example below: pushing the button "Rearrange cols to Date/Subject/From" will create an undesired order of the columns. How to arrange the colums in the desired order, based on the list_custom?
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# file: treeView_FindCol.py
import sys
from PyQt5.QtGui import *
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
def find_col_by_name(tree_obj, col_name: str) -> int:
""" Returns found column position as integer, else -1 """
pos_found: int = -1
model = tree_obj.model()
if model:
for col in range(model.columnCount()):
header = model.headerData(col, Qt.Horizontal, Qt.DisplayRole)
if str(header) == col_name:
pos_found = col
return pos_found
def find_col_by_index(tree_obj, col_index: int) -> int:
""" Returns found column position as integer, else -1 """
pos_found: int = -1
model = tree_obj.model()
header = tree_obj.header()
pos_found = header.visualIndex(col_index)
header_txt = model.headerData(pos_found, Qt.Horizontal, Qt.DisplayRole)
return pos_found
class App(QWidget):
FROM, SUBJECT, DATE = range(3)
def __init__(self):
super().__init__()
self.title = 'PyQt5 Treeview Example - pythonspot.com'
self.left = 800
self.top = 200
self.width = 640
self.height = 240
self.initUI()
def initUI(self):
self.setWindowTitle(self.title)
self.setGeometry(self.left, self.top, self.width, self.height)
pushButton = QPushButton("Rearrange cols to Date/Subject/From")
groupBox = QGroupBox("Inbox")
treeView = QTreeView()
treeView.setRootIsDecorated(False)
treeView.setAlternatingRowColors(True)
pushButton.clicked.connect(lambda: self.rearrange_column_layout(treeView))
dataLayout = QHBoxLayout()
dataLayout.addWidget(treeView)
dataLayout.addWidget(pushButton)
groupBox.setLayout(dataLayout)
model = self.createMailModel(self)
treeView.setModel(model)
self.addMail(model, 'service#github.com', 'Your Github Donation','03/25/2017 02:05 PM')
self.addMail(model, 'support#github.com', 'Github Projects','02/02/2017 03:05 PM')
self.addMail(model, 'service#phone.com', 'Your Phone Bill','01/01/2017 04:05 PM')
self.addMail(model, 'service#abc.com', 'aaaYour Github Donation','03/25/2017 02:05 PM')
self.addMail(model, 'support#def.com', 'bbbGithub Projects','02/02/2017 03:05 PM')
self.addMail(model, 'service#xyz.com', 'cccYour Phone Bill','01/01/2017 04:05 PM')
mainLayout = QVBoxLayout()
mainLayout.addWidget(groupBox)
self.setLayout(mainLayout)
self.show()
def createMailModel(self,parent):
model = QStandardItemModel(0, 3, parent)
model.setHeaderData(self.FROM, Qt.Horizontal, "From")
model.setHeaderData(self.SUBJECT, Qt.Horizontal, "Subject")
model.setHeaderData(self.DATE, Qt.Horizontal, "Date")
return model
def addMail(self,model, mailFrom, subject, date):
model.insertRow(0)
model.setData(model.index(0, self.FROM), mailFrom)
model.setData(model.index(0, self.SUBJECT), subject)
model.setData(model.index(0, self.DATE), date)
def rearrange_column_layout(self, treeView):
print("restore_column_layout() called.")
list_custom: list = ['Date', 'Subject', 'From']
list_origin: list = []
model = treeView.model()
header = treeView.header()
col_count = model.columnCount()
for col_search_index in range(col_count):
col_found = header.visualIndex(col_search_index)
header_txt = model.headerData(col_search_index, Qt.Horizontal, Qt.DisplayRole)
list_origin.append(header_txt)
print(f"{list_origin=}")
print(f"{list_custom=}")
pos_custom: int = 0
pos_origin_last: int = 0
for item_custom in list_custom:
pos_origin: int = 0
for item_origin in list_origin:
if item_custom == item_origin:
msg_txt = f"moving col '{item_origin}' from {pos_origin} to {pos_custom}."
print(msg_txt)
QMessageBox.information(self, f"{item_origin}", msg_txt)
header.moveSection(pos_origin, pos_custom)
pos_origin_last = pos_origin
pos_origin += 1
pos_custom += 1
if __name__ == '__main__':
app = QApplication(sys.argv)
ex = App()
sys.exit(app.exec_())
I want to set the column ordering based on an input list
"list_origin" contains the header names in original order, e.g.
list_origin=['From', 'Subject', 'Date']
"list_custom" is the desired order of the columns in my QTreeView, e.g.
list_custom=['Date', 'Subject', 'From']
Now I am iterating over the custom list, and want to put the columns based on this positions with header moveSection().
So the algorithm basically is:
step1: start with index 0 of the custom list, it's "Date"
step2: get the position of "Date" from origin list, which is 2.
step3: call moveSection(2,0)
Trace output:
moving col 'Date' from 2 to 0.
moving col 'Subject' from 1 to 1.
moving col 'From' from 0 to 2.
But anyway the result is "From"/"Subject"/"Date" (!) and not as desired "Date"/"Subject"/"From".
The logic is to obtain the index of each element at the time of the iteration since the visual order is changing within the loop:
def rearrange_column_layout(self, treeView):
to_list = ["Date", "Subject", "From"]
header = treeView.header()
model = treeView.model()
for i, c in enumerate(to_list[:-1]):
from_list = [
model.headerData(header.logicalIndex(visual_index), Qt.Horizontal)
for visual_index in range(header.count())
]
j = from_list.index(c)
header.moveSection(j, i)

how to disable all columns apart from one column in Qtablewidget PYQT - python

Problem Statement:
Disable specific columns and enable specific row on bases of current row index.
I am working on Qtablewidget i tried following code this working fine for disable.
i wanted to enable tablewidget row contents on bases of row index .
when i clicked pushbutton that row should be enable.I am new to the stackoverflow please guide me.I am tring to enable tablewidget cell,pushbutton,combobox i am not getting please guide me how to enble particular row on bases of index.
Code :
self.tableWidget = QtGui.QTableWidget(self.centralwidget)
self.tableWidget.setColumnCount(4)
myresult = [(u'sandeep', u'checkbox'), (u'shivaraj', u'checkbox')]
for row, result in enumerate(myresult):
self.tableWidget.insertRow(self.tableWidget.rowCount())
for column, value in enumerate(result):
item = QtGui.QTableWidgetItem(str(value))
if column == 1:
if (value == "textbox"):
self.cb = QtGui.QComboBox()
lis = ("---select---", "description", "multiple_input")
self.cb.addItems(lis)
self.cb.setCurrentIndex(1)
self.tableWidget.setCellWidget(row, column, self.cb)
self.cb.setEnabled(False)#this is for combobox disable
elif (value == "checkbox"):
self.cb = QtGui.QComboBox()
lis = ("---select---", "description", "multiple_input")
self.cb.addItems(lis)
self.cb.setCurrentIndex(2)
self.tableWidget.setCellWidget(row, column, self.cb)
self.cb.setEnabled(False)#this is for combobox disable
self.tableWidget.setItem(row, column, item)
item.setFlags(QtCore.Qt.ItemIsEnabled)#this is for text non editable
self.btn_sell = QtGui.QPushButton('Edit')
self.btn_sell1 = QtGui.QPushButton('Delete')
self.btn_sell1.setEnabled(False)#this for button disble
self.tableWidget.setCellWidget(row, 2, self.btn_sell)
self.tableWidget.setCellWidget(row, 3, self.btn_sell1)
self.btn_sell.clicked.connect(self.edit)
def edit(self):
index = self.tableWidget.currentRow()
print index
self.table = QtGui.QTableWidget()
self.table.setGeometry(QtCore.QRect(220, 100, 881, 100))
self.table.setColumnCount(4)
self.table.setRowCount(1)
self.table.setColumnWidth(0, 120)
self.table.setColumnWidth(1, 500)
self.table.setColumnWidth(2, 120)
self.table.setColumnWidth(3, 120)
indexes = self.tableWidget.currentRow()
widget = self.tableWidget.cellWidget(indexes, 2)
# print widget
if isinstance(widget, QtGui.QComboBox):
h = str(widget.currentText())
if (h == "multiple_input"):
j = "checkbox"
elif (h == "description"):
j = "textbox"
if (j == "textbox"):
self.cb = QtGui.QComboBox()
lis = ("---select---", "description", "multiple_input")
self.cb.addItems(lis)
self.cb.setCurrentIndex(1)
self.table.setCellWidget(0, 2, self.cb)
[enter image description here][1]elif (j == "checkbox"):
self.cb = QtGui.QComboBox()
lis = ("---select---", "description", "multiple_input")
self.cb.addItems(lis)
self.cb.setCurrentIndex(2)
self.table.setCellWidget(0, 2, self.cb)
n = [str(self.tableWidget.item(indexes, 0).text())]
for x in n:
f = ''.join(map(str, x))
self.table.setItem(0, 0, QtGui.QTableWidgetItem(f))
n1 = [str(self.tableWidget.item(indexes, 1).text())]
# print n1
for i in n1:
s = ''.join(i)
self.table.setItem(0, 1, QtGui.QTableWidgetItem(s))
self.li = QtGui.QPushButton("Update")
self.table.setCellWidget(0, 3, self.li)
self.li.clicked.connect(self.edit_data)
self.table.show()
click on below link has image.
Try the above code:
Select row and click on edit button it will take the current index row values into the separate table `where we can edit and update the values.This is the one way i have tried to update the value it works fine.

Exceptions not working as expected in PyQt5

I am making a form dialog in PyQt5. The objective of the program is to generate numbers according to a particular format depending on the Part type and store these numbers in an Excel sheet. The excel sheet is named 'Master Part Sheet.xlsx' and stored in the same directory as the script. The name of the excel sheet can be changed in the workbookLayer Class. The number of generated numbers is determined by the entry in the number of part numbers required field
The customer selected in the drop down menu also plays a role in generating a part number(only if it is a Machine Part). A number from 0-99 is stored in a dictionary against each customers name which is accessed while generating corresponding part numbers.
The input fields have to be checked when a user enters a value in them. Every time the value entered is incorrect(not in a certain range/ not in the specified format), an exception is called. The values are checked when the generate Qpushbutton in the form dialog is clicked.
The NTID field must be of the form UUUNUUU where U stands for upper case string and N stands for a Number. It is checked for with regular expressions and must otherwise throw exceptions.
The machine number and line number fields must be in the range 0-99, and must otherwise throw an exception prompting the user to check his/her entries in the message label.
After the entry of data by the user and these checks, the part numbers must be generated and written onto the excel sheet.
If the part type is a standard part, the part numbers are written onto the Standard parts sheet of the workbook. The max number of parts that can exist in this sheet are 10000. The part numbers must be unique.
If the sheet is a machine part, new sheets are created in the workbook where the part numbers are written onto. The sheets are created based on the customer name the machine number and the line number. One sheet can have a maximum of 1000 parts and the numbers generated shouldn't be repeated in the sheet i.e they are unique.
Below is the code
'''import all necessary libraries required for the project'''
import os
import numpy as np
import openpyxl
from openpyxl.workbook import Workbook
from openpyxl import load_workbook
import random
import time
import pickle
import re
class workbookLayer:
'''The constructor for the workbook object'''
def __init__(self, source_file='Master Part Sheet.xlsx'): # Change source_file to get different sheet as source
self.source_file = source_file
self.part_type = None
self.line_number = None
self.machine_number = None
self.customer_name = None
self.ntid = None
self.get_sheet_name()
'''Get the path of the Excel workbook and load the workbook onto the object'''
def get_sheet_name(self):
self.file_path = os.path.realpath(self.source_file)
self.wb = load_workbook(self.file_path)
self.wb.get_sheet_names()
return self.file_path, self.wb
from PyQt5.QtWidgets import (QApplication, QComboBox, QDialog,
QDialogButtonBox, QFormLayout, QGridLayout, QGroupBox, QHBoxLayout,
QLabel, QLineEdit, QMenu, QMenuBar, QPushButton, QSpinBox, QTextEdit,
QVBoxLayout, QMessageBox)
import sys
'''The Pop up Box for addition of a new customer'''
class DialogCustomerAdd(QDialog):
NumGridRows = 3
NumButtons = 4
def __init__(self):
super(DialogCustomerAdd, self).__init__()
self.createFormGroupBox()
self.ok_button=QPushButton("OK")
mainLayout = QVBoxLayout()
mainLayout.addWidget(self.formGroupBox)
mainLayout.addWidget(self.ok_button)
self.ok_button.clicked.connect(self.ok_button_action)
self.setLayout(mainLayout)
self.setWindowTitle("Add New Customer")
def ok_button_action(self):
global CUSTOMER_NAME
global CUSTOMER_ID
CUSTOMER_NAME = self.customer_name_text.text()
CUSTOMER_ID = self.customer_id_text.text()
self.close()
def createFormGroupBox(self):
self.formGroupBox = QGroupBox("Customer Details")
layout = QFormLayout()
self.customer_name_label = QLabel("Name of the Customer")
self.customer_name_text = QLineEdit()
self.customer_id_label = QLabel("Enter Customer ID")
self.customer_id_text = QLineEdit()
layout.addRow(self.customer_name_label, self.customer_name_text)
layout.addRow(self.customer_id_label, self.customer_id_text)
self.formGroupBox.setLayout(layout)
'''The Pop up Box for deletion of an existing customer'''
class DialogCustomerDelete(QDialog):
NumGridRows = 3
NumButtons = 4
def __init__(self):
super(DialogCustomerDelete, self).__init__()
self.createFormGroupBox()
self.ok_button=QPushButton("OK")
mainLayout = QVBoxLayout()
mainLayout.addWidget(self.formGroupBox)
mainLayout.addWidget(self.ok_button)
self.ok_button.clicked.connect(self.ok_button_action)
self.setLayout(mainLayout)
self.setWindowTitle("Delete Existing Customer")
def ok_button_action(self):
global POP_CUSTOMER
POP_CUSTOMER = self.customer_delete_combobox.currentText()
self.customer_delete_combobox.clear()
self.close()
def createFormGroupBox(self):
self.formGroupBox = QGroupBox("Customer Details")
layout = QFormLayout()
self.customer_name_label = QLabel(" Select the Customer to be deleted")
self.customer_delete_combobox = QComboBox()
self.customer_delete_combobox.addItems(CUSTOMER_LIST)
layout.addRow(self.customer_name_label)
layout.addRow(self.customer_delete_combobox)
self.formGroupBox.setLayout(layout)
class DialogMain(QDialog):
NumGridRows = 3
NumButtons = 4
wbl = workbookLayer()
def __init__(self):
super(DialogMain, self).__init__()
self.createFormGroupBox()
generate_button=QPushButton("Generate")
generate_button.clicked.connect(self.generateNumbers)
self.user_message_label = QLabel(" ")
buttonBox = QDialogButtonBox(QDialogButtonBox.Cancel)
mainLayout = QVBoxLayout()
mainLayout.addWidget(self.formGroupBox)
mainLayout.addWidget(self.user_message_label)
mainLayout.addWidget(generate_button)
mainLayout.addWidget(self.add_new_customer_button)
mainLayout.addWidget(self.delete_customer_button)
buttonBox.clicked.connect(self.closeIt)
mainLayout.addWidget(buttonBox)
self.setLayout(mainLayout)
self.open_or_create_pickle_file()
customer_name = [customer for customer,customer_id in self.customer_dict.items()]
self.customer_name_combobox.addItems(customer_name)
self.add_new_customer_button.clicked.connect(self.add_customer_window)
self.delete_customer_button.clicked.connect(self.delete_customer_window)
self.setWindowTitle("Part Number Generator ")
global CUSTOMER_LIST
CUSTOMER_LIST = customer_name
def open_or_create_pickle_file(self):
self.customer_dict = dict()
try:
assert open("customer_diction.pickle","rb")
pickle_in = open("customer_diction.pickle","rb")
self.customer_dict = pickle.load(pickle_in)
pickle_in.close()
except FileNotFoundError:
pickle_out = open("customer_dict.pickle","wb")
def add_new_customer_to_pickle(self):
global CUSTOMER_NAME
global CUSTOMER_ID
global CUSTOMER_LIST
self.customer_name_combobox.addItem(CUSTOMER_NAME)
self.customer_dict[CUSTOMER_NAME] = CUSTOMER_ID
customer_name = [customer for customer,customer_id in self.customer_dict.items()]
CUSTOMER_LIST = customer_name
pickle_out = open("customer_diction.pickle","wb")
pickle.dump(self.customer_dict, pickle_out)
pickle_out.close()
def delete_customer_from_pickle(self):
global POP_CUSTOMER
del self.customer_dict[POP_CUSTOMER]
customer_name = [customer for customer,customer_id in self.customer_dict.items()]
global CUSTOMER_LIST
CUSTOMER_LIST = customer_name
self.customer_name_combobox.clear()
self.customer_name_combobox.addItems(customer_name)
pickle_out = open("customer_diction.pickle","wb")
pickle.dump(self.customer_dict, pickle_out)
pickle_out.close()
def add_customer_window(self):
widget = DialogCustomerAdd()
widget.exec_()
self.add_new_customer_to_pickle()
def delete_customer_window(self):
widget = DialogCustomerDelete()
widget.exec_()
self.delete_customer_from_pickle()
global POP_CUSTOMER
self.user_message_label.setText('The customer ' + POP_CUSTOMER + ' has been deleted' )
def closeIt(self):
self.close()
def generateNumbers(self):
self.wbl.line_number = self.line_number_text.text()
self.wbl.machine_number = self.machine_number_text.text()
self.wbl.customer_name =self.customer_name_combobox.currentText()
self.wbl.part_type = self.part_type_combobox.currentText()
self.wbl.ntid = self.ntid_text.text()
try:
self.check_ntid()
except AssertionError:
self.user_message_label.setText('Please Enter User Details Correctly')
try:
if int(self.wbl.machine_number) > 99:
raise ValueError
except ValueError:
self.user_message_label.setText('Please Enter machine number within 100')
try:
if int(self.wbl.line_number) > 99:
raise ValueError
except ValueError:
self.user_message_label.setText('Please Enter Line number within 100')
self.create_sheet_and_check_values()
try:
self.part_number_generator()
except PermissionError:
self.user_message_label.setText('Please Close the excel sheet before using this application')
def createFormGroupBox(self):
self.formGroupBox = QGroupBox("Part Details")
layout = QFormLayout()
self.part_type_label = QLabel("Part Type:")
self.part_type_combobox = QComboBox()
self.part_type_combobox.addItems(['Standard Part', 'Machine Part'])
self.customer_name_label = QLabel("Customer:")
self.customer_name_combobox = QComboBox()
self.add_new_customer_button = QPushButton("Add New Customer")
self.delete_customer_button = QPushButton("Delete Existing Customer")
self.ntid_label = QLabel("NTID of the User:")
self.ntid_text = QLineEdit()
self.machine_number_label = QLabel("Machine Number of the Part:")
self.machine_number_text = QLineEdit()
self.line_number_label = QLabel("Line Number of the Part:")
self.line_number_text = QLineEdit()
self.part_numbers_label = QLabel("Number of Part Numbers Required:")
self.part_numbers_spinbox = QSpinBox()
self.part_numbers_spinbox.setRange(1, 999)
layout.addRow(self.part_type_label, self.part_type_combobox)
layout.addRow(self.customer_name_label, self.customer_name_combobox)
layout.addRow(self.ntid_label, self.ntid_text)
layout.addRow(self.machine_number_label, self.machine_number_text)
layout.addRow(self.line_number_label, self.line_number_text)
layout.addRow(self.part_numbers_label, self.part_numbers_spinbox)
self.formGroupBox.setLayout(layout)
def check_ntid(self):
pattern = re.compile("^[A-Z][A-Z][A-Z][0-9][A-Z][A-Z][A-Z]")
assert pattern.match(self.wbl.ntid)
def create_sheet_and_check_values(self):
self.check_ntid()
if self.wbl.part_type == 'Machine Part':
self.prefix = int('3' + self.customer_dict[self.wbl.customer_name] + self.wbl.line_number + self.wbl.machine_number)
self.check_and_create_customer_sheet()
else:
self.prefix = 500000
self.add_standard_parts_sheet()
self.dest_filename = self.wbl.file_path
self.ws1 = self.wbl.wb[self.wbl.sheet_name]
self.random_numbers = self.part_numbers_spinbox.value()
if (self.wbl.part_type == 'Machine Part'):
try:
assert self.random_numbers < (1002 -(self.ws1.max_row))
except AssertionError:
self.user_message_label.setText('You have exceeded the range of allowable parts in the machine, you can get only {} more part numbers'.format(1001 -(self.ws1.max_row)))
else:
try:
assert self.random_numbers < (10002 -(self.ws1.max_row))
except AssertionError:
self.user_message_label.setText('You have exceeded the range of allowable parts in this list, you can get only {} more part numbers'.format(10001 -(self.ws1.max_row)))
'''This section of the method contains the logic for the generation of random number parts
according to the required format and also checks for clashes with already existing part numbers'''
def part_number_generator(self):
if (self.wbl.part_type == 'Machine Part'):
part_numbers_list = random.sample(range( self.prefix*1000, self.prefix*1000 + 1000), self.random_numbers)
else:
part_numbers_list = random.sample(range( self.prefix*10000, self.prefix*10000 + 10000), self.random_numbers)
first_column = set([(self.ws1[x][0].value) for x in range(2,self.ws1.max_row + 1 )])
while True:
additional_part_numbers_list = random.sample(range( self.prefix*1000, self.prefix*1000 + 1000), self.random_numbers - len (part_numbers_list))
part_numbers_list = part_numbers_list + additional_part_numbers_list
part_numbers_list = list(set(part_numbers_list) - (first_column))
if len(part_numbers_list) == self.random_numbers:
break
for row in range( self.ws1.max_row + 1, self.ws1.max_row + 1 + len(part_numbers_list)):
'''write the generated part numbers onto the excel sheet corresponding to the customer'''
self.ws1.cell(column = 1 , row = row, value = part_numbers_list.pop())
self.ws1.cell(column = 3 , row = row, value = self.wbl.ntid)
self.wbl.wb.save(self.wbl.file_path)
self.user_message_label.setText(str(self.part_numbers_spinbox.value()) + " Part Numbers have been successfully generated. Please check the sheet " + self.wbl.sheet_name)
def check_and_create_customer_sheet(self):
self.wbl.sheet_name = '3' + self.customer_dict[self.wbl.customer_name] + self.wbl.line_number + self.wbl.machine_number +'xxx'
if self.customer_not_exists():
self.wbl.wb.create_sheet(self.wbl.sheet_name)
self.wbl.wb.save(self.wbl.file_path)
'''Check if the customer sheet exists'''
def customer_not_exists(self):
sheet_name = '<Worksheet "'+ '3' + self.customer_dict[self.wbl.customer_name] + self.wbl.line_number + self.wbl.machine_number +'xxx' + '">'
return True if sheet_name not in str(list(self.wbl.wb)) else False
'''Check if standard parts sheet exists. If not make a new sheet'''
def add_standard_parts_sheet(self):
sheet_name = '<Worksheet "'+ 'Standard Parts'+ '">'
if sheet_name not in str(list(self.wbl.wb)):
self.wbl.wb.create_sheet('Standard Parts')
self.wbl.wb.save(self.wbl.file_path)
self.wbl.sheet_name = 'Standard Parts'
if __name__ == '__main__':
app = QApplication(sys.argv)
dialog = DialogMain()
sys.exit(dialog.exec_())
The generateNumbers() function in the DialogMain() Class is called every time a 'generate' QpushButton is clicked in the QDialog layout. The program checks for correctness of the inputs and generates random numbers according to the required format in an excel sheet through openpyxl. The exceptions for the self.wbl.ntid and PermissionError work as expected. However the exceptions for self.wbl.machine_number and self.wb1.line_number i.e for machine number inputs and line number inputs in the dialog seem to be ignored and the program executes even when the user entry is wrong. Only the exception for NTID behaves as expected and the other exceptions fail to be raised even on wrong entry.
The dialog Box
I have added the entire code used to make it reproducible. Thanks

Categories