Stretch stopped working after parenting widget to layout pyqt - python

I am here with pyqt question again.
I think, this question was probably answered somewhere, but I can't phrase my problem properly in a way to find it in google, so sorry if it's duplicated the question.
I wrote simple code to show the problem I am facing.
import sys
from PyQt5.QtWidgets import QDialog, QVBoxLayout, QHBoxLayout, QApplication, QMenu, QWidget, QPushButton
class MainWindow(QDialog):
def __init__(self):
QDialog.__init__(self)
self.mainLayout = QVBoxLayout()
self.setLayout(self.mainLayout)
self.create()
def create(self):
btn_widget_a = QWidget()
btn_layout_a = QHBoxLayout(btn_widget_a)
self.mainLayout.addWidget(btn_widget_a)
btn_a = QPushButton()
btn_layout_a.addWidget(btn_a)
btn_a.setFixedSize(60, 60)
btn_b = QPushButton()
btn_layout_a.addWidget(btn_b)
btn_b.setFixedSize(60, 60)
tool_widget = QWidget()
btn_layout_a.addWidget(tool_widget)
tool_menu= QMenu(tool_widget)
tool_menu.addMenu('Options')
btn_a.setMenu(tool_menu)
if __name__ == '__main__':
app = QApplication(sys.argv)
main = MainWindow()
main.show()
app.exec_()
Problem is this btn_layout_a.addWidget(tool_widget) when I add QMenu inside the widget in my layout, buttons don't stretch anymore when you resize your UI. Is there a way to fix it? I know that if simply delete btn_layout_a.addWidget(tool_widget) it will work properly, but thing is that unless I put it in a widget, QMenu doesn't work properly inside the software called Maya. I tried adding stretch as an argument to widget/layout and tried command addStrerch() but nothing seems to give any result. Any suggestions? Thank you!

Related

Content of QAxWidget disappears when not in focus

I am currently exploring the possibilities of building a GUI that can directly interact with and display PowerPoint-presentations. For that I am using Python and PyQt5 on a windows machine with PowerPoint installed. The PowerPoint-presentation is embedded inside a QAxWidget and generally works as intended.
Unfortunately there is a small but hugely annoying bug that I haven't yet found a solution for. When the focus is not set on the QAxWidget, the content disappears until I click into the QAxWidget again.
Here's a gif that showcases this by clicking into other GUI elements:
I've already learned about setFocus(), but that doesn't seem to be applicable here.
Also I've looked into a couple of Qt-development-books for Python and C++, but I haven't found a single one working with ActiveX controls and QAxWidgets. Any research material working with QAxWidgets, ActiveX, anything that might suit my needs, is also highly appreciated. Only research material I am aware of is the C++ reference and posts on forums.
With the following minimal example code and the requirements stated in the beginning, you should be able to reproduce what I've shown in the GIF:
import sys
from PyQt5.QAxContainer import QAxWidget
from PyQt5.QtCore import QEvent
from PyQt5.QtWidgets import QWidget, QVBoxLayout, QPushButton, QApplication, QMainWindow
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
self.menuBar().addMenu("&About")
self.centralwidget = QWidget(self)
self.centralwidget_vlayout = QVBoxLayout(self.centralwidget)
self.axwidget = QAxWidget(self.centralwidget)
self.axwidget.installEventFilter(self)
self.axwidget.setControl(r"C:\path\to\presentation.pptx")
self.pushbutton = QPushButton(self.centralwidget)
self.pushbutton.setText("Press me!")
self.centralwidget_vlayout.addWidget(self.axwidget)
self.centralwidget_vlayout.addWidget(self.pushbutton)
self.setCentralWidget(self.centralwidget)
self.show()
def eventFilter(self, widget: QWidget, event: QEvent):
if event.type() == QEvent.Resize and widget is self.axwidget:
self.axwidget.setFixedHeight(int(self.axwidget.width() / 16 * 9))
return super(MainWindow, self).eventFilter(widget, event)
if __name__ == '__main__':
app = QApplication(sys.argv)
mainWin = MainWindow()
sys.exit(app.exec())
Now to the main question: How can I make sure that the PowerPoint-presentation is visible at all times, even when the focus is set to another GUI-element?

