© The Author(s), under exclusive license to APress Media, LLC, part of Springer Nature 2022
J. M. WillmanBeginning PyQthttps://doi.org/10.1007/978-1-4842-7999-1_9

9. Working with the Clipboard

Joshua M Willman1  
(1)
Sunnyvale, CA, USA
 

One of the major benefits of GUIs is the ability to create programs that can interact with your system and other applications. This concept goes beyond opening and saving files, or printing images.

The clipboard is a location in your computer’s memory that is used to temporarily store data that you have copied or cut from an application. The clipboard can store a number of different types of data, including text, images, and GIFs. Information that is stored on your system’s clipboard can be pasted into other applications as long as the application knows how to work with the type of data stored in the clipboard.

In this chapter, you will
  • Use the QClipboard and QMimeData classes to move data between applications

  • Find out how to run multiple instances of an application at one time

To get started, let’s find out about PyQt’s class for interacting with your computer’s clipboard.

The QClipboard Class

The QClipboard class makes your system’s clipboard available so that you can copy and paste data such as text, images, and rich text between applications. Qt widgets that can be used to manipulate textual information, such as QLineEdit and QTextEdit, support using the clipboard. Qt’s Model/View classes (which you will learn more about in Chapter 10) also have clipboard support. If you want to paste an image from the clipboard into an application, be sure to use widgets that support graphics, such as QLabel.

Including the clipboard in your project is pretty straightforward in PyQt. In order to access an application’s QClipboard, first create an instance of the clipboard with the following line:
self.clipboard = QApplication.clipboard()
The following block of code shows one way to retrieve an image that has been copied to the clipboard and apply it to a label:
label = QLabel() # Create a label to hold an image
self.clipboard = QApplication.clipboard()
label.setPixmap(self.clipboard.pixmap())

This process only works for images, so if you want to paste text or rich text, you’ll need to use setText() on a QLabel. Another way to get data is to use the QMimeData class and describe what kind of data is being moved. This topic will be covered in the “Explanation for Using QClipboard” section.

The events that occur between your system and an application built using PyQt are handled by QApplication. The clipboard instance gives you the ability to send or receive data in your application. However, the clipboard can only hold one object at a time. So if you copy an image to the clipboard and then copy text, only the text will be available, and the image will have been deleted.

In this section, you will create a simple GUI, like in Figure 9-1, that shows how to collect text from other applications and then paste it in a PyQt window.
Figure 9-1

The user can see the contents of the clipboard in the top dock widget

The top text edit widget displays the current contents of the clipboard. The user can then paste it into the main window, which is the lower text edit widget. In some applications, you may actually want to see the contents of the clipboard in a separate window before pasting it into the main window. A dock widget, especially one that can float separate from the main window, is perfect to use as a clipboard manager.

Explanation for Using QClipboard

In this section, you will see how to set up the clipboard and actually be able to visualize its contents after copying text from another window.

As this project’s MainWindow class inherits QMainWindow, you can use the main_window_template.py script from Chapter 5 to get started in Listing 9-1.
# clipboard_ex.py
# Import necessary modules
import sys
from PyQt6.QtWidgets import (QApplication, QMainWindow,
    QPushButton, QTextEdit, QDockWidget, QFrame, QVBoxLayout)
from PyQt6.QtCore import Qt
class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        self.initializeUI()
    def initializeUI(self):
        """Set up the application's GUI."""
        self.setMinimumSize(500, 300)
        self.setWindowTitle("Clipboard Example")
        self.setUpMainWindow()
        self.createClipboardDock()
        self.show()
if __name__ == "__main__":
    app = QApplication(sys.argv)
    window = MainWindow()
    sys.exit(app.exec())
Listing 9-1

Setting up the main window for using the clipboard

The upper QTextEdit in Figure 9-1 is placed in a QDockWidget. This is set up in the createClipboardDock() method.

