Changing QLineEdit with the push of a button - python

I want to do now is find a way to increment the QLineEdit box (Step_Box) with the corresponding value of one of the buttons (0.01, 1, 10...). Each time I press on one of the buttons, the entry on QLineEdit must increment by that value. How should I do this?
# -*- coding: utf8 -*-
import sys
from PyQt5.QtWidgets import (
QApplication,
QMainWindow,
QWidget,
QGridLayout,
QPushButton,
QLineEdit,
QLabel,
QComboBox,
)
class XY_Buttons(QPushButton):
def __init__(self, name):
super(XY_Buttons, self).__init__()
self.clicked.connect(self.is_clicked)
self.setText(str(name))
def is_clicked(self):
print("A")
class MainWindow(QMainWindow):
def __init__(self):
super(MainWindow, self).__init__()
self.setWindowTitle("Suruga Controller")
OuterLayout = QGridLayout() # Parent layout
ArrowsLayout = QGridLayout() # Arrows layout -- in here are the XY controller
UpDownLayout = QGridLayout() # Up and Down layout -- z controller
XYLayout = QGridLayout() # XY options layout -- step size and others
ZLayout = QGridLayout() # Z options layout -- step size and others
# Adding Widgets for ArrowsLayout
ArrowsLayout.addWidget(QPushButton("Up"), 0, 1)
ArrowsLayout.addWidget(QPushButton("Down"), 2, 1)
ArrowsLayout.addWidget(QPushButton("Left"), 1, 0)
ArrowsLayout.addWidget(QPushButton("Right"), 1, 2)
# Adding Widgets for XYLayout
xstep=0.0
Step_Box = QLineEdit(str(xstep))
Step_Box.setReadOnly(True)
Units_Box = QComboBox()
Units_Box.addItems([u"10E-6 (µm) ", "10E-3 (cm)"])
XYLayout.addWidget(XY_Buttons(0.01), 1, 0)
XYLayout.addWidget(XY_Buttons(0.1), 1, 1)
XYLayout.addWidget(XY_Buttons(1), 1, 2)
XYLayout.addWidget(XY_Buttons(10), 2, 0)
XYLayout.addWidget(XY_Buttons(100), 2, 1)
XYLayout.addWidget(XY_Buttons(1000), 2, 2)
# XYLayout.addWidget(Clear_Button(), 0, 2, 1, 2)
XYLayout.addWidget(QLabel("Step is:"), 0, 0, 1, 2)
XYLayout.addWidget(Step_Box, 0, 1)
XYLayout.addWidget(Units_Box, 3, 1)
# Nesting all layouts
OuterLayout.addLayout(ArrowsLayout, 0, 0)
OuterLayout.addLayout(XYLayout, 0, 1)
OuterLayout.addLayout(UpDownLayout, 1, 0)
OuterLayout.addLayout(ZLayout, 1, 1)
widget = QWidget()
widget.setLayout(OuterLayout)
self.setCentralWidget(widget)
app = QApplication(sys.argv)
window = MainWindow()
window.show()
app.exec()

You need some reference to the instance of QLineEdit i.e. Step_Box in order to change their values from the button click, since you have a custom button class, you can just pass the instance of the line edit and store it there, then you can just increment the value from the slot to which you have connected the clicked signal of the buttons.
import sys
from PyQt5.QtWidgets import (
QApplication,
QMainWindow,
QWidget,
QGridLayout,
QPushButton,
QLineEdit,
QLabel,
QComboBox,
)
class XY_Buttons(QPushButton):
def __init__(self, name, lineEdit:QLineEdit=None):
super(XY_Buttons, self).__init__()
self.clicked.connect(self.is_clicked)
self.setText(str(name))
self.lineEdit = lineEdit
def is_clicked(self):
print("A")
self.lineEdit.setText(str(float(self.lineEdit.text())+ float(self.text())))
class MainWindow(QMainWindow):
def __init__(self):
super(MainWindow, self).__init__()
self.setWindowTitle("Suruga Controller")
OuterLayout = QGridLayout() # Parent layout
ArrowsLayout = QGridLayout() # Arrows layout -- in here are the XY controller
UpDownLayout = QGridLayout() # Up and Down layout -- z controller
XYLayout = QGridLayout() # XY options layout -- step size and others
ZLayout = QGridLayout() # Z options layout -- step size and others
# Adding Widgets for ArrowsLayout
ArrowsLayout.addWidget(QPushButton("Up"), 0, 1)
ArrowsLayout.addWidget(QPushButton("Down"), 2, 1)
ArrowsLayout.addWidget(QPushButton("Left"), 1, 0)
ArrowsLayout.addWidget(QPushButton("Right"), 1, 2)
# Adding Widgets for XYLayout
xstep=0.0
Step_Box = QLineEdit(str(xstep))
Step_Box.setReadOnly(True)
Units_Box = QComboBox()
Units_Box.addItems([u"10E-6 (µm) ", "10E-3 (cm)"])
XYLayout.addWidget(XY_Buttons(0.01, Step_Box), 1, 0)
XYLayout.addWidget(XY_Buttons(0.1, Step_Box), 1, 1)
XYLayout.addWidget(XY_Buttons(1, Step_Box), 1, 2)
XYLayout.addWidget(XY_Buttons(10, Step_Box), 2, 0)
XYLayout.addWidget(XY_Buttons(100, Step_Box), 2, 1)
XYLayout.addWidget(XY_Buttons(1000, Step_Box), 2, 2)
# XYLayout.addWidget(Clear_Button(), 0, 2, 1, 2)
XYLayout.addWidget(QLabel("Step is:"), 0, 0, 1, 2)
XYLayout.addWidget(Step_Box, 0, 1)
XYLayout.addWidget(Units_Box, 3, 1)
# Nesting all layouts
OuterLayout.addLayout(ArrowsLayout, 0, 0)
OuterLayout.addLayout(XYLayout, 0, 1)
OuterLayout.addLayout(UpDownLayout, 1, 0)
OuterLayout.addLayout(ZLayout, 1, 1)
widget = QWidget()
widget.setLayout(OuterLayout)
self.setCentralWidget(widget)
app = QApplication(sys.argv)
window = MainWindow()
window.show()
app.exec()
But generally, passing reference like this is not considered a good practice, so it'd have been better if you connected it from within the class where the instance was created.

