I've made my first small GUI program using PyQt5 for some data process for my work.
I have a problem - I can't make QFileDialog (to open file) with the focus on its QListView widget. (In QFileDialog class by default focus is on QLineEdit).
The best solution is to make new class from QFileDialog with changed setFocus settings and unchanged all the other.
It turned out to be not so easy because I didn't find how to adress to inner widgets of QFileDialog.
I'm new in PyQt5, couldn't find any solutionts even for Qt C++.
Thank you for any advices and ideas.
class XFileDialog(QtWidgets.QFileDialog):
"magic code"
file=XFileDialog.getOpenFileName(caption="Open",
filter="FITS (*.fits *.fts *.new)")
It seems that I have found the solution. Perhaps it could be useful for someone.
class XFileDialog(QtWidgets.QFileDialog):
def __init__(self):
QtWidgets.QFileDialog.__init__(self)
self.setDirectory(progdir)
def setVisible(self,v):
super(XFileDialog, self).setVisible(v)
self.setAcceptMode(0)
self.setFileMode(1)
self.setFocusPolicy(11)
self.setNameFilter("All (*) ;; FITS (*.fts *.fits *.new)")
self.focusPreviousChild()
class MyWindow(QtWidgets.QWidget):
def __init__(self,parent=None):
QtWidgets.QWidget.__init__(self,parent)
super().__init__()
self.initUI()
def openFile(self):
global progdir
progdir=QtCore.QDir(os.getcwd())
file=XFileDialog()
file.exec()
.......
Related
I'm using Qt Designer for design GUI to use in python, after designing my desired UI in Qt Designer, convert it to python code and then I changed generated code to do some action in my python code, but if I changed the UI with Qt Designer and convert it to python code again, I lost my previous changes on my code.
how can I solve the problem?
can we Spreading a Class Over Multiple Files in python to write code in other files?
To avoid having these problems it is advisable not to modify this file but to create a new file where we implement a class that uses that design.
For example, suppose you have used the MainWindow template in the design.ui file, then convert it to Ui_Design.py like to the following structure:
from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
[...]
def retranslateUi(self, MainWindow):
[...]
Then we will create a new file that we will call logic.py where we will create the file that handles the logic and that uses the previous design:
class Logic(QMainWindow, Ui_MainWindow):
def __init__(self, *args, **kwargs):
QMainWindow.__init__(self, *args, **kwargs)
self.setupUi(self)
So even if you modify the design and generate the file again .py you will not have to modify the file of the logic.
To generalize the idea we must have the following rules but for this the logic class must have the following structure:
class Logic(PyQtClass, DesignClass):
def __init__(self, *args, **kwargs):
PyQtClass.__init__(self, *args, **kwargs)
self.setupUi(self)
PyQtClass: This class depends on the design chosen.
Template PyQtClass
─────────────────────────────────────────────
Main Window QMainWindow
Widget QWidget
Dialog with Buttons Bottom QDialog
Dialog with Buttons Right QDialog
Dialog with Without Buttons QDialog
DesignClass: The name of the class that appears in your design.
The advantage of this implementation is that you can implement all the logic since it is a widget, for example we will implement the solution closing pyqt messageBox with closeevent of the parent window :
class Logic(QMainWindow, Ui_MainWindow):
def __init__(self, *args, **kwargs):
QMainWindow.__init__(self, *args, **kwargs)
self.setupUi(self)
def closeEvent(self, event):
answer = QtWidgets.QMessageBox.question(
self,
'Are you sure you want to quit ?',
'Task is in progress !',
QtWidgets.QMessageBox.Yes,
QtWidgets.QMessageBox.No)
if answer == QtWidgets.QMessageBox.Yes:
event.accept()
else:
event.ignore()
The easiest way is to use the *.ui file directly in the python code, you don't need convert to *.py file every time you change the ui.
you can use this pseudo code in your project.
# imports
from PyQt5 import uic
# load ui file
baseUIClass, baseUIWidget = uic.loadUiType("MainGui.ui")
# use loaded ui file in the logic class
class Logic(baseUIWidget, baseUIClass):
def __init__(self, parent=None):
super(Logic, self).__init__(parent)
self.setupUi(self)
.
.
.
.
def main():
app = QtWidgets.QApplication(sys.argv)
ui = Logic(None)
ui.showMaximized()
sys.exit(app.exec_())
I use python 3.6 and pyqt5 and load my Window by self.ui = uic.loadUi("MainWindow.ui",self) Mainly there are 2 QtreeViews and one QList Widgets. I've problems with dropEvent. I have to separate it to each Widget/model so that I have different 'dropActions'. But everytime I drop I get a call from the dropEvent Function of the self.ui Object I've tried to change self.ui.treeView.viewport().installEventFilter(self) to seperate functions without success. In the docs and in some examples found here and over the net there are the Widgets subclassed with the drag and drop functions. But I find no solution for Widgets generated by the uic loader. I've tried to install drag&drop functions inside the models. But no luck at all.
How can I subclass a Widget which is loaded by uic.loadUi?
or
How to create rules from where to where drag & drop is allowed ?
The easiest way is to do your subclassing beforehand.
Create my_tree_view.py as follows
from PyQt5 import QtWidgets, QtGui
class MyTreeView(QtWidgets.QTreeView):
def __init__(self, parent=None):
super().__init__(parent)
def dropEvent(self, event: QtGui.QDropEvent):
print('MyTreeView dropEvent')
super().dropEvent(event)
Then edit your .ui file and add the following near the end after the </widget> line.
<customwidgets>
<customwidget>
<class>MyTreeView</class>
<extends>QTreeView</extends>
<header>my_tree_view</header>
</customwidget>
</customwidgets>
And then change the following line (further up in your .ui file)
from
<widget class="QTreeView" name="treeView">
to
<widget class="MyTreeView" name="treeView">
I'm trying to set the minimum size of the buttons in this GtkButtonBox. Currently they seem to be fixed - approx 85 pixels I think.
Is this possible?
If not, is there another way in Gtk to get two small sized buttons to snuggle together like in the above picture rather than having them appear to be two separate buttons? For example GtkStackSwitcher may be something I could use but there doesn't appear to be a way to respond to click events for a button.
I've used this test program to create the above (Ubuntu 14.04, Gtk+3.10 and Python3):
from gi.repository import Gtk
import sys
class MyWindow(Gtk.ApplicationWindow):
def __init__(self, app):
Gtk.Window.__init__(self, title="example", application=app)
self.set_default_size(350, 200)
self.set_border_width(10)
hbox = Gtk.ButtonBox.new(Gtk.Orientation.HORIZONTAL)
hbox.set_layout(Gtk.ButtonBoxStyle.EXPAND)
button = Gtk.Button(label="a")
hbox.add(button)
button2 = Gtk.Button(label="b")
hbox.add(button2)
self.add(hbox)
class MyApplication(Gtk.Application):
def __init__(self):
Gtk.Application.__init__(self)
def do_activate(self):
win = MyWindow(self)
win.show_all()
def do_startup(self):
Gtk.Application.do_startup(self)
app = MyApplication()
exit_status = app.run(sys.argv)
sys.exit(exit_status)
With regards to a question about the desktop environment I'm using.
I've tried Mate, Unity and Gnome-Shell. All work the same way. I've removed the title and those controls. Still the same thing happens. To me this looks more like a GTK issue.
I believe that GtkButtonBox imposes some layout constraints on its buttons that you may not want here. Try using buttons in just a regular GtkGrid, but give them the GTK_STYLE_CLASS_LINKED CSS class.
For each button, do:
button.get_style_context().add_class(Gtk.STYLE_CLASS_LINKED)
i use pyqt in autodesk maya. all work but when i try connect a context menu to my elements - maya get fatal error and closed.
import maya.OpenMayaUI as mui
import maya.api.OpenMaya as om
import sip
from PyQt4 import QtGui, QtCore, uic
#----------------------------------------------------------------------
def getMayaWindow():
ptr = mui.MQtUtil.mainWindow()
return sip.wrapinstance(long(ptr), QtCore.QObject)
#----------------------------------------------------------------------
form_class, base_class = uic.loadUiType('X:/tools/Maya/windows/2014/python/UI/perforceBrowserWnd.ui')
#----------------------------------------------------------------------
# main window class
#----------------------------------------------------------------------
class PerforceWindow(base_class, form_class):
def __init__(self, parent=getMayaWindow()):
super(base_class, self).__init__(parent)
self.setupUi(self)
# Popup Menu is not visible, but we add actions from above
self.popMenu = QtGui.QMenu( self )
self.popMenu.addAction("revert", self.on_action_revert)
self.popMenu.addAction("submit", self.on_action_submit)
self.filesListWgt.customContextMenuRequested.connect( self.filesListWgtMenuRequested )
#------------------------------------------------------------------
def filesListWgtMenuRequested(self, pos):
self.popMenu.exec_( self.filesListWgt.mapToGlobal(pos) )
def on_action_revert(self):
print('on_action_revert')
def on_action_submit(self):
print('on_action_submit')
#----------------------------------------------------------------------
# window
#----------------------------------------------------------------------
def perforceBrowser2():
perforceBrowserWnd = PerforceWindow()
perforceBrowserWnd.show()
perforceBrowser2()
dialog created in qtdesigner. i set attribute contentMenuPolicy in designer on QListWidtet. when i right click on QListWidtet or any element - i see a context menu. but if i click a menu or dismiss it - maya get fatal error
and i see log text - function on_action_revert is called. but after that - maya crashed.
what i doing wrong?
update:
i try simple test. replace a menu to simple call a function:
replace connect to:
self.filesListWgt.customContextMenuRequested.connect( self.on_action_revert )
def on_action_revert(self):
print('on_action_revert')
this crash maya too
I tested your code along with your UI file on PyQt in Maya 2013 as well as on PySide (using a QtShim) on Maya 2014 and your code ran fine. Please check your PyQt build for Maya 2014.
I recommend attempting to run your code using PySide on Maya 2014. To do this you do not need to change any of your code base. You just need to modify a few imports. It is worth checking out these: Take a look at this. You can use this to write code that is compatible in both PyQt and PySide. https://github.com/rgalanakis/practicalmayapython/blob/master/src/chapter5/qtshim.py
And to load your ui file in PySide environment take a look at this article: http://www.jason-parks.com/artoftech/?p=579
PyQt and PySide are both just python wrappers for the Qt framework. They are identical apart from a very few differences. So your code base never needs to change no matter what you use to run it in.
P.s. But for whatever reason you are so particular for using PyQt for 2014, Please use these guides to build it: http://download.autodesk.com/us/support/files/maya_documentation/pyqtmaya2014.pdf and this one: http://around-the-corner.typepad.com/adn/2013/04/building-sip-and-pyqt-for-maya-2014.html
Maya specific PyQt builds are maintained in this Github repo maintained by Marcus Ottosson. You can grab the specific build for yourself and add it to PYTHONPATH. I had the same issue in Maya 2015 and this helped.
https://github.com/pyqt
In this program below, I am testing the affect of mousePressEvent:
import sys
from PyQt4 import QtGui, Qt, QtCore
class Test(QtGui.QFrame):
def __init__(self):
QtGui.QFrame.__init__(self)
self.setGeometry(30,30,500, 500)
self.show()
def paintEvent(self, e=None):
print "paintEvent"
qp = QtGui.QPainter()
qp.begin(self)
qp.drawRect(30,30,80,80)
qp.end()
def mousePressEvent(self, event):
if event.button() == QtCore.Qt.RightButton:
print "mousePressEvent- Right"
else:
print "mousePressEvent- Left"
if __name__ == '__main__':
app = QtGui.QApplication(sys.argv)
ex = Test()
sys.exit(app.exec_())
I see that in the first left-click, the frame's paintEvent is called. Is this because when the frame get the focus, it need to be repainted? I wonder if there is any way to avoid paintEvent to be called and every drawing in the frame still intact. The reason is because in my real program, the paintEvent is really heavy, I want to run it as less times as possible.
If that is not possible, is there a way to avoid the frame getting focus when left-click on that?
I don't know whether there are platform-specific differences at play here, but when I try the example code on Linux, there is no paintEvent when clicking on the frame.
This is not surprising really, because, by default, the QFrame is not configured to accept focus of any kind. To get the example to work, I had to explicitly set the focus policy, like this:
class Test(QtGui.QFrame):
def __init__(self):
QtGui.QFrame.__init__(self)
self.setFocusPolicy(QtCore.Qt.StrongFocus)
But maybe the defaults are somehow different on other platforms, and so, to explicitly prevent the frame getting the focus, you might need to do this:
self.setFocusPolicy(QtCore.Qt.NoFocus)