Is there something wrong with the logic in my remove function? - python

My issue is that although I can get the program running correctly the first time, I am unable to edit my userList after I press the roll button.
from PyQt5 import QtWidgets, QtGui, QtCore
from PyQt5.QtWidgets import (QApplication, QMainWindow, QLineEdit, QPushButton, QScrollArea, QHBoxLayout, QCompleter,
QWidget, QVBoxLayout, QLabel, QSpacerItem, QSizePolicy)
from PyQt5.QtGui import *
from PyQt5.QtCore import Qt
import random
import sys
# create widget that will add and remove characters from the roulette pool
class AddRemoveWidget(QWidget):
def __init__(self, name, userList):
super(AddRemoveWidget, self).__init__()
self.name = name
# create a separate list as out roulette pool
self.userList = userList
self.added = False
# set up add and remove buttons
self.label = QLabel(self.name)
self.button_removed = QPushButton("Remove")
self.button_add = QPushButton("Add")
# set up the "box" for interaction
self.hBox = QHBoxLayout()
self.hBox.addWidget(self.label)
self.hBox.addWidget(self.button_removed)
self.hBox.addWidget(self.button_add)
# attribute functions to each button
self.button_add.clicked.connect(self.add)
self.button_removed.clicked.connect(self.remove)
self.setLayout(self.hBox)
self.update_button_state()
def show(self):
for w in [self, self.label, self.button_add, self.button_removed]:
w.setVisible(True)
def hide(self):
for w in [self, self.label, self.button_add, self.button_removed]:
w.setVisible(False)
def add(self, name):
self.added = True
self.userList.append(self.name)
self.update_button_state()
#return self.userList
def remove(self, name):
self.added = False
self.update_button_state()
self.userList.remove(self.name)
#return self.userList
def update_button_state(self):
if self.added == True:
self.button_add.setStyleSheet("background-color: #32CD32; color #fff;")
self.button_removed.setStyleSheet("background-color: none; color: none;")
else:
self.button_add.setStyleSheet("background-color: none; color: none;")
self.button_removed.setStyleSheet("background-color: #D32F2f; color: #fff")
# create main window (think of this as the main())
class MyWindow(QMainWindow):
def __init__(self):
super(MyWindow, self).__init__()
self.initUI()
def addCharToList(self, character):
self.charList.append(character)
def initUI(self):
self.controls = QWidget()
self.controlsLayout = QVBoxLayout()
self.userList = []
# list of elements
charactersAll = # assume generic stuff
self.widgets = []
for name in charactersAll:
item = AddRemoveWidget(name, self.userList)
self.controlsLayout.addWidget(item)
self.widgets.append(item)
spacer = QSpacerItem(1, 1, QSizePolicy.Minimum, QSizePolicy.Expanding)
self.controlsLayout.addItem(spacer)
self.controls.setLayout(self.controlsLayout)
self.setWindowTitle("Rolling")
self.setGeometry(200, 200, 1024, 680)
self.label1 = QtWidgets.QLabel(self)
self.label1.setText("<h1>Roulette</h1>")
self.label1.adjustSize()
self.label1.move(10, 15)
self.label2 = QtWidgets.QLabel(self)
self.label2.setText("Add Characters to Roulette")
self.label2.setFont(QtGui.QFont('Comic_Sans', 15))
self.label2.move(10, 55)
self.label2.adjustSize()
self.label3 = QtWidgets.QLabel(self)
self.label3.setText("<h1>Team 1:</h1>")
self.label3.adjustSize()
self.label3.move(540, 30)
self.label4 = QtWidgets.QLabel(self)
self.label4.setText("")
self.label4.move(540, 70)
self.label5 = QtWidgets.QLabel(self)
self.label5.setText("")
self.label5.move(540, 85)
self.label6 = QtWidgets.QLabel(self)
self.label6.setText("")
self.label6.move(540, 100)
self.label7 = QtWidgets.QLabel(self)
self.label7.setText("<h1>Team 2:</h1>")
self.label7.adjustSize()
self.label7.move(540, 120)
self.label8 = QtWidgets.QLabel(self)
self.label8.setText("")
self.label8.move(540, 145)
self.label9 = QtWidgets.QLabel(self)
self.label9.setText("")
self.label9.move(540, 160)
self.label10 = QtWidgets.QLabel(self)
self.label10.setText("")
self.label10.move(540, 175)
self.scroll = QScrollArea()
self.scroll.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOn)
self.scroll.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
self.scroll.setWidgetResizable(False)
self.scroll.setWidget(self.controls)
self.searchBar = QLineEdit()
self.searchBar.adjustSize()
self.searchBar.textChanged.connect(self.updateDisplay)
self.completer = QCompleter(charactersAll)
self.completer.setCaseSensitivity(Qt.CaseInsensitive)
self.searchBar.setCompleter(self.completer)
container = QWidget()
containerLayout = QVBoxLayout()
containerLayout.addWidget(self.searchBar)
containerLayout.addWidget(self.scroll)
containerLayout.setContentsMargins(10, 85, 0, 0)
container.setLayout(containerLayout)
container.setFixedSize(400, 600)
self.setCentralWidget(container)
self.b1 = QtWidgets.QPushButton(self)
self.b1.setText("Roll")
self.b1.setGeometry(10, 610, 390, 40)
self.b1.clicked.connect(self.RandRoll)
def updateDisplay(self, text):
for widget in self.widgets:
if text.lower() in widget.name.lower():
widget.show()
else:
widget.hide()
def updateText(self):
self.label4.adjustSize()
self.label6.adjustSize()
def RandRoll(self):
#create a separate list to save "names" for rerolls
self.tempList = []
selectedString = random.choice(self.userList)
self.tempList.append(selectedString)
self.label4.setText(selectedString + ", ")
self.userList.remove(selectedString)
selectedString = random.choice(self.userList)
self.tempList.append(selectedString)
self.label5.setText(selectedString + ", ")
self.userList.remove(selectedString)
selectedString = random.choice(self.userList)
self.tempList.append(selectedString)
self.label6.setText(selectedString)
self.userList.remove(selectedString)
selectedString = random.choice(self.userList)
self.tempList.append(selectedString)
self.label8.setText(selectedString + ", ")
self.userList.remove(selectedString)
selectedString = random.choice(self.userList)
self.tempList.append(selectedString)
self.label9.setText(selectedString + ", ")
self.userList.remove(selectedString)
selectedString = random.choice(self.userList)
self.tempList.append(selectedString)
self.label10.setText(selectedString)
self.userList.remove(selectedString)
# add back all "removed" names into userList
self.userList = self.userList + self.tempList
def window():
app = QApplication(sys.argv)
win = MyWindow()
win.show()
sys.exit(app.exec_())
window()
I'm thinking it is a problem with my add and remove functions under the AddRemoveWidget but I can't seem to figure out what's wrong.