I would advise to introduce a clear separation of the data, i.e. the currently selected step, from the GUI. A common approach is Model-View-Controller (MVC).
I think it is necessary for any GUI application, as it otherwise leads to spaghetti code of widgets talking to each other, patching out bugs.
There are several View and Model components already available inside Qt5 (QStringListModel + QListView for example), but it's trivial to make our own.
Here I introduce the FooModel and the corresponding view, along with buttons (Controller) that attempts to modify the model.
# -*- coding: utf8 -*-
import sys
from PyQt5.QtCore import QObject, pyqtSignal
from PyQt5.QtWidgets import (
QApplication,
QMainWindow,
QWidget,
QGridLayout,
QPushButton,
QLineEdit,
QLabel,
QComboBox,
)
class FooChanger(QPushButton):
# Note: This is the controller. It doesn't try to be clever and modify the view.
# It just modifies the model. If the model wishes to signal to any view component, that's the models responsibility.
def __init__(self, foo_model, value):
super().__init__(str(value))
self.clicked.connect(self.is_clicked)
self.model = foo_model
self.value = value
def is_clicked(self):
self.model.set_value(self.value)
class FooView(QLabel):
# This is the view. It does try to be clever either. It just updates information when the model signals.
def __init__(self, model):
super().__init__("hejsan")
self.model = model
self.model.data_changed.connect(self.refresh)
self.refresh() # initial refresh
def refresh(self):
self.setText(str(self.model.value))
class FooModel(QObject):
# This is our simple model. It only has a signal, and uses it to indicate when the data has been modified.
# Make sure to use the set_value function and don't poke at self.value directly!
data_changed = pyqtSignal()
def __init__(self, initial_value):
super().__init__()
# We are currently just storing one piece of information. The infamous Foo value!
self.value = initial_value
def set_value(self, new_value):
self.value = new_value
self.data_changed.emit()
class MainWindow(QMainWindow):
def __init__(self, model):
super().__init__()
self.setWindowTitle("Suruga Controller")
OuterLayout = QGridLayout() # Parent layout
ArrowsLayout = QGridLayout() # Arrows layout -- in here are the XY controller
UpDownLayout = QGridLayout() # Up and Down layout -- z controller
XYLayout = QGridLayout() # XY options layout -- step size and others
ZLayout = QGridLayout() # Z options layout -- step size and others
# Adding Widgets for ArrowsLayout
ArrowsLayout.addWidget(QPushButton("Up"), 0, 1)
ArrowsLayout.addWidget(QPushButton("Down"), 2, 1)
ArrowsLayout.addWidget(QPushButton("Left"), 1, 0)
ArrowsLayout.addWidget(QPushButton("Right"), 1, 2)
# Adding Widgets for XYLayout
step_box = FooView(model)
unix_box = QComboBox()
unix_box.addItems([u"10E-6 (µm) ", "10E-3 (cm)"])
XYLayout.addWidget(FooChanger(model, 0.01), 1, 0)
XYLayout.addWidget(FooChanger(model, 0.1), 1, 1)
XYLayout.addWidget(FooChanger(model, 1), 1, 2)
XYLayout.addWidget(FooChanger(model, 10), 2, 0)
XYLayout.addWidget(FooChanger(model, 100), 2, 1)
XYLayout.addWidget(FooChanger(model, 1000), 2, 2)
XYLayout.addWidget(QLabel("Step is:"), 0, 0, 1, 2)
XYLayout.addWidget(step_box, 0, 1)
XYLayout.addWidget(unix_box, 3, 1)
# Nesting all layouts
OuterLayout.addLayout(ArrowsLayout, 0, 0)
OuterLayout.addLayout(XYLayout, 0, 1)
OuterLayout.addLayout(UpDownLayout, 1, 0)
OuterLayout.addLayout(ZLayout, 1, 1)
widget = QWidget()
widget.setLayout(OuterLayout)
self.setCentralWidget(widget)
app = QApplication(sys.argv)
foo = FooModel(3.1415)
window = MainWindow(foo) # The view acts on our foo.
window.show()
app.exec()
There is no need for cross-talk between widgets here. It's just a view that only displays current data (it doesn't even care how the data got modified, it just always works), a controller that just informs the model that the user requested a change, and a model that is blissfully ignorant of the existence of either widgets or buttons.

