How to embed terminal inside PyQt5 application without QProcess? - python

I have been struggling lately with embedding a terminal inside PyQt GUI app. Tried almost every search on Internet but nothing looks like of any help.
I have a QTabWidget and I simply need one tab to have a terminal.
Is it not at all possible to do so ?
Isn't there something like QTabWidget.Tab2.show(terminal-app) and default terminal gets displayed in tab2 and every function like ls, ifconfig, cd etc works fine ?
P.S - I have already tried these but no success.
Embedding a terminal in PyQt5
(converted code here from PyQt4 to PyQt5 but this does not fulfill my needs) how to use a terminal embedded in a PyQt GUI
T.I.A

short answer: Qt5 does not provide the use of the terminal, so you will have to use QProcess.
TL;DR
The EmbTerminal class that is proposed as a solution is a widget so you must add it with addTab(), keep in mind that you must have installed the urxvt terminal (if you want to check your installation run urxvt in the terminal)
import sys
from PyQt5 import QtCore, QtWidgets
class EmbTerminal(QtWidgets.QWidget):
def __init__(self, parent=None):
super(EmbTerminal, self).__init__(parent)
self.process = QtCore.QProcess(self)
self.terminal = QtWidgets.QWidget(self)
layout = QtWidgets.QVBoxLayout(self)
layout.addWidget(self.terminal)
# Works also with urxvt:
self.process.start('urxvt',['-embed', str(int(self.winId()))])
self.setFixedSize(640, 480)
class mainWindow(QtWidgets.QMainWindow):
def __init__(self, parent=None):
super(mainWindow, self).__init__(parent)
central_widget = QtWidgets.QWidget()
lay = QtWidgets.QVBoxLayout(central_widget)
self.setCentralWidget(central_widget)
tab_widget = QtWidgets.QTabWidget()
lay.addWidget(tab_widget)
tab_widget.addTab(EmbTerminal(), "EmbTerminal")
tab_widget.addTab(QtWidgets.QTextEdit(), "QTextEdit")
tab_widget.addTab(QtWidgets.QMdiArea(), "QMdiArea")
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
main = mainWindow()
main.show()
sys.exit(app.exec_())

I've had the same problem for a few months now and the urxvt or xterm solution doesn't cut it for me, so I created a repo where I'm working on an easily embeddable terminal for PyQt5.
It works for some commands but for commands like python it just has trouble writing into a running process like that.
Feel free to contribute!
https://github.com/Fuchsiaff/PyQtTerminal

Related

PyQT with QT Creator

