QTextEditor has no attribute related to text - python

I'm new to working with python and even more new to working with PyQt4. I created this GUI using QT designer
I converted the file from .ui to a .py file already. The problem I'm running into though is that in my code it is not grabbing the data that the user inputs in the first texteditor box instead I'm getting this error message.
I also looked up the .plainText and tried to use that as well, but I could not get that working either.
import sys
from Calculator import *
class Calc(QtGui.QMainWindow):
def __init__(self, parent = None):
QtGui.QWidget.__init__(self, parent)
self.ui = Ui_MainWindow()
self.ui.setupUi(self)
QtCore.QObject.connect(self.ui.calc_payment, QtCore.SIGNAL('clicked()'), self.calculate)
def calculate(self):
if len(self.ui.Mortgage_value.text())!=0:
q = int(self.ui.Mortgage_value.text())
else:
q = 0
if len(self.ui.tax_rate.text())!=0:
r = int(self.ui.tax_rate.text())
else:
r = 0
if len(self.ui.years.text())!=0:
y = int(self.ui.years.text())
else:
y = 0
taxrate = r / 12
time = y * 12
total = q / ((1 - ((1 + r)**-y))/ r)
#if (calc_payment == 'clicked()'):#test
self.ui.results_window.setText("Monthly Payment: " +int(total))#was str
if __name__ == "__main__":
app = QtGui.QApplication(sys.argv)
myapp = Calc()
myapp.show()
sys.exit(app.exec_())
I appreciate all input that is given.
Thank you.

QTextEdit does not have a text () method, as it is an editor that can render html, it has 2 methods:
toPlainText()
toHTML()
The first returns the plain text, the second in HTML format. in your case, use the first method:
if self.ui.Mortgage_value.toPlainText() != "":
q = int(self.ui.Mortgage_value.toPlainText())
Note: Although I would recommend using QLineEdit instead of QTextEdit since in the case of QTextEdit it has several lines and how you want to convert it to a number can cause problems, besides QLineEdit supports validators like QIntValidator that helps you not to have problems with numbers.

Related

how to set a unviersal button that can do mutiple function in python

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()

How can I change the text of the edit line and add and subtract it?

