Python - display excel database in a grid - part 2 - python

My aim is to create a grid that looks like the following http://i49.tinypic.com/f39hxv.png, where each number represents the number of 'preperation flakes' found in each square (see data snippet below)
The code so far is as follows:
import csv
import os
import pygame
# the functions for display
def disp(phrase,loc,screen): # function to display phrase at loc on surface.
s = font.render(phrase, True, (255,255,255))
screen.blit(s, loc) #
def text_display_csv(filename,surface):
'''Display .csv file contents on surface'''
f = csv.reader(open("Flint catalogue v1.7 4 Feb 13 2013.csv")) # open the csv file in one line!
for row in f:
y = f.line_num # assign y as Row no.
for x,item in enumerate(row): # Get column number(x) and item
disp(item, (64*x+10,64*y+10), surface) # display item
pygame.init()
pygame.font.init()
font = pygame.font.SysFont("Courier",10) # font initialisation
screen = pygame.display.set_mode((1200,1200))
filename = 'the .csv file'
text_display_csv(filename,screen) # text is displayed
pygame.display.update() # screen updated
# Main loop, does nothing! :)
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
pygame.quit()
break
if not running:
break
I would like the grid to look a bit like this (but my trouble is putting data from the database into this array):
import csv
import os
import pygame
# Define some colors
black = ( 0, 0, 0)
white = ( 255, 255, 255)
green = ( 0, 255, 0)
red = ( 255, 0, 0)
# This sets the width and height of each grid location
width=20
height=20
# This sets the margin between each cell
margin=5
# Create a 2 dimensional array. A two dimesional
# array is simply a list of lists.
grid=[]
for row in range(10):
# Add an empty array that will hold each cell
# in this row
grid.append([])
for column in range(10):
grid[row].append(0) # Append a cell
# Set row 1, cell 5 to one. (Remember rows and
# column numbers start at zero.)
grid[1][5] = 1
grid[1][6] = 2
# Initialize pygame
pygame.init()
# Set the height and width of the screen
size=[255,255]
screen=pygame.display.set_mode(size)
# -------- Main Program Loop -----------
while done==False:
# Set the screen background
screen.fill(black)
# Draw the grid
for row in range(10):
for column in range(10):
color = white
if row[4]in c == 1:
color = green
pygame.draw.rect(screen,color,[(margin+width)*column+margin, (margin+height)*row+margin,width,height])
# Limit to 20 frames per second
clock.tick(20)
# Go ahead and update the screen with what we've drawn.
pygame.display.flip()
pygame.quit()
The data is a s follows:
http://tinypic.com/view.php?pic=2rdgn02&s=6
So to sum up. i would like different 'classifications' (see data) to be displayed in the grid like the pic at the start.
Thanks for the help
Tom

Here's some code,
That is your code other than 10 chars max (except comments and removals), but causes Big changes.
import pygame,csv
def disp(phrase,loc,screen):
s = font.render(phrase, True, (255,255,255))
screen.blit(s, loc)
# not needed
def text_display_csv(filename,surface):
'''Display .csv file contents on surface'''
f = csv.reader(open(filename))
for row in f:
y = f.line_num
for x,item in enumerate(row):
disp(item, (60*x+10,12*y-2), surface) # changed y distance
pygame.init()
pygame.font.init()
font = pygame.font.SysFont("Courier",10) # Changed font size
screen = pygame.display.set_mode((640,480)) # changed window size
filename = "Flint catalogue v1.7 4 Feb 13 2013.csv"
text_display_csv(filename,screen)
pygame.display.update()
# Main loop, does nothing! :)
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
pygame.quit()
break
if not running:
break
Update 1
Ok, I see.
So, you want to display data in P at Grid no S...
Here's the code for that too. Just change text_display_csv to this,
def text_display_csv(filename,surface):
'''Display .csv file contents on surface, after processing'''
f = csv.reader(open(filename))
# The Processing
data = {}
for row in f:
y = f.line_num
if len(row) >= 19:
# See if S contains a number,
# if No: Print the msg
# else: add to dict, in form of S:P
try:
val = int(row[18]) # S is the 19th column
except ValueError:
print "Skipped row no",y,"as column S doesn't contain number"
continue
else: # if a number, add to the dict
data[val] = row[15] # P is the 16th column
print "File Loaded"
# the display part
width = 6
for loc in data:
item = data[loc] # get item (string to display)
y = loc//width # column
x = loc % width # row
disp(item, (60*x +10,30*y +10), surface)
What's Happening here:
Load .csv
If a row has items till at least S column,
If value at S is a number, get it's integer value.
Then add information to a dictionary in the form of S : P, Where
S is the integer
P the string/value at column P.
Otherwise print a msg, and move on to the next row(if any)
Display Data at their respective places.
Now the only problem is that the numbers have to start with zero.
To overcome that, Just add
loc += 1
Before
y = loc//width
Your grid lines can be drawn using pygame.draw.line, you can figure that out.

