Tkinter / Python - Aligning Text in Character String (Project for Class) - python

Am loading a listbox in TKinter. The string is a collection of fields from a matrix. 4 fields, 1 date, 1 float, and 2 text.
However, because of proportional fonts, I cannot get the text in each line to line up into columns.
Here is my current code
from tkinter import *
from tkinter import ttk
from datetime import *
import locale
# for currency display, set the locale
locale.setlocale( locale.LC_ALL, '' )
class main_gui(Frame):
def __init__(self): #Intialize key variables and call the main screen.
self.Ledgerdata = [ ]
print ("main_gui __init__")
self.load_data()
self.main_screen()
def load_data(self):
# stub for testing - to be rewritten to read external file and load into Ledgerdata.
entry = []
entry.append("4/17/2016, 24, 1, Doctor")
entry.append("4/18/2016, 32, 1, Mechanic whose name is bill")
entry.append("4/19/2016, 45, 1, Grocery")
entry.append("4/11/2016, 19, 1, Puppet")
entry.append("4/17/2016, 119.50, 1, Johns Computer House")
entry.append("4/11/2016, 1250, 1, Sidney Barthalmew")
for entry_index in range(len(entry)):
entry[entry_index] = entry[entry_index].split(",")
date_conversion = entry[entry_index][0].split("/") # Convert date from text string
entry[entry_index][0] = date(int(date_conversion[2]), # to datetime variable
int(date_conversion[0]),
int(date_conversion[1]))
self.Ledgerdata.append((entry[entry_index][0], # Add transaction to Ledgerdata
float(entry[entry_index][1]),
int(entry[entry_index][2]),
entry[entry_index][3]))
#====== Data from file is read and loaded into Ledgerdata
#main_screen - main display screen for Ledgerdata
# Will show list of transactions, total for each category,
# with buttons to Add, Edit, Delete, Create Summary and Exit
def main_screen(self):
root = Tk() # reference to the GUI toolkit
main_window = ttk.Frame(root)
main_window.grid(row=0,column=0) # overall Frame
display_frame = ttk.Frame(main_window) # list display frame
display_frame.grid(column=0, row = 0)
option_frame = ttk.Frame(main_window) # selection frame - to be written.
option_frame.grid(column=0, row=15, columnspan=8)
summary_frame = ttk.Frame(main_window)
summary_frame.grid(column = 10, row = 0)
summary_frame.columnconfigure(0, minsize=50)
summary_frame.columnconfigure(1, minsize=250)
#Start display frame
list_header = Label(display_frame, width = 100, # Header line for List
font = 12,anchor = W, # NEEDS CLEANUP
text = " Date Amount Description Code")
list_header.grid(row=1)
list_of_transactions = Listbox(display_frame,width =100, height = 20, font = 12) #Generate listbox widget
list_of_transactions.grid(row=2, column = 0)
# Loop for number of transactions in Ledgerdata. Loop adds formatted transaction info to list_of_transactions
# liststring object for display
for x in range(len(self.Ledgerdata)):
output_string = '{0:10} {1:>8} {2:<50} {3:<40}'.format(
str(self.Ledgerdata[x][0].strftime("%m/%d/%y"))
,locale.currency(self.Ledgerdata[x][1], grouping=True)
,(self.Ledgerdata[x][3])
,(self.Ledgerdata[x][2]))
list_of_transactions.insert(x,output_string)
list_of_transactions.grid(row=x, column = 0)
#main_window.pack()
mainloop()
main_gui()
Anyone know how to get elements in strings to text to maintain the same width???
Thanks as always,
Darrell
EDITTED - Per request, added entire code segment that can be run. I had to take this out of my main program, eliminate several parts that are not relevant to the problem at hand, and make some changes that do not affect the problem at hand. So this is just a standalone testbed.

Related

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()

python error - TypeError: string indices must be integers