Related

Can I have a tab control in a widget contained in a layout?

I am trying to make a programmable sequencer where I can configure objects that I've programmed and create a list of them to run. That requires me to instantiate that object by defining it's properties and I'd like to make a tab control on the blue widget shown below that has an inputs and outputs tab.
Anytime I try to implement this, it replaces all of the other widgets:
Surely that has to be a way to use a standard tab control in a widget right?
Code below:
import sys
from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import QApplication, QGridLayout, QLabel, QMainWindow, QWidget, QTabWidget, QVBoxLayout
from PyQt5.QtGui import QColor, QPalette
# Placeholder widget for setting up layout
class Color(QWidget):
def __init__(self, color):
super().__init__()
self.setAutoFillBackground(True)
palette = self.palette()
palette.setColor(QPalette.Window, QColor(color))
self.setPalette(palette)
# Creating tab widgets
class MyTabWidget(QTabWidget):
def __init__(self):
super().__init__()
self.setTabPosition(QTabWidget.North)
for n, color in enumerate(['blue', 'purple']):
self.addTab(Color(color), color)
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
self.setWindowTitle("My App")
layout = QGridLayout()
# 1
layout.addWidget(Color("red"), 0, 0, 2, 1)
# 2
layout.addWidget(Color("yellow"), 2, 0, 3, 1)
# 3
layout.addWidget(Color("green"), 0, 1, 5, 4)
# 4
# layout.addWidget(Color("blue"), 0, 5, 5, 1)
tabs = MyTabWidget()
layout.addWidget(tabs, 0, 5, 5, 1)
widget = QWidget()
widget.setLayout(layout)
self.setCentralWidget(widget)
app = QApplication(sys.argv)
window = MainWindow()
window.show()
app.exec_()
The size policy of the QTabWidget is set to stretch and since the stretch factors of the QGridLayout items is 0 then it will cause the items to be compressed. The solution is to set the stretch factors to 1:
widget = QWidget()
widget.setLayout(layout)
self.setCentralWidget(widget)
for column in range(layout.columnCount()):
layout.setColumnStretch(column, 1)
for row in range(layout.rowCount()):
layout.setRowStretch(row, 1)

How to check if a RadioButton was checked from another class?