Related

What doesn't work in my extract frames from video code?

I tried to write 2 very simple codes to extract specific frames from a 24FPS video: One of them to extract frames based on the frame number, and the other one to extract frames at a given frequency (i.e. 1 frame every second). I am using Anaconda to create and manage my environment, and Spyder to code.
The first one:
#Extract frames based on their number
import cv2
# Open the video file
video = cv2.VideoCapture("C:\\path_to_video.mp4")
# Get the total number of frames in the video
total_frames = int(video.get(cv2.CAP_PROP_FRAME_COUNT))
# Define the frame numbers you want to extract
frame_numbers = [10, 50, 100]
# Initialize the frame counter
frame_counter = 0
# Loop over each frame of the video
while True:
# Read the current frame
ret, frame = video.read()
# Break the loop if we've reached the end of the video
if not ret:
break
# Increase the frame counter
frame_counter += 1
# If the current frame number is in the list of frame numbers to extract
if frame_counter in frame_numbers:
# Save the frame
cv2.imwrite("C:\\path_to_save_frame_" + str(frame_counter) + ".jpg", frame)
# Release the video object
video.release()
The second one:
#Extract frames with a given frequency
import cv2
# Open the video file
video = cv2.VideoCapture("C:\\path_to_video.mp4")
# Get the frames per second (fps) of the video
fps = video.get(cv2.CAP_PROP_FPS)
# Calculate the time interval (in milliseconds) between frames
frame_time_interval = int(1000 / fps)
# Define the time frequency (in this case, 1 second)
time_frequency = 1 * 1000
# Initialize the frame counter
frame_counter = 0
# Loop over each frame of the video
while True:
# Read the current frame
ret, frame = video.read()
# Break the loop if we've reached the end of the video
if not ret:
break
# Increase the frame counter
frame_counter += 1
# If the frame counter modulo the time frequency divided by the frame time interval is 0
if frame_counter % int(time_frequency / frame_time_interval) == 0:
# Save the frame
cv2.imwrite("C:\\path_to_save_frame_" + str(frame_counter) + ".jpg", frame)
# Release the video object
video.release()
The problem I encounter is the following: whenever I try to run one of the codes, the program seems to be running, but it extracts no frames and outputs no error message. The command console just look like this after a few seconds:
[In 1] :
runfile('C:/path_to_code/Extract_frames_time_frequency.py', wdir='C:/path_to_directory/Stage_M2')
[In 2] :

Python: Creating AreaChart3D in a recursive way