I have two edit lines that when a letter from a dictionary is typed in it, it returns a number, and this number is added to the next edit line number and printed on a label. Now I want the previous number to be reduced and the new number to be added when I change one of the edit lines.
import sys
from PyQt5.QtWidgets import QWidget, QApplication, QPushButton, QLabel
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.uic import loadUiType
oroh, _ = loadUiType("oroh.ui")
class OrOh(QWidget, oroh):
def __init__(self):
QWidget.__init__(self)
self.setupUi(self)
self.lin_sh1.returnPressed.connect(self.shift1)
self.lin_sh2.returnPressed.connect(self.shift2)
def shift1(self):
shifts = {"m":7, "s":7, "v":7, "t":7, "e":3, "le":5, "ld":12,
"n":12, "ln":14, "mn":19, "en":17, "me":10, "f":24}
sh1 = self.lin_sh1.text()
if sh1 in shifts.keys():
a = shifts[sh1]
print(a)
else:
a = 0
print(a)
self.lbl_shifts.setText(f"{a}")
def shift2(self):
shifts = {"m":7, "s":7, "v":7, "t":7, "e":3, "le":5, "ld":12,
"n":12, "ln":14, "mn":19, "en":17, "me":10, "f":24}
a = self.lbl_shifts.text()
sh2 = self.lin_sh2.text()
if sh2 in shifts.keys():
b = shifts[sh2]
else:
b = 0
result = int(a) + int(b)
self.lbl_shifts.setText(f"{result}")
if __name__ == '__main__':
app = QApplication(sys.argv)
window = OrOh()
window.show()
app.exec_()
Since the keys can use more than one character, a possible solution is to use the textChanged(text) signal and call the function to compute the shifts afterwards.
The fact, though, is that the two existing functions are actually flawed: they almost do the same thing, but don't consider the two fields as they should.
I suggest you to merge those functions and use the textChanged to "reset" the value only when a valid key is entered. Note that this implies a logic that is not immediate to understand (even for the user) and might also cause some confusion
class OrOh(QWidget, oroh):
def __init__(self):
QWidget.__init__(self)
self.setupUi(self)
self.shifts = {"m":7, "s":7, "v":7, "t":7, "e":3, "le":5, "ld":12,
"n":12, "ln":14, "mn":19, "en":17, "me":10, "f":24}
self.lin_sh1.returnPressed.connect(self.computeShift)
self.lin_sh2.returnPressed.connect(self.computeShift)
self.lin_sh1.textChanged.connect(self.checkText)
self.lin_sh2.textChanged.connect(self.checkText)
def checkText(self):
lin1 = self.lin_sh1.text()
lin2 = self.lin_sh2.text()
if self.lbl_shifts.text() and (not lin1 or not lin2):
self.computeShift()
def computeShift(self):
lin1 = self.lin_sh1.text()
lin2 = self.lin_sh2.text()
if ((lin1 in self.shifts and not lin2) or
(lin2 in self.shifts and not lin1) or
(lin1 in self.shifts and lin2 in self.shifts)):
shift = self.shifts.get(lin1, 0) + self.shifts.get(lin2, 0)
self.lbl_shifts.setText(str(shift))
I would suggest you to add some validation logic instead, or at least always use the textChanged signal. A simple solution would be to change the text color whenever the text entered is not in the dictionary, and eventually clear it when the focus changes:
class OrOh(QWidget, oroh):
def __init__(self):
QWidget.__init__(self)
self.setupUi(self)
self.shifts = {"m":7, "s":7, "v":7, "t":7, "e":3, "le":5, "ld":12,
"n":12, "ln":14, "mn":19, "en":17, "me":10, "f":24}
self.lin_sh1.textChanged.connect(self.computeShift)
self.lin_sh2.textChanged.connect(self.computeShift)
self.lin_sh1.editingFinished.connect(lambda: self.checkField(self.lin_sh1))
self.lin_sh2.editingFinished.connect(lambda: self.checkField(self.lin_sh2))
def checkField(self, field):
if not field.text() in self.shifts:
field.setText('')
def computeShift(self):
shift = 0
for field in (self.lin_sh1, self.lin_sh2):
value = self.shifts.get(field.text(), 0)
if field.text() and not value:
field.setStyleSheet('color: red;')
else:
field.setStyleSheet('')
shift += self.shifts.get(field.text(), 0)
self.lbl_shifts.setText(str(shift))
Another possibility would be to use an editable QComboBox (with the insert policy set to NoInsert) and check the shift only when the inserted text is in the list.

PyQt won't display my answer as a float

I am trying to make a simple calculator that will take 2 numbers that a user enters and multiplies them together. However, when I set the ans_string as a float the code doesn't work but if I set it as a str it works.
I want to be able to input numbers with decimal places and only display 2 decimal places on the answer.
If I run the code with ans_str I get the following error "AtrributeError: 'QLabel' object has no attribute 'setT'
from PyQt5.QtWidgets import QMainWindow, QWidget, QApplication
from ui_multform import Ui_CalcWindow
class MainWindow(QMainWindow, Ui_CalcWindow):
def __init__(self, parent = None):
super(MainWindow, self).__init__(parent)
self.ui = Ui_CalcWindow()
self.ui.setupUi(self)
self.ui.pushButton.clicked.connect(self.mult)
def mult(self, value):
in_num1 = float(self.ui.lineEdit.text())
in_num2 = float(self.ui.lineEdit_2.text())
ans = in_num1 * in_num2
ans_string = float(ans) # << here ans_string is created
print (ans_string) # << Where u need to put it
self.ui.label_2.setText(ans_string)
if __name__ == '__main__':
import sys
app = QApplication(sys.argv) # A new instance of QApplication
calculator = MainWindow() # We set the form to be our MainWindow (design)
calculator.show() # Show the form
sys.exit(app.exec_())
You just need to convert the float to a string, and apply the formatting that you want to use to display the correct number of decimals. You can use the python string formatting mini-language to convert the float to a string, and also only display two decimal places.
ans_string = '{0:0.2f}'.format(ans)
self.ui.label_2.setText(ans_string)

PyQt4 QDialog connections not being made

