I am trying to figure out a way to make a decent copy to clipboard button function where I can copy text values from other threads inside a QTextEdit widget.
I pull the values from other threads and display them in a QTextEdit widget and have been trying to successfully copy to clipboard the contents of QTextEdit with a button but the only way I have been able to is my code below.
What I don't like is that the copy button makes a second call in order to copy the contents.
Is there some way that the values can be stored from the first request using the get_button button and be available to copy with the copy button without having to make the same call all over again? Thanks.
from PyQt4 import QtCore, QtGui
import sys
class WorkerThread(QtCore.QThread):
get_String = QtCore.pyqtSignal(str)
def __init__(self):
super(WorkerThread, self).__init__()
def run(self):
self.get_String.emit('Some text.')
class GUI(QtGui.QWidget):
def __init__(self):
super(GUI, self).__init__()
self.get_Button = QtGui.QPushButton('Get')
self.c_Button = QtGui.QPushButton('Copy')
self.text_Box = QtGui.QTextEdit(self)
vbox = QtGui.QVBoxLayout(self)
vbox.addWidget(self.get_Button)
vbox.addWidget(self.c_Button)
vbox.addWidget(self.text_Box)
vbox.addStretch()
self.setGeometry(300, 300, 300, 300)
self.show()
self._thread = WorkerThread()
self.get_Button.clicked.connect(self.doIt)
self._thread.get_String.connect(self.text_Box.append)
self.c_Button.clicked.connect(self.copy_Stuff)
def copy_Stuff(self):
self.clipboard = QtGui.QApplication.clipboard()
self._thread.get_String.connect(self.clipboard.setText)
self._thread.start()
def doIt(self):
self.text_Box.clear()
self.text_Box.setText('')
self._thread.start()
def main():
app = QtGui.QApplication(sys.argv)
gui = GUI()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
It turns out this was much easier than I initially thought. I was confused with how to copy text correctly because of all of the different ways I seen it done in other examples and posts. After reading the docs for awhile and trying to learn a bit more about pyqt I seen that it was as easy as using these 2 lines...
self.text_Box.selectAll()
self.text_Box.copy()
with those 2 lines and my button it seems to copy all the contents of the QTextEdit widget which is all I really needed. Here is my final working code in case someone else may find it useful.
from PyQt4 import QtCore, QtGui
import sys
class WorkerThread(QtCore.QThread):
get_String = QtCore.pyqtSignal(str)
def __init__(self):
super(WorkerThread, self).__init__()
def run(self):
self.get_String.emit('Some text.')
class GUI(QtGui.QWidget):
def __init__(self):
super(GUI, self).__init__()
self.get_Button = QtGui.QPushButton('Get')
self.c_Button = QtGui.QPushButton('Copy')
self.text_Box = QtGui.QTextEdit(self)
vbox = QtGui.QVBoxLayout(self)
vbox.addWidget(self.get_Button)
vbox.addWidget(self.c_Button)
vbox.addWidget(self.text_Box)
vbox.addStretch()
self.setGeometry(300, 300, 300, 300)
self.show()
self._thread = WorkerThread()
self.get_Button.clicked.connect(self.doIt)
self._thread.get_String.connect(self.text_Box.append)
self.c_Button.clicked.connect(self.copy_Stuff)
def copy_Stuff(self):
self.text_Box.selectAll()
self.text_Box.copy()
def doIt(self):
self.text_Box.clear()
self.text_Box.setText('')
self._thread.start()
def main():
app = QtGui.QApplication(sys.argv)
gui = GUI()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
This code example shows a simple working thread with signal that returns a value to the main, displays the value in a QTextEdit widget and how to copy all of the contents of a QTextEdit widget with a single button click.
Related
I have communications working between similar widgets from imported child widgets and the parent main window and widgets. However, I am stumped when it comes to a QGraphicsScene widget imported as a module and sub-widget. I have put some simplified files below. So, QGraphicsView (from the QGraphicsScene) will be the actual widget I need to emit and signal events to other QWidgets inside the main window.
If I have all classes in one file, it works but if I have the classes as separate modules, I get "does not have attribute" errors, specifically in the simple version here for QGraphicsScene .viewport
Attribute Error "self.graphicsView.viewport().installEventFilter(self)"
I guess that the composite graphics widget is actually now a QWidget and I am not initialising the imported module functions/attributes for the QGraphicsView element. Thing is, I want it to exist that way so I can separate the GUI elements and functions of different modules. The others I have used so far have been straightforward QWidget to QWidget signals derived from QObjects, so work fine, but I haven't been able to achieve the same with the imported QGraphicsScene to QWidgets since it errors when it tries to reach QGraphicsView within the main window. Again, all fine if all classes exist in one large file.
Any kind person can point out my error here? How can I separate the module scripts to behave the same way as the single script?
Working single script:
# QWidgetAll.py
from PySide import QtGui, QtCore
class GraphicsView(QtGui.QWidget):
def __init__(self):
QtGui.QWidget.__init__(self)
self.graphicsView = QtGui.QGraphicsView(self)
self.graphicsLabel = QtGui.QLabel("Graphics View within QWidget")
self.graphicsView.setMouseTracking(True)
self.graphicsView.viewport().installEventFilter(self)
self.edit = QtGui.QLineEdit(self)
layout = QtGui.QVBoxLayout(self)
layout.addWidget(self.graphicsLabel)
layout.addWidget(self.edit)
layout.addWidget(self.graphicsView)
def eventFilter(self, source, event):
if (event.type() == QtCore.QEvent.MouseMove and
source is self.graphicsView.viewport()):
pos = event.pos()
self.edit.setText('x: %d, y: %d' % (pos.x(), pos.y()))
return QtGui.QWidget.eventFilter(self, source, event)
if __name__ == '__main__':
import sys
app = QtGui.QApplication(sys.argv)
window = GraphicsView()
window.show()
window.resize(200, 100)
sys.exit(app.exec_())
The same file as separate modules. qWidgetView.py errors with attribute error:
# qWidgetView.py
from PySide import QtGui, QtCore
from qGraphicView import GraphicsView
class WidgetView(QtGui.QWidget):
def __init__(self):
QtGui.QWidget.__init__(self)
self.graphicsView = GraphicsView()
self.graphicsView.setMouseTracking(True)
self.graphicsView.viewport().installEventFilter(self)
self.edit = QtGui.QLineEdit(self)
layout = QtGui.QVBoxLayout(self)
layout.addWidget(self.edit)
layout.addWidget(self.graphicsView)
def eventFilter(self, source, event):
if (event.type() == QtCore.QEvent.MouseMove and
source is self.graphicsView.viewport()):
pos = event.pos()
self.edit.setText('x: %d, y: %d' % (pos.x(), pos.y()))
return QtGui.QWidget.eventFilter(self, source, event)
if __name__ == '__main__':
import sys
app = QtGui.QApplication(sys.argv)
window = WidgetView()
window.show()
window.resize(200, 100)
sys.exit(app.exec_())
with imported qGraphicView.py module:
# qGraphicView.py
from PySide import QtGui, QtCore
class GraphicsView(QtGui.QWidget):
def __init__(self):
QtGui.QWidget.__init__(self)
self.graphicsView = QtGui.QGraphicsView(self)
self.graphicsLabel = QtGui.QLabel("Graphics View within QWidget")
layout = QtGui.QVBoxLayout(self)
layout.addWidget(self.graphicsLabel)
layout.addWidget(self.graphicsView)
if __name__ == '__main__':
import sys
app = QtGui.QApplication(sys.argv)
window = GraphicsView()
window.show()
window.resize(200, 100)
sys.exit(app.exec_())
You need to filter events for the QGraphicsView that is a child widget of the GraphicsView class, because you only want mouse-moves on the graphics-view itself, not the whole container widget. So I would suggest something like this:
The qGraphicView.py module:
class GraphicsView(QtGui.QWidget):
def __init__(self):
QtGui.QWidget.__init__(self)
self.graphicsView = QtGui.QGraphicsView(self)
self.graphicsView.setMouseTracking(True)
self.graphicsLabel = QtGui.QLabel("Graphics View within QWidget")
layout = QtGui.QVBoxLayout(self)
layout.addWidget(self.graphicsLabel)
layout.addWidget(self.graphicsView)
def viewport(self):
return self.graphicsView.viewport()
The qWidgetView.py module:
class WidgetView(QtGui.QWidget):
def __init__(self):
QtGui.QWidget.__init__(self)
self.graphicsView = GraphicsView()
self.graphicsView.viewport().installEventFilter(self)
self.edit = QtGui.QLineEdit(self)
layout = QtGui.QVBoxLayout(self)
layout.addWidget(self.edit)
layout.addWidget(self.graphicsView)
~EDIT (original question still below)~ when I remove the self.setGeometry() call in the new window it works as it should. Why is that? I'm still building out the UI for it, but once I do I hope I don't continue to have this problem...
~EDIT 2~ Just realized it should be self.resize() not self.setGeometry()...
self.solved()
:(
I'm just learning PyQt5 and just doing a little messing around. For some reason when I try to open a new window from the main application window, the whole thing closes. Putting in some print statements to track progress shows that it's not actually creating the new window either.
Main Window code:
import sys
from PyQt5.QtWidgets import QMainWindow, QAction, QApplication
from newLeague import *
class MainWindow(QMainWindow):
def __init__(self):
super(MainWindow, self).__init__()
newLeagueAction = QAction('Create New League', self)
newLeagueAction.setShortcut('Ctrl+N')
newLeagueAction.setStatusTip('Create a new league from scratch')
newLeagueAction.triggered.connect(self.createNewLeague)
openLeagueAction = QAction('Open Existing League', self)
openLeagueAction.setShortcut('Ctrl+E')
openLeagueAction.setStatusTip('Continue with a previously started league')
openLeagueAction.triggered.connect(self.openExistingLeague)
exitAction = QAction('Quit', self)
exitAction.setShortcut('Ctrl+Q')
exitAction.setStatusTip('Quit the application...')
exitAction.triggered.connect(self.close)
self.statusBar()
mainMenu = self.menuBar()
fileMenu = mainMenu.addMenu('&File')
fileMenu.addAction(newLeagueAction)
fileMenu.addAction(openLeagueAction)
fileMenu.addAction(exitAction)
self.resize(1920, 1080)
self.setWindowTitle("Brackets")
def createNewLeague(self):
'''shows dialog to create a new league'''
self.newLeague = CreateLeague()
self.newLeague.show()
print('New League being created...')
def openExistingLeague(self):
print('Existing League opening...')
def main():
app = QApplication(sys.argv)
window = MainWindow()
window.show()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
Here is the second window:
from PyQt5.QtWidgets import QMainWindow
class CreateLeague(QMainWindow):
def __init__(self):
super(CreateLeague, self).__init__()
self.initUI()
def initUI(self):
self.setGeometry(600, 500)
self.setWindowTitle('Create A New League')
I've looked at other examples such as this, and this, and I'm not seeing what it is I'm doing different. I've experimented with using parent as an argument in the constructors and the result is no different.
Your Main Window code is ok, but you should remove CreateLeague, self arguments from the super parameters in your second window, then, your code should work fine.
See below:
from PyQt5.QtWidgets import QMainWindow
class CreateLeague(QMainWindow):
def __init__(self):
super().__init__()
self.initUI()
def initUI(self):
self.resize(600, 500)
self.setWindowTitle('Create A New League')
I'm trying to make an application but I keep getting punched by the "simple" things like this one, how do I open a new window though a button click? I tried using new_lib_btn.clicked.connect(newlib), newlib is the file that contains my second window and new_lib_btn is the button that should open the window, it's in my main window as you can see down here:
mainwindow.py
from PyQt4 import QtCore, QtGui
import newlib
import sys
# Main Window
class Window (QtGui.QMainWindow):
def __init__(self):
super(Window, self).__init__()
centralwidget = QtGui.QWidget(self)
self.mainLayout = QtGui.QVBoxLayout(centralwidget)
self.mainLayout.setAlignment(QtCore.Qt.AlignCenter)
self.setCentralWidget(centralwidget)
self.resize(800, 600)
self.setWindowTitle("Virtual Library")
self.setStyleSheet("Window {border-image: url(lib.jpg);}")
# ExitOption
menu_action1 = QtGui.QAction("Exit", self)
menu_action1.setShortcut("Ctrl+Q")
menu_action1.setStatusTip('Exit The App')
menu_action1.triggered.connect(self.close_application)
self.statusBar()
# MenuBar
main_menu = self.menuBar()
file_menu = main_menu.addMenu('Options')
file_menu.addAction(menu_action1)
self.home()
def home(self):
# NewLibrary btn
new_lib_btn = QtGui.QPushButton("New Library", self)
new_lib_btn.setGeometry(QtCore.QRect(310, 180, 141, 41))
new_lib_btn.setStyleSheet("color: black;")
# AccessLibrary btn
access_lib_btn = QtGui.QPushButton("Access Library", self)
access_lib_btn.setGeometry(QtCore.QRect(310, 250, 141, 41))
access_lib_btn.setStyleSheet("color: black;")
# FindNewBooks btn
find_nbooks = QtGui.QPushButton("Find New Books*", self)
find_nbooks.setGeometry(QtCore.QRect(310, 320, 141, 41))
find_nbooks.setStyleSheet("color: black;")
self.mainLayout.addWidget(new_lib_btn)
self.mainLayout.addWidget(access_lib_btn)
self.mainLayout.addWidget(find_nbooks_btn)
self.show()
def close_application(self):
choice = QtGui.QMessageBox.question(self, 'Exit',
"Close the application?",
QtGui.QMessageBox.Yes | QtGui.QMessageBox.No)
if choice == QtGui.QMessageBox.Yes:
sys.exit()
else:
pass
def run():
app = QtGui.QApplication(sys.argv)
GUI = Window()
sys.exit(app.exec_())
run()
And here's my second window, the one I want to open with the new_lib_btn
newlib.py
class NewLibrary (QtGui.QMainWindow):
def __init__(self):
super(NewLibrary, self).__init__()
self.resize(800,600)
self.setWindowTitle("New Library")
self.setStyleSheet("NewLibrary {border-image: url(wood.jpg);}")
# File Options
file_action1 = QtGui.QAction("New Library", self)
file_action1.setShortcut("Ctrl+N")
file_action1.setStatusTip("Creates a new library")
file_action2 = QtGui.QAction("Exit this!", self)
file_action2.setShortcut("Ctrl+Q")
file_action2.setStatusTip("Closes The App")
file_action2.triggered.connect(self.close_application)
#File Menu
main_menu = self.menuBar()
file_menu = main_menu.addMenu("File")
file_menu.addAction(file_action1)
file_menu.addAction(file_action2)
self.newLib()
self.newLib()
def newLib(self):
centralwidget = QtGui.QWidget(self)
self.mainLayout = QtGui.QVBoxLayout(centralwidget)
self.mainLayout.setAlignment(QtCore.Qt.AlignCenter)
#some useful buttons in the future
self.setCentralWidget(centralwidget)
self.show()
def close_application(self):
choice = QtGui.QMessageBox.question(self, 'Exit',
"Close the application?",
QtGui.QMessageBox.Yes | QtGui.QMessageBox.No)
if choice == QtGui.QMessageBox.Yes:
sys.exit()
else:
pass
def runNewLib():
app = QtGui.QApplication(sys.argv)
gui = NewLibrary()
sys.exit(app.exec_())
runNewLib()
I searched a lot about this but I couldn't understand the few ones that were somewhat close to my situation, so I'm asking for help, It seems so simple but I'm not getting it :/, what should I do to open the second window by clicking new_lib_btn? pls help.
I think there are several issues with the code that you've posted. First, there are two calls to self.newLib() in the NewLibrary constructor. Second, you probably want to put that call to runNewLib() at the bottom of newlib.py behind an if __name__... block, like so:
if __name__ == '__main__':
runNewLib()
Otherwise, every time you try to import newlib.py, it will attempt to run NewLibrary as a separate application.
Getting to the question you asked, I don't think you actually want to call self.show() in either Window.home() or NewLibrary.newLib(). A more typical pattern would be to create an instance of either Window or NewLibrary and then call show() on that instance. So, in your Window class, you'd add a function to create an instance of NewLibrary and then call show on it, like this
def create_new_library_window(self):
self.new_lib = newlib.NewLibrary()
self.new_lib.show()
Note that, as ekhumoro points out, you have to keep a reference to new_lib around, otherwise it will get garbage collected when the function exits. Then in NewLibrary.home() after you've created the new_lib_btn connect it to this new function:
new_lib_btn.clicked.connect(self.create_new_library_window)
Working example
This example creates a main window with one big button that, when clicked will open a second window. It uses two classes that inherit from QMainWindow, as in your question. First, in main.py:
from PyQt4 import QtGui
from new_window import NewWindow
class Window(QtGui.QMainWindow):
def __init__(self):
super(Window, self).__init__()
self._new_window = None
self._button = QtGui.QPushButton('New Window', self)
self._button.clicked.connect(self.create_new_window)
self.setCentralWidget(self._button)
def create_new_window(self):
self._new_window = NewWindow()
self._new_window.show()
if __name__ == '__main__':
app = QtGui.QApplication([])
gui = Window()
gui.show()
app.exec_()
The __init__ function creates a button and connects it to the create_new_window function. When the button is clicked, create_new_window will be called. Inside of create_new_window, we create an instance of NewWindow and assign it to a class member to maintain a reference to the window and prevent it from being garbage collected. We then call show on this new window to display it.
At the bottom, we use the usual if __name__ == '__main__': pattern to control whether this file runs the application or not. If this file is executed from the command line (like python main.py) __name__ == '__main__' evaluates to true and the GUI application will be started. This has the advantage of allowing the file to serve a dual purpose: it can be imported as a standard python package, or executed as an application, all using the same file.
Then in new_window.py:
from PyQt4 import QtGui
class NewWindow(QtGui.QMainWindow):
def __init__(self):
super(NewWindow, self).__init__()
self._new_window = None
self._label = QtGui.QLabel('Hello, is it me you\'re looking for?')
self.setCentralWidget(self._label)
if __name__ == '__main__':
app = QtGui.QApplication([])
gui = NewWindow()
gui.show()
app.exec_()
This file defines a second QMainWindow that uses a label as its central widget. It also uses the __name__ == '__main__' pattern; this file can also be executed as a standalone application or imported as in main.py above.
Upon calling the show method on simple the simple window does not show. Why doesn't my Simple window show. :(
import sys
from PyQt4 import QtGui
class Widget(QtGui.QWidget):
def __init__(self):
super(Widget, self).__init__()
simple = Simple()
button = QtGui.QPushButton("Button", self)
button.clicked.connect(simple.show)
self.show()
class Simple(QtGui.QWidget):
def __init__(self):
super(Simple, self).__init__()
self.setGeometry(300, 250, 250, 150)
self.setWindowTitle("Simple Widget")
if __name__ =="__main__":
app = QtGui.QApplication(sys.argv)
widget = Widget()
sys.exit(app.exec_())
Please Help!
The problem with your code is that, simple in __init__ method of class Widget is a local variable, so as soon as the __init__ method finishes execution, the simple object is destroyed by the python Garbage Collector, thus the window does not appear because the object does not exist in the memory. To solve your problem, just add self at the starting of the simple variable to make it member variable.
...
self.simple = Simple()
button = QtGui.QPushButton("Button", self)
button.clicked.connect(self.simple.show)
...
I am currently attempting to create a program using python and PyQt4 (not Qt Designer).
I created a login class (QDialog) and a Homepage class (QMainWindow). However, because my program will consist of loads of pages (the navigation through the program will be large) i wanted to know how to switch layouts in QMainWindow rather than constantly creating new windows and closing old ones. For example, i would have the MainWindow ('HomePage') layout set as the default screen once logged in and would then have a subclass within MainWindow which allows me to navigate to user settings (or any other page). Instead of creating a new window and closing MainWindow, is there a way for me to swap the MainWindow layout to the User Setting layout?? (apologies if this doesnt make sense, im new to PyQt).
An example code is shown below (V.Basic code)
----------------------------------------------------------------------------------
import sys
from PyQt4.QtGui import *
from PyQt4.QtCore import *
class MainWindow(QMainWindow):
#Constructor
def __init__(self):
super(MainWindow, self).__init__() #call super class constructor
button1 = QPushButton("User Settings", self)
button1.clicked.connect(UserSelection)
button1.resize(50,50)
button1.move(350,50)
self.show()
class UserSelection(?):
...
def main():
app = QApplication(sys.argv) #Create new application
Main = MainWindow()
sys.exit(app.exec_()) #Monitor application for events
if __name__ == "__main__":
main()
from PyQt4 import QtCore, QtGui
class MainWindow(QtGui.QMainWindow):
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent)
self.central_widget = QtGui.QStackedWidget()
self.setCentralWidget(self.central_widget)
login_widget = LoginWidget(self)
login_widget.button.clicked.connect(self.login)
self.central_widget.addWidget(login_widget)
def login(self):
logged_in_widget = LoggedWidget(self)
self.central_widget.addWidget(logged_in_widget)
self.central_widget.setCurrentWidget(logged_in_widget)
class LoginWidget(QtGui.QWidget):
def __init__(self, parent=None):
super(LoginWidget, self).__init__(parent)
layout = QtGui.QHBoxLayout()
self.button = QtGui.QPushButton('Login')
layout.addWidget(self.button)
self.setLayout(layout)
# you might want to do self.button.click.connect(self.parent().login) here
class LoggedWidget(QtGui.QWidget):
def __init__(self, parent=None):
super(LoggedWidget, self).__init__(parent)
layout = QtGui.QHBoxLayout()
self.label = QtGui.QLabel('logged in!')
layout.addWidget(self.label)
self.setLayout(layout)
if __name__ == '__main__':
app = QtGui.QApplication([])
window = MainWindow()
window.show()
app.exec_()