Changing the name of the downloaded xls file in pivot view odoo - pivot

I want to change the name of the xls file that the user can download in Point of Sale module, Stock Pivot view.
on click of the download button in pivot view, Instead of "table.xls" I want it to be for example "03-17-2020.xls"
but I don't know how to change it
i tried looking for any source or example here or in the odoo forum but i can't see any

Try To this code in Your controller/ section
from collections import deque
import json
from odoo import http
from odoo.http import request
from odoo.tools import ustr
from odoo.tools.misc import xlwt
from datetime import date
from odoo.addons.web.controllers.pivot import TableExporter # Import the class
class CustomTableExporter(TableExporter):# Inherit in your custom class
#http.route('/web/pivot/export_xls', type='http', auth="user")
def export_xls(self, data, token):
jdata = json.loads(data)
nbr_measures = jdata['nbr_measures']
workbook = xlwt.Workbook()
worksheet = workbook.add_sheet(jdata['title'])
header_bold = xlwt.easyxf("font: bold on; pattern: pattern solid, fore_colour gray25;")
header_plain = xlwt.easyxf("pattern: pattern solid, fore_colour gray25;")
bold = xlwt.easyxf("font: bold on;")
# Step 1: writing headers
headers = jdata['headers']
# x,y: current coordinates
# carry: queue containing cell information when a cell has a >= 2 height
# and the drawing code needs to add empty cells below
x, y, carry = 1, 0, deque()
for i, header_row in enumerate(headers):
worksheet.write(i, 0, '', header_plain)
for header in header_row:
while (carry and carry[0]['x'] == x):
cell = carry.popleft()
for i in range(nbr_measures):
worksheet.write(y, x + i, '', header_plain)
if cell['height'] > 1:
carry.append({'x': x, 'height': cell['height'] - 1})
x = x + nbr_measures
style = header_plain if 'expanded' in header else header_bold
for i in range(header['width']):
worksheet.write(y, x + i, header['title'] if i == 0 else '', style)
if header['height'] > 1:
carry.append({'x': x, 'height': header['height'] - 1})
x = x + header['width']
while (carry and carry[0]['x'] == x):
cell = carry.popleft()
for i in range(nbr_measures):
worksheet.write(y, x + i, '', header_plain)
if cell['height'] > 1:
carry.append({'x': x, 'height': cell['height'] - 1})
x = x + nbr_measures
x, y = 1, y + 1
# Step 2: measure row
if nbr_measures > 1:
worksheet.write(y, 0, '', header_plain)
for measure in jdata['measure_row']:
style = header_bold if measure['is_bold'] else header_plain
worksheet.write(y, x, measure['measure'], style)
x = x + 1
y = y + 1
# Step 3: writing data
x = 0
for row in jdata['rows']:
worksheet.write(y, x, row['indent'] * ' ' + ustr(row['title']), header_plain)
for cell in row['values']:
x = x + 1
if cell.get('is_bold', False):
worksheet.write(y, x, cell['value'], bold)
else:
worksheet.write(y, x, cell['value'])
x, y = 0, y + 1
today = date.today()
a = str(today) + '.xls'
response = request.make_response(None,
headers=[('Content-Type', 'application/vnd.ms-excel'),
('Content-Disposition', 'attachment; filename=%s' % a)],
cookies={'fileToken': token})
workbook.save(response.stream)
return response
This will print Today date name as excel - 2020-03-18.xls

Related

texts formatting with maintaining texts alignment