I am working on an application using PyQt4 and the designer it provides. I have a main window application that works fine, but I wanted to create custom message dialogs. I designed a dialog and set up some custom signal/slot connections in the __init__ method and wrote an if __name__=='__main__': and had a test. The custom slots work fine. However, when I create an instance of my dialog from my main window application, none of the buttons work. Here is my dialog:
from PyQt4.QtGui import *
from PyQt4.QtCore import *
import sys
import encode_dialog_ui
# Ui_EncodeDialog is the python class generated by pyuic4 from the Designer
class EncodeDialog(encode_dialog_ui.Ui_EncodeDialog):
def __init__(self, parent, in_org_im, txt_file, in_enc_im):
self.qd = QDialog(parent)
self.setupUi(self.qd)
self.qd.show()
self.message = (txt_file.split("/")[-1] + " encoded into " +
in_org_im.split("/")[-1] + " and written to " +
in_enc_im.split("/")[-1] + ".")
QObject.connect(self.view_image_button, SIGNAL("clicked()"),
self.on_view_image_button_press)
self.org_im = in_org_im
self.enc_im = in_enc_im
self.encoded_label.setText(self.message)
def on_view_image_button_press(self):
print "hello world"
if __name__ == '__main__':
app = QApplication(sys.argv)
tmp = QMainWindow()
myg = EncodeDialog(tmp,'flower2.png','b','flower.png')
app.exec_()
If I run this class it works fine, and pressing the view_image_button prints hello world to the console. However when I use the call
#self.mw is a QMainWindow, the rest are strings
EncodeDialog(self.mw, self.encode_image_filename,
self.encode_txt_filename,
self.encode_new_image_filename)
in my main window class, the dialog displays correctly but the view_image_button does nothing when clicked. I have googled for a solution, but couldn't find anything useful. Let me know if you need any more information. Any help on this would be appreciated!
As requested below is some more code from my main window class for brevity's sake I have added ellipses to remove code that seemed irrelevant. If no one can think of anything still, I will add more. (If indenting is a little off, it happened in copy-pasting. The orignal code is correct)
class MyGUI(MainWindow.Ui_MainWindow):
def __init__(self):
self.mw = QMainWindow()
self.setupUi(self.mw)
self.mw.show()
self.encode_red_bits = 1
self.encode_blue_bits = 1
self.encode_green_bits = 1
self.decode_red_bits = 1
self.decode_blue_bits = 1
self.decode_green_bits = 1
self.encode_image_filename = ""
self.encode_new_image_filename = ""
self.encode_txt_filename = ""
self.decode_image_filename = ""
self.decode_txt_filename = ""
# Encode events
...
QObject.connect(self.encode_button, SIGNAL("clicked()"),
self.on_encode_button_press)
# Decode events
...
# Encode event handlers
...
def on_encode_button_press(self):
tmp = QErrorMessage(self.mw)
if (self.encode_image_filename != "" and
self.encode_new_image_filename != "" and
self.encode_txt_filename != ""):
try:
im = Steganography.encode(self.encode_image_filename, self.encode_txt_filename,
self.encode_red_bits, self.encode_green_bits,
self.encode_blue_bits)
im.save(self.encode_new_image_filename)
encode_dialog.EncodeDialog(self.mw, self.encode_image_filename,
self.encode_txt_filename,
self.encode_new_image_filename)
except Steganography.FileTooLargeException:
tmp.showMessage(self.encode_txt_filename.split("/")[-1] +
" is to large to be encoded into " +
self.encode_image_filename.split("/")[-1])
else:
tmp.showMessage("Please specify all filenames.")
# Decode event handlers
...
if __name__ == '__main__':
app = QApplication(sys.argv)
myg = MyGUI()
app.exec_()
It feels like the signal is just not getting passed from the parent down to your child QDIalog.
Try these suggestions:
Use the new method for connecting signals
Instead of extending the classes pyuic created, extend the actual QT classes and call the ones generated by pyuic
Your new code will look something like this:
class MyGUI(QMainWindow):
def __init__(self, parent=None):
QMainWindow.__init__(self, parent)
self.mw = MainWindow.Ui_MainWindow()
self.mw.setupUi(self)
self.mw.show()
...
self.encode_button.clicked.connect(self.on_encode_button_press)
...
class EncodeDialog(QDialog):
def __init__(self, parent, in_org_im, txt_file, in_enc_im):
QDialog.__init__(self, parent)
self.qd = encode_dialog_ui.Ui_EncodeDialog()
self.qd.setupUi(self)
self.qd.show()
...
self.view_image_button.clicked.connect(self.on_view_image_button_press)
...

Finding checked QRadioButton among many into a QVBoxLayout

