I am looking to write a program which outputs a bunch (around 30) of boxes like in this image:
I have been researching for weeks, I thought that to layout text in a graphically pleasing way I could use QTableWidget in PyQT, but I now realise that it is far too difficult to learn for such a simple task, there must be a quicker way. So I am thinking now to pass to Tkinter or maybe just draw the information with a Drawing Module like PyCairo and only then place each image in a PyQT interface. Sorting out all the positioning in a drawing module would be much quicker than learning how to do the same in PyQT.
But I feel I am missing something, I would have thought a much easier task to layout in a nice way a bunch of numbers in a repetitive format.
Some of the boxes will need also some graphical content has bars and charts for which I though to use plotly or cairo.
Whilst you're probably better off doing this with HTML and CSS as has been mentioned above, this isn't too difficult to do with Python and can be achieved with the use of just tkinter. Please see my code below for an example of how this could work:
from tkinter import *
root = Tk()
frame1 = []
frame2 = []
c = 0
numberofboxes = 8 #change this to increase the number of boxes
for i in range(numberofboxes):
if i % 4 == 0: #checks if the current box is the fourth in row
c = c + 1 #if the current box is the forth in the row then this runs and increases a counter which we later use to determine the row
if len(frame1) != c: #checks if the number of rows currently existing matches the number there should be
frame1.append(Frame(root)) #if the numbers don't match this runs and creates a new frame which acts as another row
frame1[c-1].pack(expand="True", fill="both") #packs the new row
frame2.append(Frame(frame1[c-1], bg="green")) #this is where the boxes are created
frame2[i].pack(ipadx="50", ipady="50", side="left", padx="10", pady="10", expand="True", fill="both") #this is where the boxes are placed on the screen
for i in range(len(frame2)): #this for loop places the items inside each box, all of this can be replaced with whatever is needed
Label(frame2[i], text="CO"+str(i), bg="green", fg="white").pack(side="top", anchor="w")
Label(frame2[i], text="12165.1"+str(i), bg="green", fg="white").pack(side="top", anchor="w")
Label(frame2[i], text="+60.7"+str(i), bg="green", fg="white").pack(side="bottom", anchor="e")
Label(frame2[i], text="+1.2"+str(i)+"%", bg="green", fg="white").pack(side="bottom", anchor="e")
root.mainloop()
So essentially, we create a frame for each row and each box is a frame which has elements packed inside it and is fitted in the "row frame" 4 to each row.
You should take a close look at all of the options for .pack() during this script as well as they are necessary to achieve the desired layout and results.
For your triangles you would most likely need to either import an image or draw them within a canvas positioned properly or (as was pointed out by Bryan Oakley below) you could use unicode characters for the arrows, which would be an awful lot simpler.
Related
I have to write a program for some coursework I'm doing, and I chose to do an A-Level revision game to help others. I'm trying to get use a ttk separator to split up the area of the window where the game part occurs, and the area of the window where the question sits. My window is split up into 3 frames; one with 4 labels in, one with 4 buttons in and one with a ttk.Separator widget and a label. I cannot get the separator however to span the whole window.
I've been looking around and testing things, but nothing seems to work. I've looked at these two previous posts in terms of on this website:
ttk.Separator set the length/width
A Label in a Frame in a window won't stretch, why?
but neither of these solutions seemed to fix my problem, and I'm now out of ideas. Any help would be greatly appreciated.
main_revision_game_question_frame = Frame(parent_window, bg='#c2f0f0')
main_revision_game_question_frame.pack()
main_revision_game_question_frame.grid_propagate(1)
main_revision_game_question_frame.config(width=screen_width, height=40)
separator = ttk.Separator(main_revision_game_question_frame, orient=HORIZONTAL)
separator.grid(column=0, row=0, sticky='ew')
question_label = Label(main_revision_game_question_frame,
text=' Placeholder text for a question goes here!
\n######################################################',
bg='#c2f0f0', fg='#ff2824', font='"Open Sans" 26 bold')
question_label.grid(column=0, row=1, sticky='ew')
main_revision_game_question_frame.grid_columnconfigure(0,weight=1)
main_revision_game_question_frame.grid_rowconfigure(0, weight=1)
This is the code I've ended up with upon trying to combine a few solutions, and this is how it looks:
You can see the separator is there, but it does not stretch across the frame. Thank you for reading.
The separator is expanding to fill the frame, but the frame hasn't been configured to fill the space it has been given.
You need to expand the frame. One way to do that is to use the fill option of pack:
main_revision_game_question_frame.pack(fill="x")
Try to use
main_revision_game_question_frame.pack(fill="both")
so the frame fills the whole space it is given
So I have a issue for where I type it will be right at the middle which I want to start it at the beginning of the box.
entry_notfor = Entry(root, textvariable=number_notoffer, width=30, bg="#1f1e1e", fg="#ffffff")
entry_notfor.place(x=5, y=390, height=100)
this is what it looks like now:
There's nothing you can do if you make the entry widget taller than it wants to be. An Entry widget is designed to be only one line tall. If you stretch it to be unnaturally tall, the text will always only appear centered vertically.
If your intent is to support multiline input then you should use a Text widget rather than an Entry.
I'm trying to write a music program that would display Chordpro files in python. Similar to this image, I want the chords, comments, and lyrics to each have different colours. I've tried these widgets:
I tried separating chords, comments and lyrics into multiple strings that could overlap on a canvas (with a different colour for each string) to make the full song, but sadly whitespace overrides previously rendered text, so I could only see the last layer.
Label/Message doesn't have functionality for multiple colours unless make a label for each line, which is very tedious, considering I want the font size to be adjustable too.
Text is editable, which I don't want.
Is there some kind of module or other tkinter widget that would allow separately coloured lines?
Just draw the separate lines at different y positions (heights) on the canvas. It's the first two parameters of the create_text() function. E.g.
self.canvas = Canvas(root, width=800, height=650, bg = '#afeeee')
self.canvas.create_text(100,10,fill="darkblue",font="Times 20 italic bold",
text="Greensleeves are my...")
So here, change 10 to the line position you want etc. Code copied from Python: how to add text inside a canvas?
I'm currently trying to print a table of data gathered from a database onto a GUI in tkinter, at the moment I'm using tabulate which runs fine in console, but as soon as I try to set the table as text as print as a label the table skews and the values are not aligned. I am still new to tkinter and so my code is by far not the most efficient or effective, any ideas on why this is happening or a way around?
order = (key_1,name_1,price_1,quantity_1,total_1)
table.append(order)
count +=1
print_table = (tabulate(table,headers=headers,tablefmt= "rst"))
c.fetchall()
conn.commit()
conn.close()
label_table = tk.Label(self,text=print_table)
label_table.pack()
Expected Result:
!file:///var/folders/59/89rdtwpd4vnf6pv7pbd3l60m0000gn/T/com.apple.Safari/WebKitDropDestination-rwh55dGn/Screen%20Shot%202019-02-03%20at%2011.49.29%20PM.png
Actual Result:
!file:///var/folders/59/89rdtwpd4vnf6pv7pbd3l60m0000gn/T/com.apple.Safari/WebKitDropDestination-tZziKT9D/Screen%20Shot%202019-02-03%20at%2011.47.15%20PM.png
https://i.stack.imgur.com/bgG4C.png
I would highly recommend creating a grid of tk.Entry widgets where you place your headers and values in discrete entries. If you recorded the tk.Entry widget objects in a list you would be able to call on them individually outside of the loop if you preferred that. You could substitute the tk.Label widget in the place of tk.Entry with other formatting changes if you were partial to the Label widget.
tableWindow=tk.Toplevel()
tableHeaderList=["Header1","Header2","Header3","Header4"]
for i in range(height): #Rows
for j in range(width): #Columns
b = tk.Entry(tableWindow, text="")
b.grid(row=i, column=j)
if i == 0:
b.insert(0,tableHeaderList[j])
I suggest you to use a monospaced font. I face the same problem and use
Cascadia Mono font and it worked. use the parameter font=("Cascadia Mono", 10) in the label. Hope it helps.
I put button on a frame like this in Tkinter for python3:
b2_1 = Button(frame1, text='aaaaaaaaaaaa')
b2_1.pack(pady=20, side=RIGHT)
b2_2 = Button(frame1, text='bbbbbbbbbbbbbbb')
b2_2.pack()
I want the buttons b2_1 and b2_2 to be stick not only to the right side, but also to the bottom, one right above another.
How can I do that?
The answer to "how do I put something in the bottom right corner?" can't be adequately answered out of context. It really depends on what else is in the window. You can use use place to easily place a widget anywhere you want, but it's rarely the right solution.
Grid is relatively easy to understand if you really do want to lay things out in a grid (eg: a right-most column with a couple rows on the bottom and one or more on top.
Pack can also be used, though it typically involves using additional frames. For example, you could create a frame for the left and right, and then pack the buttons at the bottom of the right frame.
There is also the possibility to use more than one along with additional frames. For example, you could use grid to lay out the main widget into a header, main area, and footer, and then use pack to arrange buttons in the footer.
If you literally only want two buttons, and you want them stacked in the bottom-right corner, I suggest using grid. In addition to placing them in a row on the bottom, you need to make sure that there is at least one other row and one other column that takes up any extra space. The rows and columns can be empty, but they must be configured to have a "weight".
For example:
frame1.grid_rowconfigure(0, weight=1)
frame1.grid_columnconfigure(0, weight=1)
# give the empty row 0 and column 0 a non-zero weight
# so that grid gives all extra space to those columns.
b2_1.grid(row=1, column=1)
b2_2.grid(row=2, column=1)
pack is not the ideal geometry manager in this case. You use pack in a frame where you just want to stick children one after the other. There is also a place manager which I have yet to see a 'natural' use for.
In this case use the grid manager. This is less simple than pack but has the flexibility you want. Once you figure out how many rows and columns your grid will have, placing is simple with .grid(column=lastcol,row=lastrow). Terry Jan Reedy also suggested using way too big numbers since empty columns/rows are not displayed, but I like to be pedantic so I know how my grid looks like exactly.
The .gridsize() method of a frame/ window returns a tuple with the size of the grid. i.e.
frame1.grid_size() = (number_of_columns_in_frame1, number_of_rows_in_frame1)
To put something in the bottom right of a frame use:
b2_1 = Button(frame1, text='aaaaaaaaaaaa')
b2_1.grid(row=frame1.grid_size()[1], column=frame1.grid_size()[0])
This will add a row and column to the frame1's grid and place b2_1 in the new row and column (because grid's row/ column system is zero indexed).
Adding another button below it:
Using the same system as above, i.e.
b2_2 = Button(frame1, text='bbbbbbbbbbbbbbb')
b2_2.grid(row=frame1.grid_size()[1], column=frame1.grid_size()[0])
will create another new row and column and place b2_2 to the bottom right of b2_1.
So to place b2_2 directly below b2_1(i.e. in the same column as b2_1) you have to decrease the column by 1 i.e.
b2_2 = Button(frame1, text='bbbbbbbbbbbbbbb')
b2_2.grid(row=frame1.grid_size()[1], column=frame1.grid_size()[0]-1)
grid and pack don't work together and must not be used in the same python file.
this is the command to put a button on the bottom right using the pack() method.
btn.pack(side=BOTTOM, anchor="e", padx=8, pady=8)
Position the element within the grid cell - the positions are the same as a compass: N, E, S, W, NE, NW, SE, and SW.
This example sticks it to the right bottom:
example_label.grid(column=0, row=0,sticky =SE)