I am a beginner in python but my OOPS concept from Java and Android are strong enough to motivate me in making some tool in python.
I am using PyQt for developing the application. In my application there are lot of QTabWidget used and has lot of UI controls in each TAB widget. Please see the screenshot for the same.
All of the event control of entire tool i have kept in one single file but now i want to segregate it based on one individual python file per QTab for event control inside the Tab.
My project file architecture looks like :
I know this would be some really easy thing but considering my experience with Python i am finding it difficult. I would really appreciate example with code snippet. Since i am able to control real complicated QThread from seperate files but not able get how to do it for Ui controls.
I tried making a file for it like i made for Thread classes but end up with argument passing expection to super
from generated.MainGUI import Ui_MainWindow
class SemiAuto_Create_Load(QtGui.QMainWindow):
def __init__(self,parent=none):
super(SemiAuto_Create_Load, self).__init__()
self.ui = Ui_MainWindow
self.ui.setupUi(self)
self.connectControlEvents()
Tried : self.sacl = SemiAuto_Create_Load()
Exception :
TypeError: init() takes exactly 3 arguments (1 given)
Okay i got this working with changes in
Mainwindow.py
class MainWindow(QtGui.QMainWindow):
def __init__(self, parent = None):
super(MainWindow, self).__init__(parent)
Profile().notify(None)
self.ui = Ui_MainWindow()
self.ui.setupUi(self)
self.connectButtons()
SemiAuto_Create_Load(self, self.ui)
And
SemiAuto_Create_Load
class SemiAuto_Create_Load(QtGui.QMainWindow):
def __init__(self, parent, ui):
super(SemiAuto_Create_Load, self).__init__(parent)
self.ui = ui
self.connectControlEvents()
def connectControlEvents(self):
self.ui.load_vsa_radio.clicked.connect(self.onLoad_vsa_radio)
self.ui.create_vsa_radio.clicked.connect(self.onCreate_vsa_radio)
Problem was passing the parameter with parent in init() and trying to get the object of MainGUI directly instead of as a parameter from MainWindow
Related
I have recently opted to learn programming using PyQt(6). I am trying to build a multi-tab main window where each tab is going to have their own functionality.
What I'm doing is that I designed my QMainWindow in QtDesigner and then used pyqt6.uic to import the .ui file, consequently accessing the widgets and views with findchild.
It is all going well If code like this, but the problem is that the great number of signals and slots in the code make the code very hard to debug and read after a short time.
I thought about defining each of these functionalities with their own class, but I could not make the connection between these sub classes and the main class.
I am not sure if that the correct approach to it, and if it is, then I probably miss a note or two with using multiple classes in PyQt.
Many thanks!
P.S.: I have updated the topic with a simple code to make my intention clearer Say I have main window with a tab widget consisting N tabs.
class UI(QMainWindow):
def __init__(self):
super(UI, self).__init__()
uic.loadUi('SO.ui', self)
#Instead of writing all the codes from tab1 to tabN. Use them in a clearer
#such as having their functionality in a different .py file.
#Maybe a way to inherit tab1 and tab2?
#Window runs via this method then.
class tab1(QMainWindow):
def __init__(self):
super(tab1, self).__init__()
uic.loadUi('SO.ui', self)
#signals and slots in Tab1 placed in a seperate .py file
class tab2(QMainWindow):
def __init__(self):
super(tab2, self).__init__()
uic.loadUi('SO.ui', self)
#signals and slots in Tab2 placed in a seperate .py file
I'm using PyQt, but I guess the same questions also applies to Qt C++.
Assume that I have a main window with a button that opens a wizard that collects data and that data needs to be used in the main window after the wizard has closed. standard procedure.
So there are multiple ways to do this. either I can pass a reference to the main window to the Wizard and it does all the work using the main window reference, but I'd say that breaks modularity. I can also wire up a callback to the wizard accepted rejected or finished signal, but in that callback, I don't have a reference to the wizard itself, so I cannot get to the data in the wizards fields. Unless I store a reference to the wizard as instance variable in order to access it again from the callback.
Another option is (even though I haven't fully understood it yet) to get a reference to the emitter of the signal (i.e. the wizard) in the callback using https://doc.qt.io/qt-5/qobject.html#sender. But that seems not recommended.
So whats the canonical way?
Premise: this is a bit of an opinion based question, as there is not one and only "good" way to do that. I just wanted to comment (opinion based answer/questions are discouraged here in SO), but the limited formatting isn't very helpful.
"Passing a reference" doesn't necessarily break modularity.
Instead, that's exactly what QDialog usually are initialized: the parent is the "calling" window, which is also how a QDialog can be "modal" to the parent or the whole application (meaning that no interaction outside the dialog is allowed as long as it is active).
AFAIK, I don't know if this is actually considered canonical, but the following is the most commonly suggested approach.
The idea is that you have a child object (a QDialog, usually) which might or might not be initialized everytime you need it, that's up to you; the important part is that you need a reference to it at least for the time required to update its result, which can even happen within the scope of a single method/slot.
from PyQt5 import QtWidgets
class MyWizard(QtWidgets.QDialog):
def __init__(self, parent=None):
super().__init__(parent)
layout = QtWidgets.QVBoxLayout()
self.setLayout(layout)
self.checkBox = QtWidgets.QCheckBox('check')
layout.addWidget(self.checkBox)
self.input = QtWidgets.QLineEdit()
layout.addWidget(self.input)
buttonBox = QtWidgets.QDialogButtonBox(QtWidgets.QDialogButtonBox.Ok|QtWidgets.QDialogButtonBox.Cancel)
layout.addWidget(buttonBox)
buttonBox.accepted.connect(self.accept)
buttonBox.rejected.connect(self.reject)
def setData(self, **data):
self.checkBox.setChecked(data.get('check', False))
self.input.setText(data.get('text', ''))
def getData(self):
return {'check': self.checkBox.isChecked(), 'text': self.input.text()}
def exec_(self, **data):
self.setData(**data)
return super().exec_()
class MyWindow(QtWidgets.QMainWindow):
def __init__(self):
super().__init__()
centralWidget = QtWidgets.QWidget()
self.setCentralWidget(centralWidget)
layout = QtWidgets.QHBoxLayout()
centralWidget.setLayout(layout)
self.showWizBtn = QtWidgets.QPushButton('Show wizard')
layout.addWidget(self.showWizBtn)
self.showWizBtn.clicked.connect(self.getDataFromWizard)
self.data = {}
def getDataFromWizard(self):
wiz = MyWizard(self)
if wiz.exec_(**self.data):
self.data.update(wiz.getData())
if __name__ == '__main__':
import sys
app = QtWidgets.QApplication(sys.argv)
window = MyWindow()
window.show()
sys.exit(app.exec_())
Another possibility is to create a persistent child dialog (but keep in mind that if the data can be changed by the parent, you'll have to find a way to update it, at least when executed); the concept here is that you can exec the dialog whenever you need, and you have the accepted signal connected to a slot that can get the data from the dialog. This is not a common use (nor very suggested IMHO) and should be used only for very specific scenarios.
As you already found out, using sender is not suggested: signals are asynchronous, and while in normal conditions the sender is reliable it's better to avoid using it unless absolutely necessary.
I want to create 2 windows the upper one needs to be a new project window and the lower one is the project GUI, I have created my lower window completely now I am planning to create a the upper window. How do I create my Upper(New project window)? Can I do it by including a function in my class and calling my function from my run()? or should I create a new class? I am completely confused help me!
My Lower Window(Main Project Code) Code:
class Softw(QtGui.QMainWindow, Doftw.Ui_MainWindow):
def __init__(self, parent=None):
super(Softw, self).__init__(parent)
self.setGeometry(50, 50, 700, 565)
self.setWindowTitle("Softy Softw")
-------------------------
--------(some Functions in Code)
And then finally my run() function which creates and calls the class
def run():
app=QtGui.QApplication(sys.argv)
GUI = Softw()
GUI.show()
sys.exit(app.exec_())
run()
If I do a new function in my class the window shows the upper window first then disappears and then shows the lower window:
def newProject(self):
window = QtGui.QWidget()
window.setGeometry(700,330,500,300)
window.setWindowTitle("New Project")
window.show()
I think the mistake is when to call the function when do I add my function call in this senario? In init method? or Somewhere else?
Thanks!
You can create QWidget using Qt Designer to every window that you want and then you can add them as a class to your file:
For example consider, you have created the .ui file for one window you want to create then convert it to .py using pyuic4 and then create your class like:
from Ui_created import the UI_class
class NewProject(QtGui.QMainWindow, Ui_class):
def __init__(self, parent = None):
super(NewProject, self).__init__(parent)
self.setupUi(self)
Now you can connect this to some button in the main class
So I'm quit new to working with UI in python. I'm not really grasping a core concept and i think this simple question will help flip on the light switch.
As seen in the code snippet below, I imported a ui file made in Qt. This ui has a pushbutton on it. How do I make a click event on that button? I have gone through tutorials on how to code a button and use it. I understand that. It is the question of how to access the objects and manipulate the objects that are created by the ui file. What i really want to do is see how to perform a function (or instantiate a class or whatever) when a button is clicked. that function being one that i wrote. baby steps though. any answers and elaborations would be appreciated.
import sys
from PyQt4 import QtGui, uic, QtCore
class MyWindow(QtGui.QMainWindow):
def __init__(self):
super(MyWindow, self).__init__()
uic.loadUi('myWidget.ui', self)
self.show()
if __name__ == '__main__':
app = QtGui.QApplication(sys.argv)
window = MyWindow()
sys.exit(app.exec_())
like i said. very simple question but I'm just not really grasping the core concept here. thanks you.
self.ui=uic.loadUi('curveViewer.ui', self)
#where `your_pushbutton` is the button name specified in your .ui file:
self.ui.your_pushbutton.clicked.connect(self.onBtnClicked)
or just:
uic.loadUi('curveViewer.ui', self)
self.your_pushbutton.clicked.connect(self.onBtnClicked)
then define method onBtnClicked inside your class MyWindow:
def onBtnClicked():
print 'pushbutton clicked'
see New-style Signal and Slot Support
btw, it's better to remove self.show(), and make it:
window = MyWindow()
window.show()
I'll start with the question and then try to explain:
Is there a way for an imported module to call a function in the module that imports it?
I am just learning to use Qt and am starting with Qt Designer to get some fundamentals worked out.
I have figured out how to create more than one ".ui" file in order to get the code for multiple windows and have managed to work out how to call the multiple windows from a main application by importing the code for the two windows.
For example, starting with win1.ui and win2.ui I create win1.py and win2.py - from my main application I import win1 and win2...
Note - I got this far by following this simple tutorial : http://www.youtube.com/watch?v=bHsC6WJsK-U&list=PLF4575388795F2531&index=10&feature=plpp_video
OK - now the question. If I have a button in win2, I know how to link that button to a function in the win2.py code. What I don't know how to do is link the button in win2 to a function in my main application.
My only thought would be to add a function as an argument to the class that sets up the second window but if I do that then any changes to win2.ui will wreck the code that I have changed.
Thus, Is there a way for an imported module to call a function in the module that imports it?
I hope this is clear without adding a bunch of code that isn't really relevant...
Qt is based on event-driven programming. Generally when you start building up your widgets, what you are going to be wanting to do is providing information to receiver widgets via signals that are then processed. You don't want to explicitly have a child widget know or require to call methods on a parent widget (this is not always the case, but it is good to avoid when possible).
I'm gonna post some examples that don't have UI files for ease here, but just assume you can build the same widget with designer and have it work the same way...
testwidget.py
from PyQt4 import QtGui, QtCore
class TestWidget(QtGui.QWidget):
textSaved = QtCore.pyqtSignal(str)
def __init__( self, parent = None ):
super(TestWidget, self).__init__(parent)
# create the ui (or load it)
self.__edit = QtGui.QTextEdit(self)
self.__button = QtGui.QPushButton(self)
self.__button.setText('Save')
layout = QtGui.QVBoxLayout()
layout.addWidget(self.__edit)
layout.addWidget(self.__button)
self.setLayout(layout)
# create connections
self.__button.clicked.connect(self.emitTextSaved)
def emitTextSaved( self ):
# allow Qt's blocking of signals paradigm to control flow
if ( not self.signalsBlocked() ):
self.textSaved.emit(self.__edit.toPlainText())
testwindow.py
from PyQt4 import QtGui, QtCore
import testwidget
class TestWindow(QtGui.QMainWindow):
def __init__( self, parent == None ):
super(TestWindow, self).__init__(parent)
# create the ui (or load it)
self.__editor = testwidget.TestWidget(self)
self.setCentralWidget(self.__editor)
# create connections
self.__editor.textSaved.connect(self.showMessage)
def showMessage( self, message ):
QtGui.QMessageBox.information(self, 'Message', message)
So, here you can see that instead of thinking about it like - "when I click the button in TestWidget, I want to show a message in TestWindow" and explicitly link the two methods, you expose a signal that the TestWidget will emit out when the user performs an action, then connect that signal to the showMessage slot of the TestWindow. This way, your smaller widgets become more independent, and its more a matter of how you connect to each event that drives your application.
I could have done something like self.parent().showMessage(self.__edit.toPlainText()) within the TestWidget's emitTextSaved method to call the method directly - but this is not a good design.