I used the code below to dynamically create a group of radio buttons:
self.wPaymantType.qgbSomeSelectionGroup = QtGui.QGroupBox()
vbox = QtGui.QVBoxLayout()
for row in listOfChoices:
radio = QtGui.QRadioButton(row)
if bIsFirst:
radio.setChecked(True)
bIsFirst = False
if len(row.name) > nMaxLen:
nMaxLen = len(row.name)
vbox.addWidget(radio)
self.wPaymantType.qgbSomeSelectionGroup.setLayout(vbox)
How can I iterate through all radio buttons to find out which one is checked?
I tried something like this, but I didn't get anything good from it:
qvbl = self.qgbSomeSelectionGroup.children()[0]
for i in range(0, qvbl.count()):
child = qvbl.itemAt(i)
radio = QtGui.QRadioButton(child.widget())
if radio != None:
if radio.isChecked():
print "radio button num " + str(i) + " is checked"
Your code is not minimal and self-contained, so it's really hard to help you -- but I've anyway gone through the effort of building a near-minimal self-contained approximation of what you're trying to do and which does seem to work correctly -- here comes...:
from PyQt4 import QtGui
import sys
class MainWindow(QtGui.QMainWindow):
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent)
self.dowid()
self.setCentralWidget(self.thewid)
def dowid(self):
self.thewid = QtGui.QGroupBox()
vbox = QtGui.QVBoxLayout()
self.radiobuttons = []
listOfChoices = 'one two three'.split()
for i, row in enumerate(listOfChoices):
radio = QtGui.QRadioButton(row)
if i == 0:
radio.setChecked(True)
self.radiobuttons.append(radio)
vbox.addWidget(radio)
self.thewid.setLayout(vbox)
def examine(self):
for i, radio in enumerate(self.radiobuttons):
if radio.isChecked():
print "radio button num " + str(i) + " is checked"
else:
print "radio button num " + str(i) + " is NOT checked"
if __name__ == '__main__':
app = QtGui.QApplication([])
mainWin = MainWindow()
mainWin.show()
rc = app.exec_()
mainWin.examine()
This seems to do what you want. Key change is to keep the actual Python widget objects around rather than trying to restore them from the layout vbox -- that attempt seems to not be working as intended, at least as regards correct access to the crucial detail about whether a given radio button is checked or not, which is of course the heart of your Q.
I believe the reason why it's not working is your
radio = QtGui.QRadioButton(child.widget())
call at the code where you're checking if your checkbox is checked. I think what you're trying to do is typecast the child object to QtGui.QRadioButton and it doesn't work in this case. Instead you should be creating a new widget. Try changing it to smth. like this:
qvbl = self.qgbSomeSelectionGroup.layout()
for i in range(0, qvbl.count()):
widget = qvbl.itemAt(i).widget()
if (widget!=0) and (type(widget) is QtGui.QRadioButton):
if widget.isChecked():
print "radio button num " + str(i) + " is checked"
the code above should be iterating through child objects of the layout object, check their type and print "radio button..." in case it's radio buttong and it's checked
hope this helps, regards
I guess a better way to identify which button is checked is to use a QButtonGroup, as it provides a container to organize groups of button widgets. It is not a visual object, so it doesn't substitute the layout for visually arranging your radio buttons, but it does allow you to make them mutually exclusive and associate an integer "id" with them, letting you know which one is checked without the need to iterate through all the widgets present in the layout.
If you decide to use it, your code should turn into something like this:
self.wPaymantType.qgbSomeSelectionGroup = QtGui.QGroupBox()
vbox = QtGui.QVBoxLayout()
radioGroup = QtGui.QButtonGroup()
radioGroup.setExclusive(True)
for i,row in enumerate(listOfChoices):
radio = QtGui.QRadioButton(row)
radioGroup.addButton(radio, i)
if bIsFirst:
radio.setChecked(True)
bIsFirst = False
if len(row.name) > nMaxLen:
nMaxLen = len(row.name)
vbox.addWidget(radio)
self.wPaymantType.qgbSomeSelectionGroup.setLayout(vbox)
To identify the checked button, you can use QButtonGroup's checkedId method:
buttonId = radioGroup.checkedId()
or if you want to retrive the button object itself you can use the checkedButton method:
button = radioGroup.checkedButton()

Categories

Resources