Следует ли мне переписать свой Tkinter на PyQt или наоборот? - PullRequest
0 голосов
/ 04 августа 2020

Я написал приложение Tkinter и хотел добавить фрагмент экрана, поэтому я нашел отдельную программу из GitHub (screen-snip) , написанную на PyQt, которую я импортировал и использовал в мое приложение Tkinter . Затем я решил объединить программы, чтобы задать ТАК вопрос, почему они не работают вместе. Я научился не комбинировать Tk и Qt.

Итак, теперь мой вопрос: следует ли мне переписывать свою программу на Qt или Tk?

Что лучше для в этой ситуации?

Моя текущая программа со смешанным Tk / Qt работает, когда вы выбираете файл изображения, но теперь фрагмент экрана с Qt class MyWidget(QtWidgets.QWidget): заставляет его зависать, а затем cra sh.

введите описание изображения здесь

Я думаю, что проблема может быть в результате смешивания Qt с Tk, но я не уверен. Изначально у меня было два экземпляра запущенного tkinter, что позволило мне получить экранный корабль с новым окном, но вызвал проблемы с окном button, поэтому я заменил его, пытаясь использовать tk.Toplevel

class MyWidget(QtWidgets.QWidget):
    def __init__(self, master):
        super().__init__()
        self.master = master
        self.window = tk.Toplevel(self.master)

и тут я столкнулся с проблемой. Виджет больше не работает, и программа вылетает без каких-либо подсказок или ошибок. Есть идеи, почему?

Упрощенный код:

import tkinter as tk
from tkinter import filedialog
from PIL import ImageTk, Image, ImageGrab
import sys
from PyQt5 import QtWidgets, QtCore, QtGui
import numpy as np
import cv2


class ButtonImg:

    def __init__(self, master):
        self.newWindow = None
        self.master = master
        self.fontA = ("arial", 20, "bold")

        self.canvas = tk.Canvas(height = 5)
        self.canvas.pack()

        self.button = tk.Button(bg="#61B5DA", height = 5, text = "Select Image",
                                font = self.fontA, command = self.changeImage)
        self.button.pack(fill="both")

    def changeImage(self):
        print('open second window')
        self.newWindow = tk.Toplevel(self.master)
        img = AcquireImage(self.newWindow)
        self.master.wait_window(self.newWindow)
        print('close second window')

        if img.image_selected: # check if image was selected
            self.image = img.image_selected
            self.button.configure(image=self.image, height=self.image.height())


class AcquireImage:

    def __init__(self, master):
        self.master = master
        self.fontA = ("arial", 20, "bold")

        self.frame = tk.Frame(master, bg="#96beed")
        self.frame.pack(fill="both", expand=True)

        self.button1 = tk.Button(self.frame, text="Select Image File", padx=5, pady=5, bg="#6179DA",
                              font = self.fontA, command =lambda: self.show_dialogs(1))
        self.button1.grid(row=0, column=0, sticky="nsew")

        self.button2 = tk.Button(self.frame, text="Get Screen Snip", padx=5, pady=5, bg="#6179DA",
                              font = self.fontA, command=lambda: self.show_dialogs(2))
        self.button2.grid(row=0, column=1, sticky="nsew")

        self.image_selected = None

    def show_dialogs(self, method):

        if method == 1:
            ret = filedialog.askopenfilename() #filedialog.askopenfilename(initialdir='/home/user/images/')
            if ret:
                self.image_selected = ImageTk.PhotoImage(file = ret)
                self.master.destroy()

        elif method == 2:
            newWin = MyWidget(self.master)
            newWin.show()
            ret = newWin.img
            if ret:
                self.image_selected = ImageTk.PhotoImage(file = ret)


