I have this code.....
import sys
sys.path.insert(0, '...')
import rotTool
reload(rotTool)
rotTool()
First time loading it into maya I get two windows. I realize it's because of the reload there, but without it, it won't load if you closed the window and need to reopen (while in the same session). How best should I fix this?
Thanks in advance
Let's say inside rotTool.py it looks something like this:
from PySide import QtGui
class RotTool(QtGui.QWidget):
def __init__(self, parent = None):
super(RotTool, self).__init__(parent)
self.resize(400, 400)
def run(self):
self.show()
It's a class that inherits from a QWidget, and when you call the run function, it opens up a window and shows it. Maybe you're not using PySide but the same idea should apply.
In Maya, if I need to open this window I execute:
import rotTool # Import module
myWin = rotTool.RotTool() # Create an instance of the class
myWin.run() # Call the instance's run method to open it up
If I close the window, I can just execute the last 2 lines again to create a new instance that opens the window. There could be a bit more to it than that, like making sure 2 windows can't be open at the same time, but this is how I would generally go about it.
I'm not sure what's in your module so it's hard to give specific advice to it. Instead of opening your window at the bottom of your script, you might just need to wrap it around a function, like my run() example, so that it doesn't automatically open on an import or reload. Hope that helps!
Related
I am currently creating a GUI in Python 3.7, using PyQt5 and Qt Designer in the Spyder environment. The GUI has many different windows. Basically I am starting with the UI_Start window and then open the next window when a button is pressed. The GUI is working kind of fine, however after approximately 50 windows the program suddenly doesn't show the next window anymore but also doesn't stop the execution. The weird thing about this issue is that:
the exact same window class has been called a lot of times beforehand and there have never been any issues
the problem does not only occur for one window but it can also occur for another window class (but after the same amount of windows being shown)
I tried to figure out why the .show() command is suddenly not working anymore. I used print statements to see where the program "breaks down". I saw that even the print statements after the .show() command are working but then as the window isn't shown I can't press any button to trigger the next event. So basically the program is hanging.
I am relatively new to programming in Python and creating GUIs but I thought that maybe the problem occurs due to memory leak. This is why I am now trying to open memory space when closing a window by using self.setAttribute(QtCore.Qt.WA_DeleteOnClose, True). However, now I am facing the problem that the next window doesn't show up anymore. So how can I use DeleteOnClose if I want to show a new window afterwards?
Also if anyone has a suggestion for the original problem, please let me know. I am trying to figure out the problem since like a week but have not come any further.
Thank you already!
Some part of my code to work with:
class UI_Start(QtWidgets.QMainWindow):
def __init__(self):
super(UI_Start, self).__init__() # Call the inherited classes __init__ method
uic.loadUi('Screen_Start.ui', self) # Load the .ui file
self.setAttribute(QtCore.Qt.WA_DeleteOnClose, True) # added newly
self.Start_pushButton_Start.clicked.connect(self.openKommiScreen)
def openKommiScreen(self):
self.close()
self.KommiScreen = UI_Kommi(self)
class UI_Kommi(QtWidgets.QMainWindow):
def __init__(self, parent = None):
super(UI_Kommi, self).__init__(parent)
uic.loadUi('Screen_Kommi.ui', self)
self.setAttribute(QtCore.Qt.WA_DeleteOnClose, True)
global sheetNo
sheetNo = 1
self.WeiterButton = self.findChild(QtWidgets.QPushButton,'pushButton_Weiter')
self.WeiterButton.clicked.connect(self.openScanScreen)
self.show()
def openScanScreen(self):
self.close()
self.ScanScreen = UI_Scan(self)
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
window = UI_Start()
window.show()
sys.exit(app.exec_())
At first I would guess it's a garbage collection problem. The only reference to your new window is stored in your previous one. Which is deleted, so there is no more reference to your window object and python may delete it automatically.
In these cases I often goes for a global variable to store the current windows references.
My problem is that I have a file with my UI called xxx.ui. Then as many have suggested I created another python file called test.py where I have put code to use my xxx.ui:
# imports
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5 import uic
import sys
class Ui(QtWidgets.QMainWindow):
def __init__(self):
super(Ui, self).__init__()
uic.loadUi('xxx.ui', self)
self.show()
app = QtWidgets.QApplication(sys.argv)
window = Ui()
app.exec_()
Up until this stage everything works ok. But now I would like to add a checkbox to my UI whe program starts without messing inside xxx.ui (so the checkbox will be created dynamicaly when the program runs).
How can I do that ???
Thank You in advance.
After many fail attempts I have found how to do it:
To add Checkbox outside of xxx.ui file. I went to my test.py file and added code below this line:
uic.loadUi('xxx.ui', self)
The code looks like that (I am using horizontal layout widget created in designer called seasonLayout and my checkbox is inside that layout):
self.checkBox = QtWidgets.QCheckBox(self.horizontalLayoutWidget)
self.checkBox.setObjectName("checkBox_0")
self.checkBox.setText('Hello')
self.seasonLayout.addWidget(self.checkBox)
Then if You want to get to that object all You have to do is to use code below:
(here i change text of this newly created checkbox):
self.checkBoxs = self.findChild(QtWidgets.QCheckBox, 'checkBox_0')
self.checkBoxs.setText('test')
Hopefuly it will be helpful for other because I have really tried to find answer for that almost everywhere and everyone were just using widgets from designer - noone explain how to add them outside of it.
I'm starting experimenting with Maya python, and I'm trying to do some UI.
I came across to a really strange problem, I can't get a button to stay in the center of the windows.
I've tried different things but nothing seems to work, here is the code:
import maya.cmds as cmds
cmds.window( width=200 )
WS = mc.workspaceControl("dockName", retain = False, floating = True,mw=80)
submit_widget = cmds.rowLayout(numberOfColumns=1, p=WS)
cmds.button( label='Submit Job',width=130,align='center', p=submit_widget)
cmds.showWindow()
this is a simple version but still, I can't get it to work.
can someone help me?
I honestly don't know the answer as anytime I have to dig into Maya's native UI stuff it makes me question my own life.
So I know it's not exactly what you're asking for, but I'll opt with this: Use PySide instead. At first glance it might make you go "woah, that's way too hard", but it's also a million times better (and actually easier). It's much more powerful, flexible, has great documentation, and also used outside of Maya (so actually useful to learn). Maya's own interface uses the same framework, so you can even edit it with PySide once you're more comfortable with it.
Here's a bare-bones example to create a centered button in a window:
# Import PySide libraries.
from PySide2 import QtCore
from PySide2 import QtWidgets
class MyWindow(QtWidgets.QWidget): # Create a class for our window, which inherits from `QWidget`
def __init__(self, parent=None): # The class's constructor.
super(MyWindow, self).__init__(parent) # Initialize its `QWidget` constructor method.
self.my_button = QtWidgets.QPushButton("My button!") # Create a button!
self.my_layout = QtWidgets.QVBoxLayout() # Create a vertical layout!
self.my_layout.setAlignment(QtCore.Qt.AlignCenter) # Center the horizontal alignment.
self.my_layout.addWidget(self.my_button) # Add the button to the layout.
self.setLayout(self.my_layout) # Make the window use this layout.
self.resize(300, 300) # Resize the window so it's not tiny.
my_window_instance = MyWindow() # Create an instance of our window class.
my_window_instance.show() # Show it!
Not too bad, right?
my long term goal is to build a gui for an experiment in experimental physics which has a continuously running gui. By pushing a button I would like to be able to run a pyhton script of my choice which can interact with the running gui. For example setting a number to a spin box.
I attached a starting project. A spinbox and a button. If the button is pressed a random number is set to the spinbox and as soon as the number in the spinbox changes, it prints the number.
Is there a way to call a script (at the moment with a hard coded path) by pushing the button, which then sets the number in the gui to my choice. The content of the script (in this case the number which is set to the spin box) has to be editable during the runtime of the gui.
If you could provide an example for this, I would be grateful and could build the rest myself.
Thanks in advance!
import sys
import random
from PyQt5 import QtCore, QtGui
from PyQt5.QtWidgets import QApplication, QWidget, QDoubleSpinBox, QPushButton
class GuiInteraction(QWidget):
def __init__(self):
super().__init__()
self.initGUI()
self.CallBackFunctions()
def initGUI(self):
self.resize(400, 500)
self.move(300, 300)
self.setWindowTitle('Gui Interaction')
self.doubleSpinBox = QDoubleSpinBox(self)
self.doubleSpinBox.setGeometry(QtCore.QRect(120, 130, 120, 25))
self.doubleSpinBox.setDecimals(5)
self.doubleSpinBox.setMaximum(1000)
self.doubleSpinBox.setObjectName("doubleSpinBox")
self.pushButton = QPushButton("Run Script", self)
self.pushButton.setGeometry(QtCore.QRect(100, 300, 100, 40))
self.pushButton.setObjectName("pushButton")
def CallBackFunctions(self):
self.pushButton.clicked.connect(self.buttonClicked)
self.doubleSpinBox.valueChanged.connect(self.valueChanged)
def buttonClicked(self):
self.doubleSpinBox.setValue(random.uniform(1, 200))
def valueChanged(self):
print(self.doubleSpinBox.value())
if __name__ == '__main__':
app = QApplication(sys.argv)
MyWindow = GuiInteraction()
MyWindow.show()
sys.exit(app.exec_())
I'm thinking you can call a FileDialog, pick a script and use:
mod = __import__(path)
, and than should the script be adequately built with a "run" function of some kind you can just launch it by:
mod.run()
Check this question also.
One way would be to just communicate with an external script through stdin/stdout
my_script.py
def main():
print '4.2'
if __name__ == '__main__':
main()
gui_script.py
import subprocess
...
def buttonClicked(self):
try:
value = float(subprocess.check_output(['python', 'my_script.py']).strip())
except ValueError:
value = 1.0
self.doubleSpinBox.setValue(value)
If you need to pass arguments to your function you can just pass them as additional arguments to the subprocess.check_output call, and them read them from sys.argv (they'll all come in as strings, so you'd have to convert the types if necessary, or use a library like argparse, which can do the type-casting for you) in the called script.
I went for the solution of #armatita, even though I changed it a little. After a brief research __import__ seems to be replaced by the libimport libary, which I now use. The following lines have been added:
Header:
import importlib
import threading
and in the main function:
def buttonClicked(self):
ScriptControlModule = importlib.import_module("ExternalScript")
ScriptControlModule = importlib.reload(ScriptControlModule)
ScriptControlThread = threading.Thread(target=ScriptControlModule.execute,args=(self,))
ScriptControlThread.start()
My question is answered by the importlib lines. I also wanted to start the script in a subthread, so in the case it crashes due to a typo or anything else, the whole gui does not follow the crash.
The ExternalScript is in the same folder and named ExternalScript.py
import random
def execute(self):
self.doubleSpinBox.setValue(random.uniform(1, 5))
Three simple lines of code. I can change these lines while running and get different values in the SpinBox. Works out perfectly!
Thanks in advance for taking the time to read this. Apologies that it is somewhat verbose. But hopefully it fully explains the problem. Stripped code demonstrating the issue is included.
I'm having an issue with PyQt4 SIGNAL/SLOTS. While I can make everything work fine if I am writing in a single file, I can't make things work if I some of the functions I wish to use are moved to sub-directories/classes.
I've looked through the Python Bindings document I can see how this works when using a single file. But what I am trying to do is this:
main.py file in root dir which contains the MainWindow __init__ code.
This file imports a number of widgets. Each widget is stored in its own sub-directory. All sub-directories contain an __init__.py file. These sub-directories are inside of a directory called 'bin', which is itself in the root dir
Some of these widgets need to have SIGNAL/SLOT links between them This is where I fall down.
So the file structure is:
- main.py
- bin/textEditor/__init__.py
- bin/textEditor/plugin.py
- bin/logWindow/__init__.py
- bin/logWindow/plugin.py
The following code shows the problem. This code creates a very basic main window that contains a central QTextEdit() widget and a dockable QTextEdit() widget. All that happens is that when the text in the central widget is changed, the same text is shown in the dockable widget. The example works. But it does so by connecting the signal textChanged() in the bin/textEditor/plugin.py file that creates the central QTextEdit() with a function in main.py. I would like it to do exactly the same thing but connexted to the updateUi function in bin/textEditor/plugin.py
If anyone could shed some light on this, I would be hugely grateful. I'm sure it is simple. But direction to any tutorials that cover this or statements that I am doing it all very wrong are equally appreciated!. Thanks again for your time:
### main.py
import os
import sys
# Import PyQT modules
from PyQt4.QtCore import *
from PyQt4.QtGui import *
# Start the main class
class MainWindow(QMainWindow):
# Initialise
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent)
# Name and size the main window
self.setWindowTitle("EDITOR/LOG")
self.resize(800, 600)
import bin.logWindow.plugin as logWindow
logWindow.create(self)
import bin.textEditor.plugin as textEditor
textEditor.create(self)
def updateUi(self):
# I can connect to this function from within bin/textEditor/plugin.py (see
# below) but I want to connect to the function located in
# bin/textEditor/plugin.py instead
text = self.editor.toPlainText()
self.logWidget.setText(text)
# Run the app
def main():
app = QApplication(sys.argv)
form = MainWindow()
form.show()
app.exec_()
# Call main
main()
The code inside of the two plugin files is:
### bin/textEditor/plugin.py
# Import PyQT modules
from PyQt4.QtCore import *
from PyQt4.QtGui import *
def create(self):
# Add a dockable widget
self.logDockWidget = QDockWidget("Log", self)
self.logDockWidget.setObjectName("LogDockWidget")
self.logDockWidget.setAllowedAreas(Qt.LeftDockWidgetArea|
Qt.RightDockWidgetArea)
self.logWidget = QTextEdit()
self.logDockWidget.setWidget(self.logWidget)
self.addDockWidget(Qt.LeftDockWidgetArea, self.logDockWidget)
And
### bin/logWindow/plugin.py
Import PyQT modules
from PyQt4.QtCore import *
from PyQt4.QtGui import *
def create(self):
# Create a text editing box
self.editor = QTextEdit()
# Add to main window
self.setCentralWidget(self.editor)
# connect text change to update log window. This is presumably what I need to
# change so that it connects to the function below instead of the on in main.py
self.connect(self.editor, SIGNAL("textChanged()"), self.updateUi)
def updateUi(self):
text = self.editor.toPlainText()
self.logWidget.setText(text)
For starters, is there a reason you're using a very old version of the PyQt release document? The new one is: here
There are a few things you are doing that are a bit unusual. Generally import statements in python are placed at the top of the file (to more easily see dependencies), but I assume you're doing this to support a more generalized import system for plugins in the future.
It seems like the basic problem is you're trying to connect a signal source to a slot in another object, without storing that other object in a particular place. To do this you probably need to either make the connection in main, make a neutral "updateUi" slot that emits it's own special signal that all the plugins are waiting for, or just keep a reference to those subobjects in main and be careful with the initialization order.