I'm trying to hide an error label ("*") I have created when a line edit isn't filled in, and then have it disappear when it is filled in, but my program isn't consistent.
An example of it not working:
Leave the three slots empty then press the "Add Student" button - a red asterisk will appear next to each one.
enter anything into the first ("starting date") line edit then press the "Add Student" button - the red asterisk will disappear.
repeat 2 for the first name, but the red asterisk won't disappear.
repeat 2 for the last name, but the red asterisk won't disappear for that one either.
import sys
import datetime
from PyQt5 import QtWidgets as qt, QtGui, QtCore
class AddStudent(qt.QMainWindow):
def __init__(self):
# Set the window to the dimensions of the Screen
super().__init__()
screenSize = qt.QDesktopWidget().screenGeometry(-1)
self.height = screenSize.height()
self.width = screenSize.width()
self.setGeometry(0, 0, self.width, self.height)
self.setStyleSheet("QLabel {font: 16pt}")
# Add the buttons, line edits, and table
self.foundError = False
self.setup()
# Display the GUI
self.setWindowTitle("Add Student")
def setup(self):
self.startingDateLabel()
self.firstNameLabel()
self.lastNameLabel()
self.addStudentButton()
# Button Declarations
def startingDateLabel(self):
self.dateLabel = qt.QLabel(self)
self.dateLabel.setText("Starting Date:")
# (L/R, U/D, L/R, U/D)
self.dateLabel.setGeometry(158, 150, 262, 50)
self.dateLineEdit = qt.QLineEdit(self)
date = str(datetime.date.today()).split("-")
today = date[1] + "/" + date[2] + "/" + date[0]
self.dateLineEdit.setText(today)
self.dateLineEdit.setGeometry(435, 155, 250, 50)
def firstNameLabel(self):
self.firstName = qt.QLabel(self)
self.firstName.setText("First Name:")
self.firstName.setGeometry(205, 250, 215, 50)
self.firstNameLineEdit = qt.QLineEdit(self)
self.firstNameLineEdit.setGeometry(435, 255, 250, 50)
def lastNameLabel(self):
self.lastName = qt.QLabel(self)
self.lastName.setText("Last Name:")
self.lastName.setGeometry(208, 350, 212, 50)
self.lastNameLineEdit = qt.QLineEdit(self)
self.lastNameLineEdit.setGeometry(435, 355, 250, 50)
def addStudentButton(self):
self.addStudent = qt.QPushButton(self)
self.addStudent.setText("Add Student")
self.addStudent.setGeometry(800, 1500, 150, 50)
self.addStudent.clicked.connect(self.addStudentButtonPressed)
self.addStudent.show()
def addStudentButtonPressed(self):
# Check to make sure that everything that needs to be filled out is filled out
self.errorFound = False
# Check the starting date
if (self.dateLineEdit.text() == ""):
self.error1 = qt.QLabel(self)
self.error1.setText("*")
self.error1.setStyleSheet('color: red')
self.error1.setGeometry(715, 155, 30, 50)
self.error1.show()
self.errorFound = True
else:
try:
self.error1.hide()
self.errorFound = False
except:
self.errorFound = False
# Check the first name slot
if (self.firstNameLineEdit.text() == ""):
self.error2 = qt.QLabel(self)
self.error2.setText("*")
self.error2.setStyleSheet('color: red')
self.error2.setGeometry(715, 255, 30, 50)
self.error2.show()
self.errorFound = True
else:
try:
self.error2.hide()
self.errorFound = False
except:
self.errorFound = False
# Check the last name slot
if (self.lastNameLineEdit.text() == ""):
self.error3 = qt.QLabel(self)
self.error3.setText("*")
self.error3.setStyleSheet('color: red')
self.error3.setGeometry(715, 355, 30, 50)
self.error3.show()
self.errorFound = True
else:
try:
self.error3.hide()
self.errorFound = False
except:
self.errorFound = False
# Run the window
app = qt.QApplication(sys.argv)
window = AddStudent()
window.show()
sys.exit(app.exec())
I made the following changes to your code:
Just a code style thing: I used snake case for the variable and function names. This is the standard for python code, with PyQt being an odd exception (likely due to inheriting from a C++ code base).
I removed the use of self.errorFound. As I hinted in the comments, you never really check the value of this anywhere. While it might be useful to have a single self.errorFound if you just wanted to know if any of the user inputs were wrong, a single boolean couldn't tell you which input was wrong. I just simply check if the text for the user input is empty every time the submit function is called.
I used the QFormLayout class to position the widgets on the screen. This removes all of the guesswork and math of typing in absolute coordinates of where a widget should belong. It also prevents widgets from "disappearing" if the window is too small. This comes with the limitation that each row can only hold two widgets. For this reason, I ditched the asterisk in favor of just coloring the QLabel for the corresponding wrong input. This can be worked around with QGridLayout or, if you like the QFormLayout, with nested QHBoxLayout objects per each field. Let me know if the asterisk is needed.
With all that out of the way, here is updated code:
import sys
import datetime
from PyQt5 import QtWidgets as qt, QtGui, QtCore
class MyApp(qt.QMainWindow):
def __init__(self):
super().__init__()
screenSize = qt.QDesktopWidget().screenGeometry(-1)
self.setup()
self.setWindowTitle("My Awesome App!")
def setup(self):
self.form = qt.QGroupBox("Add Student")
self.form_layout = qt.QFormLayout()
self.create_date_input()
self.create_first_name_input()
self.create_last_name_input()
self.create_submit_button()
self.form.setLayout(self.form_layout)
self.setContentsMargins(5, 5, 5, 5)
self.setCentralWidget(self.form)
def create_date_input(self):
self.date_label = qt.QLabel("Starting Date:")
self.date_entry = qt.QLineEdit(self)
date = str(datetime.date.today()).split("-")
today = date[1] + "/" + date[2] + "/" + date[0]
self.date_entry.setText(today)
self.form_layout.addRow(
self.date_label,
self.date_entry,
)
def create_first_name_input(self):
self.first_name_label = qt.QLabel("First Name:")
self.first_name_entry = qt.QLineEdit(self)
self.form_layout.addRow(
self.first_name_label,
self.first_name_entry,
)
def create_last_name_input(self):
self.last_name_label = qt.QLabel("Last Name:")
self.last_name_entry = qt.QLineEdit(self)
self.form_layout.addRow(
self.last_name_label,
self.last_name_entry,
)
def create_submit_button(self):
self.submit_button = qt.QPushButton(self)
self.submit_button.setText("Add Student")
self.submit_button.clicked.connect(self.submit)
self.form_layout.addRow(self.submit_button)
def submit(self):
default_color = "color: black"
error_color = "color: red"
date_color = default_color
first_name_color = default_color
last_name_color = default_color
#really should validate if this is a date, not just if it is empty
if (self.date_entry.text() == ""):
date_color = error_color
if (self.first_name_entry.text() == ""):
first_name_color = error_color
if (self.last_name_entry.text() == ""):
last_name_color = error_color
self.date_label.setStyleSheet(date_color)
self.first_name_label.setStyleSheet(first_name_color)
self.last_name_label.setStyleSheet(last_name_color)
app = qt.QApplication(sys.argv)
window = MyApp()
window.show()
sys.exit(app.exec())
Here's another possible solution; while it might seem a bit more complicated than the other one proposed by CodeSurgeon, it is much more extensible to my opinion - and somehow a bit more elegant.
The catch here is the subclass of QLineEdit that embeds a child QLabel, which is made visible only if there's text in the field. The resizeEvent override has been added to ensure that the "*" label is always correctly placed whenever the window is resized.
There are many advantages to this approach, but the most important is that subclassing the "main" widget, instead of creating a new one that embeds other widgets, automatically exposes all of its methods and properties.
So, all of this allows you to add as many [text] fields as you want and enables/disables the submit button only if every one of them has some content in it.
import sys
from PyQt5 import QtCore, QtWidgets
class MyLineEdit(QtWidgets.QLineEdit):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
# the "invalid" label *has* to be a child of this QLineEdit
self.invalidLabel = QtWidgets.QLabel('*', self)
self.invalidLabel.setStyleSheet('color: red;')
spacing = 2
# get default margins and re-set them accordingly
self.leftMargin, self.topMargin, self.rightMargin, self.bottomMargin = self.getContentsMargins()
self.rightMargin += self.invalidLabel.minimumSizeHint().width() + spacing
self.setContentsMargins(self.leftMargin, self.topMargin, self.rightMargin + spacing, self.bottomMargin)
self.textChanged.connect(self.setValid)
self.setValid(self.text())
def setValid(self, text):
self.invalidLabel.setVisible(not bool(text))
def isValid(self):
return bool(self.text())
def resizeEvent(self, event):
self.invalidLabel.setGeometry(self.width() - self.rightMargin, self.topMargin,
self.invalidLabel.minimumSizeHint().width(), self.height() - self.topMargin - self.bottomMargin)
class MyApp(QtWidgets.QMainWindow):
def __init__(self):
super().__init__()
self.setup()
def setup(self):
self.form = QtWidgets.QGroupBox("Add Student")
self.setCentralWidget(self.form)
self.formLayout = QtWidgets.QFormLayout()
self.form.setLayout(self.formLayout)
self.fields = []
self.create_field('Starting Date:', defaultValue=QtCore.QDate.currentDate().toString('MM/dd/yyyy'))
self.create_field('First Name:')
self.create_field('Last Name:')
self.create_submit_button()
def create_field(self, label, defaultValue=''):
field = MyLineEdit(defaultValue)
self.fields.append(field)
field.defaultValue = field.text()
self.formLayout.addRow(label, field)
field.textChanged.connect(self.checkFields)
def create_submit_button(self):
self.submitButton = QtWidgets.QPushButton('Add Student')
self.formLayout.addRow(self.submitButton)
self.submitButton.clicked.connect(self.submit)
self.submitButton.setEnabled(False)
self.checkFields()
def checkFields(self):
self.submitButton.setEnabled(all(field.isValid() for field in self.fields))
def submit(self):
# doSomething([field.text() for field in self.fields)
for field in self.fields:
field.setText(field.defaultValue)
app = QtWidgets.QApplication(sys.argv)
w = MyApp()
w.show()
sys.exit(app.exec_())
As already pointed out, you should really think about validating the date contents (as one could enter anything there) since you are using a line edit.
The better solution is to use a QDateEdit, but you'll need to subclass it as I did with QLineEdit and create a separate create function, and obviously it will be "ignored" in the submit "validation" as QDateEdit fields automatically allows valid date inputs.
Otherwise you could use a QValidator, but it would be the proverbial pain in the a**, mainly because QDateTime already provides validation and a nice calendar view by using setCalendarPopup(True), and validating a date through regular expression is really annoying.
Related
I have a QLineEdit widget to enter text (a simple search pattern) that can include spaces.
You can't see the space characters, this is especially obvious for trailing spaces. I know you can set option ShowTabsAndSpaces by calling method setDefaultTextOption on the document of a QTextEdit widget, but is there a way to set this option (or something similar) on a QLineEdit widget?
Method setDefaultTextOption is not available for QLineEdit widgets, so I tried:
item = QLineEdit(value)
option = QTextOption()
option.setFlags(QTextOption.ShowTabsAndSpaces)
item.initStyleOption(option)
But that gives me an exception:
<class 'TypeError'>:
'PySide2.QtWidgets.QLineEdit.initStyleOption' called with wrong argument types:
PySide2.QtWidgets.QLineEdit.initStyleOption(PySide2.QtGui.QTextOption)
Supported signatures:
PySide2.QtWidgets.QLineEdit.initStyleOption(PySide2.QtWidgets.QStyleOptionFrame)
How do I set option ShowTabsAndSpaces on a QLineEdit widget or is there another way to make space characters visible in a QLineEdit widget. I would be happy with only space characters that are visible.
Used the suggestion of #ekhumoro to port https://stackoverflow.com/a/56298439/4459346
This resulted in:
#!/usr/bin/env python3
import sys
import time
from PySide2.QtWidgets import (QLineEdit, QPushButton, QApplication,
QVBoxLayout, QDialog, QMessageBox, QPlainTextEdit, QGridLayout)
from PySide2.QtCore import (Qt, QMimeData)
from PySide2.QtGui import (QTextOption, QFontMetrics)
# source: https://stackoverflow.com/a/56298439/4459346
class SpacesLineEdit(QPlainTextEdit):
def __init__(self, text=None):
if text:
super().__init__(text)
else:
super().__init__()
self.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
self.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
self.setLineWrapMode(QPlainTextEdit.NoWrap)
self.setTabChangesFocus(True)
option = QTextOption()
option.setFlags(QTextOption.ShowTabsAndSpaces)
self.document().setDefaultTextOption(option)
# limit height to one line
self.setHeight(1)
# Stealing the sizeHint from a plain QLineEdit will do for now :-P
self._sizeHint = QLineEdit().sizeHint()
def minimumSizeHint(self):
return self._sizeHint
def sizeHint(self):
return self._sizeHint
def keyPressEvent(self, event):
if event.key() == Qt.Key_Return or event.key() == Qt.Key_Enter:
event.ignore()
return
super().keyPressEvent(event)
def insertFromMimeData(self, source):
text = source.text()
text = text.replace(str('\r\n'), str(' '))
text = text.replace(str('\n'), str(' '))
text = text.replace(str('\r'), str(' '))
processedSource = QMimeData()
processedSource.setText(text)
super().insertFromMimeData(processedSource)
def setText(self, text):
self.setPlainText(text)
def text(self):
return self.toPlainText()
def setHeight1(self):
# see
doc = self.document()
b = doc.begin()
layout = doc.documentLayout()
h = layout.blockBoundingRect(b).height()
self.setFixedHeight(h + 2 * self.frameWidth() + 1)
def setHeight(self, nRows):
# source: https://stackoverflow.com/a/46997337/4459346
pdoc = self.document()
fm = QFontMetrics(pdoc.defaultFont())
lineSpacing = fm.lineSpacing()
print(lineSpacing) # todo:test
margins = self.contentsMargins()
nHeight = lineSpacing * nRows + \
(pdoc.documentMargin() + self.frameWidth()) * 2 + \
margins.top() + margins.bottom()
self.setFixedHeight(nHeight)
class Form(QDialog):
def __init__(self, parent=None):
super(Form, self).__init__(parent)
demoText = " some text with spaces "
self.lineedit0 = QLineEdit(demoText)
self.lineedit1 = SpacesLineEdit(demoText)
self.lineedit2 = QLineEdit(demoText)
self.lineedit3 = QLineEdit(demoText)
layout = QGridLayout()
layout.addWidget(self.lineedit0, 0, 0)
layout.addWidget(self.lineedit1, 1, 0)
layout.addWidget(self.lineedit2, 0, 1)
layout.addWidget(self.lineedit3, 1, 1)
self.setLayout(layout)
if __name__ == '__main__':
app = QApplication(sys.argv)
form = Form()
print('starting app...')
form.show()
sys.exit(app.exec_())
The spaces are now shown, just as I wanted.
The only thing I had to add was a method to set the height. See method setHeight, which I copied from https://stackoverflow.com/a/46997337/4459346.
The original method tries to calculate the exact line height, but seems it doesn't get the height exactly right:
The one on the bottom-left is the QPlainTextEdit, the other three are just qlineEdit widgets.
I can fix the incorrect height by subtracting a few pixels, but I rather not do that without knowing what I'm doing.
Anyone, any suggestions to improve the code in method setHeight?
In the list_widget I have added a add button I also want to add a remove button which asks which item you wants to remove and remove the chosen item. I was trying it to do but I didn't had any idea to do so .Also, please explain the solution I am a beginner with pyqt5 or I'd like to say absolute beginner.
from PyQt5 import QtWidgets
from PyQt5.QtWidgets import QApplication,QMainWindow,
QListWidget, QListWidgetItem
import sys
class MyWindow(QMainWindow):
def __init__(self):
super(MyWindow, self).__init__()
self.x = 200
self.y = 200
self.width = 500
self.length = 500
self.setGeometry(self.x, self.y, self.width,
self.length)
self.setWindowTitle("Stock managment")
self.iniTUI()
def iniTUI(self):
# buttons
self.b1 = QtWidgets.QPushButton(self)
self.b1.setText("+")
self.b1.move(450, 100)
self.b1.resize(50, 25)
self.b1.clicked.connect(self.take_inputs)
# This is the button I want to define.
self.btn_minus = QtWidgets.QPushButton(self)
self.btn_minus.setText("-")
self.btn_minus.move(0, 100)
self.btn_minus.resize(50, 25)
# list
self.list_widget = QListWidget(self)
self.list_widget.setGeometry(120, 100, 250, 300)
self.item1 = QListWidgetItem("A")
self.item2 = QListWidgetItem("B")
self.item3 = QListWidgetItem("C")
self.list_widget.addItem(self.item1)
self.list_widget.addItem(self.item2)
self.list_widget.addItem(self.item3)
self.list_widget.setCurrentItem(self.item2)
def take_inputs(self):
self.name, self.done1 =
QtWidgets.QInputDialog.getText(
self, 'Add Item to List', 'Enter The Item you want
in
the list:')
self.roll, self.done2 = QtWidgets.QInputDialog.getInt(
self, f'Quantity of {str(self.name)}', f'Enter
Quantity of {str(self.name)}:')
if self.done1 and self.done2:
self.item4 = QListWidgetItem(f"{str(self.name)}
Quantity{self.roll}")
self.list_widget.addItem(self.item4)
self.list_widget.setCurrentItem(self.item4)
def clicked(self):
self.label.setText("You clicked the button")
self.update()
def update(self):
self.label.adjustSize()
def clicked():
print("meow")
def window():
apk = QApplication(sys.argv)
win = MyWindow()
win.show()
sys.exit(apk.exec_())
window()
The core issue here is the lack of separation of the view and the data. This makes it very hard to reason about how to work with graphical elements. You will almost certainly want to follow the Model View Controller design paradigm https://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93controller
which offers a systematic way to handle this separation.
Once you do so, it immediately becomes very straight forward how to proceed with the question: You essentially just have a list, and you either want to add a thing to this list, or remove one based on a selection.
I include an example here which happens to use the built-in classes QStringListModel and QListView in Qt5, but it is simple to write your own more specialized widgets and models. They all just use a simple signal to emit to the view that it needs to refresh the active information.
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
import sys
class StuffViewer(QMainWindow):
def __init__(self, model):
super().__init__()
self.setWindowTitle("Stock managment")
# 1: Use layouts.
hbox = QHBoxLayout()
widget = QWidget()
widget.setLayout(hbox)
self.setCentralWidget(widget)
# 2: Don't needlessly store things in "self"
vbox = QVBoxLayout()
add = QPushButton("+")
add.clicked.connect(self.add_new_stuff)
vbox.addWidget(add)
sub = QPushButton("-")
sub.clicked.connect(self.remove_selected_stuff)
vbox.addWidget(sub)
vbox.addStretch(1)
hbox.addLayout(vbox)
# 3: Separate the view of the data from the data itself. Use Model-View-Controller design to achieve this.
self.model = model
self.stuffview = QListView()
self.stuffview.setModel(self.model)
hbox.addWidget(self.stuffview)
def add_new_stuff(self):
new_stuff, success = QInputDialog.getText(self, 'Add stuff', 'Enter new stuff you want')
if success:
self.stuff.setStringList(self.stuff.stringList() + [new_stuff])
def remove_selected_stuff(self):
index = self.stuffview.currentIndex()
all_stuff = self.stuff.stringList()
del all_stuff[index.column()]
self.stuff.setStringList(all_stuff)
def window():
apk = QApplication(sys.argv)
# Data is clearly separated:
# 4: Never enumerate variables! Use lists!
stuff = QStringListModel(["Foo", "Bar", "Baz"])
# The graphical components is just how you interface with the data with the user!
win = StuffViewer(stuff)
win.show()
sys.exit(apk.exec_())
window()
I have a QMainWindow, inside there is a QMenu, QLineEdit, and one QPushButton.
Every time I click the button, it plays a sound and then adds a text to the QLineEdit.
In my QMenu the user must be able to choose which sound plays by checking it.
I tried to achieve this by changing a variable self.s inside the MainWindow class every time a QAction is checked, meanwhile, the other QAction's are unchecked. So in my playsound() I just put the self.view.s as the argument.
But it seems that it's only reading the original self.view.s, which is the first sound. My signals to change self.view.s does not work. Also, the other QActions aren't unchecked as I wanted them to.
Below is my code:
import sys
from functools import partial
from playsound import playsound
from threading import Thread
from PyQt6.QtCore import *
from PyQt6.QtGui import *
from PyQt6.QtWidgets import *
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
self.buttons = {}
self.setWindowTitle("Try")
central_widget = QWidget()
self.setCentralWidget(central_widget)
self.lay = QVBoxLayout(central_widget)
self.lineedit()
button = {"HEY! ": (0, 0, 0, 0)}
page = QWidget()
layout = QGridLayout(page)
for btnText, pos in button.items():
self.buttons[btnText] = QPushButton(btnText)
layout.addWidget(self.buttons[btnText], *pos)
self.lay.addWidget(page)
self.music()
def music(self):
self.s = 'sound1.mp3'
self.x = 'sound1.mp3'
self.y = 'sound2.mp3'
self.z = 'disable.mp3'
def lineedit(self):
self.le = QLineEdit()
self.le.setFixedHeight(35)
self.lay.addWidget(self.le)
def set_lineedit(self, text):
self.le.setText(text)
self.le.setFocus()
def line(self):
return self.le.text()
class Menu:
def __init__(self, MainWindow):
super().__init__()
self.view = MainWindow
self.menuBar()
#self.actionSignals()
def menuBar(self):
self.menuBar = QMenuBar()
self.view.setMenuBar(self.menuBar)
self.menu = QMenu(self.menuBar)
self.menu.setTitle('Menu')
self.sounds = QMenu(self.menu)
self.sounds.setTitle('Select Sound')
self.sound1 = QAction(self.menuBar)
self.sound2 = QAction(self.menuBar)
self.disable = QAction(self.menuBar)
self.mute = QAction(self.menuBar)
self.mute.setText('Mute Background')
self.mute.setCheckable(True)
self.mute.setChecked(False)
self.sound1.setText('Sound 1')
self.sound1.setCheckable(True)
self.sound1.setChecked(True)
self.sound2.setText('Sound 2')
self.sound2.setCheckable(True)
self.sound2.setChecked(False)
self.disable.setText('Disable Sound')
self.disable.setCheckable(True)
self.disable.setChecked(False)
self.sounds.addAction(self.sound1)
self.sounds.addAction(self.sound2)
self.sounds.addAction(self.disable)
self.menuBar.addAction(self.menu.menuAction())
self.menu.addAction(self.mute)
self.menu.addAction(self.sounds.menuAction())
def menu_signals(self):
self.sound1.triggered.connect(self.sound_1)
self.sound2.triggered.connect(self.sound_2)
self.disable.triggered.connect(self.disabled)
def sound_1(self, checked):
if checked:
self.sound2.setChecked(False)
self.disable.setChecked(False)
self.view.s = self.view.x
else:
self.sound1.setChecked(True)
def sound_2(self, checked):
if checked:
self.sound1.setChecked(False)
self.disable.setChecked(False)
self.view.s = self.view.y
else:
self.sound2.setChecked(True)
def disabled(self, checked):
if checked:
self.sound2.setChecked(False)
self.sound1.setChecked(False)
self.view.s = self.view.z
else:
self.sound1.setChecked(True)
class Controller:
def __init__(self, MainWindow):
self.view = MainWindow
self.connectSignals()
def background(self):
while True:
playsound('background.mp3')
def playsound(self):
playsound(self.view.s, False)
def buildExpression(self, sub_exp):
expression = self.view.line() + sub_exp
self.view.set_lineedit(expression)
def connectSignals(self):
for btnText, btn in self.view.buttons.items():
self.view.buttons[btnText].clicked.connect(self.playsound)
self.view.buttons[btnText].clicked.connect(partial(self.buildExpression, btnText))
app = QApplication(sys.argv)
w = MainWindow()
x = Controller(w)
Thread(target = x.background, daemon = True).start()
m = Menu(w)
w.show()
app.exec()
I want to be able to change the value within playsound() depending on which QAction is checked in the Menu Bar. While one QAction is checked, the other QAction's should be unchecked.
This is where an action group comes into play. QActionGroup allows for mutually exclusive actions. It also provides convenient access to the selected action through the checkedAction method.
Create a QActionGroup object (e.g. self.soundGroup = QActionGroup(self))
Create your actions with the group as parent (e.g. self.sound1 = QAction(self.soundGroup))
For each of your actions, set their corresponding sound as their data, e.g. self.sound1.setData('sound1.mp3')
Ensure the action group is exclusive (I believe it's the default, but you may use self.soundGroup.setExclusive(True))
Use self.soundGroup.checkedAction() to get the checked action (selected sound) instead of self.view.s: playsound(self.soundGroup.checkedAction().data(), False)
You do not need any of your wiring between the actions and updates to self.view.s anymore. Just remove all of that.
Currently, I am working on a program with python and PYQT5 where user inputs have to be only numbers. The problem is that I have no idea how to do it. For example when I get this variable
VAR_1=float(self.ui.lineEdit.text())
I need that the text entered is only a number. i mean when the user is trying to write a letter o symbol nothing happens.
Use/Try setValidator or setInputMask methods
import sys
from PyQt5.QtWidgets import *
from PyQt5.QtGui import *
from PyQt5.QtCore import *
class ButtonName(QWidget):
def __init__(self):
super().__init__()
self.setWindowTitle("Example - Validator")
self.setGeometry(800, 200, 200, 200)
self.UI()
self.layouts()
self.show()
def UI(self):
self.lbl_integer = QLabel("Integer Validator")
self.textbox_integervalidator = QLineEdit()
self.textbox_integervalidator.setPlaceholderText("upto 3 digit value only accept")
self.textbox_integervalidator.setValidator(QIntValidator(1, 999, self))
self.lbl_double = QLabel("Double Validator")
self.textbox_doublevalidator = QLineEdit()
self.textbox_doublevalidator.setValidator(QDoubleValidator(0.99, 99.99, 2))
self.lbl_regexp = QLabel("RexExp Validator")
self.textbox_regexpvalidator = QLineEdit()
reg_ex_1 = QRegExp("[0-9]+.?[0-9]{,2}") # double
# reg_ex_2 = QRegExp("[0-9]{1,5}") # minimum 1 integer number to maxiumu 5 integer number
# reg_ex_3 = QRegExp("-?\\d{1,3}") # accept negative number also
# reg_ex_4 = QRegExp("")
self.textbox_regexpvalidator.setValidator(QRegExpValidator(reg_ex_1))
def layouts(self):
mainlayout = QVBoxLayout()
mainlayout.addWidget(self.lbl_integer)
mainlayout.addWidget(self.textbox_integervalidator)
mainlayout.addWidget(self.lbl_double)
mainlayout.addWidget(self.textbox_doublevalidator)
mainlayout.addWidget(self.lbl_regexp)
mainlayout.addWidget(self.textbox_regexpvalidator)
self.setLayout(mainlayout)
def main():
app = QApplication(sys.argv)
mainwindow = ButtonName()
sys.exit(app.exec_())
if __name__ == "__main__":
main()
try make a mask for your line edit:
self.lineEdit.setInputMask("00")
When you put "0" in Mask, the code will understand that it can only be numbers. The amount of zero placed will be the amount of houses that the lineedit will accept. In this case how and "00" the lineEdit accepted two houses (the ten's houses)
You can try it like this
self.ui.lineEdit.textChanged.connect(self.accept_only_numbers)
def accept_only_numbers(self):
VAR_1=float(self.ui.lineEdit.text())
if VAR_1.isnumeric() == True:
print("ok is number")
else:
VAR_1.clear()
Does anyone know examples about how to change a qMenu styles of lines separately ( change color of text of line , color of line bg, add underline to any texts inside texts etc. ) or if can't be done , can be solved anyhow ?
Thanks,
Szabolcs
share my code:
class MainWindow(QtGui.QMainWindow):
def init(self):
super(MainWindow, self).init()
self.menus = ['alma','korte','banan','ezmegaz']
acts = []
self.qmenu = QtGui.QMenu()
self.hip_fgrp = HipFileGroup( hip_data_file )
self.hip_fgrp.RemoveRepeats()
for i,hipf in enumerate(self.hip_fgrp.hipFileArr):
short_n = hipf.shortname
# prj = hipf.shortprjname
prj = ''
prj = hipf.shortprjname
if len(hipf.add_hipfolders):
prj = prj + ' \\ ' + hipf.add_hipfolders[0]
action = QtGui.QAction( prj+' \\ '+short_n, self, triggered=self.MenuSelected)
action.setData( i)
acts.append( action)
# print short_n
mpos = QtGui.QCursor
x = mpos.pos().x()
y = mpos.pos().y()
for action in acts:
self.qmenu.addAction(action)
self.qmenu.show()
self.qmenu.setGeometry( x-20, y-20, 0, 0)
self.qmenu.exec_()
def MenuSelected( self):
action = self.sender()
hipfile_id = action.data()
hipfile = self.hip_fgrp.hipFileArr[ hipfile_id]
hipfile.show_all()
hipfile_last = hipfile.getLastVersion( hipfile.hipfullspec)
print hipfile_last
if not in_sublime:
import hou
hou.hipFile.load( hipfile_last, hip_accept)
I don't know of any easy way. And it seems to be a long-standing question. But almost anything is possible with a bit of work:
Rather than using a QAction in your menu you can use a QWidgetAction which lets you customise the widget used to represent the action in the menu. Here I use a QLabel which supports rich text. However, bear in mind that the widget needs to handle the mouse itself (here I call trigger).
import sys
from PySide import QtGui
class MyLabel(QtGui.QLabel):
def __init__(self,action):
super(MyLabel,self).__init__()
self.action = action
def mouseReleaseEvent(self,e):
self.action.trigger()
class Example(QtGui.QMainWindow):
def __init__(self):
super(Example, self).__init__()
wAction = QtGui.QWidgetAction(self)
ql = MyLabel(wAction)
ql.setText("<b>Hello</b> <i>Qt!</i>")
wAction.setDefaultWidget(ql)
wAction.triggered.connect(self.close)
menubar = self.menuBar()
fileMenu = menubar.addMenu('&File')
fileMenu.addAction(wAction)
self.setGeometry(300, 300, 250, 150)
self.setWindowTitle('Menubar')
self.show()
app = QtGui.QApplication(sys.argv)
ex = Example()
sys.exit(app.exec_())
In a more fully featured example you might subclass QWidgetAction to handle different action contexts, and use different widgets, but this should get you started.