I'm making a GUI in PyQt5 where there are multiple tabs. In one of the tabs, users select a radio button with what type of data they are entering.
This information changes what buttons are available in other tabs and I'm having trouble getting this information passed from the original tab class. I'd like to see if a radio button is checked from another tab and if it is, enable a button.
I have a function to return a string depending on which radio button is selected, but when I call the getDataType() function from the other tab, I get the error "AttributeError: module 'DataTab' has no attribute 'getDataType.'"
I've tried moving the getDataType() function outside of the DataTab class, but since the radio buttons use "self," I'm not able to access them.
I've also tried making a new class and calling that new class from the function, but then I get stuck on how to get the information to my other tab.
So in this example, once a user selects a radio button in the Data Tab, I want the corresponding push button on the Analysis Tab enabled.
When I uncomment the call to getDataType(), I get the following error: "AttributeError: module 'DataTab' has no attribute 'getDataType'"
from PyQt5.QtWidgets import (QWidget, QVBoxLayout, QLabel, QTableWidget, QTableWidgetItem,
QLineEdit, QFileDialog, QRadioButton, QGroupBox, QPushButton,
QGridLayout, QButtonGroup, QApplication, QAbstractItemView,
QTabWidget)
from PyQt5.QtGui import QIcon
import sys, os
class TabPage(QTabWidget):
def __init__(self):
super().__init__()
self.setStyleSheet('font: 15pt Tw Cen MT')
self.show()
# Creating the tabs here to have a reference
self.tabWidget = QTabWidget()
self.tabWidget.addTab(DataTab(), "Data Input")
self.tabWidget.addTab(AnalysisTab(), "Analysis")
self.layout = QVBoxLayout()
self.layout.addWidget(self.tabWidget)
self.setLayout(self.layout)
# The DataTab class holds all the GUI for the DataTab
class DataTab(QWidget):
def __init__(self):
super().__init__()
self.app = QApplication(sys.argv)
self.layout = QGridLayout()
self.intervalRadioButton = QRadioButton("Interval")
self.ordinalRadioButton = QRadioButton("Ordinal")
self.frequencyRadioButton = QRadioButton("Frequency")
self.submitButton = QPushButton("Submit Data")
self.layout.addWidget(self.intervalRadioButton, 7, 0, 1, 3)
self.layout.addWidget(self.ordinalRadioButton, 8, 0, 1, 3)
self.layout.addWidget(self.frequencyRadioButton, 9, 0, 1, 3)
self.setLayout(self.layout)
self.show()
def getDataType(self):
if self.intervalRadioButton.isChecked():
return "interval"
elif self.ordinalRadioButton.isChecked():
return "ordinal"
elif self.frequencyRadioButton.isChecked():
return "frequency"
class AnalysisTab(QWidget):
def __init__(self):
super().__init__()
self.app = QApplication(sys.argv)
self.createChooseIntervalGroup()
self.createChooseOrdinalGroup()
self.createChooseFrequencyGroup()
self.layout = QGridLayout()
self.layout.addWidget(self.ChooseIntervalGroup, 0, 1)
self.layout.addWidget(self.ChooseOrdinalGroup, 1, 1)
self.layout.addWidget(self.ChooseFrequencyGroup, 2, 1)
self.setLayout(self.layout)
self.show()
# The right side of AnalysisTab containing the buttons for
# analysis
def createChooseIntervalGroup(self):
self.ChooseIntervalGroup = QGroupBox("Tests for Interval Data")
self.analyzeIntervalButton = QPushButton("Analyze")
self.analyzeIntervalButton.setEnabled(False)
# if DataTab.getDataType() != "interval":
# self.analyzeIntervalButton.setEnabled(True)
self.layout = QGridLayout()
self.layout.addWidget(self.analyzeIntervalButton, 1, 1)
self.ChooseIntervalGroup.setLayout(self.layout)
# The right side of AnalysisTab containing the buttons for
# analysis
def createChooseOrdinalGroup(self):
self.ChooseOrdinalGroup = QGroupBox("Tests for Ordinal Data")
self.analyzeOrdinalButton = QPushButton("Analyze")
self.analyzeOrdinalButton.setEnabled(False)
# if DataTab.getDataType() != "ordinal":
# self.analyzeIntervalButton.setEnabled(True)
self.layout = QGridLayout()
self.layout.addWidget(self.analyzeOrdinalButton, 1, 1)
self.ChooseOrdinalGroup.setLayout(self.layout)
# The right side of AnalysisTab containing the buttons for
# analysis
def createChooseFrequencyGroup(self):
self.ChooseFrequencyGroup = QGroupBox("Tests for Frequency Data")
self.analyzeFrequencyButton = QPushButton("Analyze")
self.analyzeFrequencyButton.setEnabled(False)
# if DataTab.getDataType() != "frequency":
# self.analyzeIntervalButton.setEnabled(True)
self.layout = QGridLayout()
self.layout.addWidget(self.analyzeFrequencyButton, 1, 1)
self.ChooseFrequencyGroup.setLayout(self.layout)
def run():
app = QApplication(sys.argv)
tabPage = TabPage()
tabPage.show()
app.exec_()
run()
A possible solution for this case is to use the signals to notify the change of status of the QRadioButton to enable or disable the button.
import sys
from PyQt5.QtGui import QIcon
from PyQt5.QtWidgets import (
QWidget,
QVBoxLayout,
QRadioButton,
QGroupBox,
QPushButton,
QGridLayout,
QButtonGroup,
QApplication,
QTabWidget,
)
class Widget(QWidget):
def __init__(self, parent=None):
super().__init__(parent)
self.setStyleSheet("font: 15pt Tw Cen MT")
self.data_tab = DataTab()
self.analysis_tab = AnalysisTab()
self.tabWidget = QTabWidget()
self.tabWidget.addTab(self.data_tab, "Data Input")
self.tabWidget.addTab(self.analysis_tab, "Analysis")
layout = QVBoxLayout(self)
layout.addWidget(self.tabWidget)
self.data_tab.intervalRadioButton.toggled.connect(
self.analysis_tab.analyzeIntervalButton.setEnabled
)
self.data_tab.ordinalRadioButton.toggled.connect(
self.analysis_tab.analyzeOrdinalButton.setEnabled
)
self.data_tab.frequencyRadioButton.toggled.connect(
self.analysis_tab.analyzeFrequencyButton.setEnabled
)
class DataTab(QWidget):
def __init__(self, parent=None):
super().__init__(parent)
layout = QGridLayout(self)
self.intervalRadioButton = QRadioButton("Interval")
self.ordinalRadioButton = QRadioButton("Ordinal")
self.frequencyRadioButton = QRadioButton("Frequency")
self.submitButton = QPushButton("Submit Data")
layout.addWidget(self.intervalRadioButton, 7, 0, 1, 3)
layout.addWidget(self.ordinalRadioButton, 8, 0, 1, 3)
layout.addWidget(self.frequencyRadioButton, 9, 0, 1, 3)
class AnalysisTab(QWidget):
def __init__(self, parent=None):
super().__init__(parent)
self.createChooseIntervalGroup()
self.createChooseOrdinalGroup()
self.createChooseFrequencyGroup()
layout = QGridLayout(self)
layout.addWidget(self.ChooseIntervalGroup, 0, 1)
layout.addWidget(self.ChooseOrdinalGroup, 1, 1)
layout.addWidget(self.ChooseFrequencyGroup, 2, 1)
def createChooseIntervalGroup(self):
self.ChooseIntervalGroup = QGroupBox("Tests for Interval Data")
self.analyzeIntervalButton = QPushButton("Analyze")
self.analyzeIntervalButton.setEnabled(False)
layout = QGridLayout(self.ChooseIntervalGroup)
layout.addWidget(self.analyzeIntervalButton, 1, 1)
def createChooseOrdinalGroup(self):
self.ChooseOrdinalGroup = QGroupBox("Tests for Ordinal Data")
self.analyzeOrdinalButton = QPushButton("Analyze")
self.analyzeOrdinalButton.setEnabled(False)
layout = QGridLayout(self.ChooseOrdinalGroup)
layout.addWidget(self.analyzeOrdinalButton, 1, 1)
def createChooseFrequencyGroup(self):
self.ChooseFrequencyGroup = QGroupBox("Tests for Frequency Data")
self.analyzeFrequencyButton = QPushButton("Analyze")
self.analyzeFrequencyButton.setEnabled(False)
layout = QGridLayout(self.ChooseFrequencyGroup)
layout.addWidget(self.analyzeFrequencyButton, 1, 1)
def run():
app = QApplication(sys.argv)
tabPage = Widget()
tabPage.show()
app.exec_()
run()