how to open widget, have it do something, and close by itself? [closed]

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 2 years ago.
Improve this question
I am trying to do the following:
I have a QMainWindow object. From it, I want to open window, have it do something, and close by itself.
Like start something > open window with "Loading.." message > do the thing > have the window close by itself without interaction from the user.
I have tried to create a QWidget class and put the "do the thing" method in there, but when I call the .show() method on the QWidget, the whole application hangs and doesn't go further, but maybe I don't understand the logic behind it.
I am using Python 3.7.7, PyQT5, my OS is Windows 10 x64 (if it's important)
I hope that I understood your question correctly. I would solve the problem in the following way:
from PyQt5 import QtTest
from PyQt5.QtCore import QTimer, Qt
from PyQt5.QtWidgets import QApplication, QMainWindow, QPushButton, QLabel, QVBoxLayout, QWidget
import sys
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
self.loading_window = None
self.button = QPushButton("Push for Window")
self.button.clicked.connect(self.show_new_window)
self.setCentralWidget(self.button)
self.timer = QTimer()
def show_new_window(self):
if self.loading_window is None:
self.loading_window = LoadingWindow()
self.loading_window.show()
self.do_your_job()
self.close_loading_window()
def do_your_job(self):
# Do whatever you want here
QtTest.QTest.qWait(1500)
def close_loading_window(self):
self.loading_window = None
class LoadingWindow(QWidget):
def __init__(self):
super().__init__()
layout = QVBoxLayout()
self.label = QLabel('Loading')
layout.addWidget(self.label)
self.setLayout(layout)
self.setWindowFlag(Qt.FramelessWindowHint)
app = QApplication(sys.argv)
w = MainWindow()
w.show()
app.exec_()

Troubleshooting Crash within PyQt5 Application (QDialog -> QMainWindow)

I am currently trying to troubleshoot an issue I am running into with a Qt application I am developing in Python. I only have a simple Main Window and a modal QDialog setup to collect user input after triggering a menu action on the Main Window. The QDialog is behaving as expected, since I've been able to confirm the test print is executed upon the QDialog returning Accepted, but the application will completely crash, without an error message, following the print statement. The same behavior occurs when QDialog returns Rejected. To clarify, the Main Window is shown for a couple seconds and the application crashes with no error messages. I would expect the function to return focus to the Main Window (with it still being open to operate on), after receiving either result from the Process function below.
I have also tried making the QDialog modeless (using show) and the accept/reject functions from the QDialog seem to return back to the main window as expected, but calling the function that brings up the QDialog again crashes the application. I am using pyqt 5.9 for this project, but I have used an essentially identical setup to the code below in a number of other projects in pyqt 5.6 without this happening. I am trying to figure out if there are any known issues with pyqt 5.9 that could be contributing to this issue, or if I have an error in my code somewhere that is causing this crash to occur. I was considering rolling back to 5.6 to see if that would get rid of the issue, but I feel like there may be something I am misunderstanding that is causing this to occur.
I am running the code out of Anaconda prompt (Anaconda 4.8.3, Python 3.7) on Windows 10, since Qt applications still have issues being run repeatedly within Spyder. I am using the pyqt that comes with Anaconda and haven't done any additional pip installs of pyqt.
Main Window Code
import sys
from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import QApplication, QMainWindow, QWidget, QGridLayout, QHBoxLayout, QVBoxLayout, QAction, QLabel
from PyQt5.QtWidgets import QDialog, QListWidget, QListWidgetItem, QAbstractItemView, QPushButton, QLineEdit, QSpacerItem, QSizePolicy
class FirstWindow(QMainWindow):
def __init__(self):
QMainWindow.__init__(self)
self.title = 'Window 1'
self.left = 350
self.top = 150
self.width = 800
self.height = 500
self.setWindowTitle(self.title)
self.setGeometry(self.left,self.top,self.width,self.height)
widget = QWidget()
self.setCentralWidget(widget)
grid = QGridLayout()
widget.setLayout(grid)
mainMenu = self.menuBar()
mainMenu.setNativeMenuBar(False)
subMenu = mainMenu.addMenu('File')
modifyDB = QAction('Test',self)
subMenu.addAction(modifyDB)
modifyDB.triggered.connect(self.Process)
self.statusBar().showMessage('Ready')
def Process(self):
dialog = DialogWin()
if dialog.exec_() == QDialog.Accepted:
print('test passed')
if __name__ == '__main__':
if not QApplication.instance():
app = QApplication(sys.argv)
else:
app = QApplication.instance()
mainWin = FirstWindow()
mainWin.show()
app.exec_()
Dialog Code
class DialogWin(QDialog):
def __init__(self,parent=None):
super(DialogWin,self).__init__(parent)
self.title = 'Database Table Name Selection'
self.left = 350
self.top = 150
self.width = 300
self.height = 300
self.setWindowTitle(self.title)
self.setGeometry(self.left,self.top,self.width,self.height)
vbox = QVBoxLayout()
spacer = QSpacerItem(10,10,QSizePolicy.Expanding,QSizePolicy.Expanding)
self.list_exp = QLabel('Select Existing Table to Overwrite')
vbox.addWidget(self.list_exp)
self.table_list = QListWidget()
self.table_list.setSelectionMode(QAbstractItemView.SingleSelection)
vbox.addWidget(self.table_list)
hbox = QHBoxLayout()
hbox.addItem(spacer)
self.option_exp = QLabel('<span style="font-size:8pt; font-weight:600; color:#aa0000;">OR</span>')
hbox.addWidget(self.option_exp)
hbox.addItem(spacer)
vbox.addLayout(hbox)
self.new_name = QLineEdit(placeholderText='Enter New Source Name')
vbox.addWidget(self.new_name)
hbox = QHBoxLayout()
self.okButton = QPushButton('OK')
hbox.addWidget(self.okButton)
self.okButton.clicked.connect(self.accept)
self.cancelButton = QPushButton('Cancel')
hbox.addWidget(self.cancelButton)
self.cancelButton.clicked.connect(self.reject)
vbox.addLayout(hbox)
self.setLayout(vbox)
item = QListWidgetItem('No Tables Available...')
item.setFlags(item.flags() ^ Qt.ItemIsSelectable)
self.table_list.addItem(item)
Any input on this issue would be tremendously helpful. I've spent a lot of hours trying to understand how this setup could be working for me in one application and not in another.
Explanation:
The problem is that you are using the same QSpacerItem 2 times, and when the QDialog is closed as it is a local variable it will be deleted, and Qt will also eliminate the internal objects, and in that case the QSpacerItem will have a double elimination that causes the "Segmentation fault".
Solution:
You must create 2 QSpacerItem:
# ...
hbox = QHBoxLayout()
hbox.addItem(spacer)
self.option_exp = QLabel('OR')
hbox.addWidget(self.option_exp)
hbox.addItem(QSpacerItem(10,10,QSizePolicy.Expanding,QSizePolicy.Expanding))
vbox.addLayout(hbox)
# ...
Another alternative is not to use QSpacerItem but to set a stretch factor:
# ...
hbox = QHBoxLayout()
hbox.addStretch()
self.option_exp = QLabel('OR')
hbox.addWidget(self.option_exp)
hbox.addStretch()
vbox.addLayout(hbox)
# ...
Or don't use QHBoxLayout and set the QLabel directly to the QVBoxLayout by setting the alignment:
# ...
self.table_list = QListWidget()
self.table_list.setSelectionMode(QAbstractItemView.SingleSelection)
vbox.addWidget(self.table_list)
self.option_exp = QLabel('OR')
vbox.addWidget(self.option_exp, alignment=Qt.AlignHCenter)
self.new_name = QLineEdit(placeholderText='Enter New Source Name')
vbox.addWidget(self.new_name)
# ...

PyQt5 QCompleter for QLineEdit crashing with no exception visible

I'm trying to use the QCompleter class for the QLineEdit to give auto completion suggestions while typing, and update the suggestions after the user enters a new text. But when I try to update the Completer with text that starts with something that is already at the completer list, it just crashes the entire app with no visible exception! Even try-except doesn't catch this error, and I can't understand what I am doing wrong...
Below is a simpler example of my code: it is a simple "echo" console application that gets commands from QLineEdit (input text box) and writing it to QTextBrowser (output text box). When entering entirely new "command" (text) it works fine, and being added to the completer, so next time I can see it. But if the new text starts similar to other words in the completer list, choosing it crashes the entire GUI app with no exception visible, not even when I'm running in debug mode...
Please see my example below, and try writing at the upper text box options like: a, aa, aaa (that start similar to completer already word: aaa1)
What am I doing wrong??
import sys
from PyQt5.QtWidgets import QApplication, QWidget, QMainWindow, QVBoxLayout, QLineEdit, QTextBrowser, QCompleter
class MainWindow(QMainWindow):
def __init__(self):
QMainWindow.__init__(self)
self.setWindowTitle('console')
self.setGeometry(10, 50, 500, 800)
# Create text box for input
self.consoleCommandLineEdit = QLineEdit(self)
self.consoleCommandLineEdit.setFixedHeight(25)
self.consoleCommandLineEdit.editingFinished.connect(self.gotConsoleCommand)
self.completerCommands = ['aaa1','aaa2','aaa3'] # initial completer list
completer = QCompleter(self.completerCommands)
self.consoleCommandLineEdit.setCompleter(completer)
# Create text box for output
self.consoleViewer = QTextBrowser(self)
self.consoleViewer.setLineWrapMode(QTextBrowser.NoWrap)
widget = QWidget(self)
self.setCentralWidget(widget)
self.vlay = QVBoxLayout(widget)
self.vlay.addWidget(self.consoleCommandLineEdit)
self.vlay.addWidget(self.consoleViewer)
def gotConsoleCommand(self):
cmd = self.consoleCommandLineEdit.text()
self.consoleCommandLineEdit.setText('')
self.sendCommandToConsole(cmd)
def sendCommandToConsole(self,cmd):
self.consoleViewer.append(cmd) # add cmd to output box
if cmd not in self.completerCommands: # if the command is new, add it to the completer
self.completerCommands.append(cmd) # 1. add the new text to the list we have
completer = QCompleter(self.completerCommands) # 2. create a new completer object
self.consoleCommandLineEdit.setCompleter(completer) # 3. set the new completer as the LineEdit completer
if __name__ == "__main__":
app = QApplication(sys.argv)
mainWin = MainWindow()
mainWin.show()
sys.exit(app.exec_())
I have not yet found the cause of the problem but a better solution than creating a new QCompleter every time you need to add a new text. In this case it is better to use a model to store the texts that is the basis of the QCompleter information.
import sys
from PyQt5.QtGui import QStandardItem, QStandardItemModel
from PyQt5.QtWidgets import (
QApplication,
QWidget,
QMainWindow,
QVBoxLayout,
QLineEdit,
QTextBrowser,
QCompleter,
)
class MainWindow(QMainWindow):
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent)
self.setWindowTitle("console")
self.setGeometry(10, 50, 500, 800)
# Create text box for input
self.consoleCommandLineEdit = QLineEdit()
self.consoleCommandLineEdit.setFixedHeight(25)
self.consoleCommandLineEdit.editingFinished.connect(self.gotConsoleCommand)
self.model = QStandardItemModel()
self.model.appendRow([QStandardItem(text) for text in ("aaa1", "aaa2", "aaa3")])
completer = QCompleter(self.model, self)
self.consoleCommandLineEdit.setCompleter(completer)
# Create text box for output
self.consoleViewer = QTextBrowser(lineWrapMode=QTextBrowser.NoWrap)
widget = QWidget()
self.setCentralWidget(widget)
vlay = QVBoxLayout(widget)
vlay.addWidget(self.consoleCommandLineEdit)
vlay.addWidget(self.consoleViewer)
def gotConsoleCommand(self):
cmd = self.consoleCommandLineEdit.text()
self.consoleCommandLineEdit.clear()
self.sendCommandToConsole(cmd)
def sendCommandToConsole(self, cmd):
self.consoleViewer.append(cmd) # add cmd to output box
if not self.model.findItems(cmd):
self.model.appendRow(QStandardItem(cmd))
if __name__ == "__main__":
app = QApplication(sys.argv)
mainWin = MainWindow()
mainWin.show()
sys.exit(app.exec_())
Perhaps something that helps us understand the problem is that if you add a parent to QCompleter the problem does not happen: completer = QCompleter(self.completerCommands, self)
but I stress: the best solution is to use a model.