I am trying to develop a small gui using PySide and the QT Creator.
As the base implementation, I have choosen a QMainWindow.
The problem is that adding any elements to that MainWindow inside the Editor results in an empty window when I run the code.
The initially generated python code looks like this:
class MainWindow(QMainWindow):
def __init__(self):
super(MainWindow, self).__init__()
self.load_ui()
def load_ui(self):
loader = QUiLoader()
path = os.path.join(os.path.dirname(__file__), "form.ui")
ui_file = QFile(path)
ui_file.open(QFile.ReadOnly)
loader.load(ui_file, self)
ui_file.close()
if __name__ == "__main__":
app = QApplication([])
widget = MainWindow()
widget.show()
sys.exit(app.exec_())
I know that loader.load(ui_file, self) returns a widget. My solution was:
def load_ui(self):
loader = QUiLoader()
path = os.path.join(os.path.dirname(__file__), "form.ui")
ui_file = QFile(path)
ui_file.open(QFile.ReadOnly)
self.window = loader.load(ui_file, self)
self.window.show()
ui_file.close()
if __name__ == "__main__":
app = QApplication([])
widget = MainWindow()
#widget.show() <<<<<<--- removing this
sys.exit(app.exec_())
This works for me but this clearly cannot be the way this is supposed to work.
I am confused on why QT Creator gives me this non-working templaet.
Furthermore I am curious on how custom functions should be implemented.
When clicking on the clicked() slot for a button, it tells me
No ui_form.h found! (translated)
I ended up writing the functions into my MainWindow class like this:
self.window.menuopen_button.clicked.connect(lambda x: self.menu_animation(x))
self.window.minimise_button.clicked.connect(lambda x: self.menu_buttons('minimise'))
Your initial code only loads the UI file into the QUiLoader object - you don't do anything else with it. When you call widget.show(), you're calling the QMainWindow's show method which is a default window (and, hence, empty) since you haven't added the loaded widget to it.
Your solution similarly loads the UI but displays it via the widget object's show method. However, you're not amending it to your main window, so you're basically tossing your main window and just using the widget.
There are a couple ways to correctly accomplish what you want:
One way would be to, as #musicamante suggested, add the loaded UI widget to your MainWindow object (and by the way, this is supported by PySide2: https://doc.qt.io/archives/qtforpython-5.12/PySide2/QtUiTools/QUiLoader.html - the example in the detailed description shows exactly how to do this).
Or the other way, which I prefer, would be to use the uic utility, PySide2-uic, in the build process to generate a UI Python class based on the UI file.
The Qt documentation has tutorials showing both methods. The linked documentation shows PySide6 as support for PySide2 has been discontinued with the release of Qt6. The API for PySide2, PySide6, and PyQt5 are all very similar, so if you're migrating from either PySide2 or PyQt then the change should be relatively painless. I recommend using PySide6 as this is the Python binding officially supported by Qt.

How to change current color group for QPalette