Difficulty Getting PyQt GUI Elements to Correctly Show

New Python programmer here. I'm trying to make a simple-ish GUI and cannot get one of the PyQt elements to display. Perhaps someone here can point me in the right direction and give some general code comments. Don't worry. I know my code is probably awful. We've all got to start somewhere.
I have a GUI which consists of two widgets in an HBoxLayout. One widget is a simple PushButton, the other is a custom ControlWidget. I can get these to display just fine (alignment issues notwithstanding). On the ControlWidget, I have a GridLayout with some Labels, ComboBoxes, and a subclassed QTextBrowser (Debugger). This is what I can't get to appear.
Admittedly, I'm not absolutely sure how to do this. Is the inheritance wrong? Do I need to pass something else into the lower classes? etc. I want the various elements broken up into separate files and for future events to be accessible in other parts of the code, but obviously I'm missing something.
MainGUI.py
import sys
from PyQt5.QtWidgets import *
from PyQt5.QtGui import *
from PyQt5.QtCore import *
from ControlWidget import ControlWidget
# from MenuBar import MenuBar
from Debugger import Debugger
# Main
class TopWindow(QMainWindow):
def __init__(self):
super(TopWindow, self).__init__()
self.setWindowTitle('Test GUI')
self.setGeometry(0, 0, 800, 600)
self.setMaximumSize(1024, 768)
self.initUI()
def initUI(self):
# MenuBar.initMenuBar(self)
centralWidget = QWidget(self)
self.setCentralWidget(centralWidget)
hLayout = QHBoxLayout(centralWidget)
pushButton = QPushButton("Button A")
hLayout.addWidget(pushButton)
hLayout.addWidget(ControlWidget())
self.show()
# Program Entry Point
if __name__ == '__main__':
applicationInstance = QApplication(sys.argv)
ex = TopWindow()
sys.exit(applicationInstance.exec_())
ControlWidget.py
import sys
from PyQt5.QtWidgets import *
from PyQt5.QtGui import *
from PyQt5.QtCore import *
from Debugger import Debugger
class ControlWidget(QWidget):
def __init__(self, parent=None):
super(ControlWidget, self).__init__(parent)
self.left = 100
self.top = 100
self.width = 320
self.height = 100
self.numClicks = 0
self.setMaximumWidth(240)
self.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
self.initUI()
def initUI(self):
self.setGeometry(self.left, self.top, self.width, self.height)
self.createGridLayout()
def createGridLayout(self):
# Create Grid Layout
layout = QGridLayout()
self.setLayout(layout)
layout.setColumnStretch(0, 2)
layout.setColumnStretch(1, 3)
layout.setColumnStretch(2, 1)
# Instantiate Labels
labelA = QLabel()
labelB = QLabel()
labelC = QLabel()
labelD = QLabel()
labelE = QLabel()
labelF = QLabel()
self.labelFa = QLabel()
labelA.setAlignment(Qt.AlignRight | Qt.AlignVCenter)
labelB.setAlignment(Qt.AlignRight | Qt.AlignVCenter)
labelC.setAlignment(Qt.AlignRight | Qt.AlignVCenter)
labelD.setAlignment(Qt.AlignRight | Qt.AlignVCenter)
labelE.setAlignment(Qt.AlignRight | Qt.AlignVCenter)
labelF.setAlignment(Qt.AlignRight | Qt.AlignVCenter)
self.labelFa.setAlignment(Qt.AlignCenter | Qt.AlignVCenter)
labelA.setText('ABCD: ')
labelB.setText('BCDE: ')
labelC.setText('CDEF: ')
labelD.setText('DEFG: ')
labelE.setText('EFGH: ')
labelF.setText('FGHI: ')
# Instantiate Combo Boxes
comboBoxA = QComboBox()
comboBoxB = QComboBox()
comboBoxC = QComboBox()
comboBoxD = QComboBox()
comboBoxE = QComboBox()
comboBoxA.addItems(["A", "B", "C", "D"])
comboBoxB.addItems(["B", "C", "D", "E"])
comboBoxC.addItems(["C", "D", "E", "F"])
comboBoxD.addItems(["D", "E", "F", "G"])
comboBoxE.addItems(["E", "F", "G", "H"])
# Instantiate Push Buttons
pushButtonF = QPushButton()
pushButtonF.setText('Set Value')
# Spacer
spacer = QSpacerItem(10, 30, QSizePolicy.Fixed, QSizePolicy.Fixed)
# Message Box
labelDebug = QLabel()
labelDebug.setText("Debug")
labelDebug.setAlignment(Qt.AlignLeft | Qt.AlignVCenter)
debug = Debugger(self) # DOES NOT WORK
# Add to Grid Layout (item, row, column, rowspan, colspan)
layout.addWidget(labelA, 0, 0)
layout.addWidget(labelB, 1, 0)
layout.addWidget(labelC, 2, 0)
layout.addWidget(labelD, 3, 0)
layout.addWidget(labelE, 4, 0)
layout.addWidget(labelF, 5, 0)
layout.addWidget(comboBoxA, 0, 1, 1, 2)
layout.addWidget(comboBoxB, 1, 1, 1, 2)
layout.addWidget(comboBoxC, 2, 1, 1, 2)
layout.addWidget(comboBoxD, 3, 1, 1, 2)
layout.addWidget(comboBoxE, 4, 1, 1, 2)
layout.addWidget(pushButtonF, 5, 1, 1, 1)
layout.addWidget(self.labelFa, 5, 2, 1, 1)
layout.addItem(spacer, 6, 0, 1, 3)
layout.addWidget(labelDebug, 7, 0)
layout.addWidget(debug, 8, 0)
# Hook Up ComboBox Signals to Handlers
comboBoxA.currentIndexChanged.connect(self.comboBoxAHandler)
# Hook Up PushButton Signals to Handlers
pushButtonF.clicked.connect(self.pushButtonFHandler)
#self.horizontalGroupBox.setLayout(layout)
def comboBoxAHandler(self, i):
print('Combo Box A Selection Changed to {0}'.format(i))
#Debugger.write(self, 'Combo Box A Selection Changed')
def pushButtonFHandler(self):
print('Push Button F Clicked')
self.numClicks = self.numClicks + 1
self.labelFa.setText(str(self.numClicks))
Debugger.py
import sys
from PyQt5.QtWidgets import *
from PyQt5.QtGui import *
from PyQt5.QtCore import *
class Debugger(QWidget):
def __init__(self, parent=None):
super(Debugger, self).__init__(parent)
self.setMaximumWidth(240)
self.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Expanding)
self.createTextBrowser()
def createTextBrowser(self):
self.textBrowser = QTextBrowser()
self.textBrowser.setReadOnly(True)
def write(self, text):
self.textBrowser.append("• " + text)
What's the simple thing that I'm missing or doing incorrectly?
You have 2 errors:
You are not using the inheritance in Debugger, but a composition.
If you are going to want to use a variable in other methods of the same class you must make that variable member of the class.
Considering the above, the solution is:
Debugger.py
import sys
from PyQt5.QtWidgets import QTextBrowser, QSizePolicy
class Debugger(QTextBrowser):
def __init__(self, parent=None):
super(Debugger, self).__init__(parent)
self.setMaximumWidth(240)
self.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Expanding)
self.setReadOnly(True)
def write(self, text):
self.append("• " + text)
ControlWidget.py
# ...
class ControlWidget(QWidget):
# ...
def initUI(self):
# ...
# Message Box
labelDebug = QLabel()
labelDebug.setText("Debug")
labelDebug.setAlignment(Qt.AlignLeft | Qt.AlignVCenter)
self.debug = Debugger() # <---
# ...
layout.addWidget(labelDebug, 7, 0)
layout.addWidget(self.debug, 8, 0) # <---
# Hook Up ComboBox Signals to Handlers
# ...
def comboBoxAHandler(self, i):
print('Combo Box A Selection Changed to {0}'.format(i))
self.debug.write('Combo Box A Selection Changed') # <---
# ...
I recommend reading the following publications so that you understand the difference between inheritance(is-a) and composition(has-a):
Difference between Inheritance and Composition
Python: Inheritance versus Composition
My guess is that it has to do with the fact that your textbrowser object doesn't have a parent, and you never explicitly told it to .show().
Usually, when a widget has a parent, a call to the parent widget's show method will show the children as well. Since your textbrowser object has no parent, the alternative is to show that object explicitly, but you haven't done that either.
In your TopWindow class' initUI method, you call self.show(). This is the actual invokation that shows all of the parent's children (the parent being self, the window). This single call correctly shows your other widgets because you explicitly assigned the main window as their parent, but it does not show your textbrowser because you did not give it a parent. (note, because your debugger class is using composition as opposed to inheritance, you did give the debugger a parent (which is a QWidget), but the actual QTextBrowser object does not have a parent)

