I want to make a financial calculator by using GUI, which can calculate some basic calculation functions.
Currently my program has main two functions future_value and present_value.
But when I calculate, it will call my previous function and print out the result on the terminal. For example, my previous function is future_value, and now there is a new function called present_value. When I press the calculation button, It will also call future_value in the terminal and print it out,but I only want present_value to be called.
I want to set the cauculation button as universal button, so whenever there is a blank in a block, then computer know I need to calculte that specific number(function).
One of my idea is that create a function that like a title(heading) function, and that function is connected to this button, and within this function, there is a list of different functions that do something like calculte interest rate, or present_value, etc, but my main problem is how should I do it, how should I use if statement or other statement to work on it.
from PySide2.QtWidgets import QApplication
from PySide2.QtUiTools import QUiLoader
class COMPOUND_INTEREST:
def __init__(self):
self.ui = QUiLoader().load("bb\IA compound designer.ui")
self.ui.setWindowTitle("Compound interest")
self.ui.pushButton.clicked.connect(self.future_value)
self.ui.pushButton.clicked.connect(self.present_value)
def future_value(self):
N = int(self.ui.lineEdit.text())
I = float(self.ui.lineEdit_2.text())
PV = float(self.ui.lineEdit_3.text())
C_Y = int(self.ui.lineEdit_7.text())
jb = (1 + (I / (100 * C_Y))) ** N
res = abs(jb * PV)
print(res)
self.ui.lineEdit_6.setText(str(round(res, 2)))
def present_value(self):
F_V = float(self.ui.lineEdit_6.text())
N = int(self.ui.lineEdit.text())
I = float(self.ui.lineEdit_2.text())
C_Y = int(self.ui.lineEdit_7.text())
x = (1 + (I / (100 * C_Y))) ** N
res = -abs(F_V / x)
print(res)
self.ui.lineEdit_3.setText(str(round(res, 2)))
app = QApplication([])
COMPOUND_INTEREST = COMPOUND_INTEREST()
COMPOUND_INTEREST.ui.show()
app.exec_()
Based on what I understood on your post, you want a single button to change its functionality based on wether the Line Edits are filled with numbers or empty, right?
Just be careful because it gets more complex than that. For example: what if both of the Line Edits are filled with numbers? Or both of them are empty?
In the example below, I teach you how to check those 4 conditions, and ask the user to choose which method that should be executed it happens that both Edits are filled.
If both are empty, an error is displayed on the screen telling the user to fill at least one of them.
from PySide2.QtWidgets import QApplication, QMainWindow, QWidget, QMessageBox
from PySide2.QtWidgets import QLabel, QLineEdit, QPushButton, QDialog
from PySide2.QtWidgets import QDialogButtonBox, QGridLayout, QVBoxLayout
class OurCondition(QDialog):
def __init__(self):
QDialog.__init__(self)
layout = QVBoxLayout()
self.setLayout(layout)
text = "Both <b>Present</b> and <b>Future</b> fields are filled. Which function "
text += "do you want to execute?"
layout.addWidget(QLabel(text))
bbox = QDialogButtonBox()
bbox.addButton('Future Value', QDialogButtonBox.AcceptRole)
bbox.addButton('Present Value', QDialogButtonBox.AcceptRole)
bbox.addButton('Cancel', QDialogButtonBox.RejectRole)
bbox.accepted.connect(self.accept)
bbox.rejected.connect(self.reject)
bbox.clicked.connect(self.clickedBeforeAccept)
layout.addWidget(bbox)
# Set Default Option
self.conditionOption = 0
# As we are using bbox on clickedBeforeAccept below, we must reference
# bbox for later usage:
self.bbox = bbox
# Custom function to be called when a QDialogButtonBox button
# is clicked.
def clickedBeforeAccept(self, button):
if (self.bbox.buttonRole(button) == QDialogButtonBox.AcceptRole):
if (button.text() == 'Future Value'):
self.conditionOption = 1
else:
self.conditionOption = 2
def exec(self):
self.exec_()
# Return which option the user selected. Default is 0 (None).
if (self.result() == QDialog.Accepted):
return self.conditionOption
class Scene(QWidget):
def __init__(self):
QWidget.__init__(self)
layout = QVBoxLayout()
self.setLayout(layout)
editN = QLineEdit()
editI = QLineEdit()
editPV = QLineEdit()
editFV = QLineEdit()
editCY = QLineEdit()
grid = QGridLayout()
grid.addWidget(QLabel("N"), 0, 0)
grid.addWidget(editN, 0, 1)
grid.addWidget(QLabel("I"), 1, 0)
grid.addWidget(editI, 1, 1)
grid.addWidget(QLabel("Present:"), 2, 0)
grid.addWidget(editPV, 2, 1)
grid.addWidget(QLabel("Future:"), 3, 0)
grid.addWidget(editFV, 3, 1)
grid.addWidget(QLabel("C/Y"), 4, 0)
grid.addWidget(editCY, 4, 1)
layout.addLayout(grid)
layout.addSpacing(12)
button = QPushButton("Calculate")
layout.addWidget(button)
# Connecting methods to each widget:
button.clicked.connect(self.calculate)
# As we may reference these variables within the methods
# of our class, we bind them to the scene class
self.editI = editI
self.editN = editN
self.editPV = editPV
self.editFV = editFV
self.editCY = editCY
# Note I'm not using QUiLoader. So self.ui is not available
# here.
def future_value(self):
N = int(self.editN.text())
I = float(self.editI.text())
PV = float(self.editPV.text())
C_Y = int(self.editCY.text())
jb = (1 + (I / (100 * C_Y))) ** N
res = abs(jb * PV)
print(res)
self.editFV.setText(str(round(res, 2)))
def present_value(self):
F_V = float(self.editFV.text())
N = int(self.editN.text())
I = float(self.editI.text())
C_Y = int(self.editCY.text())
x = (1 + (I / (100 * C_Y))) ** N
res = -abs(F_V / x)
print(res)
self.editPV.setText(str(round(res, 2)))
def calculate(self):
# As each element in the scene is being referenced from
# this class, we can access them using the self variable.
#
# To choose which operation, we must inform the application
# which widget is empty before calling a calculation method:
fv_empty = (self.editFV.text() == "")
pv_empty = (self.editPV.text() == "")
# If both inputs are empty, show an error dialog. That cannot
# happen based on the current rules.
if (fv_empty and pv_empty):
text = 'Both Present and Future Values Fields are blank. '
text += 'Fill at least one of them.'
dialog = QMessageBox()
dialog.setWindowTitle('Error')
dialog.setText(text)
dialog.setIcon(QMessageBox.Critical)
dialog.exec_()
# If both inputs are filled, create a rule and ask the user if
# it wants to continue, otherwise, do nothing:
elif (not fv_empty and not pv_empty):
dialog = OurCondition()
choice = dialog.exec()
# Inside OutCondition class we defined if the user
# clicked on Future Value button, choice is set to 1.
#
# If Present Value button is clicked, choice is set to 2.
#
# If it clicks on Cancel, or x button, choice is set to 0.
if (choice == 1):
self.future_value()
elif (choice == 2):
self.present_value()
# At least one field is empty:
else:
# If Future Field is empty, then call future_value to fill it
if (fv_empty):
self.future_value()
# If Present Field is empty, then call present_value to fill it
elif (pv_empty):
self.present_value()
class MainWindow(QMainWindow):
def __init__(self):
QMainWindow.__init__(self)
self.setWindowTitle('Compound Test')
scene = Scene()
self.setCentralWidget(scene)
if __name__ == '__main__':
app = QApplication()
win = MainWindow()
win.show()
app.exec_()
If you want, you can also use QComboBoxes to let the user choose from multiple options, if you need 3 or more methods to execute from the single button. As the number of options grows, it gets harder to create a logic using the conditions of wether each input is empty or filled.
With only 2 of them, that's ok. But with 3 or more, I strongly recomend you to rethink your User Interface and add a QComboBox.
If that's still not what you wanted, please edit your question to state what is that you're struggling at.
What you want is to learn how to parse an input. Basically you need to make an algorithm that checks if the user has input the interest rate.
Then, you make a conditional statement that says
if inputHasInterestRate() == false:
presentValue()
else:
futureValue()
I'm using pandastable to create a list of items and edit it before converting it to a file. It's a very versatile module, but whenever I want to edit a cell, I have to press Enter to update the value, and I keep forgetting. I have read in the documentation that you can override the key bindings creating your own class. I managed to make the left click to do different things, but I can't see how to bind it to changing the value, as the "Return" key does. This is what I have tried:
import tkinter as tk
from pandastable import Table, Frame
import pandas as pd
screen = tk.Tk()
df = pd.read_csv("item_list.csv")
class MyTable(Table):
# based on original drawCellEntry() with required changes
def handle_left_click(self, event):
"""Respond to a single press"""
self.clearSelected()
self.allrows = False
#which row and column is the click inside?
rowclicked = self.get_row_clicked(event)
colclicked = self.get_col_clicked(event)
if colclicked == None:
return
self.focus_set()
if hasattr(self, 'cellentry'):
self.cellentry.update()
self.rowheader.redraw()
#ensure popup menus are removed if present
if hasattr(self, 'rightmenu'):
self.rightmenu.destroy()
if hasattr(self.tablecolheader, 'rightmenu'):
self.tablecolheader.rightmenu.destroy()
self.startrow = rowclicked
self.endrow = rowclicked
self.startcol = colclicked
self.endcol = colclicked
#reset multiple selection list
self.multiplerowlist=[]
self.multiplerowlist.append(rowclicked)
if 0 <= rowclicked < self.rows and 0 <= colclicked < self.cols:
self.setSelectedRow(rowclicked)
self.setSelectedCol(colclicked)
self.drawSelectedRect(self.currentrow, self.currentcol)
self.drawSelectedRow()
self.rowheader.drawSelectedRows(rowclicked)
self.tablecolheader.delete('rect')
if hasattr(self, 'cellentry'):
self.cellentry.update()
self.rowheader.redraw()
return
pt = MyTable(screen, dataframe=df, width=800)
pt.show()
screen.mainloop()
Any and all help will be much appreciated.
I am creating buttons, then putting them through as arguments to a function I created called (placement), but when I run them in as arguments and I try to print the type of this input, they become Event Objects, I don't understand why this occurs, I need them to remain as Button objects, because i want to change the text of a button after i click it
for column in range(self.column + 1):
new_button = Button(new_frame, text = ' ', height = 10, width = 20)
new_button.grid(row = r, column = c)
new_button.bind('<Button-1>', func = lambda x=new_button: self.placement(x))
def placement(self, button):
print(type(button))
if self.current == 1:
button.config(text = 1)
self.current = 2
else:
button.config(text = 2)
self.current = 1
A bind function requires a event argument, so you have to change your code to:
new_button.bind('<Button-1>', func = lambda event, x=new_button: self.placement(x))
Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 4 years ago.
Improve this question
So I'm trying to drag the URL from a browser (chrome) into a text box.
Here is the image of the Tkinter Text box:
It is the same way you can drag the URL into Word and it 'pastes' into the text area.
Picture for example:
.
This is possible in wxPython with wx.TextDropTarget but how would it be done in Tkinter?
Is there a way to do this in Tkinter?
so you need to jump through a few hoops, but what you want is possible.
using the information in this answer I've been able to get this working on windows 10 64 bit with 64 bit python 3.7.1
firstly I downloaded the relevant binaries from here which in my case was tkdnd2.8-win32-x86_64.tar.gz
I extracted this to get a folder called tkdnd2.8 which I put into the following location:
C:\Program Files\Python37\tcl
this means it can easily be found by the tk interpreter, you can put it elsewhere but you need to add the path to the environment variables in order for it to be found.
then I used the wrapper from that answer (but updated to python 3 names for tk):
import os
import tkinter
def _load_tkdnd(master):
tkdndlib = os.environ.get('TKDND_LIBRARY')
if tkdndlib:
master.tk.eval('global auto_path; lappend auto_path {%s}' % tkdndlib)
master.tk.eval('package require tkdnd')
master._tkdnd_loaded = True
class TkDND(object):
def __init__(self, master):
if not getattr(master, '_tkdnd_loaded', False):
_load_tkdnd(master)
self.master = master
self.tk = master.tk
# Available pre-defined values for the 'dndtype' parameter:
# text/plain
# text/plain;charset=UTF-8
# text/uri-list
def bindtarget(self, window, callback, dndtype, event='<Drop>', priority=50):
cmd = self._prepare_tkdnd_func(callback)
return self.tk.call('dnd', 'bindtarget', window, dndtype, event,
cmd, priority)
def bindtarget_query(self, window, dndtype=None, event='<Drop>'):
return self.tk.call('dnd', 'bindtarget', window, dndtype, event)
def cleartarget(self, window):
self.tk.call('dnd', 'cleartarget', window)
def bindsource(self, window, callback, dndtype, priority=50):
cmd = self._prepare_tkdnd_func(callback)
self.tk.call('dnd', 'bindsource', window, dndtype, cmd, priority)
def bindsource_query(self, window, dndtype=None):
return self.tk.call('dnd', 'bindsource', window, dndtype)
def clearsource(self, window):
self.tk.call('dnd', 'clearsource', window)
def drag(self, window, actions=None, descriptions=None,
cursorwin=None, callback=None):
cmd = None
if cursorwin is not None:
if callback is not None:
cmd = self._prepare_tkdnd_func(callback)
self.tk.call('dnd', 'drag', window, actions, descriptions,
cursorwin, cmd)
_subst_format = ('%A', '%a', '%b', '%D', '%d', '%m', '%T',
'%W', '%X', '%Y', '%x', '%y')
_subst_format_str = " ".join(_subst_format)
def _prepare_tkdnd_func(self, callback):
funcid = self.master.register(callback, self._dndsubstitute)
cmd = ('%s %s' % (funcid, self._subst_format_str))
return cmd
def _dndsubstitute(self, *args):
if len(args) != len(self._subst_format):
return args
def try_int(x):
x = str(x)
try:
return int(x)
except ValueError:
return x
A, a, b, D, d, m, T, W, X, Y, x, y = args
event = tkinter.Event()
event.action = A # Current action of the drag and drop operation.
event.action_list = a # Action list supported by the drag source.
event.mouse_button = b # Mouse button pressed during the drag and drop.
event.data = D # The data that has been dropped.
event.descr = d # The list of descriptions.
event.modifier = m # The list of modifier keyboard keys pressed.
event.dndtype = T
event.widget = self.master.nametowidget(W)
event.x_root = X # Mouse pointer x coord, relative to the root win.
event.y_root = Y
event.x = x # Mouse pointer x coord, relative to the widget.
event.y = y
event.action_list = str(event.action_list).split()
for name in ('mouse_button', 'x', 'y', 'x_root', 'y_root'):
setattr(event, name, try_int(getattr(event, name)))
return (event, )
then a simple test script:
import tkinter
from TkDND2 import TkDND
root = tkinter.Tk()
dnd = TkDND(root)
entry = tkinter.Entry()
entry.pack()
def handle(event):
event.widget.insert(0, event.data)
dnd.bindtarget(entry, handle, 'text/plain')
root.mainloop()
note that I called the wrapper TkDND2 so as not to get confused with the built in dnd.
by changing the last argument of bindtarget you can change the type of input accepted, with text/plain most textual input should be accepted from any program, with text/uri-list I was only able to drag files from explorer into text entry and get the path of that file, URLs from IE did not work.
also note that the files for download on sourceforge are quite outdated but the only place I could fine to download a pre built binary distribution.
EDIT:
I wasn't happy with the wrapper I found elsewhere so I modified it so that it patches the Widget class that everything in tkinter inherits from when its imported, this way you can use for example entry.bindtarget(callback, type) instead.
import os
import tkinter
__all__ = ['Text', 'Files', 'URL', 'HTML', 'RTF']
# the types of drop supported content
Text = 'DND_Text'
Files = 'DND_Files'
URL = 'DND_URL'
HTML = 'DND_HTML'
RTF = 'DND_RTF'
def drop_entry(event):
event.widget.insert(0, event.data)
def drop_text(event):
event.widget.insert(1.0, event.data)
def _load_tkdnd(master):
if not getattr(master, '_tkdnd_loaded', False):
tkdndlib = os.environ.get('TKDND_LIBRARY')
if tkdndlib:
master.tk.eval('global auto_path; lappend auto_path {%s}' % tkdndlib)
master.tk.eval('package require tkdnd')
master._tkdnd_loaded = True
def bindtarget(self, callback=None, dndtype=Text, event='<Drop>', priority=50):
if callback == None:
classnames = [x.__name__ for x in self.__class__.__bases__]
classnames.append(self.__class__.__name__)
print(classnames)
if 'Entry' in classnames:
callback = drop_entry
elif 'Text' in classnames:
callback = drop_text
else:
raise ValueError('No default callback')
_load_tkdnd(self)
cmd = self._prepare_tkdnd_func(callback)
return self.tk.call('dnd', 'bindtarget', self, dndtype, event, cmd, priority)
def bindtarget_query(self, dndtype=None, event='<Drop>'):
_load_tkdnd(self)
return self.tk.call('dnd', 'bindtarget', self, dndtype, event)
def cleartarget(self):
_load_tkdnd(self)
return self.tk.call('dnd', 'cleartarget', self)
def bindsource(self, callback, dndtype, priority=50):
_load_tkdnd(self)
cmd = self._prepare_tkdnd_func(callback)
return self.tk.call('dnd', 'bindsource', self, dndtype, cmd, priority)
def bindsource_query(self, dndtype=None):
_load_tkdnd(self)
return self.tk.call('dnd', 'bindsource', self, dndtype)
def clearsource(self):
return self.tk.call('dnd', 'clearsource', self)
def drag(self, actions=None, descriptions=None, cursorwin=None, callback=None):
cmd = None
if cursorwin is not None:
if callback is not None:
cmd = self._prepare_tkdnd_func(callback)
return self.tk.call('dnd', 'drag', self, actions, descriptions, cursorwin, cmd)
_subst_format = ('%A', '%a', '%b', '%D', '%d', '%m', '%T', '%W', '%X', '%Y', '%x', '%y')
_subst_format_str = " ".join(_subst_format)
def _prepare_tkdnd_func(self, callback):
funcid = self.master.register(callback, self._dndsubstitute)
cmd = ('%s %s' % (funcid, _subst_format_str))
return cmd
def _dndsubstitute(self, *args):
if len(args) != len(_subst_format):
return args
def try_int(x):
x = str(x)
try:
return int(x)
except ValueError:
return x
A, a, b, D, d, m, T, W, X, Y, x, y = args
event = tkinter.Event()
event.action = A # Current action of the drag and drop operation.
event.action_list = a # Action list supported by the drag source.
event.mouse_button = b # Mouse button pressed during the drag and drop.
event.data = D # The data that has been dropped.
event.descr = d # The list of descriptions.
event.modifier = m # The list of modifier keyboard keys pressed.
event.dndtype = T
event.widget = self # The widget that recieved the event
event.x_root = X # Mouse pointer x coord, relative to the root win.
event.y_root = Y
event.x = x # Mouse pointer x coord, relative to the widget.
event.y = y
event.action_list = str(event.action_list).split()
for name in ('mouse_button', 'x', 'y', 'x_root', 'y_root'):
setattr(event, name, try_int(getattr(event, name)))
return (event, )
tkinter.Widget.bindtarget = bindtarget
tkinter.Widget.bindtarget_query = bindtarget_query
tkinter.Widget.cleartarget = cleartarget
tkinter.Widget.bindsource = bindsource
tkinter.Widget.bindsource_query = bindsource_query
tkinter.Widget.clearsource = clearsource
tkinter.Widget.drag = drag
tkinter.Widget._prepare_tkdnd_func = _prepare_tkdnd_func
tkinter.Widget._dndsubstitute = _dndsubstitute
example use:
import tkinter
import TkDND3
from tkinter.scrolledtext import ScrolledText
root = tkinter.Tk()
entry = tkinter.Entry(root)
entry.pack(fill='both', expand=True)
text = tkinter.Text(root)
text.pack(fill='both', expand=True)
entry.bindtarget()
text.bindtarget()
stext = ScrolledText(root)
stext.pack(fill='both', expand=True)
stext.bindtarget()
root.mainloop()
this also added some default callbacks for simple widgets (entry, text and descendants) so they could be called without arguments as the example shows.
I'm building an app based on the Horizontal Menu example from Urwid.
I have an item that I want to show some information. I have an "OK" button underneath, and I want it to either pop the menu back to the previous state, or maybe restart the entire screen/loop.
In the example, they use ExitMainLoop(), but I don't want to exit - I just want to restart it.
So, I've changed the callback to a different function - but all my attempts either do nothing or crash my program.
Here's the relevant bits:
Starting the menu:
if __name__ == "__main__":
top = HorizontalBoxes()
top.open_box(menu_top([]).menu)
urwid.MainLoop(urwid.Filler(top, 'middle', 40), palette).run()
Relevant menu class/functions. My problem is - what goes in the does_nothing function?
class Choice(urwid.WidgetWrap):
def __init__(self, caption, pid):
super(Choice, self).__init__(
MenuButton(caption, partial(self.item_chosen,pid)))
self.caption = caption
def item_chosen(self, pid, button):
if self.caption == (u"System status"):
showSystemStatus()
def showSystemStatus():
response = urwid.Text( "... some text ")
done = MenuButton(u'OK', does_nothing)
response_box = urwid.Filler(urwid.Pile([response,done]))
top.open_box(urwid.AttrMap(response_box, 'options'))
def does_nothing(key):
????????????????????
return
I found a solution!
I had to add a new method to the HorizontalBoxes class. Specifically, the close_box method:
class HorizontalBoxes(urwid.Columns):
def __init__(self):
super(HorizontalBoxes, self).__init__([], dividechars=1)
def open_box(self, box):
if self.contents:
del self.contents[self.focus_position + 1:]
self.contents.append((urwid.AttrMap(box, 'options', focus_map),
self.options('given', 40)))
self.focus_position = len(self.contents) - 1
def close_box(self):
if self.contents:
del self.contents[self.focus_position :]
self.focus_position = len(self.contents) - 1
And then, it was a simple matter of calling the close_box method from my does_nothing function from before:
def does_nothing(key):
top.close_box()
return