I'm trying to change the current color group fora QPalette, but it seems that the setCurrentColorGroup method of QPalette simply does not work.
I'm running this code:
app = QtGui.QApplication(sys.argv)
button = QPushButton()
svgWidget = QSvgWidget(resources_paths.getPathToIconFile("_playableLabels/42-labelPlay-disabled-c.svg"))
button.setLayout(QHBoxLayout())
button.layout().addWidget(svgWidget)
button.setFixedSize(QSize(300, 300))
print button.palette().currentColorGroup()
button.setEnabled(False)
print button.palette().currentColorGroup()
button.palette().setCurrentColorGroup(QPalette.ColorGroup.Normal)
print button.palette().currentColorGroup()
button.show()
print button.palette().currentColorGroup()
app.exec_()
This is the output I get:
PySide.QtGui.QPalette.ColorGroup.Normal
PySide.QtGui.QPalette.ColorGroup.Disabled
PySide.QtGui.QPalette.ColorGroup.Disabled
PySide.QtGui.QPalette.ColorGroup.Disabled
Process finished with exit code -1
So... It seems that setCurrentColorGroup does exactly nothing. Any ideas on how could I change the current color group?
Thanks in advance!
(BTW, I'm running PySide 1.2.4 with Qt 4.8 on a Windows 7 system)
It seems that you are trying to change the way icons are rendered, rather than the way widgets are painted, so the palette is not the right API to use. Instead, you should use a QIcon, which allows different images to be used for various modes and states.
To use the same image for both Normal and Disabled modes, you would use code like this:
icon = QtGui.QIcon()
icon.addPixmap(QtGui.QPixmap('image.svg'), QtGui.QIcon.Normal)
icon.addPixmap(QtGui.QPixmap('image.svg'), QtGui.QIcon.Disabled)
button = QtGui.QPushButton()
button.setIcon(icon)
However, you should take careful note of this warning from the Qt docs:
Custom icon engines are free to ignore additionally added pixmaps.
So there is no guarantee that this will work with all widget styles on all platforms.
UPDATE:
If the above method doesn't work, it probably means the widget style is controlling how disabled icons are rendered. The relevant QStyle API is generatedIconPixmap, which returns a copy of the pixmap modified according to the icon mode and style options. It seems that this method may sometimes also take the palette into account (somewhat contrary to what I stated above) - but when I tested this, it did not have any affect. I reset the palette like this:
palette = self.button.palette()
palette.setCurrentColorGroup(QtGui.QPalette.Normal)
palette.setColorGroup(QtGui.QPalette.Disabled,
palette.windowText(), palette.button(),
palette.light(), palette.dark(), palette.mid(),
palette.text(), palette.brightText(),
palette.base(), palette.window(),
)
button.setPalette(palette)
which made the colours look normal when the button was disabled - but the icon was still greyed out. Still, you might want to try it in case things work differently on your platform (don't be surprised if they don't).
It seems that the correct way to control the disabling of icons is to create a QProxyStyle and override the generatedIconPixmap method. Unfortunately, this class is not available in PyQt4, but I have tested it in PyQt5, and it works. So the only working solution I have at the moment is to upgrade to PyQt5, and use QProxyStyle. Here is a demo script that shows how to implement it:
import sys
from PyQt5 import QtCore, QtGui, QtWidgets
class ProxyStyle(QtWidgets.QProxyStyle):
def generatedIconPixmap(self, mode, pixmap, option):
if mode == QtGui.QIcon.Disabled:
mode = QtGui.QIcon.Normal
return super(ProxyStyle, self).generatedIconPixmap(
mode, pixmap, option)
class Window(QtWidgets.QWidget):
def __init__(self):
super(Window, self).__init__()
self.button = QtWidgets.QPushButton(self)
self.button.setIcon(QtGui.QIcon('image.svg'))
self.button2 = QtWidgets.QPushButton('Test', self)
self.button2.setCheckable(True)
self.button2.clicked.connect(self.button.setDisabled)
layout = QtWidgets.QVBoxLayout(self)
layout.addWidget(self.button)
layout.addWidget(self.button2)
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
app.setStyle(ProxyStyle())
window = Window()
window.setGeometry(600, 100, 300, 200)
window.show()
sys.exit(app.exec_())

Different program behavior depending if its main part is enclosed in a function

Right at the beginning I would like to say that I am a very beginner when it comes to writing GUI applications and this is the topic of this question.
I am using Anaconda 4.1.1 as my Python distribution and Spyder as my IDE. I was trying to write a very simple window application using PyQt4 (already installed in Anaconda) or - to be precise - I copied some examples form the Internet and try to make some "experiments" with them. The results are really surprising for me.
The thing is that when I am running the following example in the IPython console:
import sys
from PyQt4 import QtGui
class Example(QtGui.QWidget):
def __init__(self):
super(Example, self).__init__()
self.show()
def main():
app = QtGui.QApplication(sys.argv)
ex = Example()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
everything seems to be OK - I can close the window of an application whenever I want and it successfully opens once again when I am running the code once again. But when I make a small modification in the code, namely - getting rid of the main function:
import sys
from PyQt4 import QtGui
class Example(QtGui.QWidget):
def __init__(self):
super(Example, self).__init__()
self.show()
if __name__ == '__main__':
app = QtGui.QApplication(sys.argv)
ex = Example()
sys.exit(app.exec_())
then I can run my program only once, because when I close it and try to run it once again - the IPython console displays the following message:
It seems the kernel died unexpectedly. Use 'Restart kernel' to continue using this console.
repeating every few seconds. The Kernel itself displays something like that:
QWidget: Must construct a QApplication before a QPaintDevice
I don't really understand how can it happen because for me these two pieces of code should work exactly the same since the only difference between them is presence of the main function which (I believe) is not necessary to make this program work. Why does it give an error the second way?

Open pyqt program without console with pythonw

i have a strange problem. I created a GUI program which runs in "spyder" with WinPython-64bit-3.3.2.3 with no problems, now i want to run it without the console to pop up and i try to use pythonw.exe.
When i save my GUI as gui.pyw i can open it with PythonWin by right clicking and use edit with PythonWin but simply double-clicking will make my GUI pop up for less than a second and exit the program afterwards.
Does this have to do with my GUI programming?
the "structure" is this one:
import sys
from PyQt4 import QtGui, QtCore, Qt
from Main_Window_v2 import Ui_Dialog as Dlg
class MeinDialog(QtGui.QDialog, Dlg):
def __init__(self):
QtGui.QDialog.__init__(self)
self.setupUi(self)
self.connect(self.buttonOK,
QtCore.SIGNAL("clicked()"), self.onOK)
self.connect(self.buttonAbbrechen,
QtCore.SIGNAL("clicked()"), self.onClose)
self.connect(self.buttonsql,
QtCore.SIGNAL("clicked()"), self.onsql)
def onsql(self):
login=self.login_text_box.toPlainText()
from calc import get_variables #sql query
self.get_variables=get_variables(login)
#calls a class´ __init__ in another file in my direcotry
def onOK(self):
login=self.login_text_box.toPlainText()
self.get_variables.plot(login)
#calls another function in my class "calc"
def onClose(self):
print("bye!")
self.close()
app = QtGui.QApplication(sys.argv)
dialog = MeinDialog()
dialog.show()
I also tried to get an .exe using cx_freeze and after trying to making build as described here Cx_Freeze: I have the same problem: The Main-Window of the GUI
pops up and disappears again
Just add app.exec_() at the end of your code. Your code was running well in Spyder because Spyder uses PyQt and had the main loop of events already running.
You should add app.exex_() at the end of your code, it is used to dispatch all PyQt GUI threads message or other threads info message.

Setting the Windows taskbar icon in PyQt

I'm working on an applcation in Python's PyQt4 and cannot find how to change the taskbar icon. I made my .ui files in Qt's Designer, where I can change the windowIcon properties. But that is not what I am looking for. I want to change the look of the application's icon in windows taskbar. For now it is Python logo in a window icon.
I found some information on SO: link but it's not helping me much.
I tried:
app = QtGui.QApplication([])
app.setWindowIcon(QtGui.QIcon('chip_icon_normal.png'))
app.exec_()
But the icon remains unchanged.
What i want to change, showing the picture:
(This is done calling the setWindowIcon on main window/ dialog, or the application, as shown above.)
This problem is caused by some peculiarities in how taskbar icons are handled on the Windows platform.
See this answer for details, along with a workaround using ctypes.
It seems to me that the problem may be caused by lack of icon with the right size.
The following setup worked for me in PyQT4:
# set app icon
app_icon = QtGui.QIcon()
app_icon.addFile('gui/icons/16x16.png', QtCore.QSize(16,16))
app_icon.addFile('gui/icons/24x24.png', QtCore.QSize(24,24))
app_icon.addFile('gui/icons/32x32.png', QtCore.QSize(32,32))
app_icon.addFile('gui/icons/48x48.png', QtCore.QSize(48,48))
app_icon.addFile('gui/icons/256x256.png', QtCore.QSize(256,256))
app.setWindowIcon(app_icon)
I have got a task bar icon in Windows 7 and correct icons in all windows without any changes to ui files.
You need to call setWindowIcon(...) on the window, not on the application.
Here's an example, which works for me:
#!/usr/bin/env python3
import os
import sys
import subprocess
import os.path
from PyQt4 import QtGui
from PyQt4 import QtCore
class MyWin(QtGui.QMainWindow):
def __init__(self, parent=None):
super(MyWin, self).__init__(parent)
self.setWindowTitle("My Window")
self.setWindowIcon(QtGui.QIcon('test_icon.png'))
self.show()
def main(args):
app = QtGui.QApplication([])
ww= MyWin()
sys.exit(app.exec_())
if __name__ == '__main__':
main(sys.argv[1:])
For me, the following code works for both changing task bar icon and window icon
win.setWindowIcon(QIcon('logo.png'))

Categories