QGridLayout strange widget placement

I am trying to build a simple GUI with PyQt5, where I have a few widgets which I want to align using QGridLayout.
Look at the following example code which I found on some website:
import sys
from PyQt5.QtWidgets import (QWidget, QLabel, QLineEdit,
QTextEdit, QGridLayout, QApplication)
class Example(QWidget):
def __init__(self):
super().__init__()
self.initUI()
def initUI(self):
title = QLabel('Title')
author = QLabel('Author')
review = QLabel('Review')
titleEdit = QLineEdit()
authorEdit = QLineEdit()
reviewEdit = QTextEdit()
grid = QGridLayout()
grid.setSpacing(10)
grid.addWidget(title, 1, 0)
grid.addWidget(titleEdit, 1, 1)
grid.addWidget(author, 2, 0)
grid.addWidget(authorEdit, 2, 1)
grid.addWidget(review, 3, 0)
grid.addWidget(reviewEdit, 3, 1, 5, 1)
self.setLayout(grid)
self.setGeometry(300, 300, 350, 300)
self.setWindowTitle('Review')
self.show()
if __name__ == '__main__':
app = QApplication(sys.argv)
ex = Example()
sys.exit(app.exec_())
Output
As you can see, there are three labels in the first column, each spanning one cell vertically and one cell horizontally.
In the second column there are two 1x1 widgets and one 5x1 widget.
Let's say I want to place another label called test and a lineedit called testEdit below.
Naively I would modify initUI() like this:
def initUI(self):
title = QLabel('Title')
author = QLabel('Author')
review = QLabel('Review')
test = QLabel('Test')
titleEdit = QLineEdit()
authorEdit = QLineEdit()
reviewEdit = QTextEdit()
testEdit = QTextEdit()
grid = QGridLayout()
grid.setSpacing(10)
grid.addWidget(title, 1, 0)
grid.addWidget(titleEdit, 1, 1)
grid.addWidget(author, 2, 0)
grid.addWidget(authorEdit, 2, 1)
grid.addWidget(review, 3, 0)
grid.addWidget(reviewEdit, 3, 1, 5, 1)
grid.addWidget(test, 8, 0)
grid.addWidget(testEdit, 8, 1)
self.setLayout(grid)
self.setGeometry(300, 300, 350, 300)
self.setWindowTitle('Review')
self.show()
Where I just placed the new 1x1 widgets in the 8th row, below the previous one, which produces:
Output 2
However the result is not what I would expect, since the testEdit-widget is definitely not of size 1x1 and the reviewEdit-widget is also altered.
So why doesn't it work this way?
Try it:
import sys
from PyQt5.QtWidgets import (QWidget, QLabel, QLineEdit,
QTextEdit, QGridLayout, QApplication,
)
class Example(QWidget):
def __init__(self):
super().__init__()
self.initUI()
def initUI(self):
title = QLabel('Title')
author = QLabel('Author')
review = QLabel('Review')
test = QLabel('Test')
titleEdit = QLineEdit()
authorEdit = QLineEdit()
reviewEdit = QTextEdit()
testEdit = QTextEdit()
testEdit.setMaximumHeight(20) # +++
grid = QGridLayout()
grid.setSpacing(10)
grid.addWidget(title, 1, 0)
grid.addWidget(titleEdit, 1, 1)
grid.addWidget(author, 2, 0)
grid.addWidget(authorEdit, 2, 1)
grid.addWidget(review, 3, 0)
grid.addWidget(reviewEdit, 3, 1, 5, 1)
grid.addWidget(test, 8, 0)
grid.addWidget(testEdit, 8, 1)
self.setLayout(grid)
self.setGeometry(300, 300, 350, 300)
self.setWindowTitle('Review')
self.show()
if __name__ == '__main__':
app = QApplication(sys.argv)
ex = Example()
sys.exit(app.exec_())

