PySide QFileDialog window size - pyqt

As far as I understand widget window size could be defined by calling 'setGeometry' function.
import_dialog = QtGui.QFileDialog()
import_dialog.setWindowTitle('Import File')
import_dialog.setDirectory(FILE_DIR)
import_dialog.setGeometry(100, 100, 200, 200)
import_file, _ = import_dialog.getOpenFileNames()
print(import_file)
But when I'm executing this part of my gui code, I'm facing pop up window that covers entire screen. I tried to make it smaller by calling 'setGeometry' function but with no results.
How can I make it to appear smaller?
Thanks

getOpenFileNames is a convenience static method of the QFileDialog class. It should handle creating the dialog, setting the right size as appropriate for your OS, and retrieving the result. Try calling it like this:
filenames, _ = QFileDialog.getOpenFileNames(parent, "Select file", FILE_DIR)
If that does not help, you can create the dialog yourself (as you did) and call show(), change the size, and bind the fileSelected signal to a slot.

Related

PyQt5 : how can I get the content of a QTextEdit to refresh on tab change?

[Question reformulated for clarity]
I have a simple GUI with 2 tabs, the first one contains a read only QTextEdit used for program logs, the second one contains various widgets for user input and a "generate" button.
The GUI was made using Qt Designer. Note : all elements have default attributes ( they were not edited / no options changed ) apart from the QTextEdit that is read only
The arrows are to show the QTextEdit that is used for the logs and the generate button that causes the tab change
Upon clicking on the generate button, the focused tab is switched to the first one ( with the logs ) and a separate thread is used to compute the user data and write it's log onto the QTextEdit
class GUI:
def __init__(self, core):
self.core = core
self.app = QtWidgets.QApplication(sys.argv)
self.window = Application()
self.__link_buttons__()
def __link_buttons__(self):
generate_button = self.window.get_generate_button()
if generate_button is None:
raise RuntimeError("could not find the generation button needed for the gui ( possibly an issue with the .gui file ), cannot start software")
generate_button.clicked.connect(self.__generate_threaded)
def __generate_threaded(self):
logger.logger_instance.log("user requested generation", LogLevel.NORMAL)
self.window.set_main_tab_to_log()
if self.core.check_if_generating() is True:
logger.logger_instance.log("Generator is currently in use", LogLevel.ERROR)
return
thread = threading.Thread(target=self.core.generate_gui)
thread.start()
class Application(QtWidgets.QMainWindow):
def __init__(self):
super(Application, self).__init__()
self.ui = uic.loadUi(settings.settings_instance.gui_path / "main_window.ui", self)
self.show()
self.__tabs = self.ui.findChild(QTabWidget, "mainTabs")
self.__log_tab = self.ui.findChild(QWidget, "logTab")
def get_gui_handle(self):
return self.ui.findChild(QTextEdit, "LogOutput")
def get_generate_button(self):
return self.ui.findChild(QPushButton, "generateButton")
def set_main_tab_to_log(self):
self.__tabs.setCurrentWidget(self.__log_tab)
The logger is the class that is in charge of writing to the QTextEdit on the GUI, it calls the get_gui_handle() method on start to get the QTextEdit and then uses append() to write to it ( with thread protection )
Possibly important details : I am using the standard Python threads ( import threading ), not the Qt as the rest of the software uses them and I am unsure if they can be mixed
The logger does write successfully to the QTextEdit but the application does not display the text as intended. Any new text is displayed normally but the previous logs are not and will only show upon window resize / clicking on the text / changing tabs / ... ( what I presume are events that get the application to re-render the QTextEdit )
Pictures for clarity :
First log is displayed as intended :
Second log is also displayed as intended but the first log is now invisible
First log is displayed again after window resize / text overlining / tab change
The issue was that even though I was using thread protections, I was calling the append() method from a separate thread ( thread protection is not enough ! ). Trying to refresh / actualize the gui after a call from a different thread is also a bad idea.
This is due to the fact that Qt manages internally events and that not using the main thread seems to circumvent them with in turn causes Qt to not see that it's content was changed.
Important note for the solution : using threads or qt threads is irrelevant in this specific case as qt simply wraps a threading library. It is however very recommended to use qt threads on all of the software simply to avoid having 2 different threads library used in the same program, witch in turn can cause issues.
I used pyqtSignal in order to be able to correctly communicate with the gui without having issues
from PyQt5.QtCore import QObject, pyqtSignal
class GUI(QObject):
__gui_log_signal = pyqtSignal(str)
def __init___():
# ...
self.__gui_handle = self.window.get_gui_handle() # this method returns the reference to the QTextEdit directly
self.__gui_log_signal.connect(self.__gui_handle.append)
This way I can use self.__gui_log_signal.emit("string or variable here") when needed
Important details to note : ( things that made getting the answer in the first place harder than expected )
The pyqtSygnal must be at the root of the class, not in the __init__() else it's not going to work properly ( in my instance the emit() method was not found )
the pyqtSignal can be used to pass variables, they just need to be declared as such ( example : pqtSygnal(str) ) and then connected to a method that uses the same types / variable count
The class that uses the pyqtSygnal must extend QObject or a class that extends it

