tkinter how do i save changes made to the buttons - button

thx a lot for replying my questions for making my previous code simpler.. this is the result... now for the next phase is save changes made to the buttons.. im still learning though looking for sources anything can help :)
from tkinter import*
import tkinter as tk
import tkinter.simpledialog
def onChange(i):
btn_list[i].config(text='Updating...',bg='red')
btn_list[i].grid(in_=root,row=rw[i],column=2)
ans=tk.simpledialog.askfloat('Updating....', 'What is the current price?')
if ans:
btn_list[i].config(text='RM{:,.2f}'.format(ans))
btn_list[i].config(bg='yellow')
root=Tk()
Title=['Item','Unit','Price']
Item=['Kopi O','Teh O','Teh Tarik']
Unit= '1 cup'
Price=[1,0.9,1.2]
cl=[0,1,2]
rw=[1,2,3]
btn_list=[]
for i in range(3):
btnT1=tk.Button(root,text=Title[i],width=10,bg='light green')
btnT1.grid(in_=root,row=0,column=cl[i])
for x in range(3):
btnT2=tk.Button(root,text=Item[x],width=10)
btnT2.grid(in_=root,row=rw[x],column=0)
for y in range(3):
btnT3=tk.Button(root,text=Unit,width=10)
btnT3.grid(in_=root,row=rw[y],column=1)
for z in range(3):
btnT4=tk.Button(root,text=('RM {:,.2f}'.format(Price[z])),bg='yellow',width=10,\
command=lambda i=z:onChange(i))
btnT4.grid(in_=root,row=rw[z],column=2)
btn_list.append(btnT4)
root.mainloop()

If you want changes to be saved when your program exits and restarts, you'll have to do all of the work yourself. You'll need to write a function that gathers all the data you want to save, another function to write that data to a file or database, a third to be able to read the data from the file or database, and a forth to update the UI with the saved values.

Related

Non-blocking cell execution in Jupyter

In Jupyter with an ipython kernel, is there a canonical way to execute cells in a non-blocking fashion?
Ideally I'd like to be able to run a cell
%%background
time.sleep(10)
print("hello")
such that I can start editing and running the next cells and in 10 seconds see "hello" appear in the output of the original cell.
I have tried two approaches, but haven't been happy with either.
(1) Create a thread by hand:
def foo():
time.sleep(10)
print("hello")
threading.Thread(target=foo).start()
The problem with this is that "hello" is printed in whatever cell is active in 10 seconds, not necessarily in the cell where the thread was started.
(2) Use a ipywidget.Output widget.
def foo(out):
time.sleep(10)
out.append_stdout("hello")
out = ipywidgets.Output()
display(out)
threading.Thread(target=foo,args=(out,)).start()
This works, but there are problems when I want to update the output (think of monitoring something like memory consumption):
def foo(out):
while True:
time.sleep(1)
out.clear_output()
out.append_stdout(str(datetime.datetime.now()))
out = ipywidgets.Output()
display(out)
threading.Thread(target=foo,args=(out,)).start()
The output now constantly switches between 0 and 1 lines in size, which results in flickering of the entire notebook.
This should be solvable wait=True in the call to clear_output. Alas, for me it results in the output never showing anything.
I could have asked about that issue, which seems to be a bug, specifically, but I wondered whether there is maybe another solution that doesn't require me doing all of this by hand.
I've experienced some issues like this with plotting to an output, it looks like you have followed the examples in the ipywidgets documentation on async output widgets.
The other approach I have found sometimes helpful (particularly if you know the size of the desired output) is to fix the height of your output widget when you create it.
out = ipywidgets.Output(layout=ipywidgets.Layout(height='25px'))

Python Tkinter: Placing buttons in frame

I'm trying to create different frames and switch/destroy them so that you can move between windows like you would in a normal iOS app.
To do so, I need to place the widgets (components) in frames (containers).
However, when I try to add a button to the frame it doesn't pack it to the right side.
Here is my code:
from tkinter import *
root=Tk()
root.geometry('500x500')
root.title('Good morning :)')
frame1=Frame(root,width=500,height=500,bg='green')
frame1.pack()
button1=Button(frame1,text='Hello')
button1.pack(side='bottom')
You need to expand the Frame to fill the entire top-level window, and you need to tell the Button to pack on side='right' instead of side='bottom'.
And you need to run root.mainloop() at the end.
from tkinter import *
root = Tk()
root.geometry('500x500')
root.title('Good morning :)')
frame1 = Frame(root, bg='green')
frame1.pack(expand=True, fill=BOTH)
button1 = Button(frame1, text='Hello')
button1.pack(side=RIGHT)
root.mainloop()
Also, you don't need the dimensions in the Frame statement, since it will expand to the full 500x500 stated in the geometry with the extra keyword arguments passed to the pack() function. By default, the Frame is only going to be big enough to hold the widgets inside it, so it will only be as big as the Button, unless you tell it to expand to the full size of the top-level root widget.