After importing classes and setting up the window in the previous code, let’s set the central widget of the main window as a QTextEdit widget in Listing 9-2. The central widget is where the user can edit the text that is pasted from the clipboard.
# clipboard_ex.py
    def setUpMainWindow(self):
        """Create and arrange widgets in the main window."""
        self.central_tedit = QTextEdit()
        self.setCentralWidget(self.central_tedit)
Listing 9-2

The setUpMainWindow() method for using the clipboard

Next, we’ll set up the dock widget in Listing 9-3 that is composed of a QTextEdit and a QPushButton arranged in a QVBoxLayout. A QFrame container holds the clipboard_tedit and paste_button widgets.
# clipboard_ex.py
    def createClipboardDock(self):
        """Set up the clipboard and dock widget to display
        text from the system's clipboard."""
        self.clipboard_tedit = QTextEdit()
        paste_button = QPushButton("Paste")
        paste_button.clicked.connect(self.pasteText)
        dock_v_box = QVBoxLayout()
        dock_v_box.addWidget(self.clipboard_tedit)
        dock_v_box.addWidget(paste_button)
        # Set the main layout for the dock widget,
        # then set the main widget of the dock widget
        dock_frame = QFrame()
        dock_frame.setLayout(dock_v_box)
        # Create a dock widget
        clipboard_dock = QDockWidget()
        clipboard_dock.setWindowTitle(
            "Display Clipboard Contents")
        clipboard_dock.setAllowedAreas(
            Qt.DockWidgetArea.TopDockWidgetArea)
        clipboard_dock.setWidget(dock_frame)
        # Set initial location of dock widget
        self.addDockWidget(
            Qt.DockWidgetArea.TopDockWidgetArea,
            clipboard_dock)
        # Create instance of the clipboard
        self.clipboard = QApplication.clipboard()
        self.clipboard.dataChanged.connect(
            self.copyFromClipboard)
Listing 9-3

Code for the createClipboardDock() method

The clipboard_dock widget is set so that it can either float or be attached to the top of the main window. If new text is copied from another application, then clipboard_tedit will display the text. If the user wants to retain the text, then they can press paste_button and copy it into central_tedit.

The QClipboard method dataChanged() emits a signal if the contents of the clipboard have changed. If a change has occurred, then the clipboard_tedit widget is updated to display the new clipboard text using the copyFromClipboard() method in Listing 9-4.
# clipboard_ex.py
    def copyFromClipboard(self):
        """Get the contents of the system clipboard and
        paste to the window that has focus."""
        mime_data = self.clipboard.mimeData()
        if mime_data.hasText():
            self.clipboard_tedit.setText(mime_data.text())
            self.clipboard_tedit.repaint()
    def pasteText(self):
        """Paste text from clipboard if button is clicked."""
        self.central_tedit.paste()
        self.central_tedit.repaint()
Listing 9-4

Code for the copyFromClipboard() and pasteText() slots

To check what kind of data is stored in the clipboard, we use the QMimeData class that is used for both the clipboard and the drag-and-drop system in PyQt. The Multipurpose Internet Mail Extensions (MIME) format supports not only text but also HTML, URLs, images, and color data. Objects created from the QMimeData class ensure that information can be safely moved between applications and also between objects in the same application.

The method mimeData() returns information about the data currently in the clipboard. To check if the object can return plain text, we use the hasText() method. If the data is text, then we get the text using mime_data.text() and set the text of the QTextEdit widget using setText(). A similar process is also used to access other kinds of data using QMimeData.

Finally, the QTextEdit method paste() is called in pasteText() to fetch the text in the clipboard if the button is pressed. The repaint() method is used to force the text of the widget to update.

Project 9.1 – Sticky Notes GUI

Sometimes, you have an idea, a note, or a bit of information that you need to quickly jot down. Maybe you need to remind yourself of an appointment and need to write a note to yourself. You only need a small, temporary, maybe even colorful, area to help brainstorm and organize those ideas. Sticky notes are perfect for those uses and more.

The sticky notes GUI, shown in Figure 9-2, allows you to open as many windows as you want. You can edit the text of each note individually, change the color of a note, and also paste text from the clipboard. This project demonstrates a practical use for the clipboard class and acts as a foundation if you choose to build your own sticky notes application.
Figure 9-2