My target is to create some AreaChart3D plots in an automatically way.
Precisely, for example I have the following picture:
This table is automatically outputed by a tool.
I can have only one graph, maybe 2 graphs or even 100 graphs (does not matter so much), it is important every time I will have this kind of behavior with Location, Speed, and some times inside.
Now, I would like to have in the second sheet(ws2_obj) 4 graphs or maybe 2 graphs depends how many graphs will be outputed by the tool.
If I would have had a fixed number of graph it would have been easier.
Because this graphs are not fixed i have to cover the entire sheet and I do not know how to do it.
Also, there is another question: how to handle Depth (% of base) using Python?
from openpyxl.chart import (
AreaChart3D,
Reference,
)
wb_obj = xl.load_workbook('Plots.xlsx')
ws_obj = wb_obj.active
ws2_obj = wb_obj.create_sheet("Graphs")
c1 = AreaChart3D()
c1.legend = None
c1.style = 15
cats = Reference(ws_obj, min_col=1, min_row=7, max_row=200)
data = Reference(ws_obj, min_col=2, min_row=6, max_col=8, max_row=200)
c1.add_data(data, titles_from_data=True)
c1.set_categories(cats)
ws2_obj.add_chart(c1, "A1")
wb_obj.save("Plots.xlsx")
The Code above produces only one graph, but how should I proceed to create 2 or 4 or 100 graphs?
Later edit 1:
I tried something like this and it is almost working:
for i in range(1, 4):
c1 = AreaChart3D()
cats = Reference(ws_obj, min_col=1, min_row=7, max_row=200)
data = Reference(ws_obj, min_col=2, min_row=6, max_col=i * int(step), max_row=200)
c1.title = ws_obj.cell(row=1, column=i * int(step)).value
c1.legend = None
c1.style = 15
c1.y_axis.title = 'Fire Time'
c1.x_axis.title = 'Temperature'
c1.z_axis.title = "Velocity"
c1.add_data(data, titles_from_data=True)
c1.set_categories(cats)
ws2_obj.add_chart(c1, "A2")
For me the last ws2_obj.add_chart(c1, "A2") seems to be the problematic one.
Instead of A2 I would like to use something like ws2_obj.add_chart(c1, cell(row=2, column=i)).value but does not working.
Later Edit 2
I have observed if you want to add a chart to a certain cell, you have to use something like: ws2_obj.add_chart(my_chart, "R2")
In order to use the for loop I tried to find out a way to get this value R2.
Please, see below:
my_cells = []
for i in range(1, 4):
my_cell = ws2_obj.cell(row=1, column=i * int(step) - (int(step) - 1))
my_cells.append(my_cell)
print("My_Cell:", my_cells)
new_cells = []
for i in my_cells:
new_cells.append(re.findall("\W\w\d", str(i)))
new_new_cells = []
for i in new_cells:
new_new_cells.append(i[0])
print("new_new_cells:", new_new_cells)
final_list = [re.sub('[^a-zA-Z0-9]+', '', _) for _ in new_new_cells]
print("final list:", final_list)
And the output will be ['A1', 'H1', 'O1']
and then I can output the graph:
for i in range(1, 4):
c1 = AreaChart3D()
# my_cell = ws2_obj.cell(row=i, column=i * int(step))
cats = Reference(ws_obj, min_col=1, min_row=7, max_row=255)
data = Reference(ws_obj, min_col=2, min_row=6, max_col=i * int(step), max_row=255)
c1.title = ws_obj.cell(row=1, column=i * int(step)).value
c1.legend = None
c1.style = 20
c1.y_axis.title = 'Time'
c1.x_axis.title = 'Location'
c1.z_axis.title = "Velocity"
c1.add_data(data, titles_from_data=True)
c1.set_categories(cats)
c1.x_axis.scaling.max = 75
c1.y_axis.scaling.max = 50
c1.z_axis.scaling.max = 25
ws2_obj.add_chart(c1, str(final_list[i - 1]))
You can create a list of the series data (position where the data series starts). The list has 1 element per series. Iterate the list creating a chart for each and ensure you have some means to place the chart in a unique position.
Example code with comments below.
import openpyxl as xl
from openpyxl.chart import (
AreaChart3D,
Reference,
)
def create_chart(tl, maxr, hdr, x_ax):
"""
Creates a standard Area 3D Chart
"""
cht = AreaChart3D()
cht.legend = None
cht.style = 15
cht.title = hdr + " Chart"
cht.x_axis.title = x_ax
cht.y_axis.title = 'Something' # Some text for the y axis
data = Reference(ws_obj, min_col=tl[0], min_row=tl[1], max_col=tl[0]+1, max_row=maxr-1)
cht.add_data(data, titles_from_data=True)
return cht
## Sheet constants
chart_header = 'Speed' # It is assumed this is located in a merged cell
x_axis_header = 'Location'
series_topleft_header = 25
## Load Workbook and Sheet of Excel with data series
wb_obj = xl.load_workbook('Plots.xlsx')
ws_obj = wb_obj.active
## Get the total used rows in the sheet (end of the series table)
maxrows = ws_obj.max_row
speed_row = ''
speed_col_start = ''
speed_col_end = ''
speed_col_letter = ''
## Get a list of Merged cell in the sheet these contain the Headers for position referencing
merge_list = [m.coord for m in ws_obj.merged_cells.ranges]
## Search for the row with Header name 'Speed' to use as reference for series data postioning
for merge_element in ws_obj.merged_cells:
merge_cell_val = merge_element.start_cell.internal_value
if merge_cell_val.lower() == chart_header.lower():
speed_row = merge_element.max_row
speed_col_start = merge_element.min_col
speed_col_end = merge_element.max_col
speed_col_letter = merge_element.start_cell.column_letter
series_header_row = speed_row + 1
series1_start = speed_col_letter + str(series_header_row+1)
"""
Obtain the location of the top left cell where the series data exists
This searches the row below the header (containing the text 'Speed') for the first
series header (i.e. 25 in the example) and adds each position to the series_postion_list
"""
series_position_list = []
for row in ws_obj.iter_rows(min_row=series_header_row,
max_row=series_header_row,
min_col=speed_col_start,
max_col=speed_col_end):
for cell in row:
if cell.value == series_topleft_header:
series_position_list.append([cell.column, series_header_row])
## Create the Charts
"""
With the series_position_list indicating the top left cell of the series data
and the number of rows in the series determined be the maxrows - 1. This data
can be passed to the create_chart function to create the chart.
Charts are placed below the series data table from Column A with two charts
per row. First row for chart location is 2 rows below the series table.
"""
chart_start_row = maxrows + 2
chart_col = 'A'
"""
The series_position_list is used to create 1 chart per series
The chart creation function takes the top left coordinate and max rows along
with Chart header name and x axis header name
"""
for enum, top_left in enumerate(series_position_list, 1):
chart_obj = create_chart(top_left,
maxrows,
chart_header + ' ' + str(enum),
x_axis_header)
## This sets the position the chart will be placed. Based on standard size
## of plot area the charts are 16 rows and 10 columns apart
if enum == 1:
pass
elif enum % 2 == 1:
chart_col = 'A'
chart_start_row += 16
else:
chart_col = 'J'
## Adds chart to the Excel sheet
print(f"Adding chart {chart_header + ' ' + str(enum)} to Excel:")
print(f"Series Data Start; Row:{str(top_left[1]+1)} Column:{top_left[0]}")
ws_obj.add_chart(chart_obj, chart_col + str(chart_start_row))
print("--------------\n")
wb_obj.save("Plots.xlsx")
-----------------Additional Information--------------
add_chart is a method that accepts two arguments; the chart object and optionally an anchor point (i.e the top left cell where the chart is placed in the sheet). Use of .value at the end of
ws2_obj.add_chart(c1, cell(row=2, column=i)).value
is invalid as you are not entering the method into the cell you are using the method to add the chart object c1 at position cell(row=2, column=i). Using cell(row=2, column=i) is also an invalid syntax. You may have meant to use ws2_obj.cell(row=2, column=i) as the anchor. This would be accepted by the add_chart method however when saving the worksheet there would be an error on checking the anchor point as this expects the anchor to be an "Excel style coordinate" i.e. a string like 'A2' rather than a cell object like ws2_obj.cell(row=2, column=i). Even using (2, 1) would fail the same check.
To set the anchor points I will show how to do two options; All charts on the same row and X charts across the row then start next X charts on the next row etc.
Place all charts on same row;
If you are going to put all charts on the same row then the row coord will not change and only the column position needs adjustment for each chart.
You can generate the anchor points like below, the example code uses a for loop with 18 elements;
from openpyxl.utils.cell import coordinate_to_tuple
from openpyxl.utils import get_column_letter
anchor = 'A2' # Position of anchor, first anchor point is 'A2'
column_separation = 9 # Number of columns to separate each chart
for i in range(0, 18):
coord_tuple = coordinate_to_tuple(anchor)
row = coord_tuple[0]
col_offset = column_separation if i > 0 else 0
col_new = get_column_letter(coord_tuple[1] + col_offset)
anchor = f'{col_new}{row}'
print(f'Adding chart at Anchor point {anchor}')
ws2_obj.add_chart(c1, anchor)
This will put the chart at the following achor points;
A2, J2, S2, AB2, AK2, AT2, BC2, BL2, BU2, CD2, CM2, CV2, DE2, DN2, DW2, EF2, EX2, EO2
Placing the charts is a pattern.
Placing the charts is a pattern of rows and columns is similar to the previous code however when the number of charts reaches your limit the 'row' value has to change and the column resets back to 'A'.
The example code again uses a for loop with 18 elements and splits the charts into rows of max_chart_row, set to 5 in this case;
from openpyxl.utils.cell import coordinate_to_tuple
from openpyxl.utils import get_column_letter
anchor = 'A2'
column_separation = 9
max_chart_row = 5
for i in range(0, 18):
coord_tuple = coordinate_to_tuple(anchor)
row = coord_tuple[0]
col_offset = column_separation if i > 0 else 0
# When the number of charts across the row is reached, set the row to 16 more than the current
# and reset the column offset to 0
if i % (max_chart_row) == 0 and i != 0:
row = row + 16
col_offset = 0
col_new = get_column_letter(col_offset+1)
else:
col_new = get_column_letter(coord_tuple[1] + col_offset)
anchor = f'{col_new}{row}'
print(f'Adding chart at Anchor point {anchor}')
ws2_obj.add_chart(c1, anchor)
This will put the chart at the following achor points;
A2, J2, S2, AB2, AK2,
A18, J18, S18, AB18, AK18,
A34, J34, S34, AB34, AK34,
A50, J50, S50