I have been building a flashcard app and have run into a roadblock while trying to implement a radiobutton. The issue is when run the menu shows up and your able to access the lesson, but the radiobuttons do not appear. Whenever the code is run this error shows up TypeError: string indices must be integers attached to the radiobutton functionbalancing_radio_butto1 = Radiobutton(balancing_frame, text = balancing[answer_list[0]], variable=balancing_radio, value = 1) if someone could explain the why this error shows up as well as how to fix it it would be much appreciated. Below is my code that I have so far.
from tkinter import *
from PIL import ImageTk, Image
from random import branding
import random
root = Tk()
root.title('Chemistry Flashcards')
root.geometry("500x500")
def balancing():
balancing_frame.pack(fill="both", expand=1)
global show_balancing
show_balancing = Label(balancing_frame)
show_balancing.pack(pady=15)
global balancing
balancing = ['balanced1', 'balanced2', 'balanced3', 'balanced4', 'balanced5', 'unbalanced1', 'unbalanced2', 'unbalanced3', 'unbalanced4', 'unbalanced5']
global balancing_state
balancing_state = {
'balanced1':'balanced',
'balanced2':'balanced',
'balanced3':'balanced',
'balanced4':'balanced',
'balanced5':'balanced',
'unbalanced1':'unbalanced',
'unbalanced2':'unbalanced',
'unbalanced3':'unbalanced',
'unbalanced4':'unbalanced',
'unbalanced5':'unbalanced',
}
answer_list = []
count = 1
while count < 3:
rando = randint(0, len(balancing_state)-1)
if count == 1:
answer = balancing[rando]
global balancing_image
balancing = "C:/Users/Kisitu/Desktop/project/balancing/" + balancing[rando] + ".png"
balancing_image = ImageTk.PhotoImage(Image.open(balancing))
show_balancing.config(image=balancing_image)
answer_list.append(balancing[rando])
'''random.shuffle(balancing)'''
count += 1
random.shuffle(answer_list)
global balancing_radio
balancing_radio = IntVar()
balancing_radio_butto1 = Radiobutton(balancing_frame, text = balancing[answer_list[0]], variable=balancing_radio, value = 1)
balancing_radio_butto1.pack(pady=10)
balancing_radio_butto2 = Radiobutton(balancing_frame, text = balancing[answer_list[1]], variable=balancing_radio, value = 2).pack()
my_menu = Menu(root)
root.config(menu=my_menu, bg='#B7F7BB')
lesson_menu = Menu(my_menu)
my_menu.add_cascade(label="Lesson", menu=lesson_menu)
lesson_menu.add_command(label="balancing", command=balancing)
lesson_menu.add_separator()
lesson_menu.add_command(label="Exit", command=root.quit)
balancing_frame = Frame(root, width=500, height=500, )
root.mainloop()
... text = balancing[answer_list[0]] ...
balancing is a list, you are trying to index a value from the list.
you are passing answer_list[0] as index.
answer_list contains random strings from balancing.
you are trying to index a list with a string like in
balancing["balanced2"]
maybe you could use a dictionary?

Why wont my code return a value for the data input into a tkinter text box

