I´m using this very known script from Mr. Bryan Oakley. My question is: How make class PageOne child of ScrolledWindow from Mr. Mikhail (https://stackoverflow.com/users/6470235/mikhail-tin). His script is in the question: Tkinter scrollbar for frame. But I wrote both bellow.
When I try
PageOne = ScrolledWindow(parent=SampleApp) python asks for controller, if I use container, it doesnot recognize it.
If I use PageOne = ScrolledWindow(parent=SampleApp, controller = SampleApp.container)
it says:
SampleApp has no attribute 'container'
I´m very curious about the answer of this puzzle.
Thanks in advance.
import tkinter as tk # python 3
from tkinter import font as tkfont # python 3
from tkinter import ttk
class SampleApp(tk.Tk):
def __init__(self, *args, **kwargs):
tk.Tk.__init__(self, *args, **kwargs)
self.title_font = tkfont.Font(family='Helvetica', size=18, weight="bold", slant="italic")
# the container is where we'll stack a bunch of frames
# on top of each other, then the one we want visible
# will be raised above the others
container = tk.Frame(self)
container.pack(side="top", fill="both", expand=True)
container.grid_rowconfigure(0, weight=1)
container.grid_columnconfigure(0, weight=1)
self.frames = {}
for F in (StartPage, PageOne, ScrolledWindow):
page_name = F.__name__
frame = F(parent=container, controller=self)
self.frames[page_name] = frame
# put all of the pages in the same location;
# the one on the top of the stacking order
# will be the one that is visible.
frame.grid(row=0, column=0, sticky="nsew")
self.show_frame("StartPage")
def show_frame(self, page_name):
'''Show a frame for the given page name'''
frame = self.frames[page_name]
frame.tkraise()
class StartPage(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
self.controller = controller
label = tk.Label(self, text="This is the start page", font=controller.title_font)
label.pack(side="top", fill="x", pady=10)
button1 = tk.Button(self, text="Go to Page One",
command=lambda: controller.show_frame("PageOne"))
button2 = tk.Button(self, text="ScrolledWindow",
command=lambda: controller.show_frame("ScrolledWindow"))
button1.pack()
button2.pack()
class PageOne(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
self.controller = controller
label = tk.Label(self, text="This is page 1", font=controller.title_font)
label.pack(side="top", fill="x", pady=10)
button = tk.Button(self, text="Go to the start page",
command=lambda: controller.show_frame("StartPage"))
button.pack()
class ScrolledWindow(tk.Frame):
"""
1. Master widget gets scrollbars and a canvas. Scrollbars are connected
to canvas scrollregion.
2. self.scrollwindow is created and inserted into canvas
Usage Guideline:
Assign any widgets as children of <ScrolledWindow instance>.scrollwindow
to get them inserted into canvas
__init__(self, parent, canv_w = 400, canv_h = 400, *args, **kwargs)
docstring:
Parent = master of scrolled window
canv_w - width of canvas
canv_h - height of canvas
"""
def __init__(self, parent, controller, canv_w = 400, canv_h = 400, *args, **kwargs):
"""Parent = master of scrolled window
canv_w - width of canvas
canv_h - height of canvas
"""
super().__init__(parent, *args, **kwargs)
self.parent = parent
button = tk.Button(self, text="Go to the start page",
command=lambda: controller.show_frame("StartPage"))
button.pack()
# creating a scrollbars
self.xscrlbr = ttk.Scrollbar(self.parent, orient = 'horizontal')
self.xscrlbr.grid(column = 0, row = 1, sticky = 'ew', columnspan = 2)
self.yscrlbr = ttk.Scrollbar(self.parent)
self.yscrlbr.grid(column = 1, row = 0, sticky = 'ns')
# creating a canvas
self.canv = tk.Canvas(self.parent)
self.canv.config(relief = 'flat',
width = 10,
heigh = 10, bd = 2)
# placing a canvas into frame
self.canv.grid(column = 0, row = 0, sticky = 'nsew')
# accociating scrollbar comands to canvas scroling
self.xscrlbr.config(command = self.canv.xview)
self.yscrlbr.config(command = self.canv.yview)
# creating a frame to inserto to canvas
self.scrollwindow = ttk.Frame(self.parent)
self.canv.create_window(0, 0, window = self.scrollwindow, anchor = 'nw')
self.canv.config(xscrollcommand = self.xscrlbr.set,
yscrollcommand = self.yscrlbr.set,
scrollregion = (0, 0, 100, 100))
self.yscrlbr.lift(self.scrollwindow)
self.xscrlbr.lift(self.scrollwindow)
self.scrollwindow.bind('<Configure>', self._configure_window)
self.scrollwindow.bind('<Enter>', self._bound_to_mousewheel)
self.scrollwindow.bind('<Leave>', self._unbound_to_mousewheel)
return
def _bound_to_mousewheel(self, event):
self.canv.bind_all("<MouseWheel>", self._on_mousewheel)
def _unbound_to_mousewheel(self, event):
self.canv.unbind_all("<MouseWheel>")
def _on_mousewheel(self, event):
self.canv.yview_scroll(int(-1*(event.delta/120)), "units")
def _configure_window(self, event):
# update the scrollbars to match the size of the inner frame
size = (self.scrollwindow.winfo_reqwidth(), self.scrollwindow.winfo_reqheight())
self.canv.config(scrollregion='0 0 %s %s' % size)
if self.scrollwindow.winfo_reqwidth() != self.canv.winfo_width():
# update the canvas's width to fit the inner frame
self.canv.config(width = self.scrollwindow.winfo_reqwidth())
if self.scrollwindow.winfo_reqheight() != self.canv.winfo_height():
# update the canvas's width to fit the inner frame
self.canv.config(height= self.scrollwindow.winfo_reqheight())
if __name__ == "__main__":
app = SampleApp()
app.mainloop()
After a lot of try and error I found a solution:
The Idea is to do a complete class with frame --> canvas --> desired frame outside the SampleApp and send it to the constructor loop:
This is the code to build a scrolled frame window inside the main class in a loop (after that you can change "Page_with_Scrooll_bar" to "PageOne" as I've asked in question):
import tkinter as tk # python 3
from tkinter import font as tkfont # python 3
from tkinter import ttk
class SampleApp(tk.Tk):
def __init__(self, *args, **kwargs):
tk.Tk.__init__(self, *args, **kwargs)
self.title_font = tkfont.Font(family='Helvetica', size=18, weight="bold", slant="italic")
container = tk.Frame(self)
container.pack(side="top", fill="both", expand=True)
container.grid_rowconfigure(0, weight=1)
container.grid_columnconfigure(0, weight=1)
self.frames = {}
for F in (StartPage, PageOne, PageTwo, Page_with_Scroll_bar):
page_name = F.__name__
frame = F(parent=container, controller=self)
self.frames[page_name] = frame
frame.grid(row=0, column=0, sticky="nsew")
self.show_frame("StartPage")
def show_frame(self, page_name):
'''Show a frame for the given page name'''
frame = self.frames[page_name]
frame.tkraise()
class StartPage(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
self.controller = controller
label = tk.Label(self, text="This is the start page", font=controller.title_font)
label.pack(side="top", fill="x", pady=10)
button1 = tk.Button(self, text="Go to Page One",
command=lambda: controller.show_frame("PageOne"))
button2 = tk.Button(self, text="Go to Page two",
command=lambda: controller.show_frame("PageTwo"))
button3 = tk.Button(self, text="Example with Scrollbar",
command=lambda: controller.show_frame("Page_with_Scroll_bar"))
button1.pack()
button2.pack()
button3.pack()
class PageOne(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
self.controller = controller
label = tk.Label(self, text="This is page 1", font=controller.title_font)
label.pack(side="top", fill="x", pady=10)
button = tk.Button(self, text="Go to the start page",
command=lambda: controller.show_frame("StartPage"))
button.pack()
class PageTwo(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
self.controller = controller
label = tk.Label(self, text="This is page 2", font=controller.title_font)
label.pack(side="top", fill="x", pady=10)
button = tk.Button(self, text="Go to the start page",
command=lambda: controller.show_frame("StartPage"))
button.pack()
class Page_with_Scroll_bar(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
self.canvas = tk.Canvas(self, borderwidth=0, background="#ffffff")
self.frame = tk.Frame(self.canvas, background="#ffffff")
self.vsb = tk.Scrollbar(self, orient="vertical", command=self.canvas.yview)
self.canvas.configure(yscrollcommand=self.vsb.set)
self.vsb.pack(side="right", fill="y")
self.canvas.pack(side="left", fill="both", expand=True)
self.canvas.create_window((4,4), window=self.frame, anchor="nw",
tags="self.frame")
self.frame.bind("<Configure>", self.onFrameConfigure)
self.populate()
self.button = tk.Button(self.frame, text="Go to the start page",
command=lambda: controller.show_frame("StartPage"))
self.button.grid(row=102, column=1)
def populate(self):
'''Put in some fake data'''
for row in range(100):
tk.Label(self.frame, text="%s" % row, width=3, borderwidth="1",
relief="solid").grid(row=row, column=0)
t="this is the second column for row %s" %row
tk.Label(self.frame, text=t).grid(row=row, column=1)
def onFrameConfigure(self, event):
'''Reset the scroll region to encompass the inner frame'''
self.canvas.configure(scrollregion=self.canvas.bbox("all"))
if __name__ == "__main__":
app = SampleApp()
app.mainloop()
Related
I need to move a QGraphicsPixmapItem through a circle that it is at the top left corner of the image. That is, when I grab with the mouse the circle, I need the top left corner of the image to follow the circle. I subclassed a QGraphicsEllipseItem and reimplemented the itemChange method but when I set the position of the image to that value, the image is not being positioned correctly. What should I modify in my code?
import sys
from PyQt5.QtWidgets import QMainWindow, QApplication, QGraphicsView
from PyQt5 import QtGui, QtWidgets
class MainWindow(QMainWindow):
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent)
self.scene = Scene()
self.view = QGraphicsView(self)
self.setGeometry(10, 30, 850, 600)
self.view.setGeometry(20, 22, 800, 550)
self.view.setScene(self.scene)
class Scene(QtWidgets.QGraphicsScene):
def __init__(self, parent=None):
super(Scene, self).__init__(parent)
# other stuff here
self.set_image()
def set_image(self):
image = Image()
self.addItem(image)
image.set_pixmap()
class Image(QtWidgets.QGraphicsPixmapItem):
def __init__(self, parent=None):
super(Image, self).__init__(parent)
self.setFlag(QtWidgets.QGraphicsItem.ItemIsMovable, True)
def set_pixmap(self):
pixmap = QtGui.QPixmap("image.jpg")
self.setPixmap(pixmap)
self.pixmap_controller = PixmapController(self)
self.pixmap_controller.set_pixmap_controller()
self.pixmap_controller.setPos(self.boundingRect().topLeft())
self.pixmap_controller.setFlag(QtWidgets.QGraphicsItem.ItemSendsScenePositionChanges, True)
def change_image_position(self, position):
self.setPos(position)
class PixmapController(QtWidgets.QGraphicsEllipseItem):
def __init__(self, pixmap):
super(PixmapController, self).__init__(parent=pixmap)
self.pixmap = pixmap
self.setFlag(QtWidgets.QGraphicsItem.ItemIsMovable, True)
color = QtGui.QColor(0, 0, 0)
brush = QtGui.QBrush(color)
self.setBrush(brush)
def set_pixmap_controller(self):
self.setRect(-5, -5, 10, 10)
def itemChange(self, change, value):
if change == QtWidgets.QGraphicsItem.ItemPositionChange:
self.pixmap.change_image_position(value)
return super(PixmapController, self).itemChange(change, value)
if __name__ == "__main__":
app = QApplication(sys.argv)
window = MainWindow()
window.show()
sys.exit(app.exec_())
When a graphics item has a parent, its coordinate system is based on that parent, not on the scene.
The problem is that when you try to move the PixmapController, the movement is in parent coordinates (the pixmap item). When you check for the ItemPositionChange you are you're changing the parent position but the item position is changed anyway, based on the parent coordinate system.
While you could just return an empty QPoint (which will not change the item position), this wouldn't be a good choice: as soon as you release the mouse and start to move it again, the pixmap will reset its position.
The solution is not to set the movable item flag, but filter for mouse movements, compute a delta based on the click starting position, and use that delta to move the parent item based on its current position.
class PixmapController(QtWidgets.QGraphicsEllipseItem):
def __init__(self, pixmap):
super(PixmapController, self).__init__(parent=pixmap)
self.pixmap = pixmap
# the item should *NOT* move
# self.setFlag(QtWidgets.QGraphicsItem.ItemIsMovable, True)
color = QtGui.QColor(0, 0, 0)
brush = QtGui.QBrush(color)
self.setBrush(brush)
def set_pixmap_controller(self):
self.setRect(-5, -5, 10, 10)
def mousePressEvent(self, event):
if event.button() == QtCore.Qt.LeftButton:
self.startPos = event.pos()
def mouseMoveEvent(self, event):
if event.buttons() == QtCore.Qt.LeftButton:
delta = event.pos() - self.startPos
self.parentItem().setPos(self.parentItem().pos() + delta)
If you want to use your change_image_position function, you need to change those functions accordingly; the code below does the same thing as the last line in the example above:
class Image(QtWidgets.QGraphicsPixmapItem):
# ...
def change_image_position(self, delta):
self.setPos(self.pos() + delta)
class PixmapController(QtWidgets.QGraphicsEllipseItem):
# ...
def mouseMoveEvent(self, event):
if event.buttons() == QtCore.Qt.LeftButton:
delta = event.pos() - self.startPos
self.pixmap.change_image_position(delta)
Tip: do not add a child widget to a QMainWindow like that, as it will not resize correctly when the window is resized. Use self.setCentralWidget(self.view) instead; if you want to add margins, use a container QWidget, set that widget as the central widget, add a simple QHBoxLayout (or QVBoxLayout), add the view to that layout and then set the margins with layout.setContentsMargins(left, top, right, bottom)
I want to pass the value stored in self.pin, which we get from the user through entry() in StartPage to the __init__ method of Options class but only when I click the submit button. Also this must be noted that there are multiple pages and I have used tkraise() to raise one page over the other. Explanation will be appreciated as I'm new to tkinter and oop. My whole code is given below:
import tkinter as tk
from tkinter import font as tkfont
from tkinter import messagebox
class SampleApp(tk.Tk):
def __init__(self, *args, **kwargs):
tk.Tk.__init__(self, *args, **kwargs)
self.title_font = tkfont.Font(family='Courier New', size=18, weight="bold")
container = tk.Frame(self)
container.pack(side="top", fill="both", expand=True)
container.grid_rowconfigure(0, weight=1)
container.grid_columnconfigure(0, weight=1)
self.frames = {}
for F in (StartPage, PageOne, PageTwo, PageThree, PageFour, Options):
page_name = F.__name__
frame = F(parent=container, controller=self)
self.frames[page_name] = frame
frame.grid(row=0, column=0, sticky="nsew")
self.show_frame("StartPage")
def show_frame(self, page_name):
frame = self.frames[page_name]
frame.tkraise()
class StartPage(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
self.controller = controller
label = tk.Label(self, text="Enter 4 digit pin", font=controller.title_font, fg="red")
label.pack(side="top", fill="x", pady=10)
self.pin = tk.Entry(self, bd = 4, relief="groove", show="*", font=20, justify='center')
# self.pin="786125"
self.pin.pack()
submit = tk.Button(self, text="Submit", width=12,font=tkfont.Font(size=12),command=lambda: [controller.show_frame("Options")], cursor="hand2")
submit.pack()
class Options(StartPage):
def __init__(self, parent, controller):
# StartPage.__init__(self, parent, controller)
# print(self.pin.get())
tk.Frame.__init__(self, parent)
self.controller = controller
f1=tk.Frame(self)
f1.pack()
f2=tk.Frame(self)
f2.pack(side="bottom")
button1 = tk.Button(f1, text="Balance Inquiry",relief="flat", padx=30, justify='left', cursor="hand2", fg="red", font=tkfont.Font(size=10),
command=lambda: controller.show_frame("PageOne"))
button1.pack(side="left")
button2 = tk.Button(f1, text="Deposit",relief="flat", padx=50, justify='right', cursor="hand2", fg="red", font=tkfont.Font(size=10),
command=lambda: controller.show_frame("PageTwo"))
button2.pack(side="left")
button3 = tk.Button(f2, text="Withdraw",relief="flat", padx=50, justify='left', cursor="hand2", fg="red", font=tkfont.Font(size=10),
command=lambda: controller.show_frame("PageThree"))
button3.pack(side="left")
button4 = tk.Button(f2, text="Pin Change",relief="flat", padx=50, justify='right', cursor="hand2", fg="red", font=tkfont.Font(size=10),
command=lambda: controller.show_frame("PageFour"))
button4.pack(side="left")
Personally I would keep the value stored in the controller and then access it from the other pages. Here is a modified version of your code that can access the stored pin.
This is done by adding a command to the buttons lambda and saving the entry value to the controller.
import tkinter as tk
from tkinter import font as tkfont
from tkinter import messagebox
class SampleApp(tk.Tk):
def __init__(self, *args, **kwargs):
tk.Tk.__init__(self, *args, **kwargs)
self.pin_stored = ""
self.title_font = tkfont.Font(family='Courier New', size=18, weight="bold")
container = tk.Frame(self)
container.pack(side="top", fill="both", expand=True)
container.grid_rowconfigure(0, weight=1)
container.grid_columnconfigure(0, weight=1)
self.frames = {}
for F in (StartPage, Options):
page_name = F.__name__
frame = F(parent=container, controller=self)
self.frames[page_name] = frame
frame.grid(row=0, column=0, sticky="nsew")
self.show_frame("StartPage")
def show_frame(self, page_name):
frame = self.frames[page_name]
frame.tkraise()
class StartPage(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
self.controller = controller
label = tk.Label(self, text="Enter 4 digit pin", font=controller.title_font, fg="red")
label.pack(side="top", fill="x", pady=10)
self.pin = tk.Entry(self, bd = 4, relief="groove", show="*", font=20, justify='center')
self.pin.pack()
submit = tk.Button(self, text="Submit", width=12,font=tkfont.Font(size=12), command=lambda: [controller.show_frame("Options"), self.store_pin()], cursor="hand2")
submit.pack()
def store_pin(self):
print("test")
self.controller.pin_stored = self.pin.get()
class Options(StartPage):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
self.controller = controller
f1=tk.Frame(self)
f1.pack()
f2=tk.Frame(self)
f2.pack(side="bottom")
button1 = tk.Button(f1, text="Balance Inquiry",relief="flat", padx=30, justify='left', cursor="hand2", fg="red", font=tkfont.Font(size=10),
command=lambda: controller.show_frame("PageOne"))
button1.pack(side="left")
button2 = tk.Button(f1, text="Deposit",relief="flat", padx=50, justify='right', cursor="hand2", fg="red", font=tkfont.Font(size=10),
command=lambda: controller.show_frame("PageTwo"))
button2.pack(side="left")
button3 = tk.Button(f2, text="Withdraw",relief="flat", padx=50, justify='left', cursor="hand2", fg="red", font=tkfont.Font(size=10),
command=lambda: controller.show_frame("PageThree"))
button3.pack(side="left")
button4 = tk.Button(f2, text="Pin Change",relief="flat", padx=50, justify='right', cursor="hand2", fg="red", font=tkfont.Font(size=10),
command=lambda: controller.show_frame("PageFour"))
button4.pack(side="left")
tk.Button(self, text="print stored pin", command=self.print_pin).pack()
def print_pin(self):
print(self.controller.pin_stored)
if __name__ == "__main__":
SampleApp().mainloop()
I'm having an issues and I would kindly ask you to help me as I am not able to find a solution.
Basically I am trying to inherit the DataFrame loaded into a variable in one Class (Window1) and I'm trying to use the same DataFrame in another Class (Window1). Please find below my current simplified code (Thank you):
import tkinter as tk
from PIL import ImageTk, Image
from tkinter import filedialog, messagebox
import time, os, random, string
from datetime import datetime
from time import gmtime, strftime
import pandas as pd
class Page(tk.Tk):
def __init__(self, *args, **kwargs):
tk.Tk.__init__(self, *args, **kwargs)
container=tk.Frame(self)
container.grid()
container.grid_rowconfigure(0, weight=1)
container.grid_columnconfigure(0, weight=1)
self.frames={}
for F in (PageOne, PageTwo, PageThree):
frame=F(container, self)
self.frames[F]=frame
frame.grid(row=0, column=0, sticky="nsew")
self.show_frame(PageOne)
def show_frame(self, cont):
frame=self.frames[cont]
frame.tkraise()
class PageOne(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
start_btn = tk.Button(self, text = "Start >>>", command=lambda:controller.show_frame(PageTwo), width = 10, activebackground = "#ffffff", relief="flat").grid()
pass
class PageTwo(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
self.browse_btn = tk.Button(self, text=" Browse ", command=self.select_file)
self.browse_btn.grid(row=4, column=0, padx=290, pady=10, columnspan=3, sticky="w")
self.browse_entry = tk.Entry(self, text="", width=30)
self.browse_entry.grid(row=4, column=0, columnspan=3, padx=100, pady=10, sticky="w")
self.continue_btn = tk.Button(self, text="Continue >>", borderwidth=2, width=10, bg="#00c441", fg="#ffffff", command=lambda:[self.print_df(), controller.show_frame(PageThree)])
self.continue_btn.grid(row=19, column=0, columnspan=3, padx=312, pady=5, sticky="w")
self.continue_btn.config(state=tk.NORMAL)
def select_file(self):
self.path = filedialog.askopenfilename(defaultextension="*.csv", filetypes = (("csv files","*.csv"),("All Files", "*.*")))
self.browse_entry.delete(0, tk.END)
self.browse_entry.insert(0, self.path)
###following DataFrame I would like to use in in the PageThree class.
self.df = pd.read_csv(self.path)
def print_df(self):
return self.df.head()
class PageThree(PageTwo):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
PageTwo.__init__(self, parent, controller)
start_btn = tk.Button(self, text = "Data Frame", command=self.funct01, width = 10).grid()
def funct01(self):
##this is where I would like to utilize the DataFrame (inside the function)
instance=PageTwo(parent,controller)
print(instance.select_file(self))
if __name__=="__main__":
app=Page()
app.geometry("400x400+100+100")
app.mainloop()
You have to ask the Page instance (controller) to return the PageTwo instance, and you need to store the controller in the PageTwo "constructor".
I was not aware that you can hash a class definition (use it has the key of a dictionary).
import tkinter as tk
from PIL import ImageTk, Image
from tkinter import filedialog, messagebox
import time, os, random, string
from datetime import datetime
from time import gmtime, strftime
import pandas as pd
class Page(tk.Tk):
def __init__(self, *args, **kwargs):
tk.Tk.__init__(self, *args, **kwargs)
container=tk.Frame(self)
container.grid()
container.grid_rowconfigure(0, weight=1)
container.grid_columnconfigure(0, weight=1)
self.frames={}
for F in (PageOne, PageTwo, PageThree):
frame=F(container, self)
self.frames[F]=frame
frame.grid(row=0, column=0, sticky="nsew")
self.show_frame(PageOne)
def show_frame(self, cont):
frame=self.frames[cont]
frame.tkraise()
def getFrame(self, frame):
return self.frames[frame]
class PageOne(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
start_btn = tk.Button(self, text = "Start >>>", command=lambda:controller.show_frame(PageTwo), width = 10, activebackground = "#ffffff", relief="flat").grid()
class PageTwo(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
self.controller = controller
self.browse_btn = tk.Button(self, text=" Browse ", command=self.select_file)
self.browse_btn.grid(row=4, column=0, padx=290, pady=10, columnspan=3, sticky="w")
self.browse_entry = tk.Entry(self, text="", width=30)
self.browse_entry.grid(row=4, column=0, columnspan=3, padx=100, pady=10, sticky="w")
self.continue_btn = tk.Button(self, text="Continue >>", borderwidth=2, width=10, bg="#00c441", fg="#ffffff", command=lambda:[self.print_df(), controller.show_frame(PageThree)])
self.continue_btn.grid(row=19, column=0, columnspan=3, padx=312, pady=5, sticky="w")
self.continue_btn.config(state=tk.NORMAL)
def select_file(self):
self.path = filedialog.askopenfilename(defaultextension="*.csv", filetypes = (("csv files","*.csv"),("All Files", "*.*")))
self.browse_entry.delete(0, tk.END)
self.browse_entry.insert(0, self.path)
###following DataFrame I would like to use in in the PageThree class.
self.df = pd.read_csv(self.path)
def print_df(self):
return self.df.head()
class PageThree(PageTwo):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
PageTwo.__init__(self, parent, controller)
start_btn = tk.Button(self, text = "Data Frame", command=self.funct01, width = 10).grid()
def funct01(self):
instance = self.controller.getFrame(PageTwo)
dataFrame = instance.df
print(dataFrame.head())
if __name__=="__main__":
app=Page()
app.geometry("400x400+100+100")
app.mainloop()
I have a button in widget widget which is a central widget of MyApp class, which inherits from QMainWindow. I have connected widget's button to MyApp's method called logic. This is fine, because if I write a method in my main class MyApp(QMainWindow):
def logic(self):
sender = self.sender()
print(sender)
... and click the button I get a message:
PyQt5.QtWidgets.QPushButton object at 0x7ff92e19eaf8
My question is how can I from MyApp's method called logic access to its child object like MyApp.widget.saltLine which is a QLineEdit object? I need to read that line.
class MyApp(QMainWindow):
def __init__(self, parent=None):
super().__init__(parent)
self.initui()
def initui(self):
self.setMinimumSize(500, 150)
# create central widget
widget = QWidget()
# lines for entering data
widget.saltLabel = QLabel("Salt:")
widget.hashSunkenLabel = QLabel()
widget.passwordLine = QLineEdit()
widget.resultButton = QPushButton("&Calculate", self)
# set layout
grid = QGridLayout()
grid.addWidget(widget.saltLabel, 0, 0)
grid.addWidget(widget.passwordLine, 1, 0)
grid.addWidget(widget.hashSunkenLabel, 2, 0)
grid.addWidget(widget.resultButton, 2, 1)
# set widget a grid layout and set widget
# as central widget of QMainWindows
widget.setLayout(grid)
self.setCentralWidget(widget)
# don't know how should this look like
widget.resultButton.clicked.connect(self.logic)
def logic(self):
salt = self.saltLine.text()
password = self.passwordLine.text()
resulting_hash = crypt.crypt(password, salt)
self.hashSunkenLabel.setText(resulting_hash)
Or am I definig centralwidget widget wrong? If I do it without a central widget it works fine:
#!/usr/bin/env python3
import sys
import crypt
from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import (QMainWindow, QLabel, QLineEdit, QPushButton,
QWidget, QApplication, QSystemTrayIcon, QFrame, QGridLayout)
class MyApp(QWidget):
def __init__(self, parent=None):
super().__init__(parent)
self.saltLabel = QLabel("Salt:")
self.saltLine = QLineEdit()
self.saltLine.setPlaceholderText("e.g. $6$xxxxxxxx")
self.passwordLabel = QLabel("Password:")
self.passwordLine = QLineEdit()
self.hashLabel = QLabel("Hash:")
self.hashSunkenLabel = QLabel()
# widget.hashSunkenLabel.setFrameStyle(QFrame.Box | QFrame.Sunken)
self.hashSunkenLabel.setFrameShadow(QFrame.Sunken)
self.resultButton = QPushButton("&Calculate", self)
self.resultButton.setMaximumSize(100, 50)
self.initui()
def initui(self):
# main window size, title and icon
self.setGeometry(300, 300, 500, 150)
self.setWindowTitle("Password hash calculator | Linux")
# set layout
grid = QGridLayout()
grid.addWidget(self.passwordLabel, 0, 0)
grid.addWidget(self.passwordLine, 0, 1)
grid.addWidget(self.saltLabel, 1, 0)
grid.addWidget(self.saltLine, 1, 1)
grid.addWidget(self.resultButton, 2, 1)
grid.addWidget(self.hashLabel, 3, 0)
grid.addWidget(self.hashSunkenLabel, 3, 1)
self.setLayout(grid)
self.resultButton.clicked.connect(self.logic)
def logic(self):
"""
Calculates hash from salt and password
"""
salt = self.saltLine.text()
password = self.passwordLine.text()
resulting_hash = crypt.crypt(password, salt)
self.hashSunkenLabel.setText(resulting_hash)
# sender = self.sender()
# print(sender)
# print(dir(MyApp))
def main():
app = QApplication(sys.argv)
instance = MyApp()
instance.show()
sys.exit(app.exec_())
if __name__ == "__main__":
main()
In following code, when the button is clicked, I insert the plots into the tabs of an auinotebook in another frame.
for example, When I have multiple plots in the plt window, I can drag a notebook tab into the bottom (that results in displaying two plots). Later on when I delete the bottom tab, and try to go into other plots, I see a flicker like the closed tab is still there.
I guess the issue is with my on_nb_tab_close. Because, without that I was not able to notice any such problem.
I appreciate help. Code samples will be very useful. (wxpython version 2.812)
import wx
import wx.lib.agw.aui as aui
import matplotlib as mpl
from matplotlib.backends.backend_wxagg import FigureCanvasWxAgg as mplCanvas
def create_plotter(self):
try:
self.plotter.Show()
except AttributeError:
self.plotter =PlotFrame(self, 500, 500)
self.plotter.Show()
return self.plotter
class PlotFrame(wx.Frame):
def __init__(self, parent, height, width):
wx.Frame.__init__(self, None, size=(height,width), title="plts")
self.parent=parent
self.nb = aui.AuiNotebook(self)
self.Bind(aui.EVT_AUINOTEBOOK_PAGE_CLOSE, self.on_nb_tab_close, self.nb)
def AddPlotTab(self,name="plot"):
page = Plot(self.nb)
self.nb.AddPage(page,name)
return page
def on_nb_tab_close(self, evt):
print "tab close fired"
s=self.nb.GetSelection()
v=self.nb.RemovePage(s)
if not self.nb.GetPageCount():
self.on_Close(evt)
evt.Veto()
class Plot(wx.Panel):
def __init__(self, parent, id = -1, dpi = None, **kwargs):
wx.Panel.__init__(self, parent, id=id, **kwargs)
self.figure = mpl.figure.Figure(dpi=dpi)
self.canvas = mplCanvas(self, -1, self.figure) # wxPanel object containing plot
sizer = wx.BoxSizer(wx.VERTICAL)
sizer.Add(self.canvas,1,wx.EXPAND)
self.SetSizer(sizer)
class MainFrame(wx.Frame):
def __init__(self, parent):
wx.Frame.__init__(self, parent, title="Plotting test", size=(300, 300))
self.btn1 = wx.Button(self, -1, "Print 1")
self.Bind(wx.EVT_BUTTON, self.OnBtn1, self.btn1)
def OnBtn1(self, evt):
plotter=create_plotter(self)
page1 = plotter.AddPlotTab("case 1: first_plot")
page1.figure.gca().plot(range(10),range(10),'+')
page1.figure.gca().plot(range(10),range(10),'-',color='red')
page1.figure.canvas.draw()
if __name__ == '__main__':
APP = wx.App(False)
FRAME = MainFrame(None)
FRAME.Show()
APP.MainLoop()