Qlabel in front of a QGridLayout

I am trying to make a QLabel appear over a QGridLayout, but I cannot figure how to do it.
This is a sample code:
from PyQt5.QtWidgets import QWidget, QApplication, QGridLayout, QFrame, QLabel
import sys
class Foo(QWidget):
def __init__(self):
super().__init__()
grid_layout = QGridLayout()
rect1 = QLabel('RECT1')
rect1.setStyleSheet("color: green;")
grid_layout.addWidget(rect1, 0, 1)
rect2 = QLabel('RECT2')
rect2.setStyleSheet("color: blue;")
grid_layout.addWidget(rect2, 0, 2)
self.setLayout(grid_layout)
self.show()
app = QApplication(sys.argv)
foo = Foo()
sys.exit(app.exec_())
which produces the following output:
For instance, I want to create another QLabel in red, and display it over them, in the center of the image:
red_label = QLabel('red')
red_labe.setStyleSheet("font-size:20pt; color: red;");
Something like this:
How can I achieve that?
A possible solution is to make red_label son of the QWidget for it you must pass the self parameter when the object is created. In addition to this the QLabel must change size when the window does, emulating the layout task for it will create a signal that will be emited in the resizeEvent event:
import sys
from PyQt5 import QtCore, QtWidgets
class Foo(QtWidgets.QWidget):
sizeChanged = QtCore.pyqtSignal(QtCore.QSize)
def __init__(self):
super().__init__()
grid_layout = QtWidgets.QGridLayout(self)
rect1 = QtWidgets.QLabel("RECT1")
rect1.setStyleSheet("color: green;")
grid_layout.addWidget(rect1, 0, 1)
rect2 = QtWidgets.QLabel("RECT2")
rect2.setStyleSheet("color: blue;")
grid_layout.addWidget(rect2, 0, 2)
red_label = QtWidgets.QLabel("red", self)
red_label.setAlignment(QtCore.Qt.AlignCenter)
red_label.setStyleSheet("font-size: 20pt; color: red;")
self.sizeChanged.connect(red_label.resize)
def resizeEvent(self, event):
self.sizeChanged.emit(event.size())
super().resizeEvent(event)
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
foo = Foo()
foo.show()
sys.exit(app.exec_())
This may be a little less elegant of a solution, but it does keep the red labeled centered as the window is resized.
class Foo(QWidget):
def __init__(self):
super().__init__()
grid_layout = QGridLayout()
rect1 = QLabel('RECT1')
rect1.setStyleSheet("color: green;")
grid_layout.addWidget(rect1, 0, 0)
rect2 = QLabel('RECT2')
rect2.setStyleSheet("color: blue;")
grid_layout.addWidget(rect2, 0, 2)
grid_layout_two = QGridLayout()
blank_label = QLabel()
red_label = QLabel('red')
red_label.setStyleSheet("font-size:20pt; color: red;")
grid_layout_two.addWidget(blank_label, 0, 0)
grid_layout_two.addWidget(red_label, 0, 1)
grid_layout_two.addWidget(blank_label, 0, 2)
grid_layout_three = QGridLayout()
grid_layout_three.addItem(grid_layout, 0, 0)
grid_layout_three.addItem(grid_layout_two, 0, 0)
self.setLayout(grid_layout_three)
self.show()
app = QApplication(sys.argv)
foo = Foo()
sys.exit(app.exec_())
Basically making three grid layouts, positioning the elements so that the red label is in the center of the other two labels but in a grid layout that is in front of the other labels layout.

Categories

Resources