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
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).
I am trying to create an app in PyQt5 that has a media (video) player, a graph, and a few buttons. Currently, whenever I try to add the video and graph widgets, they compete for space (lay on top of one another), even when using QGridLayout.
Here is the entire file so far. The important parts are under 'Create video object', 'Create plot', and 'Create GUI Layout'.
Any suggestions for getting the video player to occupy the top portion of the window and the graph to occupy a region underneath?
from PyQt5.QtWidgets import QApplication, QWidget, QPushButton, QGridLayout, QFileDialog, QVBoxLayout, QHBoxLayout
from pyqtgraph import PlotWidget
from PyQt5.QtMultimedia import QMediaPlayer, QMediaContent
from PyQt5.QtMultimediaWidgets import QVideoWidget
from PyQt5.QtCore import QUrl
import sys
class MainWindow(QWidget):
def __init__(self):
super().__init__()
self.setGeometry(300, 100, 900, 600)
self.init_gui()
self.show()
def init_gui(self):
# Create media object
self.mediaPlayer = QMediaPlayer(None, QMediaPlayer.VideoSurface)
# Create video object
self.videoWidget = QVideoWidget()
# Create plot (can't get to size properly)
self.plotWidget = PlotWidget()
# Create 'Video' button
self.videoBtn = QPushButton('Open Video')
self.videoBtn.clicked.connect(self.open_video)
# Create 'Event Index' Button
self.eventIndexBtn = QPushButton('Event Index')
self.eventIndexBtn.setEnabled(False)
self.eventIndexBtn.clicked.connect(self.open_event_times)
# Create 'Time Series' Button
self.timeSeriesBtn = QPushButton('Time Series')
self.timeSeriesBtn.setEnabled(False)
self.timeSeriesBtn.clicked.connect(self.open_time_series)
# Create 'Prev' Button
self.prevBtn = QPushButton('Prev')
self.prevBtn.setEnabled(False)
# Create 'Next' Button
self.nextBtn = QPushButton('Next')
self.nextBtn.setEnabled(False)
# Create 'Replay' Button
self.replayBtn = QPushButton('Replay')
self.replayBtn.setEnabled(False)
# Create file dialog layout
fileBoxLayout = QVBoxLayout()
fileBoxLayout.addWidget(self.videoBtn)
fileBoxLayout.addWidget(self.eventIndexBtn)
fileBoxLayout.addWidget(self.timeSeriesBtn)
# Create controls layout
controlBoxLayout = QHBoxLayout()
controlBoxLayout.addWidget(self.prevBtn)
controlBoxLayout.addWidget(self.nextBtn)
controlBoxLayout.addWidget(self.replayBtn)
# Create GUI layout
GUILayout = QGridLayout()
GUILayout.addWidget(self.videoWidget, 0, 0, 8, 9)
GUILayout.addWidget(self.plotWidget, 8, 0, 2, 9)
GUILayout.addLayout(fileBoxLayout, 10, 0, 2, 3)
GUILayout.addLayout(controlBoxLayout, 10, 3, 2, 6)
self.setLayout(GUILayout)
self.mediaPlayer.setVideoOutput(self.videoWidget)
def open_video(self):
video_dialog = QFileDialog(self)
video_dialog.setNameFilters(["Videos (*.mp4 *.avi *.mov *.flv *.wmv)"])
video_dialog.selectNameFilter("Videos (*.mp4 *.avi *.mov *.flv *.wmv)")
video_dialog.exec_()
video_file_name = video_dialog.selectedFiles()
if len(video_file_name) != 0:
self.eventIndexBtn.setEnabled(True)
# Load first frame
self.mediaPlayer.setMedia(QMediaContent(QUrl.fromLocalFile(video_file_name[0])))
self.mediaPlayer.setPosition(0)
self.mediaPlayer.play()
self.mediaPlayer.pause()
def open_event_times(self):
event_times_dialog = QFileDialog(self)
event_times_dialog.setNameFilters(["Text (*.csv)"])
event_times_dialog.selectNameFilter("Text (*.csv)")
event_times_dialog.exec_()
event_times_file_name = event_times_dialog.selectedFiles()
if len(event_times_file_name) != 0:
self.timeSeriesBtn.setEnabled(True)
self.nextBtn.setEnabled(True)
def open_time_series(self):
time_series_dialog = QFileDialog(self)
time_series_dialog.setNameFilters(["Text (*.csv)"])
time_series_dialog.selectNameFilter("Text (*.csv)")
time_series_dialog.exec_()
time_series_file_name = time_series_dialog.selectedFiles()
if __name__ == '__main__':
app = QApplication(sys.argv)
window = MainWindow()
sys.exit(app.exec_())
The problem is that the QVideoWidget at startup does not have to display anything so the sizeHint is (-1x-1) making it occupy the smallest possible size causing the problem you are seeing.
A possible solution is to establish the same stretching factor between rows 0 and 1, it is not necessary nor should you place the proportions in the span, considering the above the solution is:
# Create GUI layout
GUILayout = QGridLayout()
GUILayout.addWidget(self.videoWidget, 0, 0, 1, 9)
GUILayout.addWidget(self.plotWidget, 1, 0, 1, 9)
GUILayout.addLayout(fileBoxLayout, 2, 0, 2, 3)
GUILayout.addLayout(controlBoxLayout, 3, 3, 2, 6)
GUILayout.setRowStretch(0, 1)
GUILayout.setRowStretch(1, 1)
I'm working on a PyQt5 wizard to create Python virtual environments. Creating the environment works. The arguments (for example, with or without pip, ...) are passed from a QCheckBox() to the create() function of the venv module that comes with Python.
The problem is that the arguments passed to venv.create() have no effect on the creation of the virtual environment. It is always created with the default settings (for example, pip is installed by default).
But if I pass with_pip=False directly, pip will not be installed. That means, for some reason, the arguments passed from args have no effect on the creation. I tried converting the values into bool(), but that didn't work.
It is not clear to me why, because print(args) outputs True or False (as strings), depending on the isChecked() status of the corresponding QCheckBox().
Further, I'm using location and name (in args) to build the path to where the virtual environment will be installed, and this works fine.
Can somebody explain why venv.create() doesn't accept the arguments comming from isChecked() of the QCheckBox()? Or am I missing something?
Code to reproduce:
from subprocess import Popen, PIPE, CalledProcessError
from functools import partial
import venv
import os
from PyQt5 import QtWidgets, QtGui, QtCore
from PyQt5.QtGui import QIcon
from PyQt5.QtCore import Qt, QObject, QTimer, QThread, pyqtSignal, pyqtSlot
from PyQt5.QtWidgets import (QApplication, QFileDialog, QGridLayout, QLabel,
QVBoxLayout, QWizard, QWizardPage, QProgressBar,
QCheckBox, QLineEdit, QGroupBox, QToolButton,
QComboBox, QDialog, QHBoxLayout)
#]===========================================================================[#
#] FIND INSTALLED INTERPRETERS [#============================================[#
#]===========================================================================[#
# look for installed Python 3 versions
versions = ['3.9', '3.8', '3.7', '3.6', '3.5', '3.4', '3.3', '3']
notFound = []
versFound = []
pathFound = []
for i, v in enumerate(versions):
try:
# get installed python3 versions
getVers = Popen(["python" + v, "-V"],
stdout=PIPE, universal_newlines=True)
version = getVers.communicate()[0].strip()
# get paths of the python executables
getPath = Popen(["which", "python" + v],
stdout=PIPE, universal_newlines=True)
path = getPath.communicate()[0].strip()
versFound.append(version)
pathFound.append(path)
except (CalledProcessError, FileNotFoundError):
notFound.append(i)
#]===========================================================================[#
#] PROGRESS BAR [#===========================================================[#
#]===========================================================================[#
class ProgBarWidget(QDialog):
"""
The dialog that shows a progress bar during the create process.
"""
def __init__(self):
super().__init__()
self.initMe()
def initMe(self):
self.setGeometry(690, 365, 325, 80)
self.setFixedSize(325, 80)
self.setWindowTitle("Creating")
self.setWindowFlag(Qt.WindowCloseButtonHint, False)
self.setWindowFlag(Qt.WindowMinimizeButtonHint, False)
horizontalLayout = QHBoxLayout(self)
verticalLayout = QVBoxLayout()
statusLabel = QLabel(self)
statusLabel.setText("Creating virtual environment...")
self.progressBar = QProgressBar(self)
self.progressBar.setFixedSize(300, 23)
self.progressBar.setRange(0, 0)
verticalLayout.addWidget(statusLabel)
verticalLayout.addWidget(self.progressBar)
horizontalLayout.addLayout(verticalLayout)
self.setLayout(horizontalLayout)
#]===========================================================================[#
#] VENV WIZARD [#============================================================[#
#]===========================================================================[#
class VenvWizard(QWizard):
"""
Wizard for creating and setting up virtual environments.
"""
def __init__(self):
super().__init__()
self.setWindowTitle("Venv Wizard")
self.resize(535, 430)
self.move(578, 183)
self.setStyleSheet(
"""
QToolTip {
background-color: rgb(47, 52, 63);
border: rgb(47, 52, 63);
color: rgb(210, 210, 210);
padding: 2px;
opacity: 325
}
"""
)
self.addPage(BasicSettings())
self.addPage(InstallPackages())
self.addPage(Summary())
class BasicSettings(QWizardPage):
"""
Basic settings of the virtual environment being created.
"""
def __init__(self):
super().__init__()
folder_icon = QIcon.fromTheme("folder")
self.setTitle("Basic Settings")
self.setSubTitle("This wizard will help you to create and set up "
"a virtual environment for Python 3. ")
#]===================================================================[#
#] PAGE CONTENT [#===================================================[#
#]===================================================================[#
interpreterLabel = QLabel("&Interpreter:")
self.interprComboBox = QComboBox()
interpreterLabel.setBuddy(self.interprComboBox)
# add items from versFound to combobox
self.interprComboBox.addItem("---")
for i in range(len(versFound)):
self.interprComboBox.addItem(versFound[i], pathFound[i])
venvNameLabel = QLabel("Venv &name:")
self.venvNameLineEdit = QLineEdit()
venvNameLabel.setBuddy(self.venvNameLineEdit)
venvLocationLabel = QLabel("&Location:")
self.venvLocationLineEdit = QLineEdit()
venvLocationLabel.setBuddy(self.venvLocationLineEdit)
selectFolderToolButton = QToolButton()
selectFolderToolButton.setFixedSize(26, 27)
selectFolderToolButton.setIcon(folder_icon)
selectFolderToolButton.setToolTip("Browse")
placeHolder = QLabel()
# the 'options' groupbox
groupBox = QGroupBox("Options")
self.withPipCBox = QCheckBox("Install and update &Pip")
self.sitePackagesCBox = QCheckBox(
"&Make system (global) site-packages dir available to venv")
self.launchVenvCBox = QCheckBox(
"Launch a terminal with activated &venv after installation")
self.symlinksCBox = QCheckBox(
"Attempt to &symlink rather than copy files into venv")
# events
self.withPipCBox.toggled.connect(self.collectData)
self.sitePackagesCBox.toggled.connect(self.collectData)
self.launchVenvCBox.toggled.connect(self.collectData)
self.venvNameLineEdit.textChanged.connect(self.collectData)
self.venvLocationLineEdit.textChanged.connect(self.collectData)
self.interprComboBox.currentIndexChanged.connect(self.collectData)
self.symlinksCBox.toggled.connect(self.collectData)
selectFolderToolButton.clicked.connect(self.selectDir)
# store the collected data in line edits
self.interprVers = QLineEdit()
self.interprPath = QLineEdit()
self.venvName = QLineEdit()
self.venvLocation = QLineEdit()
self.withPip = QLineEdit()
self.sitePackages = QLineEdit()
self.launchVenv = QLineEdit()
self.symlinks = QLineEdit()
# register fields
self.registerField("interprComboBox*", self.interprComboBox)
self.registerField("venvNameLineEdit*", self.venvNameLineEdit)
self.registerField("venvLocationLineEdit*", self.venvLocationLineEdit)
self.registerField("interprVers", self.interprVers)
self.registerField("interprPath", self.interprPath)
self.registerField("venvName", self.venvName)
self.registerField("venvLocation", self.venvLocation)
self.registerField("withPip", self.withPip)
self.registerField("sitePackages", self.sitePackages)
self.registerField("launchVenv", self.launchVenv)
self.registerField("symlinks", self.symlinks)
# grid layout
gridLayout = QGridLayout()
gridLayout.addWidget(interpreterLabel, 0, 0, 1, 1)
gridLayout.addWidget(self.interprComboBox, 0, 1, 1, 2)
gridLayout.addWidget(venvNameLabel, 1, 0, 1, 1)
gridLayout.addWidget(self.venvNameLineEdit, 1, 1, 1, 2)
gridLayout.addWidget(venvLocationLabel, 2, 0, 1, 1)
gridLayout.addWidget(self.venvLocationLineEdit, 2, 1, 1, 1)
gridLayout.addWidget(selectFolderToolButton, 2, 2, 1, 1)
gridLayout.addWidget(placeHolder, 3, 0, 1, 2)
gridLayout.addWidget(groupBox, 4, 0, 1, 3)
self.setLayout(gridLayout)
# 'options' groupbox
groupBoxLayout = QVBoxLayout()
groupBoxLayout.addWidget(self.withPipCBox)
groupBoxLayout.addWidget(self.sitePackagesCBox)
groupBoxLayout.addWidget(self.launchVenvCBox)
groupBoxLayout.addWidget(self.symlinksCBox)
groupBox.setLayout(groupBoxLayout)
#]=======================================================================[#
#] SELECTIONS [#=========================================================[#
#]=======================================================================[#
def selectDir(self):
"""
Specify path where to create venv.
"""
folderName = QFileDialog.getExistingDirectory()
self.venvLocationLineEdit.setText(folderName)
def collectData(self, i):
"""
Collect all input data and create the virtual environment.
"""
self.interprVers.setText(self.interprComboBox.currentText())
self.interprPath.setText(self.interprComboBox.currentData())
self.venvName.setText(self.venvNameLineEdit.text())
self.venvLocation.setText(self.venvLocationLineEdit.text())
# the 'options'
self.withPip.setText(str(self.withPipCBox.isChecked()))
self.sitePackages.setText(str(self.sitePackagesCBox.isChecked()))
self.launchVenv.setText(str(self.launchVenvCBox.isChecked()))
self.symlinks.setText(str(self.symlinksCBox.isChecked()))
#]===========================================================================[#
#] WORKER [#================================================================[#
#]===========================================================================[#
class InstallWorker(QObject):
"""
Worker informing about start and finish of the create process.
"""
started = pyqtSignal()
finished = pyqtSignal()
#pyqtSlot(tuple)
def install(self, args):
self.started.emit()
name, location, with_pip, site_packages, symlinks = args
# outputs as excpected
#print(args)
#print("pip:", args[2], "\nsite-pkgs:", args[3], "\nsymlinks:", args[4])
venv.create(
os.path.join(location, name), # 'location' and 'name' works
with_pip=with_pip, # installs pip always (the default)
system_site_packages=site_packages, # not tested yet
symlinks=symlinks, # never symlinking
)
self.finished.emit()
class InstallPackages(QWizardPage):
"""
Install packages via `pip` into the created virtual environment.
"""
def __init__(self):
super().__init__()
self.setTitle("Install Packages")
self.setSubTitle("Specify the packages which you want Pip to "
"install into the virtual environment.")
self.progressBar = ProgBarWidget()
#]===================================================================[#
#] THREAD [#========================================================[#
#]===================================================================[#
thread = QThread(self)
thread.start()
self.m_install_worker = InstallWorker()
self.m_install_worker.moveToThread(thread)
self.m_install_worker.started.connect(self.progressBar.exec_)
self.m_install_worker.finished.connect(self.progressBar.close)
self.m_install_worker.finished.connect(self.reEnablePage)
#]===================================================================[#
#] PAGE CONTENT [#===================================================[#
#]===================================================================[#
# just some test content (page is still in development)
TestLabel = QLabel("This is a test label:", self)
TestLineEdit = QLineEdit(self)
TestLabel.setBuddy(TestLineEdit)
TestLabel2 = QLabel("This is a test label:", self)
TestLineEdit2 = QLineEdit(self)
TestLabel2.setBuddy(TestLineEdit2)
v_layout = QVBoxLayout(self)
v_layout.addWidget(TestLabel)
v_layout.addWidget(TestLineEdit)
v_layout.addWidget(TestLabel2)
v_layout.addWidget(TestLineEdit2)
self.setLayout(v_layout)
def initializePage(self):
#interprVers = self.field("interprVers")
self.interprPath = self.field("interprPath")
self.venvName = self.field("venvName")
self.venvLocation = self.field("venvLocation")
self.withPip = self.field("withPip")
self.sitePackages = self.field("sitePackages")
#launchVenv = self.field("launchVenv")
self.symlinks = self.field("symlinks")
# set the selected interpreter
sys.executable = self.interprPath
# run the create process
self.createProcess()
# disable the page as long as the progress bar is up
self.setEnabled(False)
def reEnablePage(self):
"""
Re-enable page after the create process has finished.
"""
self.setEnabled(True)
def createProcess(self):
"""
Create the virtual environment.
"""
args = (
self.venvName,
self.venvLocation,
self.withPip,
self.sitePackages,
self.symlinks,
)
wrapper = partial(self.m_install_worker.install, args)
QTimer.singleShot(0, wrapper)
class Summary(QWizardPage):
def __init__(self):
super().__init__()
self.setTitle("Summary")
self.setSubTitle("...............")
if __name__ == "__main__":
import sys
app = QApplication(sys.argv)
wizard = VenvWizard()
wizard.show()
sys.exit(app.exec_())
You are creating QLineEdit just to save data that is unnecessary since you can register widget properties as the status of the checkbox. You are also converting the Boolean True or False to the string "True" or "False", respectively, which you then pass to the function venv.create() that will convert it to boolean but any string not empty is truly.
The solution is to register the QCheckBox directly, in addition to the other widgets.
class BasicSettings(QWizardPage):
"""
Basic settings of the virtual environment being created.
"""
def __init__(self):
super().__init__()
# ...
groupBox.setLayout(groupBoxLayout)
selectFolderToolButton.clicked.connect(self.selectDir)
self.registerField("interprVers", self.interprComboBox, "currentText")
self.registerField("interprPath", self.interprComboBox, "currentData")
self.registerField("venvName", self.venvNameLineEdit)
self.registerField("venvLocation", self.venvLocationLineEdit)
self.registerField("withPip", self.withPipCBox)
self.registerField("sitePackages", self.sitePackagesCBox)
self.registerField("launchVenv", self.launchVenvCBox)
self.registerField("symlinks", self.symlinksCBox)
# ...
I have the below piece of code which gives the below picture.
import os
import numpy as np
from PyQt5 import QtCore, QtWidgets
import sqlite3
class Ui_Form():
def __init__(self):
#Checking if the loading database is in place
if not os.path.exists("loading_database.db"):
QtWidgets.QMessageBox.information(None,'Loading database missing','Loading database has not been found. Creation of a new one will be attempted')
self.loadingDatabaseCreator()
QtWidgets.QMessageBox.information(None,'Successful','Loading database succesfully created')
#Asking the user for the input file to be parsed and the number of componenets determined
filePath, _ = QtWidgets.QFileDialog.getOpenFileName(None, "Select input model","","Input deck (*.inp)","*.inp")
filePath = str(filePath)
self.pleaseWait = waitWindow()
self.pleaseWait.show()
#If no file has been inputted the script will exit
if not filePath:
exit()
else:
#If a file has been inputted now it will be opened and a list containing all the lines will be created
readInputFile(filePath)
#Searching in the file for all the valid components. We disregards collectors containing RBE3 elements
#as they don't require fatigue analysis
self.pleaseWait.close()
for line in model_file:
if "*ELEMENT," in line and "DCOUP3D" not in line:
#If a valid collector is found it will be added to the array of type numpy.
try:
#Checks if the collector has already been recorded as different element types partaining of the same component
#will be specified in different collectors win the input deck
if not line.split("ELSET=")[1][:-1] in self.collector_array:
self.collector_array = np.concatenate((self.collector_array,np.array([line.split("ELSET=")[1][:-1]])),axis=0)
except:
self.collector_array = np.array([line.split("ELSET=")[1][:-1]])
#model_file_obj.close
#Testing to see if the array has been created indicating the presence of at least one entity
#This will be useful if the user loads a load deck instead of a model as they have the same .inp extension
try:
self.collector_array
except:
QtWidgets.QMessageBox.information(None,'Error','File contains no element collectors')
#Creating the initial Window
self.mainWidget = QtWidgets.QWidget()
self.mainWidget.resize(500, 500)
self.mainWidget.setWindowFlags(self.mainWidget.windowFlags() | QtCore.Qt.MSWindowsFixedSizeDialogHint)
self.mainWidget.setWindowTitle("nCode analysis set-up")
#Creating the top level grid layout
self.mainGrid = QtWidgets.QGridLayout(self.mainWidget)
#Creating the boxes which will describe the analysis to be written in the .dcl file
self.analysis_type_label = QtWidgets.QLabel(self.mainWidget)
self.analysis_type_label.setText("Type of analysis")
self.mainGrid.addWidget(self.analysis_type_label,0,0)
self.analysis_type_combo = QtWidgets.QComboBox(self.mainWidget)
self.analysis_type_combo.addItems(["Fatigue","Proof plus fatigue"])
self.mainGrid.addWidget(self.analysis_type_combo,0,1,1,2)
self.load_deck_type_label = QtWidgets.QLabel(self.mainWidget)
self.load_deck_type_label.setText("Type of fatigue deck")
self.mainGrid.addWidget(self.load_deck_type_label,1,0)
self.load_deck_type_combo = QtWidgets.QComboBox(self.mainWidget)
self.load_deck_type_combo.addItems(["Regen braking","No regen braking"])
self.mainGrid.addWidget(self.load_deck_type_combo,1,1,1,2)
self.analysis_engine_type_label = QtWidgets.QLabel(self.mainWidget)
self.analysis_engine_type_label.setText("Analysis Engine")
self.mainGrid.addWidget(self.analysis_engine_type_label,2,0)
self.analysis_engine_type_combo = QtWidgets.QComboBox(self.mainWidget)
self.analysis_engine_type_combo.addItems(["EN analysis","SN analysis"])
self.mainGrid.addWidget(self.analysis_engine_type_combo,2,1,1,2)
#Creating a scrolable area to accommodate for a large number of components with possible lenghty names
self.scrollArea = QtWidgets.QScrollArea(self.mainWidget)
#The line below is absolutely required to make the scrollable area work.
self.scrollArea.setWidgetResizable(True)
self.mainGrid.addWidget(self.scrollArea,3,0,1,3)
self.secondaryWidget = QtWidgets.QWidget()
self.scrollArea.setWidget(self.secondaryWidget)
self.secondaryGrid = QtWidgets.QGridLayout(self.secondaryWidget)
#This bit creates the necessary object for every componenet that was found in the input deck.
#The globals method is used to dynamically assign objects to variables for subsequent manipulation.
for i in range(0, self.collector_array.shape[0]):
globals()["self.materialLabel"+str(i)] = QtWidgets.QLabel(self.secondaryWidget)
globals()["self.materialLabel"+str(i)].setText(self.collector_array[i]+" material")
self.secondaryGrid.addWidget(globals()["self.materialLabel"+str(i)],2+i,0)
globals()["self.materialName"+str(i)] = QtWidgets.QLineEdit(self.secondaryWidget)
globals()["self.materialName"+str(i)].setPlaceholderText("Drop material name here")
globals()["self.materialName"+str(i)].setFixedWidth(150)
self.secondaryGrid.addWidget(globals()["self.materialName"+str(i)],2+i,1)
globals()["self.materialPickingButton"+str(i)] = QtWidgets.QPushButton(self.secondaryWidget)
globals()["self.materialPickingButton"+str(i)].setText("Pick material")
globals()["self.materialPickingButton"+str(i)].clicked.connect(self.material_lookup)
self.secondaryGrid.addWidget(globals()["self.materialPickingButton"+str(i)],2+i,2)
#Creates the button that connects to the DLC_writer function
self.createDCL = QtWidgets.QPushButton(self.mainWidget)
self.createDCL.setText("Create DCL")
self.mainGrid.addWidget(self.createDCL,4,0,1,3)
self.createDCL.clicked.connect(self.DCL_guide)
self.mainWidget.show()
class waitWindow(QtWidgets.QDialog):
def __init__(self):
super().__init__()
self.setWindowTitle("Info")
self.resize(600,200)
self.VLayout = QtWidgets.QVBoxLayout(self)
self.message = QtWidgets.QLabel(self)
self.message.setFixedWidth(550)
self.message.setText("Please wait while input file is being read")
self.VLayout.addWidget(self.message)
class readInputFile():
def __init__(self,filePath):
model_file_obj = open(filePath, "r")
globals()['model_file'] = model_file_obj.readlines()
model_file_obj.close
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
ui = Ui_Form()
sys.exit(app.exec_())
The problem is my text label is missing from this window. I made it so big in case the label did not have enough space to fully display but in that case I think it would have displayed what it had space for. Hopefully someone knows why.
Edit: I have included the entire init function of Ui_Form. All my problems are caused in this bit the rest working ok.
The window you are viewing is not pleaseWait window but the mainWidget window.
The above is explained assuming that:
The file that is read is small, so the pleaseWait window will open and close instantly, so that being that synchronous action Qt will not have time to do it and for the user the window will never have been shown. For this case the solution is to give a reasonable time for the user to see the window.
The file is very large, the reading will take a long time blocking the eventloop, which will cause tasks such as displaying a window to not be performed, to avoid blocking the task must be executed in another thread.
Combining both solutions we obtain the following code:
import os
from functools import partial
from PyQt5 import QtCore, QtWidgets
class Worker(QtCore.QObject):
finished = QtCore.pyqtSignal()
contentChanged = QtCore.pyqtSignal(list)
#QtCore.pyqtSlot(str)
def read_file(self, fileName):
with open(fileName, "r") as model_file_obj:
model_file = model_file_obj.readlines()
print(model_file)
self.contentChanged.emit(model_file)
self.finished.emit()
class MainWidget(QtWidgets.QWidget):
def __init__(self, parent=None):
super().__init__(parent)
self.resize(500, 500)
self.setWindowFlags(
self.windowFlags() | QtCore.Qt.MSWindowsFixedSizeDialogHint
)
self.setWindowTitle("nCode analysis set-up")
mainGrid = QtWidgets.QGridLayout(self)
thread = QtCore.QThread(self)
thread.start()
self.m_worker = Worker()
self.m_worker.moveToThread(thread)
self.m_worker.contentChanged.connect(self.get_content)
def launch_task(self):
if not os.path.exists("loading_database.db"):
QtWidgets.QMessageBox.information(
None,
"Loading database missing",
"Loading database has not been found. Creation of a new one will be attempted",
)
# self.loadingDatabaseCreator()
QtWidgets.QMessageBox.information(
None, "Successful", "Loading database succesfully created"
)
fileName, _ = QtWidgets.QFileDialog.getOpenFileName(
None, "Select input model", "", "Input deck (*.inp)", "*.inp"
)
self.pleaseWait = WaitWindow()
self.pleaseWait.show()
self.m_worker.finished.connect(self.pleaseWait.close)
wrapper = partial(self.m_worker.read_file, fileName)
# Launch the task in a reasonable time for the window to show
QtCore.QTimer.singleShot(100, wrapper) #
#QtCore.pyqtSlot(list)
def get_content(self, lines):
print(lines)
class WaitWindow(QtWidgets.QDialog):
def __init__(self):
super().__init__()
self.setWindowTitle("Info")
self.resize(600, 200)
layout = QtWidgets.QVBoxLayout(self)
self.message = QtWidgets.QLabel(self)
self.message.setFixedWidth(550)
self.message.setText("Please wait while input file is being read")
layout.addWidget(self.message)
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
w = MainWidget()
w.show()
w.launch_task()
sys.exit(app.exec_())
Update:
import os
from functools import partial
import numpy as np
from PyQt5 import QtCore, QtWidgets
class MainWidget(QtWidgets.QWidget):
def __init__(self, parent=None):
super().__init__(parent)
self.resize(500, 500)
self.setWindowFlags(
self.windowFlags() | QtCore.Qt.MSWindowsFixedSizeDialogHint
)
self.setWindowTitle("nCode analysis set-up")
self.wait_window = WaitWindow()
thread = QtCore.QThread(self)
thread.start()
self.m_worker = Worker()
self.m_worker.moveToThread(thread)
self.m_worker.new_content_signal.connect(self.get_content)
# Creating the top level grid layout
mainGrid = QtWidgets.QGridLayout(self)
self.analysis_type_label = QtWidgets.QLabel(self)
self.analysis_type_label.setText("Type of analysis")
mainGrid.addWidget(self.analysis_type_label, 0, 0)
self.analysis_type_combo = QtWidgets.QComboBox(self)
self.analysis_type_combo.addItems(["Fatigue", "Proof plus fatigue"])
mainGrid.addWidget(self.analysis_type_combo, 0, 1, 1, 2)
self.load_deck_type_label = QtWidgets.QLabel(self)
self.load_deck_type_label.setText("Type of fatigue deck")
mainGrid.addWidget(self.load_deck_type_label, 1, 0)
self.load_deck_type_combo = QtWidgets.QComboBox(self)
self.load_deck_type_combo.addItems(
["Regen braking", "No regen braking"]
)
mainGrid.addWidget(self.load_deck_type_combo, 1, 1, 1, 2)
self.analysis_engine_type_label = QtWidgets.QLabel(self)
self.analysis_engine_type_label.setText("Analysis Engine")
mainGrid.addWidget(self.analysis_engine_type_label, 2, 0)
self.analysis_engine_type_combo = QtWidgets.QComboBox(self)
self.analysis_engine_type_combo.addItems(["EN analysis", "SN analysis"])
mainGrid.addWidget(self.analysis_engine_type_combo, 2, 1, 1, 2)
# Creating a scrolable area to accommodate for a large number of components with possible lenghty names
self.scrollArea = QtWidgets.QScrollArea(self)
# The line below is absolutely required to make the scrollable area work.
self.scrollArea.setWidgetResizable(True)
mainGrid.addWidget(self.scrollArea, 3, 0, 1, 3)
self.secondaryWidget = QtWidgets.QWidget()
self.scrollArea.setWidget(self.secondaryWidget)
self.secondaryGrid = QtWidgets.QGridLayout(self.secondaryWidget)
self.createDCL = QtWidgets.QPushButton(self)
self.createDCL.setText("Create DCL")
mainGrid.addWidget(self.createDCL, 4, 0, 1, 3)
def start_task(self):
if not os.path.exists("loading_database.db"):
QtWidgets.QMessageBox.information(
None,
"Loading database missing",
"Loading database has not been found. Creation of a new one will be attempted",
)
# self.loadingDatabaseCreator()
QtWidgets.QMessageBox.information(
None, "Successful", "Loading database succesfully created"
)
filePath, _ = QtWidgets.QFileDialog.getOpenFileName(
None, "Select input model", "", "Input deck (*.inp)", "*.inp"
)
if filePath:
self.wait_window.show()
self.m_worker.finished.connect(self.wait_window.close)
wrapper = partial(self.m_worker.read_file, filePath)
# Launch the task in a reasonable time for the window to show
QtCore.QTimer.singleShot(100, wrapper)
#QtCore.pyqtSlot(int, str)
def get_content(self, i, content):
label = QtWidgets.QLabel("{} material".format(content))
linedit = QtWidgets.QLineEdit(placeholderText="Drop material name here")
linedit.setFixedWidth(150)
button = QtWidgets.QPushButton("Pick material")
self.secondaryGrid.addWidget(label, 2 + i, 0)
self.secondaryGrid.addWidget(linedit, 2 + i, 1)
self.secondaryGrid.addWidget(button, 2 + i, 2)
class WaitWindow(QtWidgets.QDialog):
def __init__(self):
super().__init__()
self.setWindowTitle("Info")
self.resize(600, 200)
layout = QtWidgets.QVBoxLayout(self)
self.message = QtWidgets.QLabel()
self.message.setFixedWidth(550)
self.message.setText("Please wait while input file is being read")
layout.addWidget(self.message)
class Worker(QtCore.QObject):
finished = QtCore.pyqtSignal()
new_content_signal = QtCore.pyqtSignal(int, str)
#QtCore.pyqtSlot(str)
def read_file(self, fileName):
i = 0
collector_array = []
with open(fileName, "r") as model_file_obj:
for line in model_file_obj.readlines():
if "*ELEMENT," in line and "DCOUP3D" not in line:
t = line.split("ELSET=")[1][:-1]
if t not in collector_array:
self.new_content_signal.emit(i, t)
QtCore.QThread.msleep(10)
collector_array.append(t)
i += 1
self.finished.emit()
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
w = MainWidget()
w.show()
w.start_task()
sys.exit(app.exec_())
This code works perfectly for me:
import sys
from PyQt5 import QtWidgets
from PyQt5.Qt import QApplication
class waitWindow(QtWidgets.QDialog):
def __init__(self):
super().__init__()
self.setWindowTitle("Info")
self.resize(600,200)
self.VLayout = QtWidgets.QVBoxLayout(self)
self.message = QtWidgets.QLabel(self)
self.message.setFixedWidth(550)
self.message.setText("Please wait while input file is being read")
self.VLayout.addWidget(self.message)
self.show()
def closeWindow(self):
self.close()
app = QApplication(sys.argv)
w = waitWindow()
w.exec_()
Okay took your code and gutted all the unessential stuff to analyze the actual issue I have included the gutted version so you can see what is necessary in the future to see what is wrong.
In short the issue is that you need to add the following line as the last line in your waitWindow function >> self.exec() by adding that I got your label to display (see code below)
Now with that said you do have another issue and that is the QDialog box will not allow the program to continue until it has been closed (aka it stops the process flow until you release it by closing the window or use a different means). My question is why not use your "main window" to display that message then repopulate with the rest of that data. Also curious why are you not using QMainWindow?
import sys
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
class Ui_Form():
def __init__(self):
self.pleaseWait = waitWindow()
self.pleaseWait.show()
self.pleaseWait.close()
sys.exit()
class waitWindow(QDialog):
def __init__(self):
super(waitWindow, self).__init__()
self.setWindowTitle("Info")
self.resize(600,200)
self.message = QLabel(self)
self.message.setFixedWidth(550)
self.message.setText("Please wait while input file is being read")
self.VLayout = QVBoxLayout(self)
self.VLayout.addWidget(self.message)
self.setLayout(self.VLayout)
self.exec_()
if __name__ == "__main__":
app = QApplication(sys.argv)
ui = Ui_Form()
sys.exit(app.exec_())
I created a QListWidget... The QListWidgetItem composes of a QPushButton and QLineEdit aligned inside QHBoxLayout...
Another button is created with a function to loop on QListWidgetItem(s) and read the text in each QLineEdit entry and print it...
The print shows nothing, I tried a lot of different combinations with QListWidget methods but with no hope!
import sys
from PyQt4 import QtGui, QtCore
def Add_OtherItem():
ItemOther = CustomItem()
ItemOther.SetupItem(OthersCommandsWidget)
def ReadText_fn():
for index in range(0,OthersCommandsWidget.count()):
TargetItem = (OthersCommandsWidget.item(index)).text()
print(TargetItem)
app = QtGui.QApplication(sys.argv)
class CustomItem(object):
def SetupItem(self, OthersCommandList):
self.Item = QtGui.QListWidgetItem()
self.Item.setStatusTip("TItem")
self.MainWidget = QtGui.QWidget()
self.CommandLine = QtGui.QLineEdit("")
self.DeleteButton = QtGui.QPushButton()
self.DeleteButton.setFixedSize(22, 22)
self.ItemLayoutBox = QtGui.QHBoxLayout()
self.ItemLayoutBox.addWidget(self.CommandLine)
self.ItemLayoutBox.addWidget(self.DeleteButton)
self.MainWidget.setLayout(self.ItemLayoutBox)
self.Item.setSizeHint(self.MainWidget.sizeHint())
OthersCommandList.addItem(self.Item)
OthersCommandList.setItemWidget(self.Item, self.MainWidget)
AppWindow = QtGui.QMainWindow()
AppWindow.setWindowTitle("PoC ListWidget")
AppWindow.setFixedSize(550, 550)
TabWindow = QtGui.QTabWidget(AppWindow)
TabWindow.setGeometry(8, 30, 535, 505)
WorkTAB = QtGui.QWidget()
TabWindow.addTab(WorkTAB, 'Tab.01')
OthersCommandsWidget = QtGui.QListWidget(WorkTAB)
OthersCommandsWidget.setGeometry(QtCore.QRect(8, 40, 515, 430))
AddButton = QtGui.QPushButton(WorkTAB)
AddButton.setText("Add Item")
AddButton.setGeometry(QtCore.QRect(8, 8, 0, 0))
AddButton.setFixedSize(70, 22)
AddButton.clicked.connect(Add_OtherItem)
ReadButton = QtGui.QPushButton(WorkTAB)
ReadButton.setText("Read Text")
ReadButton.setGeometry(QtCore.QRect(100, 8, 0, 0))
ReadButton.setFixedSize(70, 22)
ReadButton.clicked.connect(ReadText_fn)
AppWindow.show()
sys.exit(app.exec_())
You're trying to access the text field of a QLineEdit(). To do this, you must call its text() property. But first, you must grab the QLineEdit() object.
OthersCommandsWidget is of type QListWidget() which has a QListWidgetItem() that is connected to a QWidget(). You can grab the QWidget() object using itemWidget(). QWidget() has three embedded objects: QHBoxLayout(), QLineEdit() and QPushButton() which can be accessed using children() according to the order they were added.
Change
TargetItem = (OthersCommandsWidget.item(index)).text()
To:
TargetItem = OthersCommandsWidget.itemWidget(OthersCommandsWidget.item(index)).children()[1].text()