gpt4 book ai didi

python - 如何通过tk/tcl初始化小部件

转载 作者:太空宇宙 更新时间:2023-11-03 23:59:48 25 4
gpt4 key购买 nike

我正在尝试制作GUI。我正在使用图像作为标签。
矩形表示操作区域,我可以在其中拖动标签来启动小部件或应用程序。怎么做?

我用矩形制作了一个画布,并且实现了拖放功能。

我已经使用pack()用矩形实现了画布,并且使用了拖放功能

import subprocess
from tkinter import *

class DragAndDrop:
def __init__(self, boxes, apps, width=1920, height=1080, bg="white"):
self.photos = []
self.__apps = {}
self.__boxes = set()
self.root = Tk()
self.canvas = Canvas(self.root, width=width, height=height, bg=bg)
self.canvas.pack()

for box in boxes:
self.__boxes.add(
self.canvas.create_rectangle(
box["x1"], box["y1"], box["x2"], box["y2"],
width=box["width"], fill=box["fill"]
)
)

for app in apps:
self.photos.append(PhotoImage(file=app["img"]))
self.__apps[(
self.canvas.create_image(app["x"], app["y"], image=self.photos[-1])
)] = app["cmd"]

self.__move = False
self.canvas.bind("<Button-1>", self.start_movement)
self.canvas.bind("<ButtonRelease-1>", self.stop_movement)
self.canvas.bind("<Motion>", self.movement)

def run(self):
self.root.mainloop()

def start_movement(self, event):
self.initi_x = self.canvas.canvasx(event.x)
self.initi_y = self.canvas.canvasy(event.y)
self.movingimage = self.canvas.find_closest(
self.initi_x, self.initi_y, halo=5
)

if self.movingimage[0] in self.__apps:
self.__move = True

def stop_movement(self, event):
self.__move = False
overlaps = self.canvas.find_overlapping(*self.canvas.bbox(self.movingimage))

if len(overlaps) > 1 and not self.movingimage[0] in self.__boxes and \
any(x in self.__boxes for x in overlaps):
subprocess.Popen(self.__apps[self.movingimage[0]])

def movement(self, event):
if self.__move:
end_x = self.canvas.canvasx(event.x)
end_y = self.canvas.canvasy(event.y)
deltax = end_x - self.initi_x
deltay = end_y - self.initi_y
self.initi_x = end_x
self.initi_y = end_y
self.canvas.move(self.movingimage, deltax, deltay)


if __name__ == "__main__":
boxes = (
{"x1": 618, "y1": 100, "x2": 693, "y2": 175, "width": 5, "fill": "white"},
{"x1": 693, "y1": 100, "x2": 768, "y2": 175, "width": 5, "fill": "white"},
{"x1": 618, "y1": 175, "x2": 693, "y2": 250, "width": 5, "fill": "green"},
{"x1": 693, "y1": 175, "x2": 768, "y2": 250, "width": 5, "fill": "green"},
{"x1": 618, "y1": 250, "x2": 693, "y2": 325, "width": 5, "fill": "blue"},
{"x1": 693, "y1": 250, "x2": 768, "y2": 325, "width": 5, "fill": "blue"},
{"x1": 618, "y1": 325, "x2": 693, "y2": 400, "width": 5, "fill": "yellow"},
{"x1": 693, "y1": 325, "x2": 768, "y2": 400, "width": 5, "fill": "yellow"},
{"x1": 543, "y1": 175, "x2": 618, "y2": 250, "width": 5, "fill": "dark orange"},
{"x1": 468, "y1": 175, "x2": 543, "y2": 250, "width": 5, "fill": "dark orange"},
{"x1": 768, "y1": 175, "x2": 843, "y2": 250, "width": 5, "fill": "red"},
{"x1": 843, "y1": 175, "x2": 918, "y2": 250, "width": 5, "fill": "red"},
)

apps = (
{"x": 125, "y": 125, "img": "chrome.png", "cmd": r"C:\Program Files (x86)\Google\Chrome\Application\chrome.exe"},
{"x": 125, "y": 225, "img": "firefox.png", "cmd": r"C:\Program Files\Mozilla Firefox\firefox.exe"},
{"x": 125, "y": 325, "img": "np++.png", "cmd": r"C:\Program Files\Notepad++\notepad++.exe"},
{"x": 125, "y": 425, "img": "word.png", "cmd": r"C:\Program Files\Microsoft Office\root\Office16\WINWORD.exe"},
{"x": 200, "y": 125, "img": "excel.png", "cmd": r"C:\Program Files\Microsoft Office\root\Office16\EXCEL.EXE"},
{"x": 200, "y": 225, "img": "ppt.png", "cmd": r"C:\Program Files\Microsoft Office\root\Office16\POWERPNT.EXE"},
{"x": 200, "y": 325, "img": "outlook.png", "cmd": r"C:\Program Files\Microsoft Office\root\Office16\OUTLOOK.EXE"},
{"x": 200, "y": 425, "img": "access.png", "cmd": r"C:\Program Files\Microsoft Office\root\Office16\MSACCESS.EXE"},
{"x": 50, "y": 125, "img": "onenote.png", "cmd": r"C:\Program Files\Microsoft Office\root\Office16\ONENOTE.EXE"},
{"x": 50, "y": 225, "img": "pub.png", "cmd": r"C:\Program Files\Microsoft Office\root\Office16\MSPUB.EXE"},
{"x": 50, "y": 325, "img": "vlc.png", "cmd": r"C:\Program Files\VideoLAN\VLC\vlc.exe"},
{"x": 50, "y": 425, "img": "ccl.png", "cmd": r"C:\Program Files\CCleaner\CCleaner64.exe"},

)