User resizing PyQt widgets

I'm very new to PyQt and Python, so hopefully this question makes sense.
I have a QTreeView, QListView and QTextEdit set next to one another in a column like format. As a user, lets say I'd like to alter the width of one of the columns. How do I go about achieving this? If you're a user of Maya, imagine a paneLayout, this is what I'm trying to achieve.
Example:
[1][2][3]
[ 1 ][2][3]
[1][ 2 ][ 3 ]
...rather than just resizing the whole window. I hope this makes sense...!
Thanks to anyone who can help!
I'm not a Maya user so I don't know what a paneLayout is, but it sounds like what you're looking for can be achieved with a QSplitter (see here or here).
Here's a quick example:
from PyQt4.QtGui import QWidget, QApplication, QTreeView, QListView, QTextEdit, \
QSplitter, QHBoxLayout
import sys
class MainWindow(QWidget):
def __init__(self):
QWidget.__init__(self)
treeView = QTreeView()
listView = QListView()
textEdit = QTextEdit()
splitter = QSplitter(self)
splitter.addWidget(treeView)
splitter.addWidget(listView)
splitter.addWidget(textEdit)
layout = QHBoxLayout()
layout.addWidget(splitter)
self.setLayout(layout)
if __name__ == '__main__':
app = QApplication(sys.argv)
mainWindow = MainWindow()
mainWindow.show()
sys.exit(app.exec_())

Categories

Resources