There are actually various problems, but the main issue is that you're accessing (and, most importantly, modifying) the same list from objects that should not do it.
This happens here:
# create a separate list as out roulette pool
self.userList = userList
You are not creating a separate list, you're just setting the same list as an instance attribute, but the object is shared amongst the main window instance and all AddRemoveWidget widgets.
Instead of adding and removing items from the AddRemoveWidget, just connect its buttons to the main class and let that manage the contents of the list.
class AddRemoveWidget(QWidget):
def __init__(self, name):
super(AddRemoveWidget, self).__init__()
self.name = name
self.added = False
# ...
def add(self, name):
self.added = True
self.update_button_state()
def remove(self, name):
self.added = False
self.update_button_state()
# ...
class MyWindow(QMainWindow):
# ...
def initUI(self):
# ...
for name in charactersAll:
item = AddRemoveWidget(name)
item.button_add.clicked.connect(lambda _, name=name: self.addName(name))
item.button_removed.clicked.connect(lambda _, name=name: self.removeName(name))
self.controlsLayout.addWidget(item)
self.widgets.append(item)
# ...
def addName(self, name):
if not name in self.userList:
self.userList.append(name)
def removeName(self, name):
if name in self.userList:
self.userList.remove(name)
Note that I used a lambda in order to send the name parameter to the functions, but I set that parameter as a keyword after a _ argument. This is because clicked always sends a checked parameter when emitted (and you're not interested in that) and the name argument must be set within the scope of the lambda (otherwise it will always be the last value in the for cycle). Do some research about lambda scope in for/while loops to know more.
Also note that I didn't add the RandRoll function, as I've not really understood what you're going to do there, but consider that it has an important issue: since you're always removing items from the list, you'll probably end up in having an empty list, which will cause a crash in for choice, as it raises an exception if the argument is an empty sequence.
Finally, remove both the show() and hide() implementations, as they are unnecessary (and potentially wrong).

Related

I want to change the sound that plays every time a button is pressed depending on a checked actiion in a QMenu

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.

How to change a variable from a class and be able to import it to another file?

I am making a GUI and have a code with several files and uses a controller file to switch between the files. However, I need several of the variables to be available in the other files and also want an own file where I can keep track of the values for all the variables.
I have now instantiated the variables on top of the file, and tried to change the values in the class below, but if I then import to another file it will only give the value which was instantiated first (which is fair since I did not call the class, but is a problem).
Please help.
Under i some of the code:
From file firstwindow
import sys
from PyQt5 import QtCore, QtWidgets, QtGui
LEVELS = 2
NUM_ICE = 4
NUM_CONES = 8
class Login(QtWidgets.QWidget):
switch_window = QtCore.pyqtSignal()
def __init__(self):
QtWidgets.QWidget.__init__(self)
self.setupUi(self)
self.setWindowTitle('First')
def setupUi(self, FirstWindow):
FirstWindow.setObjectName("First")
FirstWindow.setEnabled(True)
FirstWindow.resize(675,776)
FirstWindow.setFocusPolicy(QtCore.Qt.TabFocus)
layout = QtWidgets.QGridLayout()
self.spinBoxNUM_ICE = QtWidgets.QSpinBox()
self.spinBoxNUM_CONES = QtWidgets.QSpinBox()
self.spinBoxLEVELS = QtWidgets.QSpinBox()
layout.addWidget(self.spinBoxNUM_MASTERS,1,2)
layout.addWidget(self.spinBoxNUM_SLAVES,2,2)
layout.addWidget(self.spinBoxPRIORITY_LEVELS,11,2)
#CONTINUE AND QUIT BUTTON
self.QuitButton = QtWidgets.QPushButton("Quit")
self.QContinueButton = QtWidgets.QPushButton("Continue")
#actions
self.QuitButton.clicked.connect(FirstWindow.close)
self.QContinueButton.clicked.connect(self.login)
def login(self):
#global NUM_ICE
self.NUM_ICE = self.spinBoxNUM_ICE.value()
global NUM_CONES
NUM_CONES = self.spinBoxNUM_CONES.value()
global LEVELS
LEVELS = self.spinBoxLEVELS.value()
self.switch_window.emit()
And in the controller file
class Controller:
def __init__(self):
pass
def show_login(self):
self.login = Login()
self.login.switch_window.connect(self.show_main)
self.login.show()
def show_main(self):
self.window = MainWindow()
self.window.switch_window.connect(self.show_window_two)
self.login.close()
self.window.show()
And in the MainWindow file where I want to use LEVELS
import sys
from PyQt5 import QtCore, QtWidgets
from firstwindow import LEVELS
class MainWindow(QtWidgets.QWidget):
switch_window = QtCore.pyqtSignal()
#switch_window = QtCore.pyqtSignal(str)
def __init__(self):
QtWidgets.QWidget.__init__(self)
self.setupUi(self, LEVELS)
self.setWindowTitle('PriorityMap')
def setupUi(self, PriorityMap, LEVELS):
PriorityMap.setObjectName("First")
PriorityMap.setEnabled(True)
PriorityMap.resize(675,776)
PriorityMap.setFocusPolicy(QtCore.Qt.TabFocus)
layout = QtWidgets.QGridLayout()
#CREATING ELEMENTS
for i in range(0,LEVELS+2):
for j in range(0,5):
if (i==0 and j!=0):
layout.addWidget(QtWidgets.QLabel(str(j-1)),i,j)
elif (j==0 and i!=0):
layout.addWidget(QtWidgets.QLabel("LEVEL"+str(i-1)),i,j)
else:
layout.addWidget(QtWidgets.QPushButton(str(i)+","+str(j)),i,j)
#CONTINUE AND QUIT BUTTON
self.QuitButton = QtWidgets.QPushButton("Quit")
self.QContinueButton = QtWidgets.QPushButton("Continue")
#actions
self.QuitButton.clicked.connect(PriorityMap.close)
self.QContinueButton.clicked.connect(self.switch)
#LAYOUT
layout.addWidget(self.QuitButton,15,1)
layout.addWidget(self.QContinueButton,15,2)
self.setLayout(layout)
def switch(self):
self.switch_window.emit()
Avoid abusing the global variables(1), and in this case it is not necessary, you must make the dynamic creation of widgets a moment before making the change in the show_main method:
class Controller:
def show_login(self):
self.login = Login()
self.login.switch_window.connect(self.show_main)
self.login.show()
def show_main(self):
self.window = MainWindow()
levels = self.login.spinBoxLEVELS.value()
self.window.setLevels(levels)
self.window.switch_window.connect(self.show_window_two)
self.login.close()
self.window.show()
class MainWindow(QtWidgets.QWidget):
switch_window = QtCore.pyqtSignal()
# switch_window = QtCore.pyqtSignal(str)
def __init__(self):
QtWidgets.QWidget.__init__(self)
self.setupUi(self)
self.setWindowTitle('PriorityMap')
def setupUi(self, PriorityMap):
PriorityMap.setObjectName("First")
PriorityMap.setEnabled(True)
PriorityMap.resize(675,776)
PriorityMap.setFocusPolicy(QtCore.Qt.TabFocus)
layout = QtWidgets.QVBoxLayout(self)
self.m_content_widget = QtWidgets.QWidget()
layout.addWidget(self.m_content_widget, stretch=1)
#CONTINUE AND QUIT BUTTON
self.QuitButton = QtWidgets.QPushButton("Quit")
self.QContinueButton = QtWidgets.QPushButton("Continue")
#actions
self.QuitButton.clicked.connect(PriorityMap.close)
self.QContinueButton.clicked.connect(self.switch)
w = QtWidgets.QWidget()
hlay = QtWidgets.QHBoxLayout(w)
hlay.addWidget(self.QuitButton)
hlay.addWidget(self.QContinueButton)
layout.addWidget(w, alignment=QtCore.Qt.AlignRight)
def setLevels(self, levels):
layout = QtWidgets.QGridLayout(self.m_content_widget)
for i in range(0,levels+2):
for j in range(0, 5):
if (i==0 and j!=0):
layout.addWidget(QtWidgets.QLabel(str(j-1)),i,j)
elif (j==0 and i!=0):
layout.addWidget(QtWidgets.QLabel("LEVEL"+str(i-1)),i,j)
else:
layout.addWidget(QtWidgets.QPushButton(str(i)+","+str(j)),i,j)
def switch(self):
self.switch_window.emit()
(1) Why are global variables evil?
Ended up doing this, which worked!
Instantiated all variables from login window before calling login.close. Also passed in variables needed in next function. In this way I was also able to create a function which prints out the parameters.
class Controller:
def __init__(self):
pass
def show_login(self):
self.login = Login()
self.login.switch_window.connect(self.show_main)
self.login.show()
def show_main(self):
self.LEVELS = self.login.LEVELS
self.window = MainWindow(self.LEVELS)
self.window.switch_window.connect(self.show_window_two)
self.login.close()
self.window.show()
def writetofile(Controller):
f = open("f.txt", "w+")
f.write("int LEVELS = %d;\n\n" %Controller.LEVELS)
f.close()

How to have dynamic fields in PyQt5?

I have a piece of PyQt5 code like this:
def displayDefaultParameters(self):
#Create Default Parameters
self.useDFParamsLabel = QLabel(self)
self.useDFParamsLabel.setText('Default Parameters:')
self.useDFParamsLabelFont = QFont("Calibri", 15)
self.useDFParamsLabelFont.setUnderline(True)
self.useDFParamsLabel.setFont(self.useDFParamsLabelFont)
self.useDFParamsLabel.setGeometry(QRect(650, 75, 220, 50))
self.useDFParamsLabel.show()
self.DFParamsQLabel = QLabel(self)
pixmap = QPixmap('question1.png')
self.DFParamsQLabel.setPixmap(pixmap)
self.DFParamsQLabel.setToolTip('Default Parameters when \nno input is provided.')
self.DFParamsQLabel.move(860,85)
#FIELDS THAT WILL BE CHANGED DYNAMICALLY
self.paramFont = QFont("Arial", 12, QFont.Bold)
self.DLabel = QLabel(self)
self.DLabel.setText('D = ' + str(self.D))
self.DLabel.setFont(self.paramFont)
self.DLabel.setGeometry(QRect(675, 110, 220, 50))
self.DLabel.show()
self.RoNLabel = QLabel(self)
self.RoNLabel.setText('R_on = ' + str(self.R_on) + ' \u03A9')
self.RoNLabel.setFont(self.paramFont)
self.RoNLabel.setGeometry(QRect(675, 135, 220, 50))
self.RoNLabel.show()
self.RoFFLabel = QLabel(self)
self.RoFFLabel.setText('R_off = ' + str(self.R_off) + ' \u03A9')
self.RoFFLabel.setFont(self.paramFont)
self.RoFFLabel.setGeometry(QRect(675, 160, 220, 50))
self.RoFFLabel.show()
As you can see self.D, self.R_on and self.R_off are class variables. Now I have initialized these class variables with some value, but it can be also user input. After the user inputs the value I need to be able to change the values. As for how the class values will be input by the user is through a form which looks like the form on this question. After clicking 'OK' the new values should be reflected.
So far whatever I have tried it is overwriting on previous values. How can we implement this?
You have to use the signals to notify changes to the other parts of the application, in this case create a model that every time a property is changed it will be notified through signals, that signals will be connected to a slot, and in that slot it will modify the GUI.
import sys
from PyQt5 import QtCore, QtGui, QtWidgets
class Model(QtCore.QObject):
D_Changed = QtCore.pyqtSignal(float)
R_on_Changed = QtCore.pyqtSignal(float)
R_off_Changed = QtCore.pyqtSignal(float)
W_0_Changed = QtCore.pyqtSignal(float)
def __init__(self, parent=None):
super(Model, self).__init__(parent)
self._d = 0.0
self._r_on = 0.0
self._r_off = 0.0
self._w_0 = 0.0
def D(self):
return self._d
def setD(self, d):
if self._d == d: return
self._d = d
self.D_Changed.emit(d)
def R_on(self):
return self._r_on
def setR_on(self, r_on):
if self._r_on == r_on: return
self._r_on = r_on
self.R_on_Changed.emit(r_on)
def R_off(self):
return self._r_off
def setR_off(self, r_off):
if self._r_off == r_off: return
self._r_off = r_off
self.R_off_Changed.emit(r_off)
D = QtCore.pyqtProperty(float, fget=D, fset=setD, notify=D_Changed)
R_on = QtCore.pyqtProperty(float, fget=R_on, fset=setR_on, notify=R_on_Changed)
R_off = QtCore.pyqtProperty(float, fget=R_off, fset=setR_off, notify=R_off_Changed)
class Widget(QtWidgets.QWidget):
def __init__(self, parent=None):
super(Widget, self).__init__(parent)
model = Model(self)
self.d_label = QtWidgets.QLabel()
self.r_on_label = QtWidgets.QLabel()
self.r_off_label = QtWidgets.QLabel()
model.D_Changed.connect(self.on_D_Changed)
model.R_on_Changed.connect(self.on_R_on_Changed)
model.R_off_Changed.connect(self.on_R_off_Changed)
model.setD(10.0)
model.setR_on(100)
model.setR_off(16000)
d_le = QtWidgets.QDoubleSpinBox(
maximum=sys.float_info.max,
value=model.D,
valueChanged=model.setD
)
r_on_le = QtWidgets.QDoubleSpinBox(
maximum=sys.float_info.max,
value=model.R_on,
valueChanged=model.setR_on
)
r_off_le = QtWidgets.QDoubleSpinBox(
maximum=sys.float_info.max,
value=model.R_off,
valueChanged=model.setR_off
)
groub_box_input = QtWidgets.QGroupBox("Edit Values")
flay = QtWidgets.QFormLayout()
flay.addRow("D (nm)", d_le)
flay.addRow("R_on (\u03A9)", r_on_le)
flay.addRow("R_off (\u03A9)", r_off_le)
groub_box_input.setLayout(flay)
groub_box_output = QtWidgets.QGroupBox("Default Parameters:")
vlay = QtWidgets.QVBoxLayout()
vlay.addWidget(self.d_label)
vlay.addWidget(self.r_on_label)
vlay.addWidget(self.r_off_label)
groub_box_output.setLayout(vlay)
lay = QtWidgets.QVBoxLayout(self)
lay.addWidget(groub_box_input)
lay.addWidget(groub_box_output)
#QtCore.pyqtSlot(float)
def on_D_Changed(self, d):
self.d_label.setText('D = {}'.format(d))
#QtCore.pyqtSlot(float)
def on_R_on_Changed(self, r_on):
self.r_on_label.setText('R_on = {}\u03A9'.format(r_on))
#QtCore.pyqtSlot(float)
def on_R_off_Changed(self, r_off):
self.r_off_label.setText('R_off = {}\u03A9'.format(r_off))
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
app.setStyle("fusion")
w = Widget()
w.show()
sys.exit(app.exec_())

QlineEdit and signal & slot

I have created a widget with QLineEdit and QLabel, I want to get input from QlineEdit and display it with QLabel. I have used Signal and Slot connection, I do not know what I do wrong, but it is not working correctly. I would like to get both values from QLineEdit and later show it.
Current window
what I want?
Code:
import os
import sys
from PyQt5 import QtCore, QtGui, QtWidgets
class WinDialog(QtWidgets.QDialog):
currenttextedited = QtCore.pyqtSignal(int)
def __init__(self, parent=None):
super(WinDialog, self).__init__(parent)
self.setGeometry(300,300,350,300)
self.setWindowTitle("Signal & Slot")
self.propertyWidget = PropertyWidget()
section_lay = QtWidgets.QHBoxLayout()
section_label = QtWidgets.QLabel("Name: ")
section_edit = QtWidgets.QLineEdit('')
length_lay = QtWidgets.QHBoxLayout()
length_label = QtWidgets.QLabel("Input a number: L = ")
self.length_edit = QtWidgets.QLineEdit('1000')
self.length_edit.setInputMask("999999")
self.length_edit.setFocus(True)
thick_lay = QtWidgets.QHBoxLayout()
thick_label = QtWidgets.QLabel("Input a text: T = ")
thick_edit = QtWidgets.QLineEdit('')
section_lay.addWidget(section_label)
section_lay.addWidget(section_edit)
length_lay.addWidget(length_label)
length_lay.addWidget(self.length_edit)
length_lay.addStretch()
thick_lay.addWidget(thick_label)
thick_lay.addWidget(thick_edit)
thick_lay.addStretch()
VB_lay = QtWidgets.QVBoxLayout()
VB_lay.addStretch()
VB_lay.addLayout(length_lay)
VB_lay.addLayout(thick_lay)
VB_lay.addStretch()
buttonBox = QtWidgets.QDialogButtonBox()
buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.Cancel
|QtWidgets.QDialogButtonBox.Ok)
buttonBox.accepted.connect(self.accept)
buttonBox.rejected.connect(self.reject)
grid = QtWidgets.QGridLayout(self)
grid.addLayout(section_lay, 0, 0, 1, 2)
grid.addLayout(VB_lay, 1, 0)
grid.addWidget(self.propertyWidget, 2, 0)
grid.addWidget(buttonBox, 3, 0, 1, 2)
self.length_edit.textEdited.connect(self.textchanged)
def textchanged(self, text):
print(text)
self.currenttextedited.emit(text)
class PropertyWidget(QtWidgets.QWidget):
def __init__(self, parent=None):
super(PropertyWidget, self).__init__(parent)
HB_lay = QtWidgets.QHBoxLayout(self)
self.Displaylabel = QtWidgets.QLabel('')
HB_lay.addWidget(self.Displaylabel)
HB_lay.addStretch()
#QtCore.pyqtSlot(int)
def Display(self, text):
try:
L_Display = int(text)
T_Display = int(text)
fmt = "L = {}mm\nT = {}mm"
self.Displaylabel.setText(fmt.format(L_Display, T_Display))
except ValueError:
print("Error")
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
w = WinDialog()
w.show()
sys.exit(app.exec_())
according to samples in the image you want to show different texts but you are converting the same number to whole: L_Display = int(text) and T_Display = int(text) so how do you expect to show 2 different texts?, obviously the function display needs 2 entries (2 different entries to self plus I have changed to lowercase since it is recommended that the functions have a lowercase name).
Now the logic is as follows: if any of the texts of length_edit or thick_edit changes then you must call display() passing the new texts. So the solution is to use a slot that connects to the textEdited signals of both QLineEdits and in it obtain the text and pass the texts.
Finally I see that you want the QLineEdits receive only numbers so one option is to use a QIntValidator so that only numbers are acceptable (another better option is to use QSpinBox instead of QLineEdit)
import sys
from PyQt5 import QtCore, QtGui, QtWidgets
class WinDialog(QtWidgets.QDialog):
def __init__(self, parent=None):
super(WinDialog, self).__init__(parent)
self.setGeometry(300,300,350,300)
self.setWindowTitle("Signal & Slot")
self.propertyWidget = PropertyWidget()
section_lay = QtWidgets.QHBoxLayout()
section_label = QtWidgets.QLabel("Name: ")
section_edit = QtWidgets.QLineEdit('')
length_lay = QtWidgets.QHBoxLayout()
length_label = QtWidgets.QLabel("Input a number: L = ")
self.length_edit = QtWidgets.QLineEdit()
self.length_edit.setFocus(True)
val_lenght = QtGui.QIntValidator(0, 100000, self.length_edit)
self.length_edit.setValidator(val_lenght)
thick_lay = QtWidgets.QHBoxLayout()
thick_label = QtWidgets.QLabel("Input a text: T = ")
self.thick_edit = QtWidgets.QLineEdit()
val_thick = QtGui.QIntValidator(0, 100000, self.thick_edit)
self.thick_edit.setValidator(val_thick)
section_lay.addWidget(section_label)
section_lay.addWidget(section_edit)
length_lay.addWidget(length_label)
length_lay.addWidget(self.length_edit)
length_lay.addStretch()
thick_lay.addWidget(thick_label)
thick_lay.addWidget(self.thick_edit)
thick_lay.addStretch()
VB_lay = QtWidgets.QVBoxLayout()
VB_lay.addStretch()
VB_lay.addLayout(length_lay)
VB_lay.addLayout(thick_lay)
VB_lay.addStretch()
buttonBox = QtWidgets.QDialogButtonBox()
buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.Cancel
| QtWidgets.QDialogButtonBox.Ok)
buttonBox.accepted.connect(self.accept)
buttonBox.rejected.connect(self.reject)
grid = QtWidgets.QGridLayout(self)
grid.addLayout(section_lay, 0, 0, 1, 2)
grid.addLayout(VB_lay, 1, 0)
grid.addWidget(self.propertyWidget, 2, 0)
grid.addWidget(buttonBox, 3, 0, 1, 2)
self.length_edit.textEdited.connect(self.onTextEdited)
self.thick_edit.textEdited.connect(self.onTextEdited)
def onTextEdited(self):
l = self.length_edit.text()
t = self.thick_edit.text()
self.propertyWidget.display(l, t)
class PropertyWidget(QtWidgets.QWidget):
def __init__(self, parent=None):
super(PropertyWidget, self).__init__(parent)
HB_lay = QtWidgets.QHBoxLayout(self)
self.Displaylabel = QtWidgets.QLabel('')
HB_lay.addWidget(self.Displaylabel)
HB_lay.addStretch()
def display(self, l, t):
try:
L_Display = int(l)
T_Display = int(t)
fmt = "L = {}mm\nT = {}mm"
self.Displaylabel.setText(fmt.format(L_Display, T_Display))
except ValueError:
self.Displaylabel.clear()
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
w = WinDialog()
w.show()
sys.exit(app.exec_())