How to close a window when you click a button to open another window

I am working on a program that will allow someone to enter details in order to write a CV. I am using the Tkinter module (as extra practice) but am already stuck on the menu!
At the moment I have three different options the user can choose: Write CV, Review CV and Exit. I have created a button for each option and when the user presses the button it'll open, however the menu window remains open (there is a different subroutine for each option).
I understand that you need to do something like window.destroy(), however I'm not sure how to give a button two commands without doing something too fiddly like create more subroutines etc.?
The other option I think I'd prefer is is I could clear the menu screen?
Here is the programming I have at the moment:
def Main_Menu():
import tkinter
main_menu = tkinter.Tk()
main_menu.title("CV Writer")
main_menu.geometry("300x300")
main_menu.wm_iconbitmap('cv_icon.ico')
title = tkinter.Label(main_menu, text = "Main Menu", font=("Helvetica",25))
title.pack()
gap = tkinter.Label(main_menu, text = "")
gap.pack()
write_cv = tkinter.Button(main_menu, text = "1) Write CV", font=("Helvetica"), command=Write_CV)
write_cv.pack()
review_cv = tkinter.Button(main_menu, text = "2) Review CV", font=("Helvetica"), command=Review_CV)
review_cv.pack()
leave = tkinter.Button(main_menu, text = "3) Exit", font=("Helvetica"), command=Exit)
leave.pack()
main_menu.mainloop()
def Write_CV():
import tkinter
write_cv = tkinter.Tk()
write_cv.geometry("300x300")
write_cv.title("Write CV")
def Review_CV():
import tkinter
review_cv = tkinter.Tk()
review_cv.geometry("300x300")
review_cv.title("Review CV")
def Exit():
import tkinter
leave = tkinter.Tk()
leave.geometry("300x300")
leave.title("Exit")
Main_Menu()
Running the program should help make this question make more sense!
I am so sorry for the wordy question, but any kind of help would be appreciated! Please bear in mind I am only a GCSE student so simple language would also be so nice! Thank you!
I don't know why are you importing tkinter under each method, it's completely useless. Simply import it once at the beginning of your file with a syntax like this:
import tkinter as tk
So that you can refer to the widgets simply with the duo tk:
btn = tk.Button(None, text='I can simply refer to a widget with tk')
Apart from this, the structure of your program is really bad. In my opinion, you should not instantiate Tk inside your function Main_Menu, because it will only be visible inside it. If you want to refer to the master or root or whatever you want to call the instance of Tk, you can't, because it's a local instance, as I said above.
I usually instantiate Tk in the main function of my program, or in the following if __name__ == '__main__': construct:
if __name__ == '__main__':
master = tk.Tk() # note I am using "tk"
# create your objects or call your functions here
master.mainloop()
Your are creating an instance of Tkin each of your function, that is really a bad practice, never do that. You should only create one instance of Tk for each Tkinter application.
You should use the object-oriented paradigm or make all your widgets global to structure your application.
Except these details, you can simply call master.destroy() when you want to destroy your main window and all its children widgets, where master is the Tk instance.
In general, you have a lot of errors and bad practices. My advice is:
Read a tutorial on Python first and then on Tkinter, before
proceeding.

How to test drag and drop behavior in PyQt?