class MyWidget(QtWidgets.QWidget):
    def __init__(self, master):
        super().__init__()
        self.master = master
        self.window = tk.Toplevel(self.master)
        screen_width = self.thirdWin.winfo_screenwidth()
        screen_height = self.thirdWin.winfo_screenheight()
        self.setGeometry(0, 0, screen_width, screen_height)
        self.setWindowTitle(' ')
        self.begin = QtCore.QPoint()
        self.end = QtCore.QPoint()
        self.img = None

        self.setWindowOpacity(0.3)
        QtWidgets.QApplication.setOverrideCursor(
            QtGui.QCursor(QtCore.Qt.CrossCursor)
        )
        self.setWindowFlags(QtCore.Qt.FramelessWindowHint)
        print('Capture the screen...')
        self.show()

    def getRect(self):
        # a commodity function that always return a correctly sized
        # rectangle, with normalized coordinates
        width = self.end.x() - self.begin.x()
        height = abs(width * 2 / 3)
        if self.end.y() < self.begin.y():
            height *= -1
        return QtCore.QRect(self.begin.x(), self.begin.y(),
                            width, height).normalized()

    def paintEvent(self, event):
        qp = QtGui.QPainter(self)
        qp.setPen(QtGui.QPen(QtGui.QColor('black'), 3))
        qp.setBrush(QtGui.QColor(128, 128, 255, 128))
        qp.drawRect(self.getRect())

    def mousePressEvent(self, event):
        self.begin = event.pos()
        self.end = self.begin
        self.update()

    def mouseMoveEvent(self, event):
        self.end = event.pos()
        self.update()

    def mouseReleaseEvent(self, event):
        self.close()

        rect = self.getRect()
        self.img = ImageGrab.grab(bbox=(
            rect.topLeft().x(),
            rect.topLeft().y(),
            rect.bottomRight().x(),
            rect.bottomRight().y()
        ))
        #self.img.save('capture.png')
        self.img = cv2.cvtColor(np.array(self.img), cv2.COLOR_BGR2RGB)

        cv2.imshow('Captured Image', self.img)
        cv2.waitKey(0)
        #cv2.destroyAllWindows()

if __name__ == '__main__':
    root = tk.Tk()
    app = ButtonImg(root)
    root.mainloop()

1 Ответ

2 голосов
/ 05 августа 2020

Как сказано в комментариях, лучше всего использовать один GUI набор инструментов, поэтому вам нужно либо переписать код для Qt, либо переписать код сниппинга с помощью tkinter. Я мало знаю Qt, поэтому не могу помочь вам с первым вариантом. Однако на самом деле снимок экрана сделан с использованием PIL, а не какого-либо метода Qt c, поэтому код обрезки можно переписать в tkinter.

Все, что вам нужно, это полноэкранный верхний уровень, содержащий холст с перетаскиваемым прямоугольником, как в Рисование прямоугольника с использованием событий мыши в Tkinter . Чтобы сделать верхний уровень полноэкранным: toplevel.attributes('-fullscreen', True)

Верхний уровень должен быть частично прозрачным, что может быть достигнуто с помощью toplevel.attributes('-alpha', <value>). Я использую Linux (со средой рабочего стола XFCE), и мне нужно добавить toplevel.attributes('-type', 'dock'), чтобы прозрачность работала.

Все вместе в классе, это дает:

import sys
import tkinter as tk
from PIL import ImageGrab
import cv2
import numpy as np

class MyWidget(tk.Toplevel):
    def __init__(self, master):
        super().__init__(master)
        self.configure(cursor='cross')
        if sys.platform == 'linux':
            self.attributes('-type', 'dock')  # to make transparency work in Linux
        self.attributes('-fullscreen', True)
        self.attributes('-alpha', 0.3)

        self.canvas = tk.Canvas(self, bg='white')
        self.canvas.pack(fill='both', expand=True)

        self.begin_x = 0
        self.begin_y = 0
        self.end_x = 0
        self.end_y = 0

        self.canvas.create_rectangle(0, 0, 0, 0, outline='gray', width=3, fill='blue', tags='snip_rect')
        self.canvas.bind('<ButtonPress-1>', self.mousePressEvent)
        self.canvas.bind('<B1-Motion>', self.mouseMoveEvent)
        self.canvas.bind('<ButtonRelease-1>', self.mouseReleaseEvent)

        print('Capture the screen...')

    def mousePressEvent(self, event):
        self.begin_x = event.x
        self.begin_y = event.y
        self.end_x = self.begin_x
        self.end_y = self.begin_y
        self.canvas.coords('snip_rect', self.begin_x, self.begin_y, self.end_x, self.end_y)

    def mouseMoveEvent(self, event):
        self.end_x = event.x
        self.end_y = event.y
        self.canvas.coords('snip_rect', self.begin_x, self.begin_y, self.end_x, self.end_y)

    def mouseReleaseEvent(self, event):
        self.destroy()
        self.master.update_idletasks()
        self.master.after(100)  # give time for screen to be refreshed so as not to see the blue box on the screenshot
        x1 = min(self.begin_x, self.end_x)
        y1 = min(self.begin_y, self.end_y)
        x2 = max(self.begin_x, self.end_x)
        y2 = max(self.begin_y, self.end_y)

        img = ImageGrab.grab(bbox=(x1, y1, x2, y2))
        self.img = cv2.cvtColor(np.array(img), cv2.COLOR_BGR2RGB)

        cv2.imshow('Captured Image', self.img)
        cv2.waitKey(0)


if __name__ == '__main__':
    root = tk.Tk()
    tk.Button(root, text='Snip', command=lambda: MyWidget(root)).pack()
    root.mainloop()
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...