In a Excel file how to find the background color of a cell using openpyxl (Python)

`wb = openpyxl.load_workbook('file.xlsx',data_only=True)
fs_count_row = fs.max_row
fs_count_col = fs.max_column
for a in range(1,fs_count_row):
cell_color = fs.cell(column=1, row=a)
bgColor = cell_color.fill.bgColor.index
fgColor = cell_color.fill.fgColor.index`
Tried using the above code. However, it works fine for some cells and give wrong output for some other cells. Can I know the reason why?
Column number was fixed to 1 always, that's why code is not picking up the cells outside of the first column. Loop both rows and columns. Updated code below. I've added a print statement to provide the fgcolor and bgcolor of all the active cells along with the cell information. In addition, I've added a condition to ignore the blank cells where there is no foreground or background color.
Input:
Cell A1 = Yellow
Cell A2 = Blank
Cell B1 = Black
Cell B2 = Red
Code:
import openpyxl
wb = openpyxl.load_workbook('Excel.xlsx',data_only=True)
fs = wb.active
fs_count_row = fs.max_row
fs_count_col = fs.max_column
for row in range(1,fs_count_row+1):
for column in range(1,fs_count_col+1):
cell_color = fs.cell(column=column, row=row)
bgColor = cell_color.fill.bgColor.index
fgColor = cell_color.fill.fgColor.index
if (bgColor=='00000000') or (fgColor=='00000000'):
continue
else:
print("Background color index of cell (",row,column, ") is", bgColor)
print("Foreground color index of cell (",row,column, ") is", fgColor)
Output:
Background color index of cell ( 1 1 ) is 64
Foreground color index of cell ( 1 1 ) is FFFFFF00
Background color index of cell ( 1 2 ) is 64
Foreground color index of cell ( 1 2 ) is 1
Background color index of cell ( 2 2 ) is 64
Foreground color index of cell ( 2 2 ) is FFFF0000