The sticky notes GUI

The sticky notes GUI is a good project to introduce the concept of Single Document Interface (SDI) . SDI is a method that organizes GUIs into individual windows that are handled separately. Even though the sticky note application allows you to create multiple instances of the GUI at the same time, each window is separate and independent from the others. The contrast is Multiple Document Interface (MDI) , where a single parent window contains and controls multiple nested child windows. An example of MDI can be found in Chapter 12.

Explanation for the Sticky Notes GUI

The sticky note window is relatively simple, consisting of a single QTextEdit widget that serves as the central widget. The menu bar allows you to create a new note, clear the text in the QTextEdit widget, quit the application, change the background color, and paste text from the clipboard.

Beginning with the main_window_template.py script from Chapter 5, we’ll import the classes that we need in Listing 9-5. Since this window contains a menu bar, be sure to import QAction to create actions for the menu.

For this program, the MainWindow class is changed to StickyNote. You don’t have to change this, but doing so is just a subtle indication that each instance of StickyNote is its own window.
# sticky_notes.py
# Import necessary modules
import sys
from PyQt6.QtWidgets import (QApplication, QMainWindow,
    QTextEdit)
from PyQt6.QtGui import QAction
class StickyNote(QMainWindow):
    # Class variables shared by all instances
    note_id = 1
    notes = []
    def __init__(self, note_ref=str()):
        super().__init__()
        self.initializeUI()
    def initializeUI(self):
        """Set up the application's GUI."""
        self.setMinimumSize(250, 250)
        self.setWindowTitle("9.1 - Sticky Notes GUI")
        self.setUpMainWindow()
        self.createActions()
        self.createMenu()
        self.createClipboard)
        self.show()
if __name__ == "__main__":
    app = QApplication(sys.argv)
    window = StickyNote()
    sys.exit(app.exec())
Listing 9-5

Setting up the main window for the sticky notes GUI

The StickyNote class includes two class variables: note_id, used to give a unique name and reference to each new window, and notes, to keep track of the new windows that are opened by appending them to a list. The class variables are shared by all instances of the class and managed by QApplication.

A QTextEdit widget is set as the central widget in Listing 9-6. In addition, when a new note is created, the instance is appended to the notes list.
# sticky_notes.py
  def setUpMainWindow(self):
        """Create and arrange widgets in the main window.”""
        self.notes.append(self)
        self.central_tedit = QTextEdit()
        self.setCentralWidget(self.central_tedit)
Listing 9-6

Code for the SetUpMainWindow() method for the sticky notes GUI

Listing 9-7 builds the actions for a StickyNote instance. The menu bar includes actions for creating a new instance of StickyNote, changing background colors, and pasting text. You can refer to Chapter 5 for help setting up menu bars and actions.
# sticky_notes.py
    def createActions(self):
        """Create the application's menu actions."""
        # Create actions for File menu
        self.new_note_act = QAction("New Note", self)
        self.new_note_act.setShortcut("Ctrl+N")
        self.new_note_act.triggered.connect(self.newNote)
        self.close_act = QAction("Clear", self)
        self.close_act.setShortcut("Ctrl+W")
        self.close_act.triggered.connect(self.clearNote)
        self.quit_act = QAction("Quit", self)
        self.quit_act.setShortcut("Ctrl+Q")
        self.quit_act.triggered.connect(self.close)
        # Create actions for Color menu
        self.yellow_act = QAction("Yellow", self)
        self.yellow_act.triggered.connect(
            lambda: self.changeBackground(
                self.yellow_act.text()))
        self.blue_act = QAction("Blue", self)
        self.blue_act.triggered.connect(
            lambda: self.changeBackground(
                self.blue_act.text()))
        self.green_act = QAction("Green", self)
        self.green_act.triggered.connect(
            lambda: self.changeBackground(
                self.green_act.text()))
        # Create actions for Paste menu
        self.paste_act = QAction("Paste", self)
        self.paste_act.setShortcut("Ctrl+V")
        self.paste_act.triggered.connect(
            self.pasteToClipboard)