I am using open-source tesseract OCR to extract texts from the images. I need texts with proper space preserved and aligned for some information extraction tasks. I have used the tesseract .image_to_data() method. This method outputs the recognized texts along with its bounding box and confidence score.
After performing sorting I get all the texts on a string in the order they exist in the image but not formatted in the way that I want. One of my test images is
My code:
import cv2
import pytesseract
import argparse
import time
from pytesseract import Output
from utils.config import ENG_CONFIG
def check_confidence(df):
df = df[df['conf'] != -1]
return df
class OCR:
def __init__(self, conf, image):
self.conf = conf
self.image = image
def get_ocr_output(self):
result = pytesseract.image_to_data(self.image, config=self.conf, output_type=Output.DATAFRAME)
result = check_confidence(result)
lines, formatted_string = self.parse_texts(result)
return lines, formatted_string
def parse_texts(self, df):
img = self.image.copy()
parsed_info = []
start = time.time()
for row in df.itertuples(): # for index, row in df.iterrows()
loc = (row.left, row.top, row.width, row.height)
# draw box on the image
cv2.rectangle(img, (loc[0], loc[1]), (loc[0] + loc[2], loc[1] + loc[3]), (255, 255, 0), 2)
text = row.text
conf = row.conf
row_info = {'box': loc, 'text': text, 'conf': conf}
parsed_info.append(row_info)
end = time.time()
print("\n[INFO]: Total time to read the dataframe row by row is : {} seconds.".format(end - start))
print("\n[INFO]: Parsed Information before sorting boxes is: \n{}".format(parsed_info))
parsed_info = self.sort_box_vertically(parsed_info)
lines = self.get_lines_vertically(parsed_info)
lines = self.sort_box_horizontally(lines)
print("\n[INFO]: Parsed Information after sorting boxes horizontally-vertically is : \n{}".format(lines))
# formatted_strings = self.format_texts(lines)
formatted_strings = self.format_texts_by_reserving_spaces(lines)
print("\n[INFO] : Text for the image is : \n{}".format(formatted_strings))
cv2.imshow("text box", img)
cv2.waitKey(0)
return lines, formatted_strings
def sort_box_vertically(self, parsed_info):
"""
Method to sort the boxes vertically
:param parsed_info:
:return:
"""
for counter in range(len(parsed_info) - 1):
for num in range((len(parsed_info) - 1) - counter):
if self.get_box_center(parsed_info[num]['box'])[1] > self.get_box_center(parsed_info[num + 1]['box'])[
1]:
temp = parsed_info[num + 1]
parsed_info[num + 1] = parsed_info[num]
parsed_info[num] = temp
return parsed_info
def get_lines_vertically(self, parsed_info):
"""
Method to separate the lines vertically
:param parsed_info:
:return:
"""
lines = []
while len(parsed_info) != 0:
ref_box = parsed_info[0]['box']
line = [parsed_info[0]]
for i in range(1, len(parsed_info)):
if self.is_intersects(ref_box, parsed_info[i]['box']) or self.is_intersects(parsed_info[i]['box'],
ref_box):
line.append(parsed_info[i])
for i in range(len(line)):
parsed_info.remove(line[i])
lines.append(line)
return lines
def is_intersects(self, box1, box2, margin=0):
x_1, y_1, w_1, h_1 = box1
x_2, y_2, w_2, h_2 = box2
# check if box2 intersect with box1 by height
h1, k1 = self.get_box_center(box1)
h2, k2 = self.get_box_center(box2)
if int(k2) in range(int(y_1 + margin * h_1), int((y_1 + h_1 - h_1 * margin))) or int(k1) in range(
int(y_2 + margin * h_2), int((y_2 + h_2 - h_2 * margin))):
return True
return False
def get_box_center(self, box):
"""
Method to get the center of box (h,k)
:param box: box as tuple (x, y, w, h)
:return:
"""
x, y, w, h = box
x1 = x
y1 = y
x2 = x + w
y2 = y + h
# Find the center of the box
h, k = int((x1 + x2) / 2), int((y1 + y2) / 2)
return h, k
def format_texts(self, lines):
"""
Method to return the formatted texts from the image
:param lines:
:return:
"""
formatted_string = ""
for line in lines:
for word in line:
# print(word)
formatted_string += " " + word['text']
formatted_string += '\n'
return formatted_string
def format_texts_by_reserving_spaces(self, lines):
"""
An efficient Method to return the formatted texts from the image
:param lines:
:return:
"""
formatted_string = ""
for line in lines:
flag = 0
for word in line:
# print(word)
word_start = word['box'][0]
word_end = word['box'][0] + word['box'][2]
formatted_string += " " * (word_start - flag - 1) + word['text']
flag = word_end
formatted_string += '\n'
return formatted_string
def sort_box_horizontally(self, lines):
final_lines = []
for line in lines:
for counter in range(len(line) - 1):
for num in range((len(line) - 1) - counter):
# If x1 of first line is greater than second line
if self.get_box_center(line[num]['box'])[0] > self.get_box_center(line[num + 1]['box'])[0]:
temp = line[num + 1]
line[num + 1] = line[num]
line[num] = temp
final_lines.append(line)
return final_lines
def sort_box_horizontally_example(self, line_boxes):
"""
Method to sort the boxes horizontally
:param boxes: list of tuple of boxes eg:[((,),(,))]
:return:
"""
final_lines = []
for line in line_boxes:
for counter in range(len(line) - 1):
for num in range((len(line) - 1) - counter):
# If x1 of first line is greater than second line
if line[num][0][0] > line[num + 1][0][0]:
temp = line[num + 1]
line[num + 1] = line[num]
line[num] = temp
final_lines.append(line)
return final_lines
def sorting_example(self):
"""
Sorting array using bubble sort
:return:
"""
list_ = [3, 6, 8, 44, 77, 2, 44, 556, 66, 565, 34]
for i in range(len(list_) - 1):
for j in range((len(list_) - 1) - i):
if list_[j] > list_[j + 1]:
temp = list_[j]
list_[j] = list_[j + 1]
list_[j + 1] = temp
if __name__ == "__main__":
parser = argparse.ArgumentParser()
parser.add_argument('-i', '--image',
default="./intelligent_information_extractor/images/img5.png",
help="Path to input image to be OCR'd")
args = parser.parse_args()
image = cv2.imread(args.image)
image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
ocr = OCR(ENG_CONFIG, image)
results = ocr.get_ocr_output()
The output it produces can be found here
While formatting texts which ocr outputs, I have converted a pixel to a single character. But it creates the text string where texts are separated by large whitespace.
So, How to decrease the whitespace to properly format the output texts by preserving all the spaces and alignment?

How to alternate color of the graphs between blue and white?