I have written this code and for some reason it refuses to return any sort of value or input for slef.REV when used in the function post(self) however it will return a value when I try and return a value in the getlen() function which is used to reurn the number of characters in the review.I dont have this problem for any other variables that I retrieve data from within this class. Below is the relevant code, any help would be appreciated. the lines where this problem occures is the first functio calld post(lines 1-5) and 4 lines up from the bottom
def post(self):
MovieID = self.MovID
REV = self.REV
AddReview(conn,cursor,Add_Review,MovieID,REV)
print(REV)
def shrek_film(self):
self.title = "Shrek"
self.MovID = 1
self.root4 = tk.Toplevel()
self.root4.title("Watch Shreck")
self.root4.geometry("1400x800")
frame_4 = tk.Frame(self.root4, bg = "black")
frame_4.pack(fill = tk.BOTH, expand = True, padx=0 , pady=0)
frame_4.grid_columnconfigure(1,weight=1)
self.Create_canvas = tk.Canvas(frame_4, width=2000, height=1080)
self.Create_canvas.place(x=-50, y=-50)
self.Create_img = PhotoImage(file="shrek-landscape.gif")
self.Create_canvas.create_image(20, 20, anchor = NW, image=self.Create_img)
play_button= tk.Button(frame_4,bg="orange",text="play", command = self.addHistory)
play_button.place(x=700,y=400)
play_button.config(font=("Ariel","30"))
def gtelen():
Review = reviewbox.get('1.0',END)
REVLEN = len(Review)
REVLENLEFT = (231-len(Review))
if REVLEN >=230:
lenbox = tk.Label(frame_4 ,text="No words left",bg="orange")
lenbox.place(x=360,y=460)
lenbox.config(font=("Ariel","15"))
else:
lenbox = tk.Label(frame_4 ,text=REVLENLEFT,bg="orange")
lenbox.place(x=360,y=460)
lenbox.config(font=("Ariel","15"))
print(Review)
Words_button = tk.Button(frame_4, bg="orange",text="check number of words remaining", command=gtelen)
Words_button.place(x=150,y=460)
Words_button.config(font=("Ariel","10"))
reviewlable=tk.Label(frame_4,text="Write a review",bg="orange")
reviewlable.place(x=10,y=460)
reviewlable.config(font=("ariel","15"))
Review_button= tk.Button(frame_4,bg="orange",text="See Reviews")#, command = self.ViewReviews)
Review_button.place(x=490,y=450)
Review_button.config(font=("Ariel","15"))
reviewbox= Text(frame_4,width=100,height=12)
reviewbox.place(x=10,y=500)
self.REV = reviewbox.get('1.0',END)
post_button = tk.Button(frame_4,bg="orange",text="Post Review", command = self.post)
post_button.place(x=830,y=650)
post_button.config(font=("Ariel","15"))
You can use Entry instead and use a StringVar
v = StringVar() # Create StringVar
reviewbox = Entry(frame_4, width = 100, height = 12, textvariable = v) # Create Entry widget
reviewbox.place(x = 10, y = 500) # Place Entry widget
self.REV = v.get() # Get contents of StringVar
The line self.REV = reviewbox.get('1.0',END) is being called about a millisecond after creating the text widget. The user will not even have seen the widget yet, much less have had time to type in it.
You can't call the get() method until after the user has had a chance to enter data, such as inside the post method.
def post(self):
MovieID = self.MovID
REV = reviewbox.get("1.0", "end")
AddReview(conn,cursor,Add_Review,MovieID,REV)
print(REV)

Print a threading function in curses, Python