PyQT5 - how can i get the QApplication to call a method once it is loaded?

I would like to write in a QTextEdit as soon as the main window is loaded, how can I do that efficiently ?
I tried changing a boolean value once app.exec() is called but since that's the main application loop, it does not work.
The only current solutions I have ( and that I would like to avoid ) are doing a timer or asking the user to press a button that I link to the method.
I tried looking into the signals sent by QApplication, QGuiApplication and the parents but could not find a signal related to the main window having loaded anything.
If something has to happen as soon as a widget is [going to be] shown, you can do it in the showEvent() method:
class MainWindow(QtWidgets.QMainWindow):
firstShown = False
def showEvent(self, event):
super().showEvent(event)
if not self.firstShown:
self.firstShown = True
self.textEdit.setPlainText('hello')
Note that this does not exactly happen as soon as the window is shown (there's a moltitude of reason for this, including the fact that the system's "window manager" might need some time to actually show the widget); In such cases, it is safe enough to use a singleshot QTimer set to 0:
class MainWindow(QtWidgets.QMainWindow):
firstShown = False
def showEvent(self, event):
super().showEvent(event)
if not self.firstShown:
self.firstShown = True
QtCore.QTimer.singleShot(0, self.doStartupEvents)
def doStartupEvents(self):
self.textEdit.setPlainText('hello')
Another theoretical possibility is to do those events in the paintEvent (ensuring that they happen only the first time), but I wouldn't suggest it.

ResizeEvent is called indefintely when fitInView is called (QGraphicsScene)

I have this weird problem.
I create my scene inside a QGraphicsView extended class like so:
scene = new QGraphicsScene(this);
this->setScene(scene);
this->setAlignment(Qt::AlignTop|Qt::AlignLeft);
showRect.setCoords(0,0,sceneWidth,sceneHeight);
However. This same class has reimplemented the resizeEvent Method according to documentation:
void ConversationView::resizeEvent(QResizeEvent *e){
//Q_UNUSED(e);
this->fitInView(showRect,Qt::KeepAspectRatioByExpanding);
qWarning() << e->size();
}
Now I add a box the scene and nothing happens. But when I start resizing the window, there comes a point where I stop and the program hangs and I keep seing the sizing message, forever and ever with very very small variations on its size:
QSize(1342, 190)
QSize(1356, 190)
QSize(1342, 190)
QSize(1356, 190)
Any ideas?
I figured out what the problem was. Instead of redefining the ConversationView's resize event (which extends from QGraphicsView), I redefined the containing Widget's resizeEvent (in this case a class based of a QDialog).
With the exact same parameters this made the problem go away.

In Qt on Linux/X11, how do I fix main window ordering issue?

