I am trying to create a UI using Tkinter that uses LabelFrames and I want to lay them out in a grid. However, they are only appearing when I use the .pack method. I'm not sure if this is because they are a container more than a widget but if someone could help me out that would be great.
from tkinter import ttk
from tkinter import *
from tkinter.ttk import *
class MainWindow(tkinter.Tk):
def __init__(self, parent):
tkinter.Tk.__init__(self, parent)
self.parent = parent
self.initialize()
def initialize(self):
self.geometry("788x594")
self.resizable(False, False)
self.title("Testing UI")
Btn = Button(self, text = "Test")
Btn.grid(column = 0, row = 1)
testFrame = LabelFrame(self, text = "Test")
testFrame.grid(column = 0, row = 2, sticky="EW")
if __name__ == "__main__":
app = MainWindow(None)
app.mainloop()
And this is the output I get
Output
The frame is appearing. Because there's nothing in the frame, and because you haven't set a size for the frame, it is only 1 pixel wide and 1 pixel tall.
I'm trying to learn the classes in Python.
I wrote a little code with tkinter
from tkinter import *
class window():
def __init__(self, size, title, ):
self.size = size
self.title = title
window = Tk()
window.geometry(self.size)
window.title(self.title)
print('a window has been created')
window.mainloop()
def button(self, window, text, x, y):
self.window = window
self.text = text
self.x = x
self.y = y
button = Button(window, text=text).place(x=str(x), y=str(y))
but I get the error message:
self.tk = master.tk
AttributeError: 'window' object has no attribute 'tk'
you have to say from which file is this function or object is from
window = tkinter.Tk()
I can't see where you write self.tk or where you called the class. Probably, you need to add self. when you declare window.
Like this:
from tkinter import *
class window:
def __init__(self, size, title, ):
self.size = size
self.title = title
self.window = Tk()
self.window.geometry(self.size)
self.window.title(self.title)
print('a window has been created')
self.window.mainloop()
def button(self, window, text, x, y):
self.window = window
self.text = text
self.x = x
self.y = y
button = Button(window, text=text).place(x=str(x), y=str(y))
if __name__ == "__main__":
worker = window("500x500", "nice title") # <-- You need to call the class.
(If you need to declare a button, you can add this line on __init__ function above self.window.mainloop(): self.button(self.window, "Title", 10, 20))
So I want to just change the background of my frames so I can layout them properly and I can't seem to change styles for my frames.
style.configure('TFrame', background='red')
style.configure('Blue.TFrame', background='blue')
main_window = MainWindow(root, style='Blue.TFrame')
The above code resuslts in a red background, while I need it to change to blue, and if I don't change TFrame background, there is just no backgound color at all.
My MainWindow class does inherit from ttk.Frame, so I don't know if that is what's causing it...
Minimal Reproducible Example:
import tkinter as tk
from tkinter import ttk
class MainWindow(ttk.Frame):
def __init__(self, parent, *args, **kwargs):
super().__init__(parent)
self.parent = parent
# Search_company shortcut
self.search_comp = ttk.Entry(self)
self.search_comp.grid(row=0, column=0)
def main():
root = tk.Tk()
root.state('zoomed')
# Configuring styles
style = ttk.Style()
style.configure('TFrame', background='red')
style.configure('Blue.TFrame', background='blue')
main_window = MainWindow(root, style='Blue.TFrame')
main_window.grid(row=0, column=1, sticky='nsew')
root.mainloop()
if __name__ == "__main__":
main()
You aren’t passing the style option to the superclass. It needs to be this:
super().__init__(parent, *args, **kwargs)
I'm trying to display a simple Notebook widget with two tabs. Here's the code of what I tried without all the unnecessary code in between:
import tkinter as tk
from tkinter import ttk
color_bg = "gray20"
font = "Lucida Sans Typewriter"
DEFAULT_FONT_SIZE = 16
class Tab(tk.Frame):
def __init__(self, parent):
tk.Frame.__init__(self, parent)
self.parent = parent
self.pack()
self.textfield = tk.Text(
self.parent,
font = (font, DEFAULT_FONT_SIZE),
background = color_bg,
bd = 10,
relief = tk.FLAT)
self.textfield.pack(fill = tk.BOTH, expand = True)
class TabDisplay(tk.Frame):
def __init__(self, parent):
tk.Frame.__init__(self, parent)
self.parent = parent
self.pack(fill = tk.BOTH, expand = True)
self.tabholder = ttk.Notebook(parent)
self.tabholder.pack(fill = tk.BOTH, expand = True, side = tk.TOP)
self.viewtab = Tab(self.tabholder)
self.edittab = Tab(self.tabholder)
self.tabholder.add(self.viewtab, text = "View")
self.tabholder.add(self.edittab, text = "Edit")
class MainApplication(tk.Frame):
def __init__(self, parent, *args, **kwargs):
tk.Frame.__init__(self, parent, *args, **kwargs)
self.parent = parent
# Set size and position
self.x_pos = 0
self.y_pos = 0
self.width = self.parent.winfo_screenwidth()
self.height = self.parent.winfo_screenheight()
self.parent.geometry(f"{self.width}x{self.height}+{self.x_pos}+{self.y_pos}")
self.parent.resizable = True
# Add Widgets
self.tabdisplay = TabDisplay(self)
if __name__ == "__main__":
root = tk.Tk()
MainApplication(root).pack(fill = tk.BOTH, expand = True)
root.mainloop()
When I run this, it does not display an actual notebook. It just displays the two tabs below eachother. However, when I replace Tab(self.tabholder) with tk.Frame(self.tabholder) it functions perfectly (apart from not using the contents of the Tab() class).
Why does it not display correctly with my Tab() class? I have never had issues with classes that inherit from tk.Frame until I started using ttk.Notebook(). Is it an issue with ttk?
EDIT: I have since found out that the actual culprit is the Text widget within the Tab() class. Now my question is why does adding a widget break the notebook?
Think of a frame like a box. You create a box, and then you put widgets inside the box. Only, in your case you're creating widgets in the box but placing them outside the box when you add them to self.parent rather than self.
When you create a class that inherits from tk.Frame, every widget inside that class should be a direct child or descendant of self. In doing so, an instance of the class becomes a self-contained object that can be the child of any other widget.
For example:
class Tab(tk.Frame):
def __init__(self, parent):
...
self.textfield = tk.Text(
self,
...
... and:
class TabDisplay(tk.Frame):
def __init__(self, parent):
...
self.tabholder = ttk.Notebook(self)
...
I wrote a Tkinter app, and I wanted to add screen snipping, so I found a separate program from GitHub (screen-snip) written in PyQt, which I was importing and using in my Tkinter app. Then I decided to combine the programs in order to ask an SO question about why they aren't totally working together. I've learned not to combine Tk and Qt.
So now my question is, should I rewrite my program in Qt, or Tk?
Which is better for this situation?
My currently mixed-Tk/Qt program works when you select an image file, but now the screen-snip portion with Qt class MyWidget(QtWidgets.QWidget): causes it to freeze and then crash.
I think the problem might be a result of mixing Qt with Tk, but I'm not sure.
I originally had two instances of tkinter running, which allowed me to get the screen ship with a new window, but caused trouble with the button window, so I replaced this by trying to use tk.Toplevel
class MyWidget(QtWidgets.QWidget):
def __init__(self, master):
super().__init__()
self.master = master
self.window = tk.Toplevel(self.master)
and that's when I ran into trouble. The widget no longer works at all, and the program crashes without any clues or errors. Any idea why?
Simplified Code:
import tkinter as tk
from tkinter import filedialog
from PIL import ImageTk, Image, ImageGrab
import sys
from PyQt5 import QtWidgets, QtCore, QtGui
import numpy as np
import cv2
class ButtonImg:
def __init__(self, master):
self.newWindow = None
self.master = master
self.fontA = ("arial", 20, "bold")
self.canvas = tk.Canvas(height = 5)
self.canvas.pack()
self.button = tk.Button(bg="#61B5DA", height = 5, text = "Select Image",
font = self.fontA, command = self.changeImage)
self.button.pack(fill="both")
def changeImage(self):
print('open second window')
self.newWindow = tk.Toplevel(self.master)
img = AcquireImage(self.newWindow)
self.master.wait_window(self.newWindow)
print('close second window')
if img.image_selected: # check if image was selected
self.image = img.image_selected
self.button.configure(image=self.image, height=self.image.height())
class AcquireImage:
def __init__(self, master):
self.master = master
self.fontA = ("arial", 20, "bold")
self.frame = tk.Frame(master, bg="#96beed")
self.frame.pack(fill="both", expand=True)
self.button1 = tk.Button(self.frame, text="Select Image File", padx=5, pady=5, bg="#6179DA",
font = self.fontA, command =lambda: self.show_dialogs(1))
self.button1.grid(row=0, column=0, sticky="nsew")
self.button2 = tk.Button(self.frame, text="Get Screen Snip", padx=5, pady=5, bg="#6179DA",
font = self.fontA, command=lambda: self.show_dialogs(2))
self.button2.grid(row=0, column=1, sticky="nsew")
self.image_selected = None
def show_dialogs(self, method):
if method == 1:
ret = filedialog.askopenfilename() #filedialog.askopenfilename(initialdir='/home/user/images/')
if ret:
self.image_selected = ImageTk.PhotoImage(file = ret)
self.master.destroy()
elif method == 2:
newWin = MyWidget(self.master)
newWin.show()
ret = newWin.img
if ret:
self.image_selected = ImageTk.PhotoImage(file = ret)
class MyWidget(QtWidgets.QWidget):
def __init__(self, master):
super().__init__()
self.master = master
self.window = tk.Toplevel(self.master)
screen_width = self.thirdWin.winfo_screenwidth()
screen_height = self.thirdWin.winfo_screenheight()
self.setGeometry(0, 0, screen_width, screen_height)
self.setWindowTitle(' ')
self.begin = QtCore.QPoint()
self.end = QtCore.QPoint()
self.img = None
self.setWindowOpacity(0.3)
QtWidgets.QApplication.setOverrideCursor(
QtGui.QCursor(QtCore.Qt.CrossCursor)
)
self.setWindowFlags(QtCore.Qt.FramelessWindowHint)
print('Capture the screen...')
self.show()
def getRect(self):
# a commodity function that always return a correctly sized
# rectangle, with normalized coordinates
width = self.end.x() - self.begin.x()
height = abs(width * 2 / 3)
if self.end.y() < self.begin.y():
height *= -1
return QtCore.QRect(self.begin.x(), self.begin.y(),
width, height).normalized()
def paintEvent(self, event):
qp = QtGui.QPainter(self)
qp.setPen(QtGui.QPen(QtGui.QColor('black'), 3))
qp.setBrush(QtGui.QColor(128, 128, 255, 128))
qp.drawRect(self.getRect())
def mousePressEvent(self, event):
self.begin = event.pos()
self.end = self.begin
self.update()
def mouseMoveEvent(self, event):
self.end = event.pos()
self.update()
def mouseReleaseEvent(self, event):
self.close()
rect = self.getRect()
self.img = ImageGrab.grab(bbox=(
rect.topLeft().x(),
rect.topLeft().y(),
rect.bottomRight().x(),
rect.bottomRight().y()
))
#self.img.save('capture.png')
self.img = cv2.cvtColor(np.array(self.img), cv2.COLOR_BGR2RGB)
cv2.imshow('Captured Image', self.img)
cv2.waitKey(0)
#cv2.destroyAllWindows()
if __name__ == '__main__':
root = tk.Tk()
app = ButtonImg(root)
root.mainloop()
As said in the comments, the best is to use a single GUI toolkit so you need either to rewrite your code for Qt or rewrite the snipping code using tkinter. I don't know Qt much so I cannot help you with the first option. However, the screenshot is actually taken using PIL, not some Qt specific method, so the snipping code can be rewritten in tkinter.
All you need is a fullscreen toplevel containing a canvas with a draggable rectangle, like in Drawing rectangle using mouse events in Tkinter. To make the toplevel fullscreen: toplevel.attributes('-fullscreen', True)
The toplevel needs to be partially transparent, which can be achieved with toplevel.attributes('-alpha', <value>). I am using Linux (with XFCE desktop environment) and I need to add toplevel.attributes('-type', 'dock') to make the transparency work.
All put together in a class, this give:
import sys
import tkinter as tk
from PIL import ImageGrab
import cv2
import numpy as np
class MyWidget(tk.Toplevel):
def __init__(self, master):
super().__init__(master)
self.configure(cursor='cross')
if sys.platform == 'linux':
self.attributes('-type', 'dock') # to make transparency work in Linux
self.attributes('-fullscreen', True)
self.attributes('-alpha', 0.3)
self.canvas = tk.Canvas(self, bg='white')
self.canvas.pack(fill='both', expand=True)
self.begin_x = 0
self.begin_y = 0
self.end_x = 0
self.end_y = 0
self.canvas.create_rectangle(0, 0, 0, 0, outline='gray', width=3, fill='blue', tags='snip_rect')
self.canvas.bind('<ButtonPress-1>', self.mousePressEvent)
self.canvas.bind('<B1-Motion>', self.mouseMoveEvent)
self.canvas.bind('<ButtonRelease-1>', self.mouseReleaseEvent)
print('Capture the screen...')
def mousePressEvent(self, event):
self.begin_x = event.x
self.begin_y = event.y
self.end_x = self.begin_x
self.end_y = self.begin_y
self.canvas.coords('snip_rect', self.begin_x, self.begin_y, self.end_x, self.end_y)
def mouseMoveEvent(self, event):
self.end_x = event.x
self.end_y = event.y
self.canvas.coords('snip_rect', self.begin_x, self.begin_y, self.end_x, self.end_y)
def mouseReleaseEvent(self, event):
self.destroy()
self.master.update_idletasks()
self.master.after(100) # give time for screen to be refreshed so as not to see the blue box on the screenshot
x1 = min(self.begin_x, self.end_x)
y1 = min(self.begin_y, self.end_y)
x2 = max(self.begin_x, self.end_x)
y2 = max(self.begin_y, self.end_y)
img = ImageGrab.grab(bbox=(x1, y1, x2, y2))
self.img = cv2.cvtColor(np.array(img), cv2.COLOR_BGR2RGB)
cv2.imshow('Captured Image', self.img)
cv2.waitKey(0)
if __name__ == '__main__':
root = tk.Tk()
tk.Button(root, text='Snip', command=lambda: MyWidget(root)).pack()
root.mainloop()