Home > Software design >  Display python (constantly changing) 'print output' to tkinter GUI textbox (flush?)
Display python (constantly changing) 'print output' to tkinter GUI textbox (flush?)

Time:03-16

I am new to tkinter and I coded a live cps counter that displays your current cps, the cps value is therefore constantly changing. All I want to do Is to display this output in the tkinter GUI or an other GUI instead of my pycharm outpout box.

This is the output I get, note that the cps value is changing constantly

10.4 cps

In my code there is this code line that prints the cps:

print(raw_raw/sec, 'cps', end="")

Is there any way I can make this line 'print' in the tkinter GUI?

There is also this line:

print("\r", end="")

That makes the cps count stay in the same line by replacing the previous line to not print an infinite list of cps values

full code:

from pynput.mouse import Listener
import time

raw_clicks = 0
sec = 0.6

def on_click(x, y, button, pressed):
    global start
    listener.stop()
    start = time.time()
    print('')

with Listener(on_click=on_click) as listener:
    listener.join()

while True:
    def on_click(x, y, button, pressed):
            global raw_clicks
            global sec
            global start
            raw_clicks = raw_clicks   1
            if time.time() - start > sec:
                listener.stop()
                raw_raw = (raw_clicks / 2)
                raw_raw = round(raw_raw/sec, 1)
                print("\r", end="")
                print(raw_raw, 'cps', end="")
                raw_clicks = 0
                start = time.time()

    with Listener(on_click=on_click) as listener:
        listener.join()

Thanks for your help

CodePudding user response:

I made an example displaying Your cpi in cmdline, tkinter and pyqt5.

I simplified Your cpi counter and made it in class. Sorry to change Your code completely, but like that it's more suitable to work with multiple output interfaces. In principle, You can use Your code, but it must not be blocking.

Let's start with file click_counter.py, it will hold ClickCounter class, which is our back-end, counting clicks in user defined interval (default is 1 second).

from threading import Thread, Event
from typing import Callable

from pynput.mouse import Listener


class ClickCounter(Thread):
    """Click counter, counting clicks / interval"""
    click_count = 0
    stopped = Event()

    def __init__(self, callback: Callable, interval: float = 1.0) -> None:
        super().__init__()
        self.interval = interval
        self.callback = callback
        self.listener = Listener(on_click=self.click_counter)

    def run(self) -> None:
        """Start mouse click listener and timer"""
        self.listener.start()

        while not self.stopped.wait(self.interval):
            # Call callback with click counter value, after interval expires
            self.callback(self.click_count)
            # Reset counter
            self.click_count = 0

    def click_counter(self, x: float, y: float, button: int, pressed: bool) -> None:
        """Count clicks"""
        if pressed:
            # Count when mouse button is pressed
            self.click_count  = 1

    def cancel(self) -> None:
        """Cancel counter timer"""
        # Stop timer
        self.stopped.set()
        # Stop listener
        self.listener.stop()

Then we can define different output interfaces.
Let's start with cps_cmdline.py, which displays cps in command line:

import signal
from time import sleep

from click_counter import ClickCounter


def print_counter(count):
    print("\r", end="")
    print(count, 'cps', end="")


click_counter = ClickCounter(print_counter)
click_counter.start()

stopped = False

def exit_app(*args):
    """Close counter and app"""
    global stopped, click_counter
    click_counter.cancel()
    stopped = True

# Register kill signals
signal.signal(signal.SIGINT, exit_app)
signal.signal(signal.SIGTERM, exit_app)

while not stopped:
    # Just run until stopped
    sleep(0.1)

To display cps in tkinter, use code in file cps_tkinter.py:

import tkinter as tk

from cps.click_counter import ClickCounter


class Window(tk.Frame):

    def __init__(self, master=None):
        """Create label and StringVar holding its text"""
        super().__init__(master)
        self.master = master
        self.pack(fill=tk.BOTH, expand=1)
        self.cps_text = tk.StringVar(value="0 cps")
        self.cps_label = tk.Label(self, textvariable=self.cps_text)
        self.cps_label.place(relx=0.5, rely=0.5, anchor='center')

    def print_counter(self, count):
        """Thread safe variable set"""
        self.after(0, self.cps_text.set, f"{count} cps")

if __name__ == "__main__":
    root = tk.Tk()
    app = Window(root)
    root.wm_title("CPS counter")
    root.geometry("100x100")

    # Create and start counter
    click_counter = ClickCounter(app.print_counter)
    click_counter.start()

    # Start tkinter app
    root.mainloop()
    # tkinter app is over, cancel click_counter
    click_counter.cancel()

And last but not least cps_qt5.py:

import sys

from PyQt5.QtCore import pyqtSignal, pyqtSlot, Qt
from PyQt5.QtWidgets import QApplication, QWidget, QLabel, QHBoxLayout

from cps.click_counter import ClickCounter


class Window(QWidget):
    sig_print_counter = pyqtSignal(int)

    def __init__(self, parent=None):
        """Create label and StringVar holding its text"""
        super().__init__(parent)
        self.cps_label = QLabel()
        self.cps_label.setText("0 cps")
        self.resize(100, 100)
        layout = QHBoxLayout()
        layout.setAlignment(Qt.AlignHCenter)
        layout.addWidget(self.cps_label)
        self.setLayout(layout)
        self.sig_print_counter.connect(self.print_counter)

    @pyqtSlot(int)
    def print_counter(self, count):
        """Thread safe label text set"""
        self.cps_label.setText(f"{count} cps")


if __name__ == "__main__":
    app = QApplication(sys.argv)
    window = Window()
    window.show()

    # Create and start counter
    click_counter = ClickCounter(window.sig_print_counter.emit)
    click_counter.start()

    app.exec()
    # Qt app is over, cancel click_counter
    click_counter.cancel()
  • Related