I've a stacklayout in a qt5-python program with 3 pages.
I change the page by clicking on three icons and it works perfectly.
import sys
from PyQt5.QtCore import QSize, Qt
from PyQt5.QtGui import QIcon, QPixmap
from PyQt5.QtWidgets import (
QAction,
QApplication,
QHBoxLayout,
QLabel,
QMainWindow,
QPushButton,
QStackedLayout,
QVBoxLayout,
QWidget,
QStatusBar,
QToolBar,
)
### TESTING CLASS ###
from PyQt5.QtGui import QColor, QPalette
from PyQt5.QtWidgets import QWidget
class Color(QWidget):
def __init__(self, color):
super().__init__()
self.setAutoFillBackground(True)
palette = self.palette()
palette.setColor(QPalette.Window, QColor(color))
self.setPalette(palette)
### TESTING CLASS ###
I_INDEX_PAGE_DISC = 0
I_INDEX_PAGE_SONG = 1
I_INDEX_PAGE_INSTRUMENT = 2
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
self.setWindowTitle("Studio Diary")
self.setWindowIcon(QIcon("img/icon/icone_main_window.png"))
### IMPOSTA DIMENSIONE INTERFACCIA ###
self.resize(900, 300)
### CREA LA BARRA DEGLI STRUMENTI ###
tbMainToolBar = QToolBar("Studio Diary Toolbar")
tbMainToolBar.setIconSize(QSize(64, 64))
tbMainToolBar.setMovable(False)
self.addToolBar(tbMainToolBar)
lstButtonIcon = []
### CREA ICONA DISCO ###
icoDisc = QIcon()
icoDisc.addPixmap(QPixmap("img/icon/toolbar/icon_disc_on.png"))
icoDisc.addPixmap(QPixmap("img/icon/toolbar/icon_disc_sel.png"), QIcon.Disabled)
### CREA BOTTONE DISCO ###
bticDisc = QAction(icoDisc, "Dischi", self)
bticDisc.setStatusTip("Gestisci i dischi")
lstButtonIcon.append(bticDisc)
### CREA ICONA CANZONE ###
icoSong= QIcon()
icoSong.addPixmap(QPixmap("img/icon/toolbar/icon_song_on.png"))
icoSong.addPixmap(QPixmap("img/icon/toolbar/icon_song_sel.png"), QIcon.Disabled)
### CREA BOTTONE DISCO ###
bticSong = QAction(icoSong, "Canzoni", self)
bticSong.setStatusTip("Gestisci le canzoni")
lstButtonIcon.append(bticSong)
### CREA ICONA STRUMENTO ###
icoInstrument = QIcon()
icoInstrument.addPixmap(QPixmap("img/icon/toolbar/icon_instrument_on.png"))
icoInstrument.addPixmap(QPixmap("img/icon/toolbar/icon_instrument_sel.png"), QIcon.Disabled)
### CREA BOTTONE STRUMENTO ###
bticInstrument = QAction(icoInstrument, "Strumenti", self)
bticInstrument.setStatusTip("Gestisci gli strumenthi")
lstButtonIcon.append(bticInstrument)
### AGGIUNGI BOTTONE DISCO ###
bticDisc.triggered.connect(lambda lam_pageDisc: self.activateDiscPage(lstButtonIcon))
tbMainToolBar.addAction(bticDisc)
### AGGIUNGI BOTTONE CANZONE ###
bticSong.triggered.connect(lambda lam_pageSong: self.activateSongPage(lstButtonIcon))
tbMainToolBar.addAction(bticSong)
### AGGIUNGI BOTTONE CANZONE ###
bticInstrument.triggered.connect(lambda lam_pageInstrument: self.activateInstrumentPage(lstButtonIcon))
tbMainToolBar.addAction(bticInstrument)
### CREA IL LAYOUT GENERALE ###
bvloMainPageLayout = QVBoxLayout()
self.stacklayout = QStackedLayout()
bvloMainPageLayout.addLayout(self.stacklayout)
self.stacklayout.addWidget(Color("red"))
self.stacklayout.addWidget(Color("green"))
self.stacklayout.addWidget(Color("yellow"))
wgtBodyApp = QWidget()
wgtBodyApp.setLayout(bvloMainPageLayout)
self.setCentralWidget(wgtBodyApp)
self.setStatusBar(QStatusBar(self))
def buildDiscPage(self, strColor="red"):
bhloDiscPage = QVBoxLayout()
tvAlbumSong = TreeView()
bhloDiscPage.addWidget(tvAlbumSong)
bhloDiscPage.addWidget(QLabel("Song"))
def buildSongPage(self):
bhloSongPage = QHBoxLayout()
self.stacklayout.addWidget(QLabel("cyan"))
def buildInstrumentPage(self):
bhloSongPage = QHBoxLayout()
self.stacklayout.addWidget(QLabel("yellow"))
def activateDiscPage(self,mLstButtonIcon):
### IMPEDISCI CHE LA PAGINA VENGA RICHIAMATA SE E' GIA' STATA SELEZIONATA ###
bticCurrentButton = mLstButtonIcon[I_INDEX_PAGE_DISC]
blnIsEnabled = bticCurrentButton.isEnabled()
if blnIsEnabled == True:
self.buildDiscPage("green")
self.stacklayout.setCurrentIndex(I_INDEX_PAGE_DISC)
bticCurrentButton.setDisabled(True)
self.resetIconButtonActivationStatus(mLstButtonIcon,I_INDEX_PAGE_DISC)
def activateSongPage(self,mLstButtonIcon):
### IMPEDISCI CHE LA PAGINA VENGA RICHIAMATA SE E' GIA' STATA SELEZIONATA ###
bticCurrentButton = mLstButtonIcon[I_INDEX_PAGE_SONG]
blnIsEnabled = bticCurrentButton.isEnabled()
if blnIsEnabled == True:
self.buildSongPage()
self.stacklayout.setCurrentIndex(I_INDEX_PAGE_SONG)
bticCurrentButton.setDisabled(True)
self.resetIconButtonActivationStatus(mLstButtonIcon,I_INDEX_PAGE_SONG)
def activateInstrumentPage(self,mLstButtonIcon):
### IMPEDISCI CHE LA PAGINA VENGA RICHIAMATA SE E' GIA' STATA SELEZIONATA ###
bticCurrentButton = mLstButtonIcon[I_INDEX_PAGE_INSTRUMENT]
blnIsEnabled = bticCurrentButton.isEnabled()
if blnIsEnabled == True:
self.buildInstrumentPage()
self.stacklayout.setCurrentIndex(I_INDEX_PAGE_INSTRUMENT)
mLstButtonIcon[I_INDEX_PAGE_INSTRUMENT].setDisabled(True)
self.resetIconButtonActivationStatus(mLstButtonIcon,I_INDEX_PAGE_INSTRUMENT)
### RESETTA LO STATUS DI ATTIVAZIONE DEI BOTTONI
# === riattiva tutti i bottoni tranne quello corrente ===
def resetIconButtonActivationStatus(self,mLstButtonIcon,mIntCurrent):
for iButton in range (len(mLstButtonIcon)):
btIcSingleButton = mLstButtonIcon [iButton]
if iButton != mIntCurrent:
btIcSingleButton.setEnabled(True)
appMain = QApplication(sys.argv)
winMain = MainWindow()
winMain.show()
appMain.exec_()
When I click on the icon I will open the relative page, using the method activatePage() that recall buildPage()
I would that the page content will be update.
For example: In a page I will query the database or in another one I will pass some parameters to the buildPage() function to change some elements or style in the page.
How should I modify these functions?
[edit]
0
This code works perfectly but I would like to know another thing about it.
I've a layout like the one in the screenshoot.
The treeview is created by a class that builds the tree by querying a database.
I would like to update it when I add a new album. Can I update only the treeview and not all the page (maybe separating in two layouts)? I need to do it in other page of the application so I would learn how to do it.
Everytime I click on the icon/button on the toolbar should I query the database to update the view? In other pages I could add some infos.
(see the image in the answers)
QStackedLayout works with "pages" which are reusable, just like a "tabbed" interface does (in fact, QTabWidget uses a private QStackedWidget, which is based on QStackedLayout).
Considering the fact that pages are reusable, your issue is that you're not actually switching to the pages, but you're continuously adding a new page every time.
While dynamic creation of pages is obviously possible, even for optimization purposes, in your case you don't need that since you only have 3 pages.
Since those 3 pages will probably have their own "static" layout, a better solution is to create separate subclasses for each page, which allows creating specific function to update the data whenever required. This is a far better approach than creating single functions that do almost the same thing when creating/switching pages, and you can have a single interface to access/set the data from and to each page.
This is a possible implementation:
from PyQt5 import QtCore, QtWidgets
class Disco(QtWidgets.QFrame):
def __init__(self):
super().__init__()
self.artista = QtWidgets.QLineEdit()
self.titolo = QtWidgets.QLineEdit()
layout = QtWidgets.QFormLayout(self)
layout.addRow('Artista', self.artista)
layout.addRow('Titolo', self.titolo)
def setData(self, data):
self.artista.setText(data.get('artista', ''))
self.titolo.setText(data.get('titolo', ''))
def getData(self):
return {'artista': self.artista.text(), 'titolo': self.titolo.text()}
class Canzoni(QtWidgets.QFrame):
def __init__(self):
super().__init__()
layout = QtWidgets.QVBoxLayout(self)
self.table = QtWidgets.QTableWidget(0, 2)
self.table.setHorizontalHeaderLabels(('Titolo', 'Durata'))
layout.addWidget(self.table)
self.addButton = QtWidgets.QPushButton('Aggiungi')
layout.addWidget(self.addButton)
self.addButton.clicked.connect(lambda:
self.table.insertRow(self.table.rowCount()))
def setData(self, data):
self.table.clearContents()
canzoni = data.get('canzoni', [])
self.table.setRowCount(len(canzoni))
for row, (titolo, durata) in enumerate(canzoni):
self.table.setItem(row, 0, QtWidgets.QTableWidgetItem(titolo))
durataItem = QtWidgets.QTableWidgetItem()
durataItem.setData(QtCore.Qt.DisplayRole, int(durata))
self.table.setItem(row, 1, durataItem)
def getData(self):
canzoni = []
for row in range(self.table.rowCount()):
canzoni.append([
self.table.item(row, 0).text(),
self.table.item(row, 1).data(QtCore.Qt.DisplayRole),
])
return {'canzoni': canzoni}
class Strumenti(QtWidgets.QFrame):
def __init__(self):
super().__init__()
layout = QtWidgets.QVBoxLayout(self)
self.table = QtWidgets.QTableWidget(0, 2)
layout.addWidget(self.table)
self.table.setHorizontalHeaderLabels(('Artista', 'Strumenti'))
self.table.verticalHeader().hide()
self.addButton = QtWidgets.QPushButton('Aggiungi')
layout.addWidget(self.addButton)
self.addButton.clicked.connect(lambda:
self.table.insertRow(self.table.rowCount()))
def setData(self, data):
self.table.clearContents()
strumenti = data.get('strumenti', [])
self.table.setRowCount(len(strumenti))
for row, (artista, strumento) in enumerate(strumenti):
self.table.setItem(row, 0, QtWidgets.QTableWidgetItem(artista))
self.table.setItem(row, 1, QtWidgets.QTableWidgetItem(strumento))
def getData(self):
strumenti = []
for row in range(self.table.rowCount()):
strumenti.append([
self.table.item(row, 0).text(),
self.table.item(row, 1).text(),
])
return {'strumenti': strumenti}
class MainWindow(QtWidgets.QMainWindow):
def __init__(self):
super().__init__()
self.stackWidget = QtWidgets.QStackedWidget()
self.setCentralWidget(self.stackWidget)
self.disco = Disco()
self.stackWidget.addWidget(self.disco)
self.canzoni = Canzoni()
self.stackWidget.addWidget(self.canzoni)
self.strumenti = Strumenti()
self.stackWidget.addWidget(self.strumenti)
self.pages = self.disco, self.canzoni, self.strumenti
self.loadAction = self.menuBar().addAction('Load')
self.loadAction.triggered.connect(self.loadData)
self.saveAction = self.menuBar().addAction('Save')
self.saveAction.triggered.connect(self.saveData)
for i, actionName in enumerate(('Disco', 'Canzoni', 'Strumenti')):
action = self.menuBar().addAction(actionName)
action.setData(i)
self.menuBar().triggered.connect(self.switchPageAction)
self.setStyleSheet('''
Disco {
background: red;
}
Canzoni {
background: green;
}
Strumenti {
background: yellow;
}
''')
self.switchPage(0)
def loadData(self):
data = {
'artista': 'Elio e le Storie Tese',
'titolo': 'Elio Samaga Hukapan Kariyana Turu',
'canzoni': [
['John Holmes', 206],
['Nubi di ieri sul nostro domani odierno', 253],
],
'strumenti': [
['Elio', 'Voci'],
['Rocco', 'Tastiere'],
]
}
for page in self.pages:
page.setData(data)
def saveData(self):
data = {}
for page in self.pages:
data.update(page.getData())
print(data)
def switchPageAction(self, action):
index = action.data()
if index is not None:
self.switchPage(index)
def switchPage(self, index):
self.stackWidget.setCurrentIndex(index)
for action in self.menuBar().actions():
action.setEnabled(action.data() != index)
Note that newly created rows in QTableWidget always return None for item() if no value has been set yet, so the saveFunction() above may throw exceptions, I obviously didn't add all required checkings; it's up to you to add proper data type checking.
Also consider that setting the palette on a parent widget propagates the palette to all its children too, including subchildren, that's why I used stylesheets instead, which allows using a proper selector for the class that requires specific properties; also note that I chose to use QFrame to simplify things, as QWidget doesn't directly support stylesheet drawing. Alternatively, you can use self.setAttribute(QtCore.Qt.WA_StyledBackground).
Related
I have embedded the vtkOrientationMarkerWidget to the QTWidget as an axes indicator, but I met a problem there arises a bug:
ERROR: In C:\Dev\Soft\vtk\source\Rendering\OpenGL2\vtkWin32OpenGLRenderWindow.cxx, line 217
vtkWin32OpenGLRenderWindow (00000278F82D1AD0): wglMakeCurrent failed in MakeCurrent(), error: Handle
Invalid
when I multiply generate QTWidget instances by clicking the button.
Because I want to add some buttons in the 3D view, I use QTWidget as a container, which is the root cause of the problem. Are there some solutions for this?
My environment is Python 3.8+PyQT5.15.0+VTK9.0.1
The code is:
The main window:
from PyQt5.QtWidgets import QMainWindow, QDesktopWidget, QFrame, QVBoxLayout, QApplication, QPushButton
from axes_widget import VTKWidget2
from axes import Axes
from axes2 import Axes2
class Widget:
def __init__(self):
self.window = QMainWindow()
self.window.resize(500, 400)
self.window.move(300, 310)
self.button = QPushButton('3D', self.window)
self.button.clicked.connect(self.click_3d)
self.button.move(380, 80)
def click_3d(self):
self._win_vtk = VTKWidget2()
self._win_vtk.show()
if __name__ == '__main__':
app = QApplication([])
widget_ins=Widget()
widget_ins.window.show()
app.exec_()
The VTK window:
from PyQt5.QtWidgets import QMainWindow, QDesktopWidget, QFrame, QVBoxLayout, QApplication
import sys
import vtk
import vtkmodules.qt
vtkmodules.qt.QVTKRWIBase = "QGLWidget"
# QGLWidget as the base class of the QVTKRenderWindowInteractor, instead of QWidget.
# This change is because it is reported that sometimes QWidget can cause rendering problems.
from vtkmodules.qt.QVTKRenderWindowInteractor import QVTKRenderWindowInteractor
from serum import dependency, singleton, inject
# #singleton
class VTKWidget2(QMainWindow):
def __init__(self, parent=None):
super(VTKWidget2, self).__init__(parent)
self.camera_focal_point = [0, 0, 0]
self.camera_position = [-4500, 0, 0]
self.camera_view_up = [0, -1, 0] # default, [0,1,0]
self.camera_azimuth = 0 # -20
self.camera_zoom = 1.8 # 1.75
self.z_angle = 0
self.x_angle = 0
self.y_angle = 0
self.x_transform = 200
self.y_transform = -900 # -820
self.z_transform = 1000
# vtk.vtkOutputWindow.SetGlobalWarningDisplay(0)
self.initial()
def initial(self):
self.setWindowTitle("3D_Model")
self.resize(1000, 800)
screen = QDesktopWidget().geometry()
self.self_size = self.geometry()
self.move(int((screen.width() - self.self_size.width()) / 2),
int((screen.height() - self.self_size.height()) / 2)) #
self.colors = vtk.vtkNamedColors()
# Create an actor
# self.actor = vtk.vtkActor()
self.left_text_actor = vtk.vtkTextActor()
self.right_text_actor = vtk.vtkTextActor()
# A renderer and render window
self.renderer = vtk.vtkRenderer()
# renderWindow = vtk.vtkRenderWindow()
# renderWindow.SetWindowName("Display Coordinate Axes")
# renderWindow.AddRenderer(renderer)
# An interactor
# renderWindowInteractor = vtk.vtkRenderWindowInteractor()
# renderWindowInteractor.SetRenderWindow(renderWindow)
self.renderWindowInteractor = QVTKRenderWindowInteractor()
self.renderWindow = self.renderWindowInteractor.GetRenderWindow()
self.renderWindow.AddRenderer(self.renderer)
# Add the actors to the scene
# self.renderer.AddActor(self.actor)
self.renderer.SetBackground(self.colors.GetColor3d("SlateGray"))
# add mouse interaction mode
vtkStyle = vtk.vtkInteractorStyleTrackballCamera()
# vtkStyle = MyInteractorStyle(self.renderWindow)
# vtkStyle = vtk.vtkInteractorStyleSwitch()
# vtkStyle = vtk.vtkInteractorStyleTrackballActor()
self.renderWindowInteractor.SetInteractorStyle(vtkStyle)
# self.renderWindow.GetInteractor().SetInteractorStyle(vtkStyle)
self.transform = vtk.vtkTransform()
self.transform.Translate(self.x_transform, self.y_transform, self.z_transform)
# set axes
self.add_axes()
self.vtkCamera = vtk.vtkCamera()
self.update_camera()
frame = QFrame()
self.setCentralWidget(frame) # QMainWindow's property
vl = QVBoxLayout()
vl.addWidget(self.renderWindowInteractor)
# vl.addWidget(self.widget) # problem: wrong: no widget for QT
frame.setLayout(vl)
# Begin mouse interaction
self.renderWindowInteractor.Initialize()
self.renderWindowInteractor.Start()
def add_axes(self):
# set axes
### important: widget must be set as field, otherwise it doesn't show.
# problem: no widget for QT
self.widget = vtk.vtkOrientationMarkerWidget()
self.axis = vtk.vtkAxesActor()
rgba = [0] * 4
self.colors.GetColor("Carrot", rgba)
self.widget.SetOutlineColor(rgba[0], rgba[1], rgba[2])
self.widget.SetOrientationMarker(self.axis) ### important
self.widget.SetInteractor(self.renderWindowInteractor)
self.widget.SetViewport(0.0, 0.0, 0.4, 0.4)
self.widget.SetEnabled(1)
self.widget.InteractiveOn()
def update_camera(self):
self.renderer.ResetCamera()
self.renderer.SetActiveCamera(self.vtkCamera)
self.vtkCamera.SetFocalPoint(self.camera_focal_point)
self.vtkCamera.SetPosition(self.camera_position)
self.vtkCamera.SetViewUp(self.camera_view_up)
self.vtkCamera.Azimuth(self.camera_azimuth)
self.vtkCamera.Zoom(self.camera_zoom)
self.renderWindow.Render()
The problem lies in Widget.click_3d(self). Every time you click the button you reassign a new VTKWidget2 to self._win_vtk. Since self._win_vtk is the only reference in your program to any of the VTKWidget2 windows, as soon as you assign a new value to self._win_vtk, the previous VTKWidget2 window will be deleted by the garbage collector (which apparently causes problems with the underlying QGLWidget). One way around this is to make a persistent reference to all the windows, for example by putting them in a list, e.g.
class Widget:
def __init__(self):
....
self.vtk_windows = []
def click_3d(self):
win = VTKWidget2()
self.vtk_windows.append(win)
win.show()
This still causes a bunch of errors when the last window is closed an the program exits which seems to be cause by using a QGLWidget as the base of your QVTKRenderWindowInteractor.
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 don't know why but i can't move a button using Pyside2.
I named a button Lina and i tried to move it using :
self.__ButtonLina.move(400,400)
but it doesn't work, maybe i place this line in the wrong place in the code ?
Here is the code: (Lina's button is in the last tab)
# -*- coding: utf-8 -*-
import sys
from PySide2 import QtCore, QtGui
from PySide2.QtWidgets import *
from PySide2.QtGui import *
# orange Continental rgb(255, 128, 0)
class Dialog(QDialog):
def __init__(self, parent=None):
QDialog.__init__(self,parent)
# Les champs
self.__champTexteNomAuteur = QLineEdit("")
self.__champTextePrenomAuteur = QLineEdit("")
self.__champDateNaissanceAuteur = QDateEdit()
self.__champDateNaissanceAuteur.setCalendarPopup(True)
self.__champTexteTitreLivre = QLineEdit("")
self.__champDatePublication = QDateEdit()
self.__champDatePublication.setCalendarPopup(True)
self.__ButtonLina = QPushButton("Lina")
self.__ButtonLina.setMaximumWidth(145)
self.__ButtonLina.move(400,400)#------<----here is the problem
self.__ButtonLina.setStyleSheet("background-color: rgb(255, 128, 0);")
# Les widgets
self.__widgetAuteur = QWidget()
self.__widgetLivre = QWidget()
self.__widget1 = QWidget()
self.__widget2 = QWidget()
self.__Tools = QWidget()
self.__widget1.setStyleSheet("background-color: black;");
# Les layouts des onglets
self.__layoutTools = QFormLayout()
self.__layoutTools.addRow(self.__ButtonLina)
self.__Tools.setLayout(self.__layoutTools)
self.__layoutAuteur = QFormLayout()
self.__widgetAuteur.setLayout(self.__layoutAuteur)
# La boîte à onglets
self.__tabWidget = QTabWidget()
self.__tabWidget.addTab(self.__widgetAuteur, " Single Simulation ")
self.__tabWidget.addTab(self.__widgetLivre, " Batch Simulation ")
self.__tabWidget.addTab(self.__widget1, " Vehicule Simulation Tool ")
self.__tabWidget.addTab(self.__widget2, " Simulation ")
self.__tabWidget.addTab(self.__Tools, " Tools ")
# Le layout final
self.__mainLayout = QVBoxLayout()
self.__mainLayout.addWidget(self.__tabWidget)
self.setLayout(self.__mainLayout)
self.resize(1200,800)
self.setWindowTitle('VSS Vehicule Simulation Suite')
self.setStyleSheet("color: black;"
"background-color: black");
app = QApplication(sys.argv)
dialog = Dialog()
dialog.exec_()
if you have any idea it could be cool !
The layouts are used to manage the position and size of the widgets that it handles, in your case the position of the __ButtonLina is handled by __layoutTools, therefore the position you set manually is not applied. So if you want to establish a fixed position you should not use a layout.
In this case the solution is to remove the layout and set __ButtonLina to __Tools as parent of __ButtonLina to __Tools
# ...
self.__widget1.setStyleSheet("background-color: black;");
# Set as parent of__ButtonLina to __Tools
# so that the position of __ButtonLina is relative to __Tools
self.__ButtonLina.setParent(self.__Tools)
# remove layout
# Les layouts des onglets
# self.__layoutTools = QFormLayout()
# self.__layoutTools.addRow(self.__ButtonLina)
# self.__ButtonLina.clicked.connect(Lina)
# self.__Tools.setLayout(self.__layoutTools)
self.__layoutAuteur = QFormLayout()
# ...
I have a mainwindow which has a button which opens a second window when clicked. This second window contains a mayavi plot. Every time I click this button an error is shown
QCoreApplication::exec: The event loop is already running
I have looked at multiple similar questions on SO, but I cannot fix my problem.
The code of the mainwindow maingui.py:
# Standard library imports
import re
import sys
import string
import inspect
import platform
import importlib
import ctypes
from os.path import abspath, join, dirname, realpath
from functools import wraps
import inspect
import matplotlib as mpl
# Local imports
from loadhistory.stresscorrection import DnvThicknessStressCorrection
# PyQt5 imports
from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import QApplication, QMainWindow, QMessageBox, QListView, QWidget, QGridLayout
from PyQt5.QtGui import QPixmap
# Gui package import
import gui.design
import gui.resources
from gui.inputs import Inputs
from gui.filemanager import Filemanager
from gui.datawrapper import RunThread
from gui.mayaviwindow import MayaviWindow
class MainWindow(QMainWindow, gui.design.Ui_MainWindow):
'''main window of the gui'''
def __init__(self):
QMainWindow.__init__(self)
super().__init__()
# Bypass needed to set taskbar icon if on windows
if "Windows" in platform.system():
myappid = 'LaboSoete'
ctypes.windll.shell32.SetCurrentProcessExplicitAppUserModelID(myappid)
self.inputs = Inputs(self)
self.filemanager = Filemanager(self)
self.mayaviwindow = MayaviWindow()
self.setupUi(self)
self.setWindowTitle("Endurance framework SafeLife")
# Apply css stylesheet
sshFile = "gui/stylesheet.css"
with open(sshFile, "r") as fh:
self.setStyleSheet(fh.read())
# Initialize combobox design codes
self.designcodes = self.inputs.code_dict()
codes = list(self.designcodes.keys())
self.design_code_combo.addItems(codes)
self.design_cat_combo.addItems(self.designcodes[codes[0]])
self.scrollbar_QComboBox(self.design_code_combo)
self.scrollbar_QComboBox(self.design_cat_combo)
# Initialize combobox Damagemodels
module = importlib.import_module('damagemodels.damagemodels')
for m in module.__dict__.values():
if inspect.isclass(m):
if 'damagemodels.damagemodels.' in str(m):
if not inspect.isabstract(m):
self.dmgBox.addItem(m.__name__)
###### Menubar ######
self.actionSave.triggered.connect(self.filemanager.save)
self.actionSave_as.triggered.connect(self.filemanager.save_as)
self.actionLoad.triggered.connect(self.filemanager.load)
self.actionOutput.triggered.connect(self.filemanager.change_output_dir)
self.actionQuit.triggered.connect(self.close)
###### Button handeling ######
self.loadhistory_btn.clicked.connect(self.inputs.lh_browse_file)
self.odb_btn.clicked.connect(self.inputs.odb_browse_file)
self.design_code_combo.currentIndexChanged.connect(
self.inputs.design_combo_currentIndexChanged)
self.btn_snCustom.clicked.connect(self.inputs.sn_browse_file)
self.run_btn.clicked.connect(self.run_analysis)
self.newBtn.clicked.connect(self.filemanager.new)
self.abaqus_results_btn.clicked.connect(self.filemanager.open_odb_results)
self.damageNnumber.textChanged.connect(lambda: self.check_numeric_entry(self.damageNnumber))
self.hss_visualization_btn.clicked.connect(self.mayaviwindow.open_new_window)
# DNV thickness correction initialization
self.thicknessFrame.hide()
self.design_cat_combo.currentIndexChanged.connect(self.show_thickness_correction)
self.loadhistory_txt.textChanged.connect(self.loadhistory_txt_changed)
self.radioBtn_snBasquin.toggled.connect(self.show_thickness_correction)
self.radioBtn_snCustom.toggled.connect(self.show_thickness_correction)
self.radioBtn_snDesign.toggled.connect(self.show_thickness_correction)
self.radioBtn_snLog.toggled.connect(self.show_thickness_correction)
###### Tab/plot handeling ######
# Loadhistory plot
self.tabWidget.setCurrentIndex(0)
self.loadhistory_txt.textChanged.connect(lambda: self.LhWidget.run(self))
self.thicknessCorrCheckbox.toggled.connect(lambda: self.LhWidget.run(self))
self.t_txt.textChanged.connect(lambda: self.LhWidget.run(self))
self.tref_txt.textChanged.connect(lambda: self.LhWidget.run(self))
self.k_txt.textChanged.connect(lambda: self.LhWidget.run(self))
self.mscCheckBox.toggled.connect(lambda: self.LhWidget.run(self))
self.mscSelectionBox.currentIndexChanged.connect(lambda: self.LhWidget.run(self))
# Sncurve plot
self.design_cat_combo.currentIndexChanged.connect(self.inputs.design_cat_combo_change)
self.design_code_combo.currentIndexChanged.connect(self.inputs.design_combo_currentIndexChanged)
self.sncurve_txt.textChanged.connect(lambda: self.SnWidget.run(self))
self.radioBtn_snCustom.clicked.connect(lambda: self.SnWidget.run(self))
self.radioBtn_snDesign.clicked.connect(lambda: self.SnWidget.run(self))
self.radioBtn_snBasquin.clicked.connect(self.clear_sntxt_fields)
self.radioBtn_snLog.clicked.connect(self.clear_sntxt_fields)
self.Nf_txt.textChanged.connect(lambda: self.SnWidget.run(self))
self.uts_txt.textChanged.connect(lambda: self.SnWidget.run(self))
self.m1_txt.textChanged.connect(lambda: self.SnWidget.run(self))
self.B1_txt.textChanged.connect(lambda: self.SnWidget.run(self))
self.m2_txt.textChanged.connect(lambda: self.SnWidget.run(self))
self.B2_txt.textChanged.connect(lambda: self.SnWidget.run(self))
# HSS handeling
self.hss_checkbox.toggled.connect(self.show_hss_image)
self.hss_combobox.currentIndexChanged.connect(self.show_hss_image)
self.hss_combobox.currentIndexChanged.connect(self.show_hide_hss_spinbox)
###### Output related ######
self.hss_visualization_btn.hide()
self.hideTab(3)
self.abaqus_results_btn.hide()
self.newBtn.hide()
self.progressBar.hide()
#staticmethod
def eval_eq(equation):
'''
Check if the string 'equation' only contains allowable characters
'''
regex = re.compile(r'(\d+([.]|\*))?\d+(e|[.]|\*)?\d*(e|[.]|\*)?\d*?$')
if (equation.endswith('e') or equation.endswith('.')) and re.match(regex, equation):
return eval(equation[:-1])
elif re.match(regex, equation) and equation.count('e') < 2:
return eval(equation)
else:
raise ValueError(
'Illegal expression used to define the S-N curve, only e^*'+string.digits+' allowed')
def check_numeric_entry(self, myQlineEdit):
'''
For QlineEdit which can only contain numbers, check whether
no non-numeric characters are entered
'''
if myQlineEdit.text().isdigit() or len(myQlineEdit.text()) < 1:
return
else:
reply = QMessageBox.warning(self, 'TypeError', 'This field only allows for numeric entries')
if reply == QMessageBox.Ok:
myQlineEdit.setText(myQlineEdit.text()[:-1])
return
def loadhistory_txt_changed(self):
'''
If a loadhistory is selected the meanstresscorrection option
becomes enabled
'''
if len(self.loadhistory_txt.text())>0:
self.mscCheckBox.setEnabled(True)
else:
self.mscCheckBox.setDisabled(True)
def show_thickness_correction(self):
'''
Determine whether or not the thickness_correction option should be shown and enabled.
This is only the case when a DNV SN curve is selected
'''
if 'DNV' in self.design_code_combo.currentText() and self.radioBtn_snDesign.isChecked() and len(self.loadhistory_txt.text())>0:
self.thicknessFrame.show()
self.thicknessCorrCheckbox.setEnabled(True)
else:
self.thicknessFrame.hide()
self.thicknessCorrCheckbox.setChecked(False)
self.thicknessCorrCheckbox.setDisabled(True)
def show_hss_image(self):
if self.hss_checkbox.isChecked():
filename = self.hss_combobox.currentText()
filepath = abspath(join(dirname(realpath(__file__)), 'gui', 'icons', filename))
pixmap = QPixmap(filepath)
self.hss_imagelabel.setPixmap(pixmap)
self.hss_imagelabel.show()
self.tabWidget.setCurrentIndex(2)
if not self.hss_checkbox.isChecked():
self.hss_imagelabel.hide()
def get_thickness_correction(self):
'''
Return a thickness correction object using the values from the
gui to initialize the object
'''
if "DNV" in self.design_code_combo.currentText():
tref = self.eval_eq(self.tref_txt.text())
t = self.eval_eq(self.t_txt.text())
k = self.eval_eq(self.k_txt.text())
if k >= 1:
reply = QMessageBox.warning(
self, 'ValueError', 'k needs to be less than one, please consult DNV-GL-RP203 for the correct values')
if reply == QMessageBox.Ok or reply == QMessageBox.Cancel:
self.k_txt.clear()
return
else: return
return DnvThicknessStressCorrection(tref, t, k)
def show_hide_hss_spinbox(self):
'''
hide the hss_spinbox and hss_t_label widgets if a hss method, that does not
use the plate thickness to determine the extrapolation coords, is selected
'''
test = self.hss_combobox.currentText()
lst = ['1', '2', '3']
if any(x in test for x in lst):
self.hss_spinbox.show()
self.hss_t_label.show()
else:
self.hss_t_label.hide()
self.hss_t_label.show()
def scrollbar_QComboBox(self, combo):
'''
Add a scrollbar to QComboBoxes if the selected text is wider than
the combobox
'''
view = QListView(combo)
combo.setView(view)
view.setTextElideMode(Qt.ElideNone)
view.setHorizontalScrollBarPolicy(Qt.ScrollBarAsNeeded)
def closeEvent(self, event):
'''
Handle the event of the user wanting to close the application
'''
msgBox = QMessageBox()
msgBox.setWindowTitle('Exit application?')
msgBox.setText('Do you want to exit the application?')
exit_btn = msgBox.addButton('Exit', QMessageBox.YesRole)
save_exit_btn = msgBox.addButton('Save and Exit', QMessageBox.YesRole)
cancel_btn = msgBox.addButton('Cancel', QMessageBox.RejectRole)
msgBox.setEscapeButton(cancel_btn)
msgBox.exec_()
if msgBox.clickedButton() == exit_btn:
event.accept()
elif msgBox.clickedButton() == save_exit_btn:
self.filemanager.save()
event.accept()
elif msgBox.clickedButton() == cancel_btn:
event.ignore()
def start_thread(self, thread):
'''Pass all MainWindow attributes to the new thread and start the thread'''
thread.progressBar = self.progressBar
thread.loadhistory_txt = self.loadhistory_txt
thread.loadhistory_btn = self.loadhistory_btn
thread.odb_txt = self.odb_txt
thread.odb_btn = self.odb_btn
thread.dmgBox = self.dmgBox
thread.dmax_box = self.dmax_box
thread.radioBtn_snCustom = self.radioBtn_snCustom
thread.radioBtn_snBasquin = self.radioBtn_snBasquin
thread.radioBtn_snDesign = self.radioBtn_snDesign
thread.sncurve_txt = self.sncurve_txt
thread.btn_snCustom = self.btn_snCustom
thread.design_cat_combo = self.design_cat_combo
thread.design_code_combo = self.design_code_combo
thread.mscCheckBox = self.mscCheckBox
thread.mscSelectionBox = self.mscSelectionBox
thread.calcLifetimeBox = self.calcLifetimeBox
thread.calcLifetimeCheckBox = self.calcLifetimeCheckBox
thread.damageNcheckBox = self.damageNcheckBox
thread.damageNnumber = self.damageNnumber
thread.newBtn = self.newBtn
thread.m1_txt = self.m1_txt
thread.B1_txt = self.B1_txt
thread.m2_txt = self.m2_txt
thread.B2_txt = self.B2_txt
thread.uts_txt = self.uts_txt
thread.Nf_txt = self.Nf_txt
thread.SnWidget = self.SnWidget
thread.LhWidget = self.LhWidget
thread.Inputs = self.inputs
thread.thicknessCorrCheckbox = self.thicknessCorrCheckbox
thread.k_txt = self.k_txt
thread.t_txt = self.t_txt
thread.tref_txt = self.tref_txt
thread.hss_spinbox = self.hss_spinbox
thread.hss_combobox = self.hss_combobox
thread.hss_checkbox = self.hss_combobox
thread.start()
def clear_sntxt_fields(self):
'''Clear all text fields related to SN curve equations'''
self.Nf_txt.clear()
self.uts_txt.clear()
self.m1_txt.clear()
self.m2_txt.clear()
self.B1_txt.clear()
self.B2_txt.clear()
def initialize(self):
self.hss_visualization_btn.hide()
self.run_btn.setDisabled(True)
self.newBtn.hide()
self.abaqus_results_btn.hide()
self.hideTab(3)
self.output_txt.clear()
def finished(self):
if self.hss_checkbox.isChecked():
self.hss_visualization_btn.show()
self.run_btn.setEnabled(True)
self.newBtn.show()
self.abaqus_results_btn.show()
self.hide_progressbar()
self.showTab(3)
def show_progressbar(self):
self.progressBar.show()
def hide_progressbar(self):
self.progressBar.hide()
def clear_lhtxt(self):
self.loadhistory_txt.clear()
self.loadhistory_txt.setFocus()
def clear_odbtxt(self):
self.odb_txt.clear()
self.odb_txt.setFocus()
def clear_sncustomtxt(self):
self.sncurve_txt.clear()
self.sncurve_txt.setFocus()
def show_messagebox(self, errortype, msg, methodname):
reply = QMessageBox.warning(self, errortype, msg)
if methodname is not "None":
handle_error = getattr(self, methodname)
if reply == QMessageBox.Ok:
if 'handle_error' in locals():
handle_error()
return
def dmg_curve_plot(self, Dplot, nNplot):
self.DmgWidget.run(self, Dplot, nNplot)
def hideTab(self, tabIndex):
self.tabWidget.setTabEnabled(tabIndex, False)
def showTab(self, tabIndex):
self.tabWidget.setTabEnabled(tabIndex, True)
def check_required_inputs(self):
if "".__eq__(self.loadhistory_txt.text()) or "".__eq__(self.odb_txt.text()) \
or not self.odb_txt.text().endswith('odb'):
return False
if self.radioBtn_snCustom.isChecked() and "".__eq__(self.sncurve_txt.text()):
return False
if self.radioBtn_snBasquin.isChecked() or self.radioBtn_snLog.isChecked():
if ("".__eq__(self.m1_txt.text()) or "".__eq__(self.Nf_txt.text()) or \
"".__eq__(self.uts_txt.text()) or "".__eq__(self.B1_txt.text())):
return False
return True
def run_analysis(self):
mpl.pyplot.close(fig="all")
if self.check_required_inputs() is False:
self.show_messagebox('IOError', 'Please check all required fields', "None")
return
self.run_thread = RunThread(self)
self.run_thread.initialize.connect(self.initialize)
self.run_thread.clear_lhtxt.connect(self.clear_lhtxt)
self.run_thread.clear_odbtxt.connect(self.clear_odbtxt)
self.run_thread.clear_odbtxt.connect(self.clear_sncustomtxt)
self.run_thread.show_messagebox.connect(self.show_messagebox)
self.run_thread.show_progressbar.connect(self.show_progressbar)
self.run_thread.hide_progressbar.connect(self.hide_progressbar)
self.run_thread.finished.connect(self.finished)
self.run_thread.output.connect(self.filemanager.to_output)
self.run_thread.dmg_curve_plot.connect(self.dmg_curve_plot)
self.start_thread(self.run_thread)
if __name__ == '__main__':
app = QApplication(sys.argv)
window = MainWindow()
window.show()
sys.exit(app.exec_())
The code of the second window mayaviwindow.py:
import os
import sys
import mayavi.mlab as mlab
os.environ['ETS_TOOLKIT'] = 'qt4'
from pyface.qt import QtGui, QtCore
from traits.api import HasTraits, Instance, on_trait_change
from traitsui.api import View, Item
from mayavi.core.ui.api import MayaviScene, MlabSceneModel, SceneEditor
class MayaviWindow(QtGui.QMainWindow):
def open_new_window(self):
container = QtGui.QWidget()
container.setWindowTitle("Hot spot stress")
layout = QtGui.QGridLayout(container)
mayavi_widget = MayaviQWidget(container)
layout.addWidget(mayavi_widget, 1, 1)
container.show()
self.window = QtGui.QMainWindow()
self.window.setCentralWidget(container)
self.window.show()
class Visualization(HasTraits):
scene = Instance(MlabSceneModel, ())
#on_trait_change('scene.activated')
def update_plot(self):
self.scene.mlab.test_points3d()
# the layout of the dialog screated
view = View(Item('scene', editor=SceneEditor(scene_class=MayaviScene),
height=250, width=300, show_label=False),
resizable=True # We need this to resize with the parent widget
)
class MayaviQWidget(QtGui.QWidget):
def __init__(self, parent=None):
QtGui.QWidget.__init__(self, parent)
layout = QtGui.QVBoxLayout(self)
layout.setContentsMargins(0, 0, 0, 0)
layout.setSpacing(0)
self.visualization = Visualization()
# The edit_traits call will generate the widget to embed.
self.ui = self.visualization.edit_traits(parent=self,
kind='subpanel').control
layout.addWidget(self.ui)
self.ui.setParent(self)
Could someone explain to me what the problem is?
Edit
changing the class MayaviWindow to inherit from QWidget instead of QMainwindow did not solve the problem
class MayaviWindow(QtGui.QWidget):
def open_new_window(self):
self.container = QtGui.QWidget()
self.container.setWindowTitle("Hot spot stress")
layout = QtGui.QGridLayout(self.container)
mayavi_widget = MayaviQWidget(self.container)
layout.addWidget(mayavi_widget, 1, 1)
self.container.show()
Is there a way to customize the default toolbar of a mayavi scene? I would like to delete some buttons as I don't need them (e.g. the save button). Here you can see which toolbar I am talking about:
The code is just an example code:
import os
os.environ['ETS_TOOLKIT'] = 'qt4'
from pyface.qt import QtGui, QtCore
from traits.api import HasTraits, Instance, on_trait_change
from traitsui.api import View, Item
from mayavi.core.ui.api import MayaviScene, MlabSceneModel, SceneEditor
from tvtk.pyface.api import DecoratedScene
from pyface.api import ImageResource
from pyface.action.api import Action
class MyCustomScene(DecoratedScene):
def _actions_default(self):
actions = [
Action(
image = ImageResource("path to image",
search_path = [self._get_image_path()],
),
tooltip = "blabla",
on_perform = self._save_snapshot,
)
]
actions.extend(DecoratedScene._actions_default(self))
return actions
#The actual visualization
class Visualization(HasTraits):
scene = Instance(MlabSceneModel, ())
#on_trait_change('scene.activated')
def update_plot(self):
# We can do normal mlab calls on the embedded scene.
self.scene.mlab.test_points3d()
# the layout of the dialog screated
view = View(Item('scene', editor=SceneEditor(scene_class=MyCustomScene),
height=250, width=300, show_label=False),
resizable=True # We need this to resize with the parent widget
)
################################################################################
# The QWidget containing the visualization, this is pure PyQt4 code.
class MayaviQWidget(QtGui.QWidget):
def __init__(self, parent=None):
QtGui.QWidget.__init__(self, parent)
layout = QtGui.QVBoxLayout(self)
layout.setContentsMargins(0,0,0,0)
layout.setSpacing(0)
self.visualization = Visualization()
# The edit_traits call will generate the widget to embed.
self.ui = self.visualization.edit_traits(parent=self,
kind='subpanel').control
layout.addWidget(self.ui)
self.ui.setParent(self)
if __name__ == "__main__":
# Don't create a new QApplication, it would unhook the Events
# set by Traits on the existing QApplication. Simply use the
# '.instance()' method to retrieve the existing one.
app = QtGui.QApplication.instance()
container = QtGui.QWidget()
container.setWindowTitle("Embedding Mayavi in a PyQt4 Application")
# define a "complex" layout to test the behaviour
layout = QtGui.QGridLayout(container)
# put some stuff around mayavi
label_list = []
for i in range(3):
for j in range(3):
if (i==1) and (j==1):continue
label = QtGui.QLabel(container)
label.setText("Your QWidget at (%d, %d)" % (i,j))
label.setAlignment(QtCore.Qt.AlignHCenter|QtCore.Qt.AlignVCenter)
layout.addWidget(label, i, j)
label_list.append(label)
mayavi_widget = MayaviQWidget(container)
layout.addWidget(mayavi_widget, 1, 1)
container.show()
window = QtGui.QMainWindow()
window.setCentralWidget(container)
window.show()
# Start the main event loop.
app.exec_()
I think it is somewhere hidden in the MayaviScene. Maybe it is necessary to create a new class with a new scene or something like this?
You should check _actions_default for MayaviScene and for DecoratedScene to see how to create one of your own. The second one shows how to create a toolbar from scratch while the first one shows how your toolbar code interfaces with other components.
class MyCustomScene(DecoratedScene):
# …
def _actions_default(self):
actions = [
# add icons here
# …
]
return actions