Tkinter Formatting Buttons in Same Row using .grid

How can I display a row of buttons across the bottom of the screen using Tkinter? The number of buttons is a variable. Could be 2 or could be 10. The number of items being displayed is also a variable. In this case it's 5, but could be 2 or could be 10. I am using Tkinter and currently have a working program that outputs a grid that looks like this:
-------------------------
| |Title| |
|Item1| |Quantity1|
|Item2| |Quantity2|
|Item3| |Quantity3|
|Item4| |Quantity4|
| | (Intentionally blank)
|Item5| |Quantity5|
-------------------------- (end)
I am outputting like so:
from tkinter import *
window = Tk()
itemLabel = Label(
window,
text = "Item1",
).grid(row = 2, column = 0, sticky = 'w')
However, when I try to add buttons, I can't seem to get the formatting correct. If I do it similarly to the label, with "sticky = 'w'", then it overlaps on the left. I only have 3 columns so if I have more than 3 buttons I run out of columns. Below is my desired output (hopefully all of the buttons will be of equal width):
-------------------------
| |Title| |
|Item1| |Quantity1|
|Item2| |Quantity2|
|Item3| |Quantity3|
|Item4| |Quantity4|
| | (Intentionally blank)
|Item5| |Quantity5|
--------------------------
|B#1| B#2|B#3|B#4| B#5|B#6|
--------------------------- (end)
I had a similar problem, but for radiobuttons - a variable number of options which can change, in this case updated on the click of a button. I set the max number of columns as I spill over into more rows if need be.
def labelling_add_radiobutton(self):
'''add selected values as radiobuttons for labelling'''
# destroy any existing radiobuttons in frame
for child in self.frame3_labelling_labels.winfo_children():
if isinstance(child, ttk.Radiobutton):
child.destroy()
# label variable and create radio button
self.checkbox_validation_output = tk.StringVar(value = '')
num_cols = 8 # sets max number of columns, rows are variable
for count, value in enumerate(list_of_buttons):
row = int((count + .99) / num_cols) + 1
col = (((row - 1) + count) - ((row - 1) * num_cols) - (row - 1)) + 1
ttk.Radiobutton(self.frame3_labelling_labels, text = value,
variable = self.checkbox_validation_output, value = value,
style = 'UL_radiobutton.TRadiobutton').grid(column = col, row = row,
sticky = 'w', padx = 10, pady = 10)

