实战wxPython:052 - 在程序中使用拖放

在GUI程序中,拖放是单击虚拟对象并将其拖到不同位置或另一个虚拟对象上的操作(或对该操作的支持)。一般来说,它可用于调用多种类型的操作,或在两个抽象对象之间创建各种类型的关联。

拖放是图形用户界面中最直观的操作,通过它可以做很多复杂的事情。在拖放中,我们将一些数据从一个源位置移动到目标位置,所以我们必须有:

 

  • 一些数据。
  • 一个数据来源。
  • 一个数据目标。

wxPython提供了几种不同类型的拖放,它们是:

  • wx.FileDropTarget。
  • wx.TextDropTarget。
  • wx.PyDropTarget。

一、文件拖放wx.FileDropTarget

wx.FileDropTarget是一个拖放目标,它接受从文件管理器中拖出的文件。wxPython工具包使得创建拖放目标非常简单。我们子类化wx. FileDropTarget, 创建类MyFileDropTarget,重载方法OnDropFiles。它接受鼠标的x/y位置和被删除的文件路径,然后将它们写到文本控件中。在MyDnDPanel类调用文本控件的SetDropTarget方法并将其设置为拖放目标类的实例。

#文件拖放操作

import wx

class MyFileDropTarget(wx.FileDropTarget):

    def __init__(self, window):
        wx.FileDropTarget.__init__(self)
        self.window = window

    def OnDropFiles(self, x, y, filenames):
        self.window.SetInsertionPointEnd()
        self.window.UpdateText("\n%d file(s) dropped at %d, %d:\n" % (len(filenames), x, y))
        for filepath in filenames:
            self.window.UpdateText(filepath + "\n")

class MyDnDPanel(wx.Panel):
    
    def __init__(self, parent):
        wx.Panel.__init__(self, parent=parent)

        file_drop_target = MyFileDropTarget(self)
        lbl = wx.StaticText(self, label="拖一些文件到这里:")
        self.fileTextCtrl = wx.TextCtrl(self, style = wx.TE_MULTILINE|wx.HSCROLL|wx.TE_READONLY)
        self.fileTextCtrl.SetDropTarget(file_drop_target)

        sizer = wx.BoxSizer(wx.VERTICAL)
        sizer.Add(lbl, 0, wx.ALL, 5)
        sizer.Add(self.fileTextCtrl, 1, wx.EXPAND|wx.ALL, 5)
        self.SetSizer(sizer)

    def SetInsertionPointEnd(self):
        """
        将插入点放在文本控件的末尾,以防止覆盖
        """
        self.fileTextCtrl.SetInsertionPointEnd()

    def UpdateText(self, text):
        """
        向文本控件添加文本
        """
        self.fileTextCtrl.WriteText(text)

class SampleFileDnD(wx.Frame):

    def __init__(self, *args, **kw):
        super(SampleFileDnD, self).__init__(*args, **kw)

        self.InitUi()

    def InitUi(self):
        self.SetTitle("实战wxPython: 文件拖放操作演示")
        self.SetSize(600, 400)

        panel = MyDnDPanel(self)

        self.Centre()

def main():
    app = wx.App()
    sample = SampleFileDnD(None)
    sample.Show()
    app.MainLoop()

if __name__ == "__main__":
    main()

在实例代码中,在面板类MyDnDPanel中创建了一个wx.TextCtrl, 它用来显示从外部拖入文件的名称。

def OnDropFiles(self, x, y, filenames):
        self.window.SetInsertionPointEnd()
        self.window.UpdateText("\n%d file(s) dropped at %d, %d:\n" % (len(filenames), x, y))
        for filepath in filenames:
            self.window.UpdateText(filepath + "\n")

我们可以用文件管理器拖入一个或者多个文件,并在wx.TextCtrl中显示这些拖入文件的完整路径名称。

 图1:文件拖放操作演示

二、文本拖放wx.TextDropTarget

wx.TextDropTarget是一个预定义的拖放目标,用于接收文本数据。常见的例子之一是将网页上的URL拖到地址栏,或者将一些文本拖到浏览器的搜索框中。

# 文本拖放演示

import os
from pathlib import Path
import wx

class MyTextDropTarget(wx.TextDropTarget):

    def __init__(self, object):
        wx.TextDropTarget.__init__(self)
        self.object = object

    def OnDropText(self, x, y, data):
        self.object.InsertItem(0, data)
        return True

class SampleDropText(wx.Frame):

    def __init__(self, *args, **kw):
        super(SampleDropText, self).__init__(*args, **kw)

        self.InitUi()

    def InitUi(self):
        self.SetTitle("实战wxPython: 文本拖放演示")
        self.SetSize(600, 400)

        spliter1 = wx.SplitterWindow(self, style=wx.SP_3D)
        spliter2 = wx.SplitterWindow(spliter1, style=wx.SP_3D)

        home_dir = str(Path.home())

        self.dirWid = wx.GenericDirCtrl(spliter1, dir=home_dir, style=wx.DIRCTRL_DIR_ONLY)

        self.lc1 = wx.ListCtrl(spliter2, style = wx.LC_LIST)
        self.lc2 = wx.ListCtrl(spliter2, style = wx.LC_LIST)

        dt = MyTextDropTarget(self.lc2)
        self.lc2.SetDropTarget(dt)

        self.Bind(wx.EVT_LIST_BEGIN_DRAG, self.OnDragInit, id = self.lc1.GetId())

        tree = self.dirWid.GetTreeCtrl()

        spliter2.SplitHorizontally(self.lc1, self.lc2, 150)
        spliter1.SplitVertically(self.dirWid, spliter2, 200)

        self.Bind(wx.EVT_TREE_SEL_CHANGED, self.OnSelect, id = tree.GetId())

        self.OnSelect(0)

        self.Centre()

    def OnSelect(self, e):
        list = os.listdir(self.dirWid.GetPath())

        self.lc1.ClearAll()
        self.lc2.ClearAll()

        for i in range(len(list)):
            if list[i][0] != ".":
                self.lc1.InsertItem(0, list[i])

    def OnDragInit(self, e):
        text = self.lc1.GetItemText(e.GetIndex())
        tdo = wx.TextDataObject(text)
        tds = wx.DropSource(self.lc1)

        tds.SetData(tdo)
        tds.DoDragDrop(True)

def main():
    app = wx.App()
    sample = SampleDropText(None)
    sample.Show()
    app.MainLoop()

if __name__ == "__main__":
    main()

在这个例子中,我们在wx.GenricDirCtlr中显示了一个文件系统。文件系统中所选目录的内容将显示在右上角的列表控件中。可以将这些文件名拖放到右下角的列表控件中。

def OnDropText(self, x, y, data):
        self.object.InsertItem(0, data)
        return True

当我们将文本数据拖放到目标上时,数据会通过InsertItem()方法插入到列表控件中。

dt = MyTextDropTarget(self.lc2)
 self.lc2.SetDropTarget(dt)

创建一个拖放目标。使用SetDropTarget()方法将下拉目标设置为第二个列表控件。

self.Bind(wx.EVT_LIST_BEGIN_DRAG, self.OnDragInit, id=self.lc1.GetId())

当拖动操作开始时,将调用OnDragInit()方法。

def OnDragInit(self, e):
        text = self.lc1.GetItemText(e.GetIndex())
        tdo = wx.TextDataObject(text)
        tds = wx.DropSource(self.lc1)

在OnDragInit()方法中,我们创建了一个wx.TextDataObject,其中包含我们的文本数据。从第一个列表控件创建拖放源。

tds.SetData(tdo)
 tds.DoDragDrop(True)

使用SetData()将数据设置为拖放源,并使用DoDragDrop()启动拖放操作。

 图2:文本拖放操作演示

三、一般对象拖放wx.PyDropTarget

下面是例子中,我们使用一些基于URLDragAndDrop演示的代码来解释PyDropTarget。

# 一般对象拖放演示

import wx

class MyURLDropTarget(wx.PyDropTarget):

    def __init__(self, window):
        wx.PyDropTarget.__init__(self)
        self.window = window

        self.data = wx.URLDataObject()
        self.SetDataObject(self.data)

    def OnDragOver(self, x, y, d):
        return wx.DragLink

    def OnData(self, x, y, d):
        if not self.GetData():
            return wx.DragNone

        url = self.data.GetURL()
        self.window.AppendText(url + "\n")

        return d

class MyDnDPanel(wx.Panel):

    def __init__(self, parent):
        wx.Panel.__init__(self, parent=parent)

        font = wx.Font(12, wx.FONTFAMILY_SWISS, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_BOLD, False)

        # 创建并设置第一组控件
        lbl = wx.StaticText(self, label="从浏览器中拖一些url到这里:")
        lbl.SetFont(font)
        self.dropText = wx.TextCtrl(self, size=(200,200), style=wx.TE_MULTILINE|wx.HSCROLL|wx.TE_READONLY)

        dt = MyURLDropTarget(self.dropText)
        self.dropText.SetDropTarget(dt)
        firstSizer = self.AddWidgetsToSizer([lbl, self.dropText])

        # 创建并设置第二组控件
        lbl = wx.StaticText(self, label="将这里的url拖到浏览器:")
        lbl.SetFont(font)
        self.draggableURLText = wx.TextCtrl(self, value="https://cn.bing.com")
        self.draggableURLText.Bind(wx.EVT_MOTION, self.OnStartDrag)
        secondSizer = self.AddWidgetsToSizer([lbl, self.draggableURLText])

        # Add sizers to main sizer
        mainSizer = wx.BoxSizer(wx.VERTICAL)
        mainSizer.Add(firstSizer, 0, wx.EXPAND)
        mainSizer.Add(secondSizer, 0, wx.EXPAND)

        self.SetSizer(mainSizer)

    def AddWidgetsToSizer(self, widgets):

        sizer = wx.BoxSizer(wx.VERTICAL)
        for widget in widgets:
            if isinstance(widget, wx.TextCtrl):
                sizer.Add(widget, 1, wx.EXPAND|wx.ALL, 5)
            else:
                sizer.Add(widget, 0, wx.ALL, 5)

        return sizer

    def OnStartDrag(self, e):
        if e.Dragging():
            url = self.draggableURLText.GetValue()
            data = wx.URLDataObject()
            data.SetURL(url)

            dropSource = wx.DropSource(self.draggableURLText)
            dropSource.SetData(data)
            dropSource.DoDragDrop()

class SampleFileDnD(wx.Frame):
    
    def __init__(self, *args, **kw):
        super(SampleFileDnD, self).__init__(*args, **kw)

        self.InitUi()

    def InitUi(self):
        self.SetTitle("实战wxPython: 一般对象拖放操作演示")
        self.SetSize(480, 360)

        panel = MyDnDPanel(self)

        self.Centre()

def main():
    app = wx.App()
    sample = SampleFileDnD(None)
    sample.Show()
    app.MainLoop()

if __name__ == "__main__":
    main()

在这个例子中,我们不仅创建了一个可以接受拖放文本的控件,还可以将一些文本从另一个控件拖到浏览器中,如果该文本是一个合法的地址,则浏览器将打开该地址。

self.data = wx.URLDataObject()
self.SetDataObject(self.data)

创建存储URL信息的wx.URLDataObject的实例,然后设置为wx.PyDropTarget的数据对象。

def OnData(self, x, y, d):
        if not self.GetData():
            return wx.DragNone

        url = self.data.GetURL()
        self.window.AppendText(url + "\n")

在OnData方法中,提取URL并将其附加到绑定的文本控件。

dt = MyURLDropTarget(self.dropText)
self.dropText.SetDropTarget(dt)

创建一个拖放目标。使用SetDropTarget()方法将下拉目标设置为面板控件。

self.draggableURLText.Bind(wx.EVT_MOTION, self.OnStartDrag)

绑定鼠标移动事件

def OnStartDrag(self, e):
        if e.Dragging():
            url = self.draggableURLText.GetValue()
            data = wx.URLDataObject()
            data.SetURL(url)
            ...

在鼠标移动事件处理程序(OnStartDrag)中,我们检查以确保用户正在拖动。如果是,那么我们从文本框中获取值,并将其添加到新创建的URLDataObject中。

dropSource = wx.DropSource(self.draggableURLText)
 dropSource.SetData(data)
 dropSource.DoDragDrop()

创建一个DropSource实例,并将我们的第二个文本控件传递给它,因为它是源。我们将源数据设置为URLDataObject。最后,在dropSource (文本控件)上调用DoDragDrop,它将通过移动、复制、取消或失败来响应。如果将URL拖到浏览器的地址栏,它会复制。否则它可能不会起作用。

 图3:一般对象拖动演示

四、本文知识点

  • 了解拖放操作的基本流程。
  • 操作文件拖放。
  • 操作文本拖放。
  • 操作其他对象的拖放。

前一篇:实战wxPython:051- 自定义控件

<think>好的,用户问的是如何用Python直接把Excel拖进去。首先,我需要理解“拖进去”是什么意思。可能用户是想通过拖放文件到某个界面来导入Excel数据,而不是传统的文件路径选择方式。 接下来,回忆一下Python常用的GUI库,比如Tkinter、PyQt、wxPython等。其中Tkinter是标准库,适合快速实现。用户可能需要一个支持拖放功能的GUI界面,让用户把Excel文件拖到窗口里,然后程序读取数据。 需要确认如何在这些库中实现拖放功能。例如,Tkinter本身可能不支持拖放,但可以通过第三方库如tkinterdnd2来实现。或者使用PyQt的QDragEnterEvent和QDropEvent来处理拖放事件。 然后,考虑文件处理的步骤。用户拖入Excel文件后,程序需要获取文件路径,然后用pandas读取Excel数据。可能需要安装pandas和openpyxl库来处理xlsx文件。 另外,用户可能对Python不太熟悉,所以需要步骤清晰,代码示例简单易懂。要提醒安装必要的库,比如tkinterdnd2、pandas、openpyxl,并提供示例代码,展示如何创建窗口、处理拖放事件、读取数据。 还需要考虑不同操作系统的兼容性问题,比如在Windows、macOS和Linux上拖放功能的实现可能有所不同,但使用跨平台的库如PyQt可能更好。不过为了简便,可能先以Tkinter加tkinterdnd2为例,因为Tkinter是内置的,适合快速上手。 最后,验证示例代码的正确性,确保用户复制后能够运行,并成功拖放Excel文件读取数据。可能需要测试代码,确保没有语法错误或遗漏的步骤。</think>要实现将Excel文件直接拖拽到Python程序中处理,可以通过以下两种主要方式实现: --- ### 方法一:使用Tkinter图形界面(简单版) ```python # 先安装必要库 pip install pandas tkinterdnd2 openpyxl ``` ```python import tkinter as tk from tkinterdnd2 import DND_FILES, TkinterDnD import pandas as pd def handle_drop(event): file_path = event.data.strip("{}") # 处理Windows路径的特殊符号 if file_path.endswith(('.xls', '.xlsx')): df = pd.read_excel(file_path) print("成功读取数据:") print(df.head()) else: print("仅支持Excel文件") root = TkinterDnD.Tk() root.title("拖拽Excel文件到这里") label = tk.Label(root, text="拖拽Excel文件到此区域", width=40, height=10) label.pack(padx=10, pady=10) # 绑定拖放事件 root.drop_target_register(DND_FILES) root.dnd_bind('<<Drop>>', handle_drop) root.mainloop() ``` --- ### 方法二:使用PyQt5(更专业) ```python pip install pandas pyqt5 openpyxl ``` ```python import sys from PyQt5.QtWidgets import QApplication, QWidget, QLabel from PyQt5.QtCore import Qt import pandas as pd class DragDropWidget(QWidget): def __init__(self): super().__init__() self.initUI() def initUI(self): self.setWindowTitle('Excel文件拖放区') self.setGeometry(300, 300, 400, 200) self.setAcceptDrops(True) self.label = QLabel("拖拽Excel文件到这里", self) self.label.setAlignment(Qt.AlignCenter) self.label.resize(380, 180) def dragEnterEvent(self, event): if event.mimeData().hasUrls(): event.accept() else: event.ignore() def dropEvent(self, event): files = [u.toLocalFile() for u in event.mimeData().urls()] for file in files: if file.endswith(('.xls', '.xlsx')): df = pd.read_excel(file) print("读取成功,前5行数据:") print(df.head()) if __name__ == '__main__': app = QApplication(sys.argv) ex = DragDropWidget() ex.show() sys.exit(app.exec_()) ``` --- ### 关键要点说明 1. **文件路径处理**: - Windows系统路径会被`{}`包裹需要去除 - macOS/Linux路径直接可用 2. **依赖关系**: - `pandas`用于读取Excel数据 - `openpyxl`用于处理xlsx格式文件 - `tkinterdnd2`或`PyQt5`提供拖拽功能 3. **扩展应用**: - 可在拖拽后添加数据处理逻辑 - 支持多文件同时拖放 - 添加文件类型过滤(如只接受.xlsx) 两种方案都支持跨平台运行,其中PyQt5版本更专业但需要额外安装库,Tkinter版本更轻量但需要`tkinterdnd2`扩展。建议开发正式工具时选择PyQt5方案。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值