I've got a Qt application that has two main windows. On Linux, when one main window brings up a modal dialog, it comes up behind the other main window. What can I do to cause the dialog to always come up on top of ALL main windows?
NOTE: This only happens on Linux. We build this app on MacOSX as well, and the problem does not occur there.
Here's the code that brings up the dialog. The stuff in the #if is all the things I've tried to bring the window forward. I've tried various combinations and orders of these things.
QMessageBox dialog;
dialog.setIcon( QMessageBox::Information );
dialog.setWindowTitle( _documentName );
dialog.setText( tr("This document has unsaved changes. Do you want to save before closing?") );
dialog.setInformativeText( tr("Your changes will be lost if you don't save them.") );
dialog.setStandardButtons( QMessageBox::Save | QMessageBox::Discard | QMessageBox::Cancel );
dialog.setDefaultButton( QMessageBox::Save );
dialog.setFixedSize( dialog.size() ); // non-resizable window
#if STUFF_I_TRIED
dialog.show();
dialog.setVisible(true);
dialog.open();
dialog.activateWindow();
dialog.raise();
#endif
int result = dialog.exec();
I realize that exec() should be all I need to show the window. My idea in calling show() or open() was just to allow activateWindow() or raise() to take affect. Just foolin' around trying to get that damn dialog to come forward.
TIA for any help!
All the sequcence between #if 1_ and #endif looks pretty weird to me.
Normally, to show modal dialog, only exec() is needed:
QMessageBox msgBox;
msgBox.setText("They killed Kenny, again.");
int ret = msgBox.exec();
Reference.
You are doing quite a bit of things between your #if 1, that is likely confusing X11.
You need only ONE of those. Since you are working with Mac and X11, I suspect you want to use open() and get a sheet.
IIRC, show() vs. open() causes different window flags to be set, so
calling them right after each other may get the window into a strange
state. Also calling show() or open() should always activate or raise the window if it is a dialog, which QMessageBox is.
Try only using one of these and seeing what happens.

PyQt4 QSpinBox.selectAll() not working as expected

Python 2.5.4
PyQt4
I sub-classed a QDoubleSpinBox to emit a signal on a focusIn event:
#Custom widgets for DPL GUI
from PyQt4.QtCore import *
from PyQt4.QtGui import *
class DPLDoubleSpinBox(QDoubleSpinBox):
__pyqtSignals__ = ("valueChanged(double)", "focusIn()")
def __init__(self, *args):
QDoubleSpinBox.__init__(self, *args)
def event(self, event):
if(event.type()==QEvent.FocusIn):
self.emit(SIGNAL("focusIn()"))
#self.clear() Works as expected
self.selectAll() #See below
return QDoubleSpinBox.event(self, event)
if __name__ == "__main__":
import sys
app = QApplication(sys.argv)
widget = DPLDoubleSpinBox()
widget2 = DPLDoubleSpinBox()
widget.show()
widget2.show()
sys.exit(app.exec_())
If you click inside one box, then kill the other window, it works. If you click inside one, then the other, then focus any other window on the desktop, it seems to work.
I think it's a focus problem, but can't track it down. I just need it to select all when clicked on. I tried doing it through its line edit pointer, but I get the same results. Tried forcing focus to other widgets, but still same result.
You can connect a custom slot to fire when it emits "focusIn()". You can then anyQSpinBox.selectAll(), and it works, just not on itself.
I know this question is more than two years old, but since it is one of the first results when googling "qspinbox select on focus", I would like to leave a solution for future generations.
The problem is the behavior of the QSpinBox.lineEdit(). With the focusInEvent, you can call selectAll(), but for some reason, the mousePressEvent of QLineEdit clears the selection right after the focus event. See here for an explanation.
The solution is to install an event filter for the QSpinBox.lineEdit() widget or subclass QLineEdit and call QSpinBox.setLineEdit(). Either way, the link above will show you how to achieve the desired behavior by keeping a boolean flag around and filtering both focusInEvent and mousePressEvent.
According to this, put a QTimer.singleShot call to selectAll inside the overriden focusInEvent, and then magic happens.
class SpinBox(QSpinBox):
def focusInEvent(self, event: QFocusEvent) -> None:
QTimer.singleShot(0, self.selectAll)
or like this (not recommended):
b = QSpinBox()
b.focusInEvent = lambda _: QTimer.singleShot(0, b.selectAll)
I changed the event to QEvent.Enter
Now it will self.selectAll()
I can get away with this because it's for a touch screen application, so it wouldn't be obvious to the user that something is amiss. I'd still love to know what I'm missing, or if this is just a bug.

Resources