In my project I have to print some data stored in a database on the console. I have two functions that return string. I should print those data in two columns.
I thought of using the module curses in python to create those two columns.
So far all is good.
Another thing, is that my two function use the threading.Timer. So the string generated changes every 10 seconds for example if I set my Timer on 10.
So when I put the result of my function on the addstr() of a column, it prints the first String correctly, but nothing changes even if my string change. Otherwise when I resize for example the console, I notice that the string changes.
Here is my code :
import sys,os
import curses , curses.panel
import datetime
import time
import threading
def time_func():
printStr = threading.Timer(10,time_func)
printStr.start()
s = "Simple example that display datetime \n" + datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
return s
def draw_menu():
k = 0
cursor_x = 0
cursor_y = 0
# Clear and refresh the screen
stdscr = curses.initscr()
stdscr.clear()
stdscr.refresh()
# Initialize colors in curses
curses.start_color()
curses.init_pair(1, curses.COLOR_CYAN, curses.COLOR_BLACK)
curses.init_pair(2, curses.COLOR_RED, curses.COLOR_BLACK)
curses.init_pair(3, curses.COLOR_BLACK, curses.COLOR_WHITE)
curses.curs_set(0)
# Loop where k is the last character pressed
while (k != ord('q')):
# Declaration of strings
statusbarstr = "Press 'q' to exit | STATUS BAR "
'''Draw borders of the different columns'''
height, width = stdscr.getmaxyx()
stdscr.border()
stdscr.vline(1, 3 * height // 4, '|', width - 2)
stdscr.vline(1, height // 4, '|', width - 2)
stdscr.refresh()
'''Initialize the Statistics column'''
column_one = curses.newwin(height - 2, 3 * width // 4, 1,
1)
_, a_x = column_one.getmaxyx()
column_one.attron(curses.color_pair(1))
column_one.attron(curses.A_BOLD)
column_one.addstr(0, a_x // 2 - 2, "column one")
column_one.hline(1, 0, '-', a_x)
column_one.attroff(curses.color_pair(1))
column_one.attroff(curses.A_BOLD)
# I want to add my string here for example :
line = time_func()
column_one.addstr(3,1, line)
column_one.noutrefresh()
'''Initialize the Alerts column'''
column_two = curses.newwin(height - 2, width // 4, 1,
3 * width // 4 )
_, s_x = column_two.getmaxyx()
column_two.attron(curses.color_pair(1))
column_two.attron(curses.A_BOLD)
column_two.addstr(0, s_x // 2 - 5, "column two")
column_two.hline(1, 0, '-', s_x)
column_two.attroff(curses.color_pair(1))
column_two.attroff(curses.A_BOLD)
column_two.addstr(3, s_x // 2 - 5,"test")
column_two.noutrefresh()
# Render status bar
stdscr.attron(curses.color_pair(3))
stdscr.addstr(height-1, 0, statusbarstr)
stdscr.addstr(height-1, len(statusbarstr), " " * (width - len(statusbarstr) - 1))
stdscr.attroff(curses.color_pair(3))
# Turning on attributes for title
stdscr.attron(curses.color_pair(2))
stdscr.attron(curses.A_BOLD)
# Rendering title
# Turning off attributes for title
stdscr.attroff(curses.color_pair(2))
stdscr.attroff(curses.A_BOLD)
# Print rest of text
stdscr.move(height - 2, width - 2)
# Refresh the screen
stdscr.refresh()
# Wait for next input
k = stdscr.getch()
def main():
curses.wrapper(draw_menu())
if __name__ == "__main__":
main()
The curses function draw_menu is waiting for input at the end of the loop. If you resize the terminal, that
k = stdscr.getch()
returns a KEY_RESIZE, and runs through the loop again.
You could change that to wait a short time before giving up on the getch. That would return a -1.
Usually that's done using timeout (in curses), e.g., 50 milliseconds. There's a wrapper for that in Python (and by the way its description is incorrect). You should read the ncurses manual page for timeout.
Sometimes people say to use nodelay, which is usually poor advice since it would make the draw_menu thread use most of the CPU time.

(Python) Change pagesize and format of PDF file generated with xtopdf

I want to convert an xlsx with Python. I used the modules tablib and xtopdf to build a well structured table. Works excellent! Unfortunately the content does not fit on one pdf page. So I wanted to change the pagesize and format to horizontal A3. But I don't know how that could work. My code:
import random
import tablib
from openpyxl import load_workbook
from xtopdf import PDFWriter
from pyPdf import PdfFileWriter, PdfFileReader
workbook = load_workbook('C:/Users/user1/Testexcel.xlsx', guess_types=True, data_only=True)
worksheet = workbook.get_sheet_by_name('Testsheet')
ws_range = worksheet.iter_rows('A4:H6')
# Helper function to output a string to both screen and PDF.
def print_and_write(pw, strng):
print strng
pw.writeLine(strng)
# Create an empty Dataset and set its headers.
data = tablib.Dataset()
data.headers = ['col1', 'col2', 'col3', 'col4']
widths = [30, 20, 10, 20] # Display widths for columns.
for row in ws_range:
col1 = str(row[0].value)
col2 = str(row[1].value)
col3 = str(row[2].value)
col4 = str(row[3].value)
columns = [col1, col2, col3, col4]
row = [ str(col).center(widths[idx]) for idx, col in enumerate(columns) ]
data.append(row)
# Set up the PDFWriter.
pw = PDFWriter('C:/Users/user1/Test.pdf')
pw.setFont('Courier', 10)
pw.setHeader('Test')
pw.setFooter('Test')
# Generate header and data rows as strings; output them to screen and PDF.
separator = '-' * sum(widths)
print_and_write(pw, separator)
# Output headers
header_strs = [ header.center(widths[idx]) for idx, header in enumerate(data.headers) ]
print_and_write(pw, ''.join(header_strs))
print_and_write(pw, separator)
# Output data
for row in data:
print_and_write(pw, ''.join(row))
print_and_write(pw, separator)
pw.close()
Found out that the PDFWriter from xtopdf itself instanciates an canvas object of the reportlab library. In the canvas class an attribute pagesize is declared which is setted by default to 'A4'. But if I change the entry to 'A3' the result pdf still is in 'A4'.
class Canvas(textobject._PDFColorSetter):
from reportlab.pdfgen import canvas
c = canvas.Canvas("hello.pdf")
from reportlab.lib.units import inch
# move the origin up and to the left
c.translate(inch,inch)
# define a large font
c.setFont("Helvetica", 80)
# choose some colors
c.setStrokeColorRGB(0.2,0.5,0.3)
c.setFillColorRGB(1,0,1)
# draw a rectangle
c.rect(inch,inch,6*inch,9*inch, fill=1)
# make text go straight up
c.rotate(90)
# change color
c.setFillColorRGB(0,0,0.77)
# say hello (note after rotate the y coord needs to be negative!)
c.drawString(3*inch, -3*inch, "Hello World")
c.showPage()
c.save()
"""
def __init__(self,filename,
pagesize='A3',
bottomup = 1,
pageCompression=None,
encoding = None,
invariant = None,
verbosity=0):
"""Create a canvas of a given size. etc.
You may pass a file-like object to filename as an alternative to
a string.
Most of the attributes are private - we will use set/get methods
as the preferred interface. Default page size is A4."""
if pagesize is None: pagesize = 'A3'
if encoding is None: encoding = rl_config.defaultEncoding
if invariant is None: invariant = rl_config.invariant
self._filename = filename
self._encodingName = encoding
self._doc = pdfdoc.PDFDocument(encoding,
compression=pageCompression,
invariant=invariant, filename=filename)
#this only controls whether it prints 'saved ...' - 0 disables
self._verbosity = verbosity
#this is called each time a page is output if non-null
self._onPage = None
self._pagesize = pagesize
self._pageRotation = 0
#self._currentPageHasImages = 0
self._pageTransition = None
self._pageDuration = None
self._destinations = {} # dictionary of destinations for cross indexing.
self.setPageCompression(pageCompression)
self._pageNumber = 1 # keep a count
#self3 = [] #where the current page's marking operators accumulate
# when we create a form we need to save operations not in the form
self._codeStack = []
self._restartAccumulators() # restart all accumulation state (generalized, arw)
self._annotationCount = 0
self._outlines = [] # list for a name tree
self._psCommandsBeforePage = [] #for postscript tray/font commands
self._psCommandsAfterPage = [] #for postscript tray/font commands
#PostScript has the origin at bottom left. It is easy to achieve a top-
#down coord system by translating to the top of the page and setting y
#scale to -1, but then text is inverted. So self.bottomup is used
#to also set the text matrix accordingly. You can now choose your
#drawing coordinates.
self.bottomup = bottomup
self.imageCaching = rl_config.defaultImageCaching
self._make_preamble()
self.init_graphics_state()
self.state_stack = []
edit: I think the changes in the reportlab module are not accepted by the system. Tried to remove the dictionary reportlab and tried to import it then in the commandline. Ironically it works ylthough python should not find that module anymore.
try this
from reportlab.pdfgen import canvas
from reportlab.lib.units import mm
c = canvas.Canvas("hello.pdf", pagesize = (297 * mm, 420 * mm))
# or (420 * mm, 297 * mm) if you want it in portrait format
# values for inch: 11.69 * inch , 16.53 * inch
#the following would create an empty page
c.showPage()
c.save()
Just forked a project named xtopdf at bitbucket and made the following change:
##------------------------ PDFWriter.__init__ ----------------------------
- def __init__(self, pdf_fn):
+ def __init__(self, pdf_fn, pagesize='A4'):
'''
Constructor.
"pdf_fn" arg is the name of the PDF file to be created.
'''
self.__pdf_fn = pdf_fn # file name of PDF file
- self.__canv = canvas.Canvas(pdf_fn) # canvas to write on
+ self.__canv = canvas.Canvas(pdf_fn, pagesize) # canvas to write on
self.__font_name = None # font name
self.__font_size = None # font size
self.__header_str = None # header string (partial)
Can you try it? use pw = PDFWriter('C:/Users/user1/Test.pdf', 'A3').

Categories