I am currently trying to code a pdf merger in Python and I am using tkinter to code the GUI of my application. Please note that it is my first real project in Python.
I have coded a class for my main application and I created another class named browseButton to manipulate more easily a special type of button that inherits from tkinter.Button.
However, the buttons created through this class don't appear in the main application after using grid() to place them.
Here is my code
import tkinter as tk
from tkinter import filedialog
import openFile
class mainApplication(tk.Tk):
def __init__(self, parent=None):
tk.Tk.__init__(self, parent)
self.parent=parent
self.initialize()
def initialize(self):
self.grid()
self.browseLabel=tk.Label(self, text='Select the 2 PDF files to merge.', anchor='w', fg='black')
self.browseLabel.grid(row=1, columnspan=10, pady=20)
createQuitButton(self)
createMergeButton(self)
self.browseButton1=browseButton(self)
self.browseButton2=browseButton(self)
self.browseButton1.button.grid(row=5, column=2)
self.browseButton2.button.grid(row=5, column=9)
self.browseButton1.buttonLabel.grid(row=4, column=2)
self.browseButton2.buttonLabel.grid(row=4, column=9)
def merge(self):
pass
class browseButton(tk.Button):
def __init__(self, parent=None):
tk.Button.__init__(self, parent)
self.parent=parent
self.createButton()
def openFile(self):
openFile.askOpenFile()
self.changeButtonLabel()
def createButton(self):
browseButton.buttonLabel=tk.Label(self, text='Select file', anchor='w', fg='black')
browseButton.button=tk.Button(self, text='Browse', command=self.openFile)
def changeButtonLabel(self):
self.buttonLabel['text']='File Found!
Please can someone tell me why this is not working?
Related
To preface this, I have this working, I was just hoping somebody could explain why the code is behaving the way it is. I'm not understanding the grid system inside of classes very well apparently.
from tkinter import ttk
class App(tk.Tk):
def __init__(self):
super().__init__()
self.geometry('400x400')
self.rowconfigure(0, uniform=True, weight=1)
self.rowconfigure(1, uniform=True, weight=1)
self.rowconfigure(2, uniform=True, weight=1)
self.header_frame = HeaderFrame(self)
self.header_frame.grid(column=0,row=0)
self.login_frame = LoginFrame(self)
self.login_frame.grid(column=0,row=1)
self.button_frame = ButtonFrame(self)
self.button_frame.grid(column=0,row=2)
class HeaderFrame(ttk.Frame):
def __init__(self, parent):
super().__init__()
self.parent = parent
self.columnconfigure(0, uniform=True, weight=1)
self.canvas = tk.Canvas(self, bg='black')
self.canvas.grid(column=0,row=0)
class loginFrame(ttk.Frame):
def __init__(self, parent):
super().__init__()
self.parent=parent
self.columnconfigure(0, uniform=True, weight=1)
self.entryBox = ttk.Entry(self, width=33)
self.entryBox.grid(column0,row1)
class ButtonFrame(ttk.Frame):
def __init__(self, parent):
super().__init__()
self.parent=parent
self.columnconfigure(0, uniform=True, weight=1)
self.btn = ttk.Button(self, text='test')
self.btn.grid(column=0,row=2)
if __name__ == '__main__':
App().mainloop()
Now my question comes down to the grid placement. When I call each class to place the items, I specify different grid locations. header_frame.grid(column=0, row=0), login_frame(column=0,row=1), etc. However, the initial App class having a grid placement does not affect the layout of the gui whatsoever. I can place them all on row 0 and they will all still show up on separate rows until I change the grid placement inside of the individual class. I thought my grid inside of the class was being placed inside of cells of the App class, what am I missing here?
Thanks in advance for your help.
Consider this code:
class HeaderFrame(ttk.Frame):
def __init__(self, parent):
super().__init__()
super().__init__() is what actually creates the widget. You aren't passing parent to super().__init__()) so the widget will be created as a child of the root window. You need to pass parent like in the following example.
super().__init__(parent)
You need to make a similar change for all of your classes.
I'm trying to enter a file name into your graphics window and then read that file and display the file in a graphics window using a file menu. When I press new another window opens to ask for a name and displays it in the main window, but I can't get the new window to open and do the rest. I have the TopLevel to open a new window, but I get NameError: name 'TopLevel' is not defined and can't continue from there. What can I do to make it work?
from tkinter import Tk, Frame, Menu
from tkinter.ttk import *
from tkinter import filedialog as fd
class Example(Frame):
def __init__(self, parent):
Frame.__init__(self, parent)
self.parent = parent
self.initUI()
def initUI(self):
self.parent.title("Simple menu")
menubar = Menu(self.parent)
self.parent.config(menu=menubar)
fileMenu = Menu(menubar)
fileMenu.add_command(label="New", command=self.onNew)
menubar.add_cascade(label="File", menu=fileMenu)
def onNew(self):
print("do New")
top = TopLevel()
Label(self, text='Enter Your Name').grid(row=0)
e1 = Entry(self)
e1.grid(row=0, column=1)
def main():
root = Tk()
root.geometry("250x150+300+300")
app = Example(root)
root.mainloop()
if __name__ == '__main__':
main()
The problem is simply that you're not defining or importing anything named Toplevel. The way you're importing tkinter makes this an easy problem to have.
My recommendation is to remove these statements:
from tkinter import Tk, Frame, Menu
from tkinter.ttk import *
... and replace them with these:
import tkinter as tk
from tkinter import ttk
From then on, you have access to almost everything in the tkinter and ttk packages. You simply need to add a tk. or ttk. prefix to everything you use from those packages.
This keeps global namespace pollution at a minimum (ie: you only add two names to the global namespace), and makes your code more self-documenting.
class Example(tk.Frame):
def __init__(self, parent):
ttk.Frame.__init__(self, parent)
self.parent = parent
self.initUI()
def initUI(self):
self.parent.title("Simple menu")
menubar = tk.Menu(self.parent)
self.parent.config(menu=menubar)
fileMenu = tk.Menu(menubar)
fileMenu.add_command(label="New", command=self.onNew)
menubar.add_cascade(label="File", menu=fileMenu)
def onNew(self):
print("do New")
top = tk.TopLevel()
ttk.Label(self, text='Enter Your Name').grid(row=0)
e1 = ttk.Entry(self)
e1.grid(row=0, column=1)
def main():
root = tk.Tk()
root.geometry("250x150+300+300")
app = Example(root)
root.mainloop()
Hi I have a small GUI which has a button using which two functions must be executed in different processors. In reality these two functions are heavy calculations. I do not want to use multi threading. I want them to run on 2 different processors. When I try to execute the button, another instance of the GUI gets created and it says
File "C:\Python3.7\lib\multiprocessing\reduction.py", line 60, in dump.
ForkingPickler(file, protocol).dump(obj)
TypeError: can't pickle _tkinter.tkapp objects
My code is as follows.
from multiprocessing import Process
from tkinter import Button, Tk, Frame
class GUI(Frame):
def __init__(self):
super().__init__()
self.button = Button(self, text="Start", command=self.execute)
self.button.pack()
self.pack()
def F1(self):
print("Hello")
def F2(self):
print("World")
def execute(self):
self.P1 = Process(target = self.F1)
self.P2 = Process(target = self.F2)
self.P1.start()
self.P2.start()
self.P1.join()
self.P2.join()
Root = Tk()
Software = GUI()
Root.mainloop()
Please click here
The problem lies with pickling tkinter widgets. You simply cannot do it, as the Tcl interpreter does not understand the python pickle format.
Coming to your code, I tried the following and it prints as expected:
from multiprocessing import Process
from tkinter import Button, Tk, Frame
class GUI(Frame):
def __init__(self):
super().__init__()
self.button = Button(self, text="Start", command=self.execute)
self.button.pack()
self.pack()
#staticmethod
def F1():
print("Hello")
#staticmethod
def F2():
print("World")
def execute(self):
self.P1 = Process(target = GUI.F1())
self.P2 = Process(target = GUI.F2())
self.P1.start()
self.P2.start()
self.P1.join()
self.P2.join()
Root = Tk()
Software = GUI()
Root.mainloop()
When I load a dialog (QMainWindow) window from within my mainwindow (QMainWindow), it loads without layout, even though the setupUi() function is called.
The important pieces of code are here below, click here for pastebin link to full code
class Ui_Dialog(QMainWindow):
def __init__(self, parent=None):
super(Ui_Dialog, self).__init__(parent)
self.setupUi(self)
def setupUi(self, Dialog):
...
class MainWindow(QMainWindow):
def __init__(self):
super(MainWindow, self).__init__()
self.setupUi(self)
self.show()
....
def setupUi(self, Form):
...
self.auto_sap_btn = QPushButton(Form)
self.auto_sap_btn.setGeometry(QRect(0, 0, 61, 25))
self.auto_sap_btn.setObjectName('auto_sap_btn')
self.auto_sap_btn.clicked.connect(self.openDialog)
def openDialog(self):
self.window = Ui_Dialog(self)
self.window.setupUi(self.window)
self.window.move(600, 500)
self.window.show()
Right now my dialog looks like this:
Failed dialog layout
When I load the dialog on its own from its own script created by:
pyuic5 -x dialog.ui -o dialog.py
it looks like this:
Proper dialog layout
What am I missing?
When you create a design based on a Template in Qt Designer, then when you have to pass the appropriate widget, when you created Ui_Dialog you surely used Dialog with Buttons Right so in this case you should use a QDialog instead of QMainWindow:
class Ui_Dialog(QDialog): # change QMainWindow to QDialog
def __init__(self, parent=None):
super(Ui_Dialog, self).__init__(parent)
self.setupUi(self)
[...]
Another mistake is to use the setupUi() method a second time since this method is responsible for filling in the widget, by calling it 2 times you will be adding more widgets unnecessarily:
def openDialog(self):
self.window = Ui_Dialog(self)
self.window.move(600, 500)
self.window.show()
I have a simple example of of a dialog window that has the keyPressEvent method. However, no matter what is typed when the sub window has focus, the event is not triggered.
import sys
from PyQt4.QtCore import *
from PyQt4.QtGui import *
import PyQt4.Qt
class KpeWindow(QDialog):
def __init__(self, parent=None):
super().__init__(parent)
main = QVBoxLayout(self)
label = QLabel(self)
label.setText('Test the keyPressEvent')
self.adjustSize()
self.setLayout(main)
def keyPressEvent(self, event):
QMessageBox.warning(self, 'MDI', 'keyPressEvent')
super().keyPressEvent(event)
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
self.setWindowTitle('KeyPressEvent Test')
child = KpeWindow()
self.setCentralWidget(child)
if __name__ == '__main__':
app = QApplication(sys.argv)
mainWin = MainWindow()
mainWin.show()
sys.exit(app.exec_())
The following code works:
class KpeWindow(QWidget):
def __init__(self, parent=None):
QWidget.__init__(self,parent)
main = QVBoxLayout(self)
label = QLabel(self)
label.setText('Test the keyPressEvent')
main.addWidget(label)
self.adjustSize()
self.setLayout(main)
def keyPressEvent(self, event):
QMessageBox.warning(self, 'MDI', 'keyPressEvent')
self.parent().keyPressEvent(event)
class MainWindow(QMainWindow):
def __init__(self):
QMainWindow.__init__(self)
self.setWindowTitle('KeyPressEvent Test')
main = QVBoxLayout(self)
child = KpeWindow(self)
child.setFocusPolicy(Qt.StrongFocus)
self.setFocusProxy(child)
main.addWidget(child)
child.setFocus(True)
self.adjustSize()
self.setLayout(main)
I am not sure which of my changes work, I suspect setFocusProxy. In general I would recommend using QWidget as the child, and putting things into layouts even when there are no siblings.
The keyPressEvent is sensitive to the focus policy. In your example, the event is going to the QMainWindow (if you move the keyPressEvent to there, it does receive key events).
Is there any reason to have a dialog within a window? If you launch the dialog in the usual way, using child.show(), child.exec_() instead of setCentralWidget, it shows in a separate window and captures the key event.