Aligning images with openpyxl

I have been looking for a couple days now and cannot find an answer. I am creating a worksheet with python and is using the openpyxl module. I am able to format cells and insert values in the cells. I can insert images in cells, but i need to align the images.
align = Alignment(horizontal = 'left', vertical = 'center', wrap_text = True
IMAGECELL = ws.cell['A1']
IMAGECELL.alignment = align
This is my code for aligning text. It is not working with images.
I know my solution is not perfect but it worked for me, just want to help others save time. I wanted to add multiple images too and align them but when I tried adding images they were placed on top of each other. It turned out that you have to use anchors in openpyxl to position images. and I wanted to put them in 2 x 3 Grid. Here's my solution.
def excel_writer(self, _location=None, images=None):
if images is None or len(images) == 0:
return
for image in images:
proto_image = Image(str(image))
# A bit resizing
proto_image.height = 250
proto_image.width = 300
if count == 0:
proto_image.anchor = 'A3'
if count == 1:
proto_image.anchor = 'F3'
if count == 2:
proto_image.anchor = 'L3'
if count == 3:
proto_image.anchor = 'A20'
if count == 4:
proto_image.anchor = 'F20'
if count == 5:
proto_image.anchor = 'L20'
if count == 6:
proto_image.anchor = 'A43'
sheet.add_image(proto_image, proto_image.anchor)
count += 1
workbook.save(_location)
So, I am going through a list of images and adding them on sheet at anchors position. For first image, the count is 0 and the anchor is A3; Which means 1st column, 3rd row. For 2nd image, same 3rd row but different column and so on.
One thing to notice is that I have calculated the space between columns manually. If your image is greater in width, you'll need to further move it along +ve x-axis like from column A to L.

Categories