Listing 9-7

Code for the createActions() method for the sticky notes GUI

Depending upon your system, you may see warnings appear in your shell talking about mismatched keys. These are due to the different rules for virtual key codes set by each platform. Other warnings can also arise due to hard-coded key bindings for Qt input widget classes. If you see an error, don’t worry at the moment. For this example, the actions will still perform as designated by the specified shortcut keys.

Listing 9-8 creates the File, Color, and Paste menus and their options.
# sticky_notes.py
    def createMenu(self):
        """Create the application's menu bar."""
        self.menuBar().setNativeMenuBar(False)
        # Create File menu and add actions
        file_menu = self.menuBar().addMenu("File")
        file_menu.addAction(self.new_note_act)
        file_menu.addAction(self.close_act)
        file_menu.addAction(self.quit_act)
        # Create Color menu and add actions
        color_menu = self.menuBar().addMenu("Color")
        color_menu.addAction(self.yellow_act)
        color_menu.addAction(self.blue_act)
        color_menu.addAction(self.green_act)
        # Create Paste menu and add actions
        paste_menu = self.menuBar().addMenu("Paste")
        paste_menu.addAction(self.paste_act)
Listing 9-8

Code for the createMenu() method for the sticky notes GUI

The Color menu allows the user to select a background color for each note. If the user wants to paste text from the clipboard into a widget, they can either use the Paste menu option or the hotkey, Ctrl+V.

The createClipboard() method creates the clipboard object, and the copyToClipboard() slot in Listing 9-9 is triggered when data is changed in the clipboard by the dataChanged signal.
# sticky_notes.py
    def createClipboard(self):
        """Set up the clipboard."""
        self.clipboard = QApplication.clipboard()
        self.clipboard.dataChanged.connect(
            self.copyToClipboard)
        self.mime_data = self.clipboard.mimeData()
    def copyToClipboard(self):
        """Get the contents of the system clipboard."""
        self.mime_data = self.clipboard.mimeData()
    def newNote(self):
        """Create new instance of StickyNote class."""
        StickyNote().show()
        self.note_id += 1
Listing 9-9

Code for setting up the clipboard in the sticky notes GUI

The variable mime_data holds the current data copied to the clipboard and is updated when the data is changed. A new note instance is created in newNote(). Using show(), that new StickyNote instance appears on screen. Each new note is given a reference number, note_id, when it is created. A challenge for you would be to find a PyQt method that allows you to specify the 2D location where the new window will appear.

The other slots in Listing 9-10 allow you to clear with the text, change the background color of central_tedit, or check if mime_data has text and, if so, paste it into central_tedit.
# sticky_notes.py
    def clearNote(self):
        """Delete the current note's text."""
        self.central_tedit.clear()
    def changeBackground(self, color_text):
        """Change a note's background color."""
        if color_text == "Yellow":
            self.central_tedit.setStyleSheet(
                "background-color: rgb(248, 253, 145)")
        elif color_text == "Blue":
            self.central_tedit.setStyleSheet(
                "background-color: rgb(145, 253, 251)")
        elif color_text == "Green":
            self.central_tedit.setStyleSheet(
                "background-color: rgb(148, 253, 145)")
    def pasteToClipboard(self):
        """Get the contents of the system clipboard and
        paste into the note."""
        if self.mime_data.hasText():
            self.central_tedit.paste()
Listing 9-10

Additional slots for the menu items in the sticky notes GUI

The sticky note GUI shows one practical use case when copying data between applications can be helpful.

Summary

The QClipboard class allows GUI applications to receive and send data from the system’s clipboard. The QMimeData class handles various kinds of data types for both clipboard and drag-and-drop systems, ensuring proper data handling.

Many of PyQt’s widgets for editing text already include the ability to interact with the clipboard, so you won’t often need to include the code for the clipboard in your program.

In the next chapter, you will learn about Qt’s classes for handling data. You will also learn how to apply drag-and-drop functionality in your GUIs so that data can be passed between widgets and other programs.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset