Related
I am struggling to retrieve results from a QDialog window and pass them back to the previous window. I want the user to be able to select a value/values, and then once they click okay in the dialog window, it should return them back to the previous window, and pass the text into the byVariables Textbox. The error returns on the line self.procUnivariate.byVariables.setText(item.text()). The error states:
AttributeError: 'ProcUnivariateVariables' object has no attribute 'procUnivariate'
I also tried using a return statement as you can see that is commented out. Unfortunately it seemed like the code got stuck and wasn't moving to the next line as I threw a print statement after and it never printed that line. I need help figuring out how to pass it back to the ProcUnivariate class once the okay button is clicked. Also, if I can figure this out, I'll likely turn the ProcUnivariate Class into a dialog window as well because it should take away the focus from the main window and shouldn't stay open if the main window is closed.
from PyQt5.QtWidgets import QApplication, QWidget, QLabel, QPushButton, QListWidget, QLineEdit, QTextEdit, QGridLayout, QHBoxLayout, QVBoxLayout, QDialog, QSizePolicy, QFileDialog, QTabWidget, QCheckBox
import PyQt5.QtGui as qtg
import glob
import os
from PyQt5.QtCore import Qt, QSettings
import inspect
from PyQt5 import QtCore
import pandas as pd
import pathlib
import pyreadstat
import json
class ProcUnivariateVariables(QDialog):
def __init__(self):
super().__init__()
self.procUnivariateByVariables = []
self.layout = QGridLayout()
self.setLayout(self.layout)
self.allVariables = QListWidget()
self.allVariables.setSelectionMode(3)
self.variablesSelected = QListWidget()
self.variablesSelected.setSelectionMode(3)
self.layout.addWidget(self.allVariables, 1,0, 4, 1)
self.layout.addWidget(self.variablesSelected, 1, 1, 4, 1)
self.okButton = QPushButton("Ok")
self.resetButton = QPushButton("Reset")
self.cancelButton = QPushButton("Cancel")
self.layout.addWidget(self.okButton, 1, 2)
self.layout.addWidget(self.resetButton, 2, 2)
self.layout.addWidget(self.cancelButton, 3, 2)
self.addByVariable = QPushButton(">")
self.removeVariable = QPushButton("<")
self.layout.addWidget(self.addByVariable, 5, 0)
self.layout.addWidget(self.removeVariable, 5, 1)
self.button_Connections()
def setItems(self, items):
self.allVariables.clear()
for item in items:
self.allVariables.addItem(item)
def add_by_variable(self):
selected_elements=[item.text() for item in self.allVariables.selectedItems()]
variableItem = self.variablesSelected.insertItems(self.variablesSelected.count(),selected_elements)
def button_Connections(self):
self.addByVariable.clicked.connect(self.add_by_variable)
self.removeVariable.clicked.connect(self.remove_variable)
self.okButton.clicked.connect(self.okay_clicked)
def remove_variable(self):
removeVariables = [item.text() for item in self.variablesSelected.selectedItems()]
self.selectedVariables.takeItems(removeVariables)
#sourceItem = self.currentSource.takeItem(oldSource)
def okay_clicked(self):
self.byVariablesSelected = [item.text() for item in self.variablesSelected.selectedItems()]
#self.procUnivariateByVariables = byVariablesSelected
print(self.byVariablesSelected)
self.accept()
#return self.byVariablesSelected
for item in self.byVariablesSelected:
print(item)
self.procUnivariate.byVariables.setText(item.text())
class ProcUnivariate(QWidget):
def __init__(self):
super().__init__()
self.procUnivariateVariables = None
layout = QGridLayout(self)
##Proc Univariate window Widgets
self.by = QLabel("By")
self.byVariables = QLineEdit()
self.byVariableList = QListWidget()
self.byButton = QPushButton("...")
self.varLabel = QLabel("Var")
self.classLabel = QLabel("Class")
self.freqLabel = QLabel("Freq")
self.histogramLabel = QLabel("Histogram")
self.freqBox = QLineEdit()
self.histBox = QLineEdit()
self.varBox = QLineEdit()
self.varButton = QPushButton("...")
self.sourceLabel = QLabel("Name")
self.sourceText = QLineEdit()
self.sourceButton = QPushButton("...")
self.selectionLabel = QLabel("Selection criteria")
self.selectionCriteria = QTextEdit()
self.statisticLabel = QLabel("Output")
self.statisticSearch = QLineEdit()
self.statisticList = QListWidget()
self.statisticList.setSortingEnabled(True)
self.outputLabel = QLabel("Output Statement")
self.outputText = QTextEdit()
self.fileOutputPath = QLineEdit("Output File Path")
self.runButton = QPushButton("Run")
self.previewButton = QPushButton("Preview")
self.okButton = QPushButton("Ok")
self.resetButton = QPushButton("Reset")
self.cancelButton = QPushButton("Cancel")
self.secondWindowConnections()
layout.addWidget(self.by, 1,0,1,1)
layout.addWidget(self.byVariables, 2, 0, 1, 4)
layout.addWidget(self.byButton, 2, 5, 1, 1)
#layout.addWidget(self.byVariableList, 3, 0, 1, 1)
layout.addWidget(self.freqLabel, 3, 0)
layout.addWidget(self.freqBox, 4, 0, 1, 4)
layout.addWidget(self.histogramLabel, 5, 0)
layout.addWidget(self.histBox, 6, 0, 1, 4)
layout.addWidget(self.varLabel, 7, 0, 1, 4)
layout.addWidget(self.varBox, 8, 0)
layout.addWidget(self.varButton, 8,5)
layout.addWidget(self.sourceLabel, 1, 6)
layout.addWidget(self.sourceText, 2, 6, 1, 4)
layout.addWidget(self.sourceButton, 2, 10)
layout.addWidget(self.selectionLabel, 3, 6)
layout.addWidget(self.selectionCriteria, 4, 6, 1, 4)
layout.addWidget(self.statisticLabel, 5, 6)
layout.addWidget(self.statisticSearch, 6, 6, 1, 4)
layout.addWidget(self.statisticList, 7, 6, 3, 4)
layout.addWidget(self.outputLabel, 10, 6)
layout.addWidget(self.outputText, 11, 6, 1, 4)
layout.addWidget(self.runButton, 12, 6)
layout.addWidget(self.previewButton, 12, 7)
layout.addWidget(self.okButton, 12, 8)
layout.addWidget(self.resetButton, 12, 9)
layout.addWidget(self.cancelButton, 12, 10)
self.setLayout(layout)
def secondWindowConnections(self): # this had a typo
self.byButton.clicked.connect(self.show_third_window)
def show_third_window(self):
if self.procUnivariateVariables is None: # if window has been created yet
self.procUnivariateVariables = ProcUnivariateVariables() # create window
if not self.procUnivariateVariables.isVisible(): # if window is showing
self.procUnivariateVariables.show() # show window
self.procUnivariateVariables.setItems(self.procUnivariateVariablesItems) # send items to window
def send_items(self, items): # this is to collect the variable that
self.procUnivariateVariablesItems = items # move to the third window
class MainWindow(QWidget):
def __init__(self):
super().__init__()
# Add a title
self.setWindowTitle("GUI Querying Program")
self.layout = QHBoxLayout()
self.setLayout(self.layout)
self.initUI()
self.setButtonConnections()
self.sw = None # dont initialize until neccessary.
def initUI(self):
subLayouts = {}
subLayouts['LeftColumn'] = QGridLayout()
self.layout.addLayout(subLayouts['LeftColumn'],1)
self.buttons = {}
self.buttons['addVariable'] = QPushButton('>')
self.buttons['removeVariable'] = QPushButton('<')
self.buttons['Toolkit'] = QPushButton('Toolkit')
self.variables = QListWidget()
self.selectedVariables = QListWidget()
subLayouts['LeftColumn'].addWidget(self.variables, 7,0,4,1)
subLayouts['LeftColumn'].addWidget(self.selectedVariables, 7,1,4,1)
subLayouts['LeftColumn'].addWidget(self.buttons['addVariable'], 10,0,1,1)
subLayouts['LeftColumn'].addWidget(self.buttons['removeVariable'], 10,1,1,1)
subLayouts['LeftColumn'].addWidget(self.buttons['Toolkit'], 11,1,1,1)
names = ['apple', 'banana', 'Cherry']
self.variables.insertItems(0, names)
def setButtonConnections(self):
self.buttons['addVariable'].clicked.connect(self.add_variable)
self.buttons['Toolkit'].clicked.connect(self.show_new_window)
# self.buttons['Toolkit'].clicked.connect(self.add_selected_variables)
# only use one connnect slot
def add_variable(self):
for item in self.variables.selectedItems():
self.selectedVariables.addItem(item.clone())
def show_new_window(self):
if self.sw is None: # check if window has been constructed
self.sw = ProcUnivariate() # construct window
if not self.sw.isVisible(): # If winow is not showing
self.sw.show() # show window
self.sw.send_items(self.add_selected_variables()) # send selected
# variables to second window
def add_selected_variables(self):
items = []
for i in range(self.selectedVariables.count()):
items.append(self.selectedVariables.item(i).clone())
# self.tw.setItems(items) ... self.tw doesnt exist so return them
return items
if __name__ == "__main__":
import sys
app = QApplication([])
mw = MainWindow()
mw.show()
app.exec()
What you need to do is connect a slot to the accepted signal of the dialog, and then retrieve the variables via a dedicated method of the dialog when that signal is emitted.
Here are all the relevant changes (fully tested as working):
class ProcUnivariateVariables(QDialog):
...
def button_Connections(self):
...
# connect to the dialog's built-in accept slot
self.okButton.clicked.connect(self.accept)
# add a method for retrieving the variables
def byVariablesSelected(self):
return [item.text() for item in self.variablesSelected.selectedItems()]
class ProcUnivariate(QWidget):
...
def secondWindowConnections(self): # this had a typo
self.byButton.clicked.connect(self.show_third_window)
self.varButton.clicked.connect(self.show_third_window)
def show_third_window(self):
if self.procUnivariateVariables is None: # if window has been created yet
self.procUnivariateVariables = ProcUnivariateVariables() # create window
# connect the new slot to the dialog's built-in accepted signal
self.procUnivariateVariables.accepted.connect(self.receive_selected_variables)
if not self.procUnivariateVariables.isVisible(): # if window is showing
self.procUnivariateVariables.show() # show window
self.procUnivariateVariables.setItems(self.procUnivariateVariablesItems) # send items to window
# set an identifier from the source button
self.procUnivariateVariables.sourceName = self.sender().text()
# add a slot for handling the dialog's accepted signal
def receive_selected_variables(self):
variables = self.procUnivariateVariables.byVariablesSelected()
text = ', '.join(variables)
if self.procUnivariateVariables.sourceName == 'byButton':
self.byVariables.setText(text)
else:
self.varBox.setText(text)
(Note that the above changes make the okay_clicked method of ProcUnivariateVariables redundant, so it should be removed).
Good Evening,
I am working on one of my first Python programs utilizing the MVC framework. I have looked at several tutorials and examples of MVC and I followed a youTube series of creating a calulator using MVC from Life in Code. https://www.youtube.com/channel/UCb__or4F5GkZLGYBhvfa5hg/featured
I am running into an issue with my program and I can't figure out why, I believe I have narrowed the issue down to the "View" class doesn't not see the "Controller" class to pass the function. I just don't know why. I am doing what I believe to be the same thing as Life in Code is doing with his example.
atdsController.py
import sys
from atdsCreatorUi import displayUi
#from atdsModel import atdsModel
from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import QApplication, QMainWindow
class atdsController:
def __init__(self):
#self.model = atdsModel()
self.view = displayUi(self)
def on_button_click(self, btnTxt):
print("I'm back in controller")
#file = self.model.openFile()
def main():
atdsFileCreator = QApplication(sys.argv)
atdsFileCreator.setStyle('Fusion')
#Show GUI
view = displayUi(QMainWindow)
view.show()
sys.exit(atdsFileCreator.exec_())
if __name__ == "__main__":
main()
atdsCreatorUi.
from PyQt5.QtCore import QCoreApplication, QDateTime, QTimer, QMetaObject
from PyQt5.QtGui import QFont
from PyQt5.QtWidgets import (
QMainWindow,
QFrame,
QWidget,
QHBoxLayout,
QVBoxLayout,
QGridLayout,
QGroupBox,
QLabel,
QPushButton,
QLineEdit,
QFileDialog,
QTableWidget,
QHeaderView,
QMenuBar,
QProgressBar,
QStatusBar,
QAction,
QWidgetAction,
QToolBar,
QPlainTextEdit)
__version__ = "1.0"
__author__ = "Daniel DeWitte"
ERROR_MSG = 'ERROR'
class VLine(QFrame):
def __init__(self):
super(VLine, self).__init__()
self.setFrameShape(self.VLine|self.Sunken)
class displayUi(QMainWindow):
def _createLeftLayout(self):
leftLayout = QVBoxLayout()
# Create Buttons
loadFile = QPushButton("Load File")
#btnTxt = loadFile.text()
loadFile.clicked.connect(lambda checked, btnTxt = loadFile.text():
self.controller.on_button_click(btnTxt))
loadFile.setFont(QFont('Times', 16))
leftLayout.addWidget(loadFile)
# Create Header Information
header = QGroupBox("Imported File Information:")
header.setObjectName("Header")
header.setStyleSheet("""QGroupBox#Header {border: 1px solid gray;
border-radius: 5px; margin-top: 0.5em;
font-size: 18px; font-weight: bold;}
QGroupBox:title {subcontrol-origin: margin;
subcontrol-position: top center; padding: 0px 0px;}""")
headerInfo = QGridLayout()
# Create QLineEdits()
empNum = QLineEdit()
empNum.setPlaceholderText("Employee Number")
empNum.setReadOnly(True)
empNum.setFixedWidth(200)
file = QLineEdit()
file.setPlaceholderText("File selected for conversion")
file.setReadOnly(True)
file.setFixedWidth(200)
testType = QLineEdit()
testType.setPlaceholderText("Test to be converted")
testType.setReadOnly(True)
testType.setFixedWidth(200)
partNum = QLineEdit()
partNum.setPlaceholderText("Part Number of UUT")
partNum.setReadOnly(True)
partNum.setFixedWidth(200)
serialNum = QLineEdit()
serialNum.setPlaceholderText("Serial Number of UUT")
serialNum.setReadOnly(True)
serialNum.setFixedWidth(200)
testStand = QLineEdit()
testStand.setPlaceholderText("Test stand test was performed on")
testStand.setReadOnly(True)
testStand.setFixedWidth(200)
standCal = QLineEdit()
standCal.setPlaceholderText("Calibration date of Test Stand")
standCal.setReadOnly(True)
standCal.setFixedWidth(200)
swBuildName = QLineEdit()
swBuildName.setPlaceholderText("OFP SW Version")
swBuildName.setReadOnly(True)
swBuildName.setFixedWidth(200)
buildName = QLineEdit()
buildName.setPlaceholderText("Relay Emulator Tools SW Version")
buildName.setReadOnly(True)
buildName.setFixedWidth(200)
buildToolsName = QLineEdit()
buildToolsName.setPlaceholderText("OM Build Tools SW Version")
buildToolsName.setReadOnly(True)
buildToolsName.setFixedWidth(200)
supportBuildName = QLineEdit()
supportBuildName.setPlaceholderText("Support Tools SW Version")
supportBuildName.setReadOnly(True)
supportBuildName.setFixedWidth(200)
numFilesGen = QLineEdit()
numFilesGen.setPlaceholderText("Number of ATDS files to be generated")
numFilesGen.setReadOnly(True)
numFilesGen.setFixedWidth(200)
# Create Layout
headerInfo.addWidget(loadFile, 0, 0, 1, 2)
headerInfo.addWidget(QLabel("Employee Number:"), 1, 0)
headerInfo.addWidget(empNum, 1, 1)
headerInfo.addWidget(QLabel("TDR File:"), 2, 0)
headerInfo.addWidget(tdrFile, 2, 1)
headerInfo.addWidget(QLabel("Test Name:"), 3, 0)
headerInfo.addWidget(testType, 3, 1)
headerInfo.addWidget(QLabel("Part Number:"), 4, 0)
headerInfo.addWidget(partNum, 4, 1)
headerInfo.addWidget(QLabel("Serial Number:"), 5, 0)
headerInfo.addWidget(serialNum, 5, 1)
headerInfo.addWidget(QLabel("Test Stand:"), 6, 0)
headerInfo.addWidget(testStand, 6, 1)
headerInfo.addWidget(QLabel("Test Stand Calibration Date:"), 7, 0)
headerInfo.addWidget(standCal, 7, 1)
headerInfo.addWidget(QLabel("OFP SW Version:"), 8, 0)
headerInfo.addWidget(ofpBuildName, 8, 1)
headerInfo.addWidget(QLabel("Relay Emulator Tools SW Version:"), 9, 0)
headerInfo.addWidget(reBuildName, 9, 1)
headerInfo.addWidget(QLabel("OM Build Tools SW Version:"), 10, 0)
headerInfo.addWidget(omBuildToolsName, 10, 1)
headerInfo.addWidget(QLabel("Support Tools SW Version:"), 11, 0)
headerInfo.addWidget(supportBuildName, 11, 1)
headerInfo.addWidget(QLabel("Number of ATDS Files to be Created:"), 12, 0)
headerInfo.addWidget(numFilesGen, 12, 1)
#headerInfo.setRowStretch(13, 1)
# Add Header to Frame
header.setLayout(headerInfo)
leftLayout.addWidget(header)
convertFile = QPushButton("Convert file to ATDS Format")
#convertFiles.clicked.connect(self.convertFile)
convertFile.setFont(QFont('Times', 16))
leftLayout.addWidget(convertFile)
# Add left layout to Main Layout
self.mainLayout.addLayout(leftLayout, 1)
def _createRightLayout(self):
rightLayout = QVBoxLayout()
#Create ATDS File Table
rightLayout.convTable = QTableWidget()
rightLayout.convTable.setRowCount(12)
rightLayout.convTable.setColumnCount(5)
# Table to fit the screen horizontally
rightLayout.convTable.horizontalHeader().setStretchLastSection(True)
rightLayout.convTable.horizontalHeader().setSectionResizeMode(QHeaderView.Interactive)
rightLayout.convTable.horizontalHeader().setMinimumSectionSize(115)
# Set Column Names
rightLayout.convTable.setHorizontalHeaderLabels(["ATDS File Name:", "ATDS File Desc:", "Conversion Status:",
"Test Section:","Pass / Fail:"])
rightLayout.addWidget(rightLayout.convTable)
# Detailed information of selected files
#Create ATDS File Table
rightLayout.detailedInfoTable = QTableWidget()
rightLayout.detailedInfoTable.setRowCount(10)
rightLayout.detailedInfoTable.setColumnCount(7)
# Table to fit the screen horizontally
rightLayout.detailedInfoTable.horizontalHeader().setStretchLastSection(True)
rightLayout.detailedInfoTable.horizontalHeader().setSectionResizeMode(QHeaderView.Interactive)
rightLayout.detailedInfoTable.horizontalHeader().setMinimumSectionSize(115)
# Set Column Names
rightLayout.detailedInfoTable.setHorizontalHeaderLabels(["Test Section:", "Test Description:", "Units:",
"Lower Limit:", "Upper Limit:", "Value:", "Pass / Fail:"])
rightLayout.addWidget(rightLayout.detailedInfoTable)
# Add right layout to Main Layout
self.mainLayout.addLayout(rightLayout, 3)
def findFile(self):
doc = QFileDialog.getOpenFileName(self,
"Load TDR File",
r'S:\\EDE\\adv_sys_mfg',
"Excel Files (*.xls*);; LibreOffice Files (*.ods)")
def _createMenu(self):
menu = QMenuBar(self)
# Create Menus
fileMenu = menu.addMenu("&File")
fileMenu.addAction(self.loadAction)
fileMenu.addSeparator()
fileMenu.addAction(self.quit)
aboutMenu = menu.addMenu("&Help")
self.setMenuBar(menu)
def _createActions(self):
self.loadAction = QAction("&Load New File", self)
self.loadAction.setShortcut("Ctrl+N")
self.loadAction.triggered.connect(self.findFile)
self.quit = QAction("Quit", self)
self.quit.setShortcut("Ctrl+Q")
self.quit.triggered.connect(QCoreApplication.instance().quit)
def _createStatusBar(self):
statusBar = QStatusBar()
# Create Status Bar
statusMsg = "Click Load File or Ctrl + N to begin"
statusBar.showMessage(statusMsg)
statusBar.addPermanentWidget(VLine())
# Create Progress Bar
progressBar = QProgressBar()
statusBar.addPermanentWidget(progressBar)
progressBar.setValue(50)
statusBar.addPermanentWidget(VLine())
# Add Date Time Widget to status bar
self.display = QLabel()
statusBar.addPermanentWidget(self.display)
current = QDateTime.currentDateTime()
self.display.setText(current.toString("yyyy-MMM-dd h:mm:ss AP"))
# Create date and time widget
timer = QTimer()
timer.timeout.connect(self.displayTime)
timer.start(1000)
self.displayTime()
self.setStatusBar(statusBar)
def displayTime(self):
current = QDateTime.currentDateTime()
self.display.setText(current.toString("yyyy-MMM-dd h:mm:ss AP"))
def __init__(self, atdsController):
# View Initializer
super().__init__()
# Connect atdsController
self.controller = atdsController()
# Set Window Properties
self.setWindowTitle("TDR File Conversion to ATDS")
self.setMinimumSize(1250, 600)
# Set Central Widget and general layout
self.mainLayout = QHBoxLayout()
self._centralWidget = QWidget(self)
self.setCentralWidget(self._centralWidget)
self._centralWidget.setLayout(self.mainLayout)
# Create a display and buttons
self._createActions()
self._createLeftLayout()
self._createRightLayout()
self._createMenu()
self._createStatusBar()
When I run this code, I get the following error:
"runfile('H:/C4ISR S2 NPI/Projects/Voice of the Process/ATDS TDR Importer - Python/ATDS File Creator/atdsController.py', wdir='H:/C4ISR S2 NPI/Projects/Voice of the Process/ATDS TDR Importer - Python/ATDS File Creator')
Reloaded modules: atdsCreatorUi
Traceback (most recent call last):
File "H:\C4ISR S2 NPI/Projects/Voice of the Process/ATDS TDR Importer - Python/ATDS File Creator\atdsCreatorUi.py", line 51, in
loadFile.clicked.connect(lambda checked, btnTxt = loadFile.text(): self.controller.on_button_click(btnTxt))
AttributeError: 'QMainWindow' object has no attribute on_button_click'
"
From what it looks like is that atdsCreatorUi is not seeing the atdsController but I don't know why.
Any help will be appreciated,
~Dan
In PyQt, I have a basic program. It consists of 2 combo boxes, 1 line edit and 3 checkboxes. What I want to do is, depending on the item of the first combo box, hide / show specific widgets. However, I keep getting an error: 'ExportDialog' object has no attribute 'exportSetDelimiter_lbl'. I have defined this widget above in initUI, and I run initUIininit`, so I'm not sure why I am getting this error. Here is my code:
from PyQt5 import QtGui, QtCore, QtWidgets
import sys
class ExportDialog(QtWidgets.QMainWindow):
def __init__(self,imagePath):
super(ExportDialog, self).__init__()
self.initUI(imagePath)
#Set The GUI Position And Size
self.setGeometry(500, 500, 600, 450)
#Set The GUI Title
self.setWindowTitle("Export Deck")
#Set The GUI Icon
self.setWindowIcon(QtGui.QIcon('MainFlashcardAppIcon.png'))
def initUI(self, PATH):
#Create The New Deck Label
self.exportFormat_lbl = QtWidgets.QLabel(self)
self.exportFormat_lbl.setText("Export Format: ")
exportFormat_font = QtGui.QFont()
exportFormat_font.setPointSize(8)
self.exportFormat_lbl.setFont(exportFormat_font)
self.exportFormat_lbl.adjustSize()
self.exportFormat_combo = QtWidgets.QComboBox()
self.exportFormat_combo.setMinimumHeight(35)
self.exportFormat_combo.setFixedWidth(380)
self.exportFormat_combo.currentTextChanged.connect(self.on_combobox_changed)
self.exportDeckName_lbl = QtWidgets.QLabel(self)
self.exportDeckName_lbl.setText("Include: ")
self.exportDeckName_lbl.setFont(exportFormat_font)
self.exportDeckName_lbl.adjustSize()
self.exportDeckName_combo = QtWidgets.QComboBox()
self.exportDeckName_combo.setMinimumHeight(35)
self.exportDeckName_combo.setFixedWidth(380)
self.exportFormat_combo.addItem(".TXT")
self.exportFormat_combo.addItem(".CSV")
self.exportFormat_combo.addItem(".DB")
self.exportSetDelimiter_lbl = QtWidgets.QLabel()
self.exportSetDelimiter_lbl.setText("Set Delimiter (Leave blank for standard delimited):")
self.exportSetDelimiter_txt = QtWidgets.QLineEdit()
self.exportSetDelimiter_txt.setMaxLength(1)
self.exportSetDelimiter = QtWidgets.QLineEdit()
vboxExport_setDelimiter = QtWidgets.QVBoxLayout()
vboxExport_setDelimiter.addWidget(self.exportSetDelimiter_lbl)
vboxExport_setDelimiter.addWidget(self.exportSetDelimiter_txt)
self.includeMedia_check = QtWidgets.QCheckBox("Include HTML and Media References")
self.includeTags_check = QtWidgets.QCheckBox("Include Tags")
self.includeAllSQL_check = QtWidgets.QCheckBox("Include All SQL Tables")
self.exportFormat_combo.addItem("B3 Biology")
self.exportFormat_combo.addItem("B2 Biology")
self.exportFormat_combo.addItem("B1 Biology")
self.allComboList = ["B3 Biology", "B2 Biology", "B1 Biology"]
self.exportDeckName_combo.setCurrentIndex(self.allComboList.index(PATH))
#Create Confirm Button
self.confirmButton = QtWidgets.QPushButton(self)
self.confirmButton.setText("OK")
self.confirmButton.clicked.connect(self.createDeck)
#Create Cancel Button
self.cancelButton = QtWidgets.QPushButton(self)
self.cancelButton.setText("Cancel")
self.cancelButton.clicked.connect(self.close)
hboxExportFormat = QtWidgets.QHBoxLayout()
hboxExportFormat.addWidget(self.exportFormat_lbl)
hboxExportFormat.addStretch()
hboxExportFormat.addWidget(self.exportFormat_combo)
hboxExportName = QtWidgets.QHBoxLayout()
hboxExportName.addWidget(self.exportDeckName_lbl)
hboxExportName.addStretch()
hboxExportName.addWidget(self.exportDeckName_combo)
hboxButtonsBottom = QtWidgets.QHBoxLayout()
hboxButtonsBottom.addStretch()
hboxButtonsBottom.addWidget(self.confirmButton)
hboxButtonsBottom.addWidget(self.cancelButton)
#Create The VBoxLayout
mainLayout = QtWidgets.QVBoxLayout(self)
mainLayout.addLayout(hboxExportFormat)
mainLayout.addLayout(hboxExportName)
mainLayout.addLayout(vboxExport_setDelimiter)
mainLayout.addWidget(self.includeMedia_check)
mainLayout.addWidget(self.includeTags_check)
mainLayout.addWidget(self.includeAllSQL_check)
mainLayout.addStretch()
mainLayout.addLayout(hboxButtonsBottom)
def on_combobox_changed(self, i):
if i == ".TXT":
self.exportSetDelimiter_lbl.show()
self.exportSetDelimiter_txt.show()
self.includeMedia_check.show()
self.includeTags_check.show()
self.includeAllSQL_check.hide()
elif i == ".CSV":
self.exportSetDelimiter_lbl.hide()
self.exportSetDelimiter_txt.hide()
self.includeMedia_check.show()
self.includeTags_check.show()
self.includeAllSQL_check.hide()
elif i == ".DB":
self.exportSetDelimiter_lbl.hide()
self.exportSetDelimiter_txt.hide()
self.includeMedia_check.show()
self.includeTags_check.show()
self.includeAllSQL_check.show()
def createDeck(self):
print("Exported Sucessfully")
self.close()
#Create A Windows
app = QtWidgets.QApplication(sys.argv)
window = ExportDialog("B1 Biology")
window.show()
sys.exit(app.exec_())
This is my first question, so if you need any additional information, I will add it in. Any help would be much appreciated. Thank you!
When a combobox is newly created, it has an invalid current index (-1) and no current text set. As soon as the first item is added, the index is automatically updated to 0 and the current text changes to that of the item.
You've connected to the currentTextChanged signal before adding new items, and since the function currentTextChanged assumes that the whole ui has been already created (including exportSetDelimiter_lbl), you get the attribute error.
While there's no rule for the placing of signal connections, it's usually a good habit to group all connections at the end of the function that creates them, or anyway, ensure that everything required by their connection has already been created.
So, just move the signal connection at the end of initUI and everything will work fine.
Well... No. Because you didn't set a central widget for the main window and tried to set the layout on it (which is not allowed, since a QMainWindow has a private and unaccessible layout).
Add a QWidget, call self.setCentralWidget(someWidget) and create the layout for that widget.
So I'm creating an image gallery browser in PYQT5.
I can choose a directory and load the image file icons in a scrollable widget like so:
If there is less than 20 or so images in the directory, it works fine. However when there's more than that the image labels for some reason don't show:
If I take a few of those images that aren't showing and put them into a new folder on their own, then try to load only those images, it works, only if the application hasn't already tried to load them before hand and failed, otherwise, the empty square happens again.
So this seems to me to be some sort of framework/memory limitation? Can anyone shed some light on this?
Here is my code:
# MainWindow Set-Up : Creating Widgets and Layouts
class ClassUi(object):
def setup(self, MainW):
MainW.setObjectName("MainW")
MainW.resize(400,500)
self.mainlayout = QtWidgets.QVBoxLayout()
self.centralwidget = QtWidgets.QWidget(MainW)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.centralwidget.sizePolicy().hasHeightForWidth())
self.centralwidget.setSizePolicy(sizePolicy)
self.centralwidget.setLayout(self.mainlayout)
MainW.setCentralWidget(self.centralwidget)
#direcwidget is a container widget for the top part of the program where a directory is chosen
self.direcwidget = QtWidgets.QWidget()
self.direclayout = QtWidgets.QGridLayout()
self.direcwidget.setLayout(self.direclayout)
self.mainlayout.addWidget(self.direcwidget)
self.opdirbut = QtWidgets.QPushButton() #Button that opens directory dialog when pressed
self.opdirbut.setText("Choose")
self.opdirbut.setFixedSize(50,50)
self.linepath = QtWidgets.QLineEdit() #Line Edit that displays current directory
self.linepath.setText(os.getcwd())
self.backpath = QtWidgets.QPushButton() #Button that changes directory to parent directory of the current one
self.backpath.setFixedSize(20,20)
self.backpath.setText("^")
#Positioning of widgets inside widget container direcwidget
self.direclayout.addWidget(self.opdirbut, 0,0, 2, 1)
self.direclayout.addWidget(self.linepath, 0,2, 1, 3)
self.direclayout.addWidget(self.backpath, 1,4, 1, 1)
#Scrollwidget is the area wherein a container widget widgetforscroll holds all image icons in a grid
self.scrollwidget = QtWidgets.QScrollArea()
self.mainlayout.addWidget(self.scrollwidget)
self.scrollwidget.setWidgetResizable(True)
self.scrollwidget.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOn)
self.scrollgrid = QtWidgets.QGridLayout()
self.widgetforscroll = QtWidgets.QWidget()
self.widgetforscroll.setLayout(self.scrollgrid)
self.scrollwidget.setWidget(self.widgetforscroll)
#Contains logic of program
class MainWindow(QtWidgets.QMainWindow, ClassUi):
def __init__(self):
super().__init__()
self.setup(self)
#Counter variables for keeping track of where to layout items
self.picturerow = 0
self.picturecolumn = 0
self.howmany = 0
#Assigns class methods to directory buttons
self.opdirbut.clicked.connect(self.opdial)
self.backpath.clicked.connect(self.uppath)
# Each time this function is called, a new widget called newwidget is created containing "pic" in a pixmap label and a text label and layed out
# on the widgetforscroll widget through its scrollgrid layout. Each time the function is called, picture column increments by one at the end of the function
# when all the columns in a row are filled, picture column is reset to 0 and and picture row is incremented. Picture row and picture column are used in positioning
# the newwidgets in the scrollgrid.
def addpicture(self, pic):
if self.picturecolumn == 3:
self.picturecolumn = 0
self.picturerow += 1
self.howmany += 1
#newwidget is object of picwidg class containing pixmap and text label
newwidget = picwidg(self.howmany, pic)
#This function was not required to be created, it was only created for the purpose of the Qtimer singleshot implementation.
#The newwidget is being positioned on the scrollgrid layout here.
def addnewone(lyout,nw,rw,cl):
lyout.addWidget(nw, rw, cl)
QtCore.QTimer.singleShot(
self.howmany*500,
lambda sc=self.scrollgrid, nr = newwidget, ow = self.picturerow, mn=self.picturecolumn : addnewone(sc,nr,ow,mn)
)
#Incrementing column by 1 for the next time function is called
self.picturecolumn += 1
#This is the function connected to the choose dialog button. It opens a QFileDialog window which allows you to only choose a directory folder.
#When the folder is chosen:
# 1: The linepath text is set the to the new directory
# 2: Any previous picwidg objects are cleared from the scrollgrid layout
# 3: Picture column and picture row variables are reset for positioning
# 4: A for loop scans the new directory for files with .jpg or .png extensions
# 5: The addpicture method is called with the filename as the argument
def opdial(self):
dialogbox = dialog()
try:
os.chdir(dialogbox.getExistingDirectory(options=QtWidgets.QFileDialog.DontUseNativeDialog))
self.linepath.setText(os.getcwd())
for i in reversed(range(self.scrollgrid.count())):
widgetToRemove = self.scrollgrid.itemAt(i).widget()
# remove it from the layout list
self.scrollgrid.removeWidget(widgetToRemove)
# remove it from the gui
widgetToRemove.setParent(None)
self.picturecolumn =0
self.picturerow =0
self.howmany = 0
for a, b, c in os.walk(os.getcwd()):
for i in c:
if i[-4:].lower() == ".png" or i[-4:].lower() == ".jpg":
self.addpicture(i)
except:
pass
#This is the function for reaching the parent directory. It works very similar to the above function, the only difference
#being that instead of grabbing a new directory from a QFileDialog, the directory processed is taken from the current linepath text
#and goes to the parent directory instead, then removes widgets from the scrolllayout and adds new pictures to the scrolllayout
def uppath(self):
newpath = os.path.dirname(self.linepath.text())
os.chdir(newpath)
self.linepath.setText(newpath)
for i in reversed(range(self.scrollgrid.count())):
widgetToRemove = self.scrollgrid.itemAt(i).widget()
# remove it from the layout list
self.scrollgrid.removeWidget(widgetToRemove)
# remove it from the gui
widgetToRemove.setParent(None)
self.picturecolumn = 0
self.picturerow = 0
self.howmany = 0
for a, b, c in os.walk(os.getcwd()):
for i in c:
# print(i[-4:].lower())
if i[-4:].lower() == ".png" or i[-4:].lower() == ".jpg":
self.addpicture(i)
# This is the class where newwidget instances are created
# Here 2 labels are created, one for the image, one for the text and packed in a vertical layout
class picwidg(QtWidgets.QWidget):
whoshover = None
picwidglist =[]
def __init__(self, numb, pic):
super().__init__()
self.setMouseTracking(True)
self.numb = numb
self.pic = pic
picwidg.picwidglist.append(self)
SizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed)
newwidgetlayout = QtWidgets.QVBoxLayout()
self.setLayout(newwidgetlayout)
self.setSizePolicy(SizePolicy)
self.setMinimumSize(QtCore.QSize(115, 140))
self.setMaximumSize(QtCore.QSize(115, 140))
#Pic Label
self.newpic = QtWidgets.QLabel()
QtCore.QTimer.singleShot(self.numb*500, self.addingnewpic)
self.newpic.setScaledContents(True)
self.newpic.setSizePolicy(SizePolicy)
self.newpic.setGeometry(0, 0, 100, 100)
self.newpic.setStyleSheet("border:1px solid gray")
#Picture text label
self.newtext = QtWidgets.QLabel()
font_metrics = QtGui.QFontMetrics(self.font())
self.newtext.setAlignment(QtCore.Qt.AlignCenter)
elided_text = font_metrics.elidedText(pic, QtCore.Qt.ElideRight, 100)
self.newtext.setText(elided_text)
newwidgetlayout.addWidget(self.newpic)
newwidgetlayout.addWidget(self.newtext)
def addingnewpic(self):
self.newpic.setPixmap(QtGui.QPixmap(self.pic))
#Class for QFileDialog for selecting only directories
class dialog(QtWidgets.QFileDialog):
def __init__(self):
super().__init__()
self.setFileMode(QtWidgets.QFileDialog.DirectoryOnly)
self.setOption(QtWidgets.QFileDialog.DontUseNativeDialog, True)
self.setOption(QtWidgets.QFileDialog.ShowDirsOnly, False)
if __name__ == "__main__" :
import sys
app = QtWidgets.QApplication(sys.argv)
w = MainWindow()
w.show()
sys.exit(app.exec_())
If you want to show a lot of QPixmap then it is not optimal to use a lot of QLabel, in that case it is better to use a QListView (or QListWidget) since it handles memory better.
In your code you add and remove QLabels but in the case of the model only items are added or removed and the view is repainted avoiding the excessive use of memory.
Considering the above I have implemented the following solution:
import os
from PyQt5 import QtCore, QtGui, QtWidgets
ICON_SIZE = 100
class StyledItemDelegate(QtWidgets.QStyledItemDelegate):
def initStyleOption(self, option, index):
super().initStyleOption(option, index)
option.text = option.fontMetrics.elidedText(
index.data(), QtCore.Qt.ElideRight, ICON_SIZE
)
class MainWindow(QtWidgets.QMainWindow):
def __init__(self, parent=None):
super().__init__(parent)
self.choose_btn = QtWidgets.QPushButton(
self.tr("Choose"), clicked=self.on_choose_btn_clicked
)
self.choose_btn.setFixedSize(50, 50)
self.path_le = QtWidgets.QLineEdit()
self.back_btn = QtWidgets.QPushButton(
self.tr("^"), clicked=self.on_back_btn_clicked
)
self.back_btn.setFixedSize(20, 20)
self.pixmap_lw = QtWidgets.QListWidget(
viewMode=QtWidgets.QListView.IconMode,
iconSize=ICON_SIZE * QtCore.QSize(1, 1),
movement=QtWidgets.QListView.Static,
resizeMode=QtWidgets.QListView.Adjust,
)
delegate = StyledItemDelegate(self.pixmap_lw)
self.pixmap_lw.setItemDelegate(delegate)
central_widget = QtWidgets.QWidget()
self.setCentralWidget(central_widget)
grid_layout = QtWidgets.QGridLayout(central_widget)
grid_layout.addWidget(self.choose_btn, 0, 0, 2, 1)
grid_layout.addWidget(self.path_le, 0, 1)
grid_layout.addWidget(self.back_btn, 1, 1, alignment=QtCore.Qt.AlignRight)
grid_layout.addWidget(self.pixmap_lw, 2, 0, 1, 2)
self.resize(640, 480)
self.timer_loading = QtCore.QTimer(interval=50, timeout=self.load_image)
self.filenames_iterator = None
#QtCore.pyqtSlot()
def on_choose_btn_clicked(self):
directory = QtWidgets.QFileDialog.getExistingDirectory(
options=QtWidgets.QFileDialog.DontUseNativeDialog
)
if directory:
self.start_loading(directory)
#QtCore.pyqtSlot()
def on_back_btn_clicked(self):
directory = os.path.dirname(self.path_le.text())
self.start_loading(directory)
def start_loading(self, directory):
if self.timer_loading.isActive():
self.timer_loading.stop()
self.path_le.setText(directory)
self.filenames_iterator = self.load_images(directory)
self.pixmap_lw.clear()
self.timer_loading.start()
#QtCore.pyqtSlot()
def load_image(self):
try:
filename = next(self.filenames_iterator)
except StopIteration:
self.timer_loading.stop()
else:
name = os.path.basename(filename)
it = QtWidgets.QListWidgetItem(name)
it.setIcon(QtGui.QIcon(filename))
self.pixmap_lw.addItem(it)
def load_images(self, directory):
it = QtCore.QDirIterator(
directory,
["*.jpg", "*.png"],
QtCore.QDir.Files,
QtCore.QDirIterator.Subdirectories,
)
while it.hasNext():
filename = it.next()
yield filename
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
w = MainWindow()
w.show()
sys.exit(app.exec_())
UPDATE I made a function to get the text from QLineEdit in the logingui file, and it works. I have no idea why the same function wouldn't work if imported from a different file.
I am trying to get text from QLineEdit on a button click, I have 2 files, logingui and loginlogic. Both the files import each other. Here's the logingui's code
import sys
from PyQt4.QtGui import *
from PyQt4.QtCore import *
import login.loginlogic
class LoginForm(QWidget):
def __init__(self):
QWidget.__init__(self, parent=None)
object = login.loginlogic
formLayout = QFormLayout()
# vboxlayout = QVBoxLayout()
serifFont = QFont("Arial", 10)
unamelbl = QLabel("Username")
unamelbl.setFont(serifFont)
pwdlbl = QLabel("Password")
pwdlbl.setFont(serifFont)
self.unamele = QLineEdit()
self.pwdle = QLineEdit()
self.unamele.setPlaceholderText("Username")
self.pwdle.setPlaceholderText("Password")
self.loginButton = QPushButton("Login")
self.loginButton.setStyleSheet("background-color: #99ff99; font-size: 15px;")
self.loginButton.setFont(serifFont)
self.loginButton.clicked.connect(object.handleLogin)
formLayout.addRow(unamelbl)
formLayout.addRow(self.unamele)
formLayout.addRow(pwdlbl)
formLayout.addRow(self.pwdle)
formLayout.addRow(self.loginButton)
formLayout.setSpacing(20)
self.setLayout(formLayout)
self.setStyleSheet("background-color: rgb(255,255,255)")
self.setMinimumSize(420, 320)
self.setMaximumSize(420, 320)
self.setContentsMargins(60, 50, 60, 50)
self.setWindowIcon(QIcon("favicon.ico"))
self.show()
def handlelogin(self):
u = self.unamele.text()
print(u)
if __name__ == '__main__':
app = QApplication(sys.argv)
form = LoginForm()
app.exec_()
here's the code from loginlogic
import login.logingui
class handleLogin():
def __init__(self):
self.obj = login.logingui.LoginForm()
self.username = self.obj.unamele.text()
print("the code did reach here my man ")
print(self.username)
if __name__ == '__main__':
a = handleLogin()
now the output that I get is
your code reached here
*blank line*
I know that's a blank line cause when I click the button again it just leaves an empty line and the reached here text again. Also when I run the logingui, I do write some text in it. Not leaving it blank.