I have a list D containing 50 sub-lists. The number of elements in these sub-lists are decreasing. I visualize the list D by
for i, array in enumerate(D):
plt.scatter([i]*len(array), array)
I have 50 functions taking values from St_Sp, and Y is a list containing 50 elements, each of them is the output of each function. I visualize these functions
fig, ax = plt.subplots()
for i in range(len(Y)):
ax.plot(St_Sp, Y[i])
I found that too many colors are not easy to eyes. I would like to ask how to alternate color of the graphs between blue and white? I mean the color of the functions and dots in D are white > blue > white > blue ...
Could you please elaborate on how to do so?
##### Import packages
import numpy as np
import scipy.linalg as la
import time
import matplotlib
import matplotlib.pyplot as plt
##### Initial conditions
N = 100
lamda = 7
mu = 2
a = np.exp(-0.05)
r = - np.log(a).copy()
St_Sp = np.arange(- N, N + 1)
Card = St_Sp.shape[0]
##### Define infintesimal generator
def LL(x, y):
if x == N or x == - N: re = 0
elif x - y == - 1: re = lamda
elif x - y == 1: re = mu
elif x - y == 0: re = - (mu + lamda)
else: re = 0
return re
def L(x):
return - LL(x, x)
##### Define function Phi
def Phi(x):
return max(x, 0)
Phi = np.vectorize(Phi)
##### Define vector b
b = np.array(Phi(St_Sp))
##### Define function Psi
def Psi(x):
return L(x) / (L(x) + r)
Psi = np.vectorize(Psi)
##### Generate a Boolean vector whose all elements are False
d = np.array([0] * Card).astype(bool)
##### Define matrix A
A = np.zeros((Card, Card))
for i in range(Card):
for j in range(Card):
if (i != j) & (L(St_Sp[i]) != 0):
A[i, j] = LL(St_Sp[i], St_Sp[j]) / L(St_Sp[i])
elif (i != j) & (L(St_Sp[i]) == 0):
A[i, j] = 0
elif (i == j) & (Psi(St_Sp[i]) != 0):
A[i, j] = - 1 / Psi(St_Sp[i])
else: A[i, j] = 1
##### Row names of A
rows = np.arange(0, Card)
##### Define matrix B
B = np.zeros((Card, Card))
for i in range(Card):
for j in range(Card):
if i != j:
B[i, j] = LL(St_Sp[i], St_Sp[j])
else: B[i, j] = LL(St_Sp[i], St_Sp[j]) - r
start = time.time()
##### Generate I_0
I = [np.array([1] * Card).astype(bool), d.copy()]
Z = np.array(b.copy())
Z = Z.astype(float)
D = [St_Sp]
index0 = np.matmul(B, Z) <= 0
index1 = ~ index0
Y = [b.copy()]
##### Iterations
for i in range(1, Card):
I = [I[0] & index0, I[1] | index1]
Z = np.array(b.copy())
Z = Z.astype(float)
A1 = A[np.ix_(rows[I[1]], rows[I[1]])]
A2 = A[np.ix_(rows[I[1]], rows[I[0]])]
Z[I[1]] = la.solve(A1, - np.matmul(A2, Z[I[0]]))
Y = np.concatenate((Y, [Z]))
D.append(St_Sp[I[0]])
index = np.matmul(B[I[0]], Z) <= 0
index0, index1 = d.copy(), d.copy()
index0[I[0]], index1[I[0]] = index, ~ index
if (I[0] == index0).all() == True: break
for i, array in enumerate(D):
plt.scatter([i]*len(array), array)
fig, ax = plt.subplots()
for i in range(len(Y)):
ax.plot(St_Sp, Y[i])
The easiest approach is to set a custom color cycler. Instead of cycling between the 10 typical colors, the default colors for the plots will cycle through the given colors.
from cycler import cycler
custom_cycler = cycler(color=['white', 'blue'])
plt.gca().set_prop_cycle(custom_cycler)
for i, array in enumerate(D[:-1]):
plt.scatter([i] * len(array), array)
plt.scatter([len(D) - 1] * len(D[-1]), D[-1], color='crimson')
fig, ax = plt.subplots()
ax.set_prop_cycle(custom_cycler)
for i in range(len(Y) - 1):
ax.plot(St_Sp, Y[i])
ax.plot(St_Sp, Y[len(Y) - 1], color='crimson')
plt.show()

PyQt - Oriented Flow Layout

I'm trying to adapt this PyQt implementation of FlowLayout to allow vertical flow as well as horizontal. This is my current implementation:
from PyQt5.QtWidgets import *
from PyQt5.QtGui import *
from PyQt5.QtCore import *
class FlowLayout(QLayout):
def __init__(self, orientation=Qt.Horizontal, parent=None, margin=0, spacing=-1):
super().__init__(parent)
self.orientation = orientation
if parent is not None:
self.setContentsMargins(margin, margin, margin, margin)
self.setSpacing(spacing)
self.itemList = []
def __del__(self):
item = self.takeAt(0)
while item:
item = self.takeAt(0)
def addItem(self, item):
self.itemList.append(item)
def count(self):
return len(self.itemList)
def itemAt(self, index):
if index >= 0 and index < len(self.itemList):
return self.itemList[index]
return None
def takeAt(self, index):
if index >= 0 and index < len(self.itemList):
return self.itemList.pop(index)
return None
def expandingDirections(self):
return Qt.Orientations(Qt.Orientation(0))
def hasHeightForWidth(self):
return self.orientation == Qt.Horizontal
def heightForWidth(self, width):
return self.doLayout(QRect(0, 0, width, 0), True)
def hasWidthForHeight(self):
return self.orientation == Qt.Vertical
def widthForHeight(self, height):
return self.doLayout(QRect(0, 0, 0, height), True)
def setGeometry(self, rect):
super().setGeometry(rect)
self.doLayout(rect, False)
def sizeHint(self):
return self.minimumSize()
def minimumSize(self):
size = QSize()
for item in self.itemList:
size = size.expandedTo(item.minimumSize())
margin, _, _, _ = self.getContentsMargins()
size += QSize(2 * margin, 2 * margin)
return size
def doLayout(self, rect, testOnly):
x = rect.x()
y = rect.y()
offset = 0
horizontal = self.orientation == Qt.Horizontal
for item in self.itemList:
wid = item.widget()
spaceX = self.spacing() + wid.style().layoutSpacing(QSizePolicy.PushButton, QSizePolicy.PushButton, Qt.Horizontal)
spaceY = self.spacing() + wid.style().layoutSpacing(QSizePolicy.PushButton, QSizePolicy.PushButton, Qt.Vertical)
if horizontal:
next = x + item.sizeHint().width() + spaceX
if next - spaceX > rect.right() and offset > 0:
x = rect.x()
y += offset + spaceY
next = x + item.sizeHint().width() + spaceX
offset = 0
else:
next = y + item.sizeHint().height() + spaceY
if next - spaceY > rect.bottom() and offset > 0:
x += offset + spaceX
y = rect.y()
next = y + item.sizeHint().height() + spaceY
offset = 0
if not testOnly:
item.setGeometry(QRect(QPoint(x, y), item.sizeHint()))
if horizontal:
x = next
offset = max(offset, item.sizeHint().height())
else:
y = next
offset = max(offset, item.sizeHint().width())
return y + offset - rect.y() if horizontal else x + offset - rect.x()
if __name__ == '__main__':
class Window(QWidget):
def __init__(self):
super().__init__()
#flowLayout = FlowLayout(orientation=Qt.Horizontal)
flowLayout = FlowLayout(orientation=Qt.Vertical)
flowLayout.addWidget(QPushButton("Short"))
flowLayout.addWidget(QPushButton("Longer"))
flowLayout.addWidget(QPushButton("Different text"))
flowLayout.addWidget(QPushButton("More text"))
flowLayout.addWidget(QPushButton("Even longer button text"))
self.setLayout(flowLayout)
self.setWindowTitle("Flow Layout")
import sys
app = QApplication(sys.argv)
mainWin = Window()
mainWin.show()
sys.exit(app.exec_())
This implementation has 2 (likely related) problems when handling vertical layouts:
QLayout has the hasHeightForWidth and heightForWidth methods, but not their inverses hasWidthForHeight and widthForHeight. I implemented the latter two methods regardless, but I doubt they're ever actually getting called.
When using the horizontal variant of the layout, the window is automatically appropriately sized to contain all the items. When using the vertical variant, this is not the case. However, the vertical layout does work properly if you manually resize the window.
How do I properly implement a vertical flow layout?
As you already found out, Qt layouts don't support widthForHeight, and, in general, these kinds of layouts are discouraged, mostly because they tend to behave erratically in complex situation with nested layouts and mixed widget size policies. Even when being very careful about their implementation, you might end up in recursive calls to size hints, policies etc.
That said, a partial solution is to still return a height for width, but position the widgets vertically instead of horizontally.
def doLayout(self, rect, testOnly):
x = rect.x()
y = rect.y()
lineHeight = columnWidth = heightForWidth = 0
for item in self.itemList:
wid = item.widget()
spaceX = self.spacing() + wid.style().layoutSpacing(QSizePolicy.PushButton, QSizePolicy.PushButton, Qt.Horizontal)
spaceY = self.spacing() + wid.style().layoutSpacing(QSizePolicy.PushButton, QSizePolicy.PushButton, Qt.Vertical)
if self.orientation == Qt.Horizontal:
nextX = x + item.sizeHint().width() + spaceX
if nextX - spaceX > rect.right() and lineHeight > 0:
x = rect.x()
y = y + lineHeight + spaceY
nextX = x + item.sizeHint().width() + spaceX
lineHeight = 0
if not testOnly:
item.setGeometry(QRect(QPoint(x, y), item.sizeHint()))
x = nextX
lineHeight = max(lineHeight, item.sizeHint().height())
else:
nextY = y + item.sizeHint().height() + spaceY
if nextY - spaceY > rect.bottom() and columnWidth > 0:
x = x + columnWidth + spaceX
y = rect.y()
nextY = y + item.sizeHint().height() + spaceY
columnWidth = 0
heightForWidth += item.sizeHint().height() + spaceY
if not testOnly:
item.setGeometry(QRect(QPoint(x, y), item.sizeHint()))
y = nextY
columnWidth = max(columnWidth, item.sizeHint().width())
if self.orientation == Qt.Horizontal:
return y + lineHeight - rect.y()
else:
return heightForWidth - rect.y()
This is how the widget appears as soon as it's shown (which is almost the same as the horizontal flow):
Now, resizing to allow less vertical space:
And even smaller height:
While the answer provided by #musicamente works, it is incomplete:
What is missing is the widthForHeight mecanism: as items are added into the layout, the minimumWidth of the container widget is not updated.
For some reason, Qt decided that heightForWidth mecanism should exist but not widthForHeight.
It would seem that when using the heightForWidth mecanism, the minimumHeight of the parent widget is automatically updated via the Qt framework (I may be wrong but I think it is the case).
In the example provided by #musicamente, as the main window is resizable this limitation is not easilly seen.
However when using a QScrollArea, this limitation is cleary observable as the scrollbar doesn't show up and the view is truncated.
So we need to determine which row of the FlowLayout is the widest and set the minimumWidth of the parent widget accordingly.
I've implemented it like so:
As the items are placed, that they are assigned i and j indexes which represent their position in a 2D array.
Then once all of them are placed, we determine the width of the widest row (including spacing between items) and let the parent widget know using a dedicated signal which can be connected to the setMinimumWidth method.
My solution might not be perfect nor a great implementation, but it is the best alternative I found so far to achieve what I wanted.
The following code will provide a working version, while I don't find my solution very elegant, it works.
If you have ideas on how to optimize it feel free to improve my implementation by making a PR on my GitHub : https://github.com/azsde/BatchMkvToolbox/tree/main/ui/customLayout
class FlowLayout(QLayout):
widthChanged = pyqtSignal(int)
def __init__(self, parent=None, margin=0, spacing=-1, orientation=Qt.Horizontal):
super(FlowLayout, self).__init__(parent)
if parent is not None:
self.setContentsMargins(margin, margin, margin, margin)
self.setSpacing(spacing)
self.itemList = []
self.orientation = orientation
def __del__(self):
item = self.takeAt(0)
while item:
item = self.takeAt(0)
def addItem(self, item):
self.itemList.append(item)
def count(self):
return len(self.itemList)
def itemAt(self, index):
if index >= 0 and index < len(self.itemList):
return self.itemList[index]
return None
def takeAt(self, index):
if index >= 0 and index < len(self.itemList):
return self.itemList.pop(index)
return None
def expandingDirections(self):
return Qt.Orientations(Qt.Orientation(0))
def hasHeightForWidth(self):
return True
def heightForWidth(self, width):
if (self.orientation == Qt.Horizontal):
return self.doLayoutHorizontal(QRect(0, 0, width, 0), True)
elif (self.orientation == Qt.Vertical):
return self.doLayoutVertical(QRect(0, 0, width, 0), True)
def setGeometry(self, rect):
super(FlowLayout, self).setGeometry(rect)
if (self.orientation == Qt.Horizontal):
self.doLayoutHorizontal(rect, False)
elif (self.orientation == Qt.Vertical):
self.doLayoutVertical(rect, False)
def sizeHint(self):
return self.minimumSize()
def minimumSize(self):
size = QSize()
for item in self.itemList:
size = size.expandedTo(item.minimumSize())
margin, _, _, _ = self.getContentsMargins()
size += QSize(2 * margin, 2 * margin)
return size
def doLayoutHorizontal(self, rect, testOnly):
# Get initial coordinates of the drawing region (should be 0, 0)
x = rect.x()
y = rect.y()
lineHeight = 0
i = 0
for item in self.itemList:
wid = item.widget()
# Space X and Y is item spacing horizontally and vertically
spaceX = self.spacing() + wid.style().layoutSpacing(QSizePolicy.PushButton, QSizePolicy.PushButton, Qt.Horizontal)
spaceY = self.spacing() + wid.style().layoutSpacing(QSizePolicy.PushButton, QSizePolicy.PushButton, Qt.Vertical)
# Determine the coordinate we want to place the item at
# It should be placed at : initial coordinate of the rect + width of the item + spacing
nextX = x + item.sizeHint().width() + spaceX
# If the calculated nextX is greater than the outer bound...
if nextX - spaceX > rect.right() and lineHeight > 0:
x = rect.x() # Reset X coordinate to origin of drawing region
y = y + lineHeight + spaceY # Move Y coordinate to the next line
nextX = x + item.sizeHint().width() + spaceX # Recalculate nextX based on the new X coordinate
lineHeight = 0
if not testOnly:
item.setGeometry(QRect(QPoint(x, y), item.sizeHint()))
x = nextX # Store the next starting X coordinate for next item
lineHeight = max(lineHeight, item.sizeHint().height())
i = i + 1
return y + lineHeight - rect.y()
def doLayoutVertical(self, rect, testOnly):
# Get initial coordinates of the drawing region (should be 0, 0)
x = rect.x()
y = rect.y()
# Initalize column width and line height
columnWidth = 0
lineHeight = 0
# Space between items
spaceX = 0
spaceY = 0
# Variables that will represent the position of the widgets in a 2D Array
i = 0
j = 0
for item in self.itemList:
wid = item.widget()
# Space X and Y is item spacing horizontally and vertically
spaceX = self.spacing() + wid.style().layoutSpacing(QSizePolicy.PushButton, QSizePolicy.PushButton, Qt.Horizontal)
spaceY = self.spacing() + wid.style().layoutSpacing(QSizePolicy.PushButton, QSizePolicy.PushButton, Qt.Vertical)
# Determine the coordinate we want to place the item at
# It should be placed at : initial coordinate of the rect + width of the item + spacing
nextY = y + item.sizeHint().height() + spaceY
# If the calculated nextY is greater than the outer bound, move to the next column
if nextY - spaceY > rect.bottom() and columnWidth > 0:
y = rect.y() # Reset y coordinate to origin of drawing region
x = x + columnWidth + spaceX # Move X coordinate to the next column
nextY = y + item.sizeHint().height() + spaceY # Recalculate nextX based on the new X coordinate
# Reset the column width
columnWidth = 0
# Set indexes of the item for the 2D array
j += 1
i = 0
# Assign 2D array indexes
item.x_index = i
item.y_index = j
# Only call setGeometry (which place the actual widget using coordinates) if testOnly is false
# For some reason, Qt framework calls the doLayout methods with testOnly set to true (WTF ??)
if not testOnly:
item.setGeometry(QRect(QPoint(x, y), item.sizeHint()))
y = nextY # Store the next starting Y coordinate for next item
columnWidth = max(columnWidth, item.sizeHint().width()) # Update the width of the column
lineHeight = max(lineHeight, item.sizeHint().height()) # Update the height of the line
i += 1 # Increment i
# Only call setGeometry (which place the actual widget using coordinates) if testOnly is false
# For some reason, Qt framework calls the doLayout methods with testOnly set to true (WTF ??)
if not testOnly:
self.calculateMaxWidth(i)
self.widthChanged.emit(self.totalMaxWidth + spaceX * self.itemsOnWidestRow)
return lineHeight
# Method to calculate the maximum width among each "row" of the flow layout
# This will be useful to let the UI know the total width of the flow layout
def calculateMaxWidth(self, numberOfRows):
# Init variables
self.totalMaxWidth = 0
self.itemsOnWidestRow = 0
# For each "row", calculate the total width by adding the width of each item
# and then update the totalMaxWidth if the calculated width is greater than the current value
# Also update the number of items on the widest row
for i in range(numberOfRows):
rowWidth = 0
itemsOnWidestRow = 0
for item in self.itemList:
# Only compare items from the same row
if (item.x_index == i):
rowWidth += item.sizeHint().width()
itemsOnWidestRow += 1
if (rowWidth > self.totalMaxWidth):
self.totalMaxWidth = rowWidth
self.itemsOnWidestRow = itemsOnWidestRow
To use it do the following:
When declaring a FlowLayout, specify its orientation :
myFlowLayout = FlowLayout(containerWidget, orientation=Qt.Vertical)
Connect the FlowLayout's widthChanged signal to the setMinimumWidth method of the container:
myFlowLayout.widthChanged.connect(containerWidget.setMinimumWidth)