dnd = DragAndDrop(boxes, apps)
dnd.run()

最佳答案

首先,这是一个非常酷的项目,欢迎来到SO!



重构

在继续添加新逻辑之前,值得花时间清理当前代码。

列表和循环

现在,将变量命名为image0image1 ... image12的方法非常严格且不可扩展。如果您需要添加另一个框或应用程序图标,则基本上要重写所有代码以适应更改。至于可伸缩性,如果要50、100或1000个应用程序怎么办?这将是很多打字!

这就是为什么发明了lists和类似的阵列状结构的原因。这个想法是将类似物品放入其中的单个容器。您可以在列表上loop并对列表中的每个项目执行操作。我不会去学习有关列表和循环的完整教程,但是它们是完成任何编程任务的必备工具,因此必须学习如何使用它们,以使自己成为编码人员。

作为您即时代码中的一个具体示例,请使用一个变量images = []代替image1 ... image12。在花括号内,添加图像数据,然后使用images[n]访问其中一个,其中n是要处理的图像的索引。您可以使用以下结构遍历它们:

for image in images:
# do something with this image


您也可以将元组用作无法更改的列表(我在整个应用程序中都使用元组-与列表类似,但它们看起来像 apps = ())。

辞典

列表是水平的,并且像项目一样存储在集合中,而 dictionaries是垂直的,或与组相关但不同的属性一起组合到单个实体中。在您的代码中,“ app”实体由一些字符串和数字描述,如下所示:

{
"x": 125,
"y": 125,
"img": "np++.png",
"cmd": r"C:\Program Files (x86)\Notepad++\notepad++.exe"
}


我列出了这些词典,以存储有关我希望框和应用程序显示在何处以及与每个命令和图像关联的命令和图像(或颜色/宽度)的数据。

套装

Sets对于检查成员资格很有用。在此应用程序中,我们需要确定哪些画布实体是应用程序图标,哪些是投递箱。我使用集合来执行此标记逻辑,其中两个不相交的集合分别包含应用程序和框的ID。

封装形式

当前,您的代码在许多不同的地方都有相关的逻辑。该类访问全局状态中包含的许多数据。这是不安全的:如果您更改了有关全局状态的某些信息,则很可能导致错误或破坏类。尝试编写具有强 encapsulation的函数和类,并尽可能减少组件之间的依赖关系。在此应用程序中,可以将所有内容整齐地打包到 DragAndDrop类中,并简单地传入参数以告知其操作方法。这样,调用者只能使用该类的可用公共功能,并且故障很容易被隔离和可预测。

样式清理

通过 Python convention,将 snake_case用作变量和函数名称,将 UpperCamelCase用作类。发布代码时,请确保缩进正确,因为Python使用缩进来确定每行代码所在的块范围。

除了我重命名为 dndDragAndDrop类之外,您的变量名也很清晰,值得称赞!



添加新行为

重构并设置数据结构后,我们可以开始自由添加新功能。

碰撞

虽然您的拖放功能很漂亮,但是尚无代码来确定何时将应用程序图标拖放到框上。这有点棘手:我们可以使用 canvas.find_overlapping()来检查是否重叠,但是我们需要确保将图标放到一个盒子上而不是另一个图标上。移动停止后,我们可以调用此函数来执行此操作:

def stop_movement(self, event):
self.__move = False
overlaps = self.canvas.find_overlapping(*self.canvas.bbox(self.movingimage))

if len(overlaps) > 1 and not self.movingimage[0] in self.__boxes and \
any(x in self.__boxes for x in overlaps):
subprocess.call(self.__apps[self.movingimage[0]])


该函数使用了我之前提到的一些数据结构来建立不同实体之间的关系。

创建和终止进程

使用 subprocess.call()进行系统调用以打开一个新进程并阻塞直到其关闭。如果要打开多个应用程序而不会阻塞等待它们完成,则可以使用 subprocess.Popen()。请查看 docs以获取更多信息。我使用字典来映射带有正确命令的应用程序ID,以传递到 subprocess.Popen()