PYQT Accessing and changing dynamically added controls

I am a beginner with GUI's and PYQT. What I am trying to do is dynamically set up a grid of QComboBox's and QLineEdit's. From the QComboBox you can select a choice and from that choice, it will fill in the corresponding QLineEdit with some numbers. The problem I'm having is creating the link between the first QComboBox and the first QLineEdit box. I could make a function for each row but I would like to know a better way. I will post some sample code. Thank you for any help or advice that you might have.
import sys
from PyQt5.QtCore import QCoreApplication
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
class window(QMainWindow):
def __init__(self):
super(window, self).__init__()
self.setGeometry(50, 50, 700, 600)
self.home()
def home(self):
Test1Choices = ['Test1:','Choice1', 'Choice2', 'Choice3', 'Choice4','Choice5', 'Choice6', 'Choice7', 'Choice8', 'Choice9']
Test2Choices= ['Test2:','1','2','3','4','5','6','7','8','9','10','11','12','13','14','15']
for i in range(0,10):
Choice1ComboBox = QComboBox(self)
Choice1ComboBox.addItems(Test1Choices)
Choice1ComboBox.resize(150,25)
Choice1ComboBox.move(30,(150+(i*35)))
Choice1ComboBox.setCurrentIndex(2)
Choice2ComboBox = QComboBox(self)
Choice2ComboBox.setObjectName("Choice2ComboBox"+str(i))
Choice2ComboBox.addItems(Test2Choices)
Choice2ComboBox.resize(75,25)
Choice2ComboBox.move(200,(150+(i*35)))
Choice2ComboBox.setCurrentIndex(2)
Choice2ComboBox.activated[str].connect(self.doSomething)
numTextBox = QLineEdit(self)
numTextBox.setObjectName("numBox"+str(i))
numTextBox.move(325,(150+(i*35)))
numTextBox.resize(35,25)
result1TextBox = QLineEdit(self)
result1TextBox.setObjectName("result1Box"+str(i))
result1TextBox.move(400,(150+(i*35)))
result1TextBox.resize(100,25)
result1TextBox.setEnabled(0)
result2TextBox = QLineEdit(self)
result2TextBox.setObjectName("result2Box"+str(i))
result2TextBox.move(525,(150+(i*35)))
result2TextBox.resize(100,25)
result2TextBox.setEnabled(0)
self.show()
def doSomething(self):
numbers=['result1','result2','result3','result4','result5','result6','result7','result8','result9','result10','result11','result12','result13','result14','result15']
def run():
app = QApplication(sys.argv)
Gui = window()
sys.exit(app.exec_())
run()
To summarize I would like to bring in the index of the selected QComboBox. Then use that index number to reference the answer that is in the "numbers" array. Then print that result in the QLineEdit that is in the same row
We use sender() to get the object that emits the signal, then we look for the name of that object with setObjectName(), and we search the index, then we get the other objects with findChildren(), for example the output will be the union of the selected texts.
add name to Choice1ComboBox:
Choice1ComboBox.setObjectName("Choice1ComboBox"+str(i))
doSomething function:
def doSomething(self, _):
sender = self.sender()
l = sender.objectName().split("Choice1ComboBox")
if len(l) > 1:
number = l[1]
else:
number = sender.objectName().split("Choice2ComboBox")[1]
combo1 = self.findChildren(QComboBox, "Choice1ComboBox"+number)[0]
combo2 = self.findChildren(QComboBox, "Choice2ComboBox"+number)[0]
obj = self.findChildren(QLineEdit, "numBox"+number)[0]
obj.setText(combo1.currentText() + " " + combo2.currentText())
Complete code:
import sys
from PyQt5.QtCore import QCoreApplication
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
class window(QMainWindow):
def __init__(self):
super(window, self).__init__()
self.setGeometry(50, 50, 700, 600)
self.home()
def home(self):
Test1Choices = ['Test1:','Choice1', 'Choice2', 'Choice3', 'Choice4','Choice5', 'Choice6', 'Choice7', 'Choice8', 'Choice9']
Test2Choices= ['Test2:','1','2','3','4','5','6','7','8','9','10','11','12','13','14','15']
for i in range(0,10):
Choice1ComboBox = QComboBox(self)
Choice1ComboBox.setObjectName("Choice1ComboBox"+str(i))
Choice1ComboBox.addItems(Test1Choices)
Choice1ComboBox.resize(150,25)
Choice1ComboBox.move(30,(150+(i*35)))
Choice1ComboBox.setCurrentIndex(2)
Choice1ComboBox.activated[str].connect(self.doSomething)
Choice2ComboBox = QComboBox(self)
Choice2ComboBox.setObjectName("Choice2ComboBox"+str(i))
Choice2ComboBox.addItems(Test2Choices)
Choice2ComboBox.resize(75,25)
Choice2ComboBox.move(200,(150+(i*35)))
Choice2ComboBox.setCurrentIndex(2)
Choice2ComboBox.activated[str].connect(self.doSomething)
numTextBox = QLineEdit(self)
numTextBox.setObjectName("numBox"+str(i))
numTextBox.move(325,(150+(i*35)))
numTextBox.resize(35,25)
result1TextBox = QLineEdit(self)
result1TextBox.setObjectName("result1Box"+str(i))
result1TextBox.move(400,(150+(i*35)))
result1TextBox.resize(100,25)
result1TextBox.setEnabled(0)
result2TextBox = QLineEdit(self)
result2TextBox.setObjectName("result2Box"+str(i))
result2TextBox.move(525,(150+(i*35)))
result2TextBox.resize(100,25)
result2TextBox.setEnabled(0)
self.show()
def doSomething(self, _):
sender = self.sender()
l = sender.objectName().split("Choice1ComboBox")
if len(l) > 1:
number = l[1]
else:
number = sender.objectName().split("Choice2ComboBox")[1]
combo1 = self.findChildren(QComboBox, "Choice1ComboBox"+number)[0]
combo2 = self.findChildren(QComboBox, "Choice2ComboBox"+number)[0]
obj = self.findChildren(QLineEdit, "numBox"+number)[0]
obj.setText(combo1.currentText() + " " + combo2.currentText())
def run():
app = QApplication(sys.argv)
Gui = window()
sys.exit(app.exec_())
run()

Categories