Exporting a cellular automaton data to csv in Python

I've been working in Reaction-Diffusion cellular automata with the cellpylib library for a course in my university (I wrote it all in one script so you don't have to install/download anything). I'd like to save the evolution of the automata data to a csv file to run some statistics. That is, I'd like to save the data in columns where the first column is 'number of "1"' and the second column: 'time steps'.
Thus, I need help in:
(1) Creating a variable that saves the amount of '1' per time step (I think so).
(2) I need to export all that data to a csv file (number of "1" and the corresponding iteration, from 1 to time_steps in the code below).
The code is the following.
#Libraries
import matplotlib
matplotlib.matplotlib_fname()
import matplotlib.pyplot as plt
import matplotlib as mpl
import matplotlib.animation as animation
import numpy as np
import csv
# Conditions
#############################
theta = 1 # this is the condition for Moore neighbourhood
Int = 100 # this is the iteration speed (just for visualization)
time_steps = 100 # Iterations
size = 8 # this is the size of the matrix (8x8)
#############################
# Definitions
def plot2d_animate(ca, title=''):
c = mpl.colors.ListedColormap(['green', 'red', 'black', 'gray'])
n = mpl.colors.Normalize(vmin=0,vmax=3)
fig = plt.figure()
plt.title(title)
im = plt.imshow(ca[0], animated=True, cmap=c, norm=n)
i = {'index': 0}
def updatefig(*args):
i['index'] += 1
if i['index'] == len(ca):
i['index'] = 0
im.set_array(ca[i['index']])
return im,
ani = animation.FuncAnimation(fig, updatefig, interval=Int, blit=True)
plt.show()
def init_simple2d(rows, cols, val=1, dtype=np.int):
x = np.zeros((rows, cols), dtype=dtype)
x[x.shape[0]//2][x.shape[1]//2] = val
return np.array([x])
def evolve2d(cellular_automaton, timesteps, apply_rule, r=1, neighbourhood='Moore'):
_, rows, cols = cellular_automaton.shape
array = np.zeros((timesteps, rows, cols), dtype=cellular_automaton.dtype)
array[0] = cellular_automaton
von_neumann_mask = np.zeros((2*r + 1, 2*r + 1), dtype=bool)
for i in range(len(von_neumann_mask)):
mask_size = np.absolute(r - i)
von_neumann_mask[i][:mask_size] = 1
if mask_size != 0:
von_neumann_mask[i][-mask_size:] = 1
def get_neighbourhood(cell_layer, row, col):
row_indices = [0]*(2*r+1)
for i in range(-r,r+1):
row_indices[i+r]=(i+row) % cell_layer.shape[0]
col_indices = [0]*(2*r+1)
for i in range(-r,r+1):
col_indices[i+r]=(i+col) % cell_layer.shape[1]
n = cell_layer[np.ix_(row_indices, col_indices)]
if neighbourhood == 'Moore':
return n
elif neighbourhood == 'von Neumann':
return np.ma.masked_array(n, von_neumann_mask)
else:
raise Exception("unknown neighbourhood type: %s" % neighbourhood)
for t in range(1, timesteps):
cell_layer = array[t - 1]
for row, cell_row in enumerate(cell_layer):
for col, cell in enumerate(cell_row):
n = get_neighbourhood(cell_layer, row, col)
array[t][row][col] = apply_rule(n, (row, col), t)
return array
def ca_reaction_diffusion(neighbourhood, c, t):
center_cell = neighbourhood[1][1]
total = np.sum(neighbourhood==1)
if total >= theta and center_cell==0:
return 1
elif center_cell == 1:
return 2
elif center_cell == 2:
return 3
elif center_cell == 3:
return 0
else:
return 0
# Initial condition
cellular_automaton = init_simple2d(size, size, val=0, dtype=int)
# Excitable initial cells
cellular_automaton[:, [1,2], [1,1]] = 1
# The evolution
cellular_automaton = evolve2d(cellular_automaton,
timesteps=time_steps,
neighbourhood='Moore',
apply_rule=ca_reaction_diffusion)
animation=plot2d_animate(cellular_automaton)
Explanation of the code:
As you can see, there are 4 states: 0 (green), 1 (red), 2 (black) and 3 (gray). The way the automata evolves is with the cellular_automaton conditions. That is, for example, if a center cell has a value of 0 (excitable cell) and at least one cell (theta value) on its Moore neighbourhood is in state 1, in the following time step the same cell will be at state 1 (excited).
To notice:
The configuration of this matrix is toroidal, and the definitions are taken from the cellpylib library.
I've been stuck with this for over a week, so I'd really appreciate some help. Thanks in advance!
I am not well-experienced in this subject matter (and I was not fully clear on what you intended for me to do). I went through and implemented the counting of the specific "0", "1", "2" and "3" value cells in "evolve2d" function. This code should be viewed as "starter code"; whatever specifically you are trying to do should piggyback off of what I have given you. Additionally, this task could have been accomplished through some better code design and definitely, better planning of your function locations (as part of better coding practice and overall cleaner code that is easy to debug). Please peruse and UNDERSTAND the changes that I made.
#Libraries
import matplotlib
matplotlib.matplotlib_fname()
import matplotlib.pyplot as plt
import matplotlib as mpl
import matplotlib.animation as animation
import numpy as np
import csv
# Conditions
#############################
theta = 1 # this is the condition for Moore neighbourhood
iter_speed = 100 # this is the iteration speed (just for visualization)
time_steps = 100 # Iterations
size = 8 # this is the size of the matrix (8x8)
#############################
# Definitions
def plot2d_animate(ca, title=''):
c = mpl.colors.ListedColormap(['green', 'red', 'black', 'gray'])
n = mpl.colors.Normalize(vmin=0,vmax=3)
fig = plt.figure()
plt.title(title)
im = plt.imshow(ca[0], animated=True, cmap=c, norm=n)
i = {'index': 0}
def updatefig(*args):
i['index'] += 1
if i['index'] == len(ca):
i['index'] = 0
im.set_array(ca[i['index']])
return im,
ani = animation.FuncAnimation(fig, updatefig, interval=iter_speed, blit=True)
plt.show()
#############I ADDED EXTRA ARGUMENTs FOR THE FUNCTION BELOW
def get_neighbourhood(cell_layer, row, col, r = 1, neighbourhood = "Moore"):
row_indices = [0]*(2*r+1)
for i in range(-r,r+1):
row_indices[i+r]=(i+row) % cell_layer.shape[0]
col_indices = [0]*(2*r+1)
for i in range(-r,r+1):
col_indices[i+r]=(i+col) % cell_layer.shape[1]
n = cell_layer[np.ix_(row_indices, col_indices)]
if neighbourhood == 'Moore':
return n
elif neighbourhood == 'von Neumann':
return np.ma.masked_array(n, von_neumann_mask)
else:
raise Exception("unknown neighbourhood type: %s" % neighbourhood)
def init_simple2d(rows, cols, val=1, dtype=np.int):
x = np.zeros((rows, cols), dtype=dtype)
x[x.shape[0]//2][x.shape[1]//2] = val
return np.array([x])
#Inner functions was moved due to bad coding practice. Arguments were also changed. Make sure you understand what I did.
def evolve2d(cellular_automaton, timesteps, apply_rule, r=1, neighbourhood='Moore'):
_, rows, cols = cellular_automaton.shape
array = np.zeros((timesteps, rows, cols), dtype=cellular_automaton.dtype)
array[0] = cellular_automaton
von_neumann_mask = np.zeros((2*r + 1, 2*r + 1), dtype=bool)
for i in range(len(von_neumann_mask)):
mask_size = np.absolute(r - i)
von_neumann_mask[i][:mask_size] = 1
if mask_size != 0:
von_neumann_mask[i][-mask_size:] = 1
#################################################
#These lists keep track of values over the course of the function:
Result_0 = ["Number of 0"]
Result_1 = ["Number of 1"]
Result_2 = ["Number of 2"]
Result_3 = ["Number of 3"]
#################################################
for t in range(1, timesteps):
#################################################
#This dictionary keeps track of values per timestep
value_iter_tracker = {0: 0, 1: 0, 2: 0, 3: 0 }
#################################################
cell_layer = array[t - 1]
for row, cell_row in enumerate(cell_layer):
for col, cell in enumerate(cell_row):
n = get_neighbourhood(cell_layer, row, col)
################################################
res = apply_rule(n, (row, col), t)
value_iter_tracker[res]+=1
array[t][row][col] = res
################################################
print(value_iter_tracker)
########################################################
#Now we need to add the results of the iteration dictionary to the corresponding
#lists in order to eventually export to the csv
Result_0.append(value_iter_tracker[0])
Result_1.append(value_iter_tracker[1])
Result_2.append(value_iter_tracker[2])
Result_3.append(value_iter_tracker[3])
########################################################
############################################################
#function call to export lists to a csv:
timesteps_result = list(range(1, timesteps))
timesteps_result = ["Time Step"] + timesteps_result
#If you don't understand what is going on here, put print statement and/or read docs
vals = zip(timesteps_result, Result_0, Result_1, Result_2, Result_3)
write_to_csv_file(list(vals))
############################################################
return array
################################################################################
#THIS CODE IS FROM:
#https://stackoverflow.com/questions/14037540/writing-a-python-list-of-lists-to-a-csv-file
import pandas as pd
def write_to_csv_file(data):
data = [list(x) for x in data]
my_df = pd.DataFrame(data)
my_df.to_csv('output1.csv', index=False, header=False)
################################################################################
def ca_reaction_diffusion(neighbourhood, c, t):
center_cell = neighbourhood[1][1]
total = np.sum(neighbourhood==1)
if total >= theta and center_cell==0:
return 1
elif center_cell == 1:
return 2
elif center_cell == 2:
return 3
elif center_cell == 3:
return 0
else:
return 0
# Initial condition
cellular_automaton = init_simple2d(size, size, val=0, dtype=int)
# Excitable initial cells
cellular_automaton[:, [1,2], [1,1]] = 1
# The evolution
cellular_automaton = evolve2d(cellular_automaton,
timesteps=time_steps,
neighbourhood='Moore',
apply_rule=ca_reaction_diffusion)
animation=plot2d_animate(cellular_automaton)
I have left comments that should clarify the changes that I made. Essentially, when you call the evolve2d function, a csv file called "output1.csv" is created with the timestep results. I used the pandas package to write the data into a csv but other methods could have been used as well. I will leave it to you to take advantage of the changes that I made for your use. Hope this helps.

Why is this now giving me an UnboundedLocalError?

This was working and now has stopped and is giving me an error I can't see what's wrong with it?
def grade(R):
x = str(R)
if 'Practitioner' in x:
y = x.replace('Practitioner', ' ')
elif 'P' in x:
y = x.replace('P', ' ')
elif 'p' in x:
y = x.replace('p', ' ')
elif 'Graduate' in x:
y = x.replace('Graduate', ' ')
elif 'G' in x:
y =x.replace('G', ' ')
elif 'g' in x:
y = x.replace('g', ' ')
y = int(y)
g = inf.number_to_words(y)
return (g)
Full Script
import datetime
import os
from random import choice
from string import digits
import subprocess
import inflect
import pandas as pd
import reportlab
from reportlab.pdfbase import pdfmetrics
from reportlab.pdfbase.ttfonts import TTFont
from reportlab.pdfgen import canvas
# import xlrd
from reportlab.lib.units import mm
# set inflect
inf = inflect.engine()
# set fonts
folder = os.path.dirname(reportlab.__file__) + os.sep + 'fonts'
ttfFile_b = os.path.join(folder, 'VeraBd.ttf')
ttfFile_r = os.path.join(folder, 'Vera.ttf')
pdfmetrics.registerFont(TTFont("VeraBd", ttfFile_b))
pdfmetrics.registerFont(TTFont("Vera", ttfFile_r))
# Set date usage stuff
w = datetime.datetime.now()
d = datetime.date.today()
m = d.month
y = d.year + 1
p = str(w.hour) + '-' + str(w.minute) + '_' + str(w.second)
end_date = str(m) + '/' + str(y)
def grade(R):
x = str(R)
if 'Practitioner' in x:
y = x.replace('Practitioner', ' ')
elif 'P' in x:
y = x.replace('P', ' ')
elif 'p' in x:
y = x.replace('p', ' ')
elif 'Graduate' in x:
y = x.replace('Graduate', ' ')
elif 'G' in x:
y =x.replace('G', ' ')
elif 'g' in x:
y = x.replace('g', ' ')
y = int(y)
g = inf.number_to_words(y)
return (g)
def level(R):
x = str(R)
y = x.replace('Practitioner', ' ')
int(y)
g = inf.number_to_words(y)
return (g)
# Routine to get location
def location(L):
x = str(L)
if 'London' in x:
l = str("London")
elif 'Stratford' in x:
l = str("Stratford Upon Avon")
elif 'Bristol' in x:
l = str("Bristol")
elif 'Penrith' in x:
l = str("Penrith")
else:
l = str("United Kingdom")
return (l)
# Get date
def when(D):
dt = D.strftime('%d %B %Y')
d = str(dt)
return (d)
# get random diploma number
def dip():
x = ''.join(choice(digits) for i in range(6))
k = str(x)
return (k)
def p_certs(df):
# set PDF file name and canvas size
# file_location = 'C:\Users\Suely\Desktop\Ouput_PDF\'
# Set date usage stuff
w = datetime.datetime.now()
d = datetime.date.today()
m = d.month
y = d.year + 1
p = str(w.hour) + '-' + str(w.minute) + '_' + str(w.second)
# end_date = str(m) + '/' + str(y)
file_name = 'CERTIFICATES_' + str(d) + '_' + str(p) + '.pdf'
c = canvas.Canvas(file_name, pagesize=(210 * mm, 297 * mm))
# 1mm = 0.35277777 pt
# draw each page
for index, row in df.iterrows():
# D = str(row['Date'])
# R = row['Grading Level (National Grading)']
# L = row['Select Grading Event']
# g = location(L)
# A = row['Grading Committee']
# I = row['Instructor']
g = row['Location']
fn = str.strip(row['First Name'])
ln = str.strip(row['Last Name'])
f = row['Grade']
# r = row['Grade']
r = grade(f)
# d = str('11 August 2018')
# d = when(D)
d = str(row['Date'])
N = fn + ' ' + ln
# N = row['First Name'] + ' ' + row['Last Name']
# N = row['First Name'] + row['Last Name']
k = dip()
#k2 = str('KMG-UK -') + str(k)
k2 = row['Diploma Number']
# Draw Name
c.setFont('VeraBd', 24, leading=None)
c.drawCentredString(297.63786, 160 * mm, str.title(N))
# Draw Grade
c.setFont('VeraBd', 18, leading=None)
c.drawCentredString(110 * mm, 130 * mm, str.upper(r))
# Draw Place
c.setFont('VeraBd', 14, leading=None)
# c.drawCentredString(297.63786, 261.8581643, str.title(g))
c.drawCentredString(105 * mm, 91 * mm, str.upper(g))
# Draw Date
c.setFont('VeraBd', 14, leading=None)
# c.drawCentredString(297.63786, 236.3463527, str.title(d))
c.drawCentredString(105 * mm, 81 * mm, str(d))
# Draw Diploma number
c.setFont('VeraBd', 11, leading=None)
c.drawCentredString(105 * mm, 71 * mm, str.upper(k2))
c.showPage()
# Draw Administration & Instructor
# c.setFont('VeraBd', 12, leading=None)
# c.drawCentredString(60*mm, 25*mm, str.upper(I))
# c.setFont('VeraBd', 12, leading=None)
# c.drawCentredString(105*mm, 25*mm, str.upper(A))
c.save()
subprocess.Popen([file_name], shell=True)
# set data file
data_location = '007. P1_P2 Data Sheet.csv'
file_name2 = '007. P1_P2 Data Sheet.xlsx'
# read dataframe
df = pd.read_csv(data_location, encoding='latin1', na_values=['nan'], keep_default_na=False)
# df = pd.read_excel(file_name2, 'Sheet1', index_col=None, na_values=['NA'])
p_certs(df)
The Error
C:\Users\James\AppData\Local\Programs\Python\Python37\python.exe "D:/Dropbox/Dropbox/00000001 Licence Print/P certs/002 P1 Certificates/Certificatea.py"
Traceback (most recent call last):
File "D:/Dropbox/Dropbox/00000001 Licence Print/P certs/002 P1 Certificates/Certificatea.py", line 179, in
p_certs(df)
File "D:/Dropbox/Dropbox/00000001 Licence Print/P certs/002 P1 Certificates/Certificatea.py", line 127, in p_certs
r = grade(f)
File "D:/Dropbox/Dropbox/00000001 Licence Print/P certs/002 P1 Certificates/Certificatea.py", line 50, in grade
y = int(y)
UnboundLocalError: local variable 'y' referenced before assignment
Process finished with exit code 1
Your local variable y would not be initialized before its usage in the statement y = int(y) if none of the if and elif conditions before it are met.
You can assign to y a default value in an else clause instead:
def grade(R):
x = str(R)
if 'Practitioner' in x:
y = x.replace('Practitioner', ' ')
elif 'P' in x:
y = x.replace('P', ' ')
elif 'p' in x:
y = x.replace('p', ' ')
elif 'Graduate' in x:
y = x.replace('Graduate', ' ')
elif 'G' in x:
y =x.replace('G', ' ')
elif 'g' in x:
y = x.replace('g', ' ')
else:
y = '0'
y = int(y)
g = inf.number_to_words(y)
return (g)
Or you can try fixing your input data to ensure that the argument R would always result in satisfying one of the conditions in the if and elif clauses.

Resources