I want to unittest drag and drop for our widgets. At the moment, I instantiate a QDragEnterEvent, but this is discouraged with a big warning on the Qt documentation, because it relies on the Qt library internal state. In fact, I get segfaults that appear to be due to a violation of this Warning.
Given this premise, how can one test drag and drop behavior?
If using Unix we can use QTest, however to get a cross-platform solution, we can implement a solution where we circumvent Qt.
Using QTest
Although the Qt documentation for drag and drop says that it will not block the main event loop, a closer look at QDrag.exec will reveal that this is not true for Windows.
The call to QTest.mousePress causes the test to block until the mouse is physically moved by the user.
I got around this in Linux by using a timer to schedule the mouse move and release:
def testDragAndDrop(self):
QtCore.QTimer.singleShot(100, self.dropIt)
QtTest.QTest.mousePress(dragFromWidget, QtCore.Qt.LeftButton)
# check for desired behaviour after drop
assert something
def dropIt(self):
QtTest.QTest.mouseMove(dropToWidget)
QtTest.QTest.mouseRelease(dropToWidget, QtCore.Qt.LeftButton, delay=15)
For this solution, it is necessary to include a delay in the mouseRelease call, and to have called show on your widget.
Note that I have verified this works using pyqt4 and Python 2.7 on Fedora 20
Cross-Platform
You can use the mouse manipulation methods from the PyUserInput package. Put the mouse interaction in separate thread to avoid the locking up of the Qt main event loop. We can do this since we are not using Qt at all in our mouse control. Make sure that you have called show on the widgets you are dragging to/from.
from __future__ import division
import sys, time, threading
import numpy as np
from PyQt4 import QtGui, QtCore, QtTest
from pymouse import PyMouse
...
def mouseDrag(source, dest, rate=1000):
"""Simulate a mouse visible mouse drag from source to dest, rate is pixels/second"""
mouse = PyMouse()
mouse.press(*source)
# smooth move from source to dest
npoints = int(np.sqrt((dest[0]-source[0])**2 + (dest[1]-source[1])**2 ) / (rate/1000))
for i in range(npoints):
x = int(source[0] + ((dest[0]-source[0])/npoints)*i)
y = int(source[1] + ((dest[1]-source[1])/npoints)*i)
mouse.move(x,y)
time.sleep(0.001)
mouse.release(*dest)
def center(widget):
midpoint = QtCore.QPoint(widget.width()/2, widget.height()/2)
return widget.mapToGlobal(midpoint)
def testDragAndDrop(self):
# grab the center of the widgets
fromPos = center(dragFromWidget)
toPos = center(dropToWidget)
dragThread = threading.Thread(target=mouseDrag, args=((fromPos.x(),fromPos.y()), (toPos.x(), toPos.y())))
dragThread.start()
# cannot join, use non-blocking wait
while dragThread.is_alive():
QtTest.QTest.qWait(1000)
# check that the drop had the desired effect
assert dropToWidget.hasItemCount() > 0
Note I have tested this using PyQt4 and Python 2.7 on Fedora and Windows 7
Haven't tried, but if your drag & drop process is Qt internal (meaning, you're dragging from and to a Qt widget), QTest might help.
Basically by doing something along the lines:
QTest.mousePress(drag_widget, Qt.LeftButton) # simulate mouse press on whatever you want to drag
QTest.mouseMove(drop_widget) # move the mouse to the target - maybe this can be skipped
QTest.mouseRelease(drop_widget, Qt.LeftButton) # simulate mouse release where you want to drop
All functions may be supplied with further positional information (e.g. to click a list item within a widget) and with optional delays to emulate a human user.
Not a copy-pasteable answer, but maybe it serves as a starter...

In Tkinter how do def work?

I made a Tkinter book on a grid, the navergation buttons scroll down the left row and the photos are displayed on the right column spaning many rows so buttons display as they should. When making this Tkinter book.
I made a button on a grid
left1 = Button(win, text=" Captian Scarlet ")# win, is root master
left1.configure(command=but1)# but1 is my first def
left1.grid(row=1, column=0)# all the buttons are on the left list
This displays and works like a button without a def
Then I made a def
def but1():
img = Image.open("captain_scarlett.gif")# loads the gif file
intro = ImageTk.PhotoImage(img)# loads image drivers I belevie
right1 = Label(win, image=intro)# I think Lable is used the same as html <span>
right1.grid(row=0, column=1, rowspan=13)# image formatting to display correctly with buttons
Because I had a lack of education at the time, I could only get the image to displaplay outside a def. So in frustration I posted
"this code works purfect when not put into a def".
When I settled down I needed knowledge that I couldn't find online so I asked the question:
So How do I get this code to work inside a def ?
What makes you think it doesn't work? There's nothing special about Tkinter in this regard; anything that works outside a def definitely works inside a def. The only caveats are the same for all python code. For example, any variables you create inside the def are local unless declared otherwise, and objects (but not widgets) may get garbage collected after the def executes.
Probably what is happening is that you're creating the image and storing a reference to it in a local variable. When the def stops executing the image object is garbage collected. You'll need to keep a reference to the image that can persist. One simple solution might be to do right1.image=intro.
This code works purfecty. Now my production can go full steam ahead
Here is the finished code
def but1():
img = Image.open("captain_scarlett.gif")
intro = ImageTk.PhotoImage(img)
right = Label(win, image=intro)
right.grid(row=0, column=1, rowspan=14)
right.image=intro

Resources