根据您的其他请求,在创建的子进程上调用 kill()将其杀死。我将所有这些信息保存在 self.__app词典中,但是它可能使用对类的重构来进行适当的封装,因为应用程序正在累积自己的属性和行为逻辑。





注意,这只是添加了新行为的初始重构。总会有改进的空间,我在组织数据方面所做的一些选择可能并不符合您的喜好,因此我建议您进一步探索并调整口味。我也只想添加两个文本编辑器应用程序,但是您可以根据需要将尽可能多的应用程序添加到 apps元组中,以进行进一步测试。

import subprocess
from tkinter import *

class DragAndDrop:
def __init__(self, boxes, apps, width=1920, height=1080, bg="white"):
self.photos = []
self.__apps = {}
self.__boxes = set()
self.root = Tk()
self.canvas = Canvas(self.root, width=width, height=height, bg=bg)
self.canvas.pack()

for box in boxes:
self.__boxes.add(
self.canvas.create_rectangle(
box["x1"], box["y1"], box["x2"], box["y2"],
width=box["width"], fill=box["fill"]
)
)

for app in apps:
self.photos.append(PhotoImage(file=app["img"]))
self.__apps[(
self.canvas.create_image(app["x"], app["y"], image=self.photos[-1])
)] = {"cmd": app["cmd"], "running": False, "proc": None}

self.__move = False
self.canvas.bind("<Button-1>", self.start_movement)
self.canvas.bind("<ButtonRelease-1>", self.stop_movement)
self.canvas.bind("<Motion>", self.movement)

def run(self):
self.root.mainloop()

def start_movement(self, event):
self.initi_x = self.canvas.canvasx(event.x)
self.initi_y = self.canvas.canvasy(event.y)
self.movingimage = self.canvas.find_closest(
self.initi_x, self.initi_y, halo=5
)

if self.movingimage[0] in self.__apps:
self.__move = True

def stop_movement(self, event):
self.__move = False
overlaps = self.canvas.find_overlapping(*self.canvas.bbox(self.movingimage))
app = self.movingimage[0]

if len(overlaps) > 1 and app not in self.__boxes and not self.__apps[app]["running"] \
and any(x in self.__boxes for x in overlaps):
self.__apps[app]["proc"] = subprocess.Popen(self.__apps[app]["cmd"])
self.__apps[app]["running"] = True
elif app not in self.__boxes and self.__apps[app]["running"] \
and not any(x in self.__boxes for x in overlaps):
self.__apps[app]["proc"].kill()
self.__apps[app]["running"] = False

def movement(self, event):
if self.__move:
end_x = self.canvas.canvasx(event.x)
end_y = self.canvas.canvasy(event.y)
deltax = end_x - self.initi_x
deltay = end_y - self.initi_y
self.initi_x = end_x
self.initi_y = end_y
self.canvas.move(self.movingimage, deltax, deltay)


if __name__ == "__main__":
boxes = (
{"x1": 618, "y1": 100, "x2": 693, "y2": 175, "width": 5, "fill": "white"},
{"x1": 693, "y1": 100, "x2": 768, "y2": 175, "width": 5, "fill": "white"},
{"x1": 618, "y1": 175, "x2": 693, "y2": 250, "width": 5, "fill": "green"},
{"x1": 693, "y1": 175, "x2": 768, "y2": 250, "width": 5, "fill": "green"},
{"x1": 618, "y1": 250, "x2": 693, "y2": 325, "width": 5, "fill": "blue"},
{"x1": 693, "y1": 250, "x2": 768, "y2": 325, "width": 5, "fill": "blue"},
{"x1": 618, "y1": 325, "x2": 693, "y2": 400, "width": 5, "fill": "yellow"},
{"x1": 693, "y1": 325, "x2": 768, "y2": 400, "width": 5, "fill": "yellow"},
{"x1": 543, "y1": 175, "x2": 618, "y2": 250, "width": 5, "fill": "dark orange"},
{"x1": 468, "y1": 175, "x2": 543, "y2": 250, "width": 5, "fill": "dark orange"},
{"x1": 768, "y1": 175, "x2": 843, "y2": 250, "width": 5, "fill": "red"},
{"x1": 843, "y1": 175, "x2": 918, "y2": 250, "width": 5, "fill": "red"},
)

apps = (
{"x": 125, "y": 125, "img": "np++.png", "cmd": r"C:\Program Files (x86)\Notepad++\notepad++.exe"},
{"x": 125, "y": 225, "img": "vim.png", "cmd": r"C:\Program Files (x86)\Vim\vim74\vim.exe"},
)

dnd = DragAndDrop(boxes, apps)
dnd.run()




演示版

这是Windows上该程序的快速运行。我打开几个文本编辑器并检查碰撞。

enter image description here

关于python - 如何通过tk/tcl初始化小部件,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56064099/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com