سؤال

لدي تطبيق wxPython الذي يعمل على نظام التشغيل MS Windows وأرغب في أن يدعم السحب والإفلات بين مثيلاته (بحيث يفتح المستخدم تطبيقي 3 مرات ويسحب البيانات من مثيل إلى آخر).

يعمل السحب والإفلات البسيط في wxPython بهذه الطريقة:

  1. يبدأ المستخدم السحب:تقوم النافذة المصدر بحزم البيانات الضرورية في wx.DataObject()، وإنشاء wx.DropSource جديد، وتعيين بياناتها واستدعاء dropSource.DoDragDrop()
  2. يقوم المستخدم بإسقاط البيانات على النافذة المستهدفة:يستدعي هدف الإسقاط وظيفة المكتبة GetData() التي تنقل البيانات الفعلية إلى مثيل wx.DataObject الخاص بها، وأخيرًا - تقوم dataObject.GetData() بتفكيك البيانات الفعلية.

أرغب في الحصول على بعض عمليات السحب والإفلات الأكثر تعقيدًا والتي تسمح للمستخدم باختيار البيانات التي يتم سحبها بعد يسقط.
سيناريو احلامي:

  1. يبدأ المستخدم السحب:يتم تعبئة بعض المؤشرات إلى النافذة المصدر فقط (بعض الوظائف أو الكائنات).
  2. يقوم المستخدم بإسقاط البيانات على النافذة المستهدفة:يتم عرض مربع حوار لطيف يسأل المستخدم عن وضع السحب والإفلات الذي يختاره (مثل - سحب عنوان الأغنية فقط، أو عنوان الأغنية واسم الفنان أو الألبوم الكامل للفنان الذي تم سحبه).
  3. يختار المستخدمون وضع السحب والإفلات:يستدعي هدف الإسقاط بعض الوظائف على كائن البيانات المسحوبة، والذي يقوم بعد ذلك باسترداد البيانات من مصدر السحب ونقلها إلى هدف الإسقاط.

يبدو سيناريو أحلامي قابلاً للتنفيذ في نظام التشغيل MS Windows، لكن المستندات الخاصة بـ wxWidgets وwxPython معقدة وغامضة جدًا.ليست كل فئات wx.DataObject متوفرة في wxPython (فقط wx.PySimpleDataObject)، لذلك أود أن يشارك شخص ما تجربته مع هذا النهج.هل يمكن تنفيذ مثل هذا السلوك في wxPython دون الحاجة إلى ترميزه مباشرة في winAPI؟

يحرر:أعطى Toni Ruža إجابة باستخدام مثال السحب والإفلات، ولكن هذا ليس هو السيناريو تمامًا احلامي.يعالج الكود الخاص به البيانات عند سقوطها (ملف انخفاض مؤشر() تظهر القائمة المنبثقة)، ولكن يتم إعداد البيانات عند بدء السحب (في On_ElementDrag()).يجب أن يكون هناك ثلاثة أوضاع مختلفة للسحب والإفلات في تطبيقي، ويتطلب بعضها إعدادًا يستغرق وقتًا طويلاً.لهذا السبب أريد تأجيل استرجاع البيانات إلى اللحظة التي يسقط فيها المستخدم البيانات ويختار وضع d&d (الذي قد يكون مكلفًا).

وبالنسبة لمسألة حماية الذاكرة - أريد استخدام آليات OLE للاتصال بين العمليات، كما يفعل MS Office.يمكنك نسخ مخطط Excel ولصقه في MS-Word حيث سيكون بمثابة صورة (حسنًا، نوعًا ما).نظرًا لأنه يعمل، أعتقد أنه يمكن القيام به في winAPI.لا أعرف ما إذا كان بإمكاني ترميزه في wxPython.

هل كانت مفيدة؟

المحلول 2

حسنًا، يبدو أنه لا يمكن تنفيذ الأمر بالطريقة التي أردتها.

الحلول الممكنة هي:

  1. قم بتمرير بعض المعلمات في d&d وقم بإجراء بعض الاتصالات بين العمليات بنفسك، بعد أن يقوم المستخدم بإسقاط البيانات في نافذة العمليات المستهدفة.
  2. يستخدم DataObjectComposite لدعم تنسيقات السحب والإفلات المتعددة ومعدلات لوحة المفاتيح لاختيار التنسيق الحالي.سيناريو:
    1. يبدأ المستخدم السحب.يتم التحقق من حالة CTRL وALT وSHIFT، وبناءً عليها يتم تحديد تنسيق d&d.تم إنشاء DataObjectComposite، وقام بتعيين البيانات بالتنسيق المختار.
    2. يقوم المستخدم بإسقاط البيانات في النافذة المستهدفة.يطلب هدف الإسقاط DataObject الذي تم إسقاطه للحصول على التنسيق المدعوم ويقوم باسترداد البيانات، معرفة ما هو التنسيق الذي هو عليه.

أنا أختار الحل 2., ، لأنه لا يتطلب اتصالًا يدويًا بين العمليات ويسمح لي بتجنب استرجاع البيانات غير الضرورية عندما يريد المستخدم سحب أبسط البيانات فقط.

على أية حال - توني، شكرا لإجابتك!لقد لعبت بها قليلاً وجعلتني أفكر في d&d وتغيير أسلوبي في التعامل مع المشكلة.

نصائح أخرى

نظرًا لأنه لا يمكنك استخدام أحد معيار تنسيقات البيانات لتخزين المراجع إلى كائنات بايثون أوصي باستخدام تنسيق بيانات نصية لتخزين المعلمات التي تحتاجها لاستدعاءات الطريقة الخاصة بك بدلاً من إنشاء تنسيق بيانات جديد.وعلى أية حال، لن يكون من الجيد تمرير مرجع إلى كائن من تطبيق إلى آخر لأن الكائن المعني لن يكون قابلاً للوصول (هل تتذكر حماية الذاكرة؟).

إليك مثال بسيط لمتطلباتك:

import wx


class TestDropTarget(wx.TextDropTarget):
    def OnDropText(self, x, y, text):
        wx.GetApp().TopWindow.HandleDrop(text)

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


class Test(wx.Frame):
    def __init__(self):
        wx.Frame.__init__(self, None)

        self.numbers = wx.ListCtrl(self, style = wx.LC_ICON | wx.LC_AUTOARRANGE)
        self.field = wx.TextCtrl(self)

        sizer = wx.FlexGridSizer(2, 2, 5, 5)
        sizer.AddGrowableCol(1)
        sizer.AddGrowableRow(0)
        self.SetSizer(sizer)
        sizer.Add(wx.StaticText(self, label="Drag from:"))
        sizer.Add(self.numbers, flag=wx.EXPAND)
        sizer.Add(wx.StaticText(self, label="Drag to:"), flag=wx.ALIGN_CENTER_VERTICAL)
        sizer.Add(self.field)

        for i in range(100):
            self.numbers.InsertStringItem(self.numbers.GetItemCount(), str(i))

        self.numbers.Bind(wx.EVT_LIST_BEGIN_DRAG, self.On_ElementDrag)
        self.field.SetDropTarget(TestDropTarget())

        menu_id1 = wx.NewId()
        menu_id2 = wx.NewId()
        self.menu = wx.Menu()
        self.menu.AppendItem(wx.MenuItem(self.menu, menu_id1, "Simple copy"))
        self.menu.AppendItem(wx.MenuItem(self.menu, menu_id2, "Mess with it"))
        self.Bind(wx.EVT_MENU, self.On_SimpleCopy, id=menu_id1)
        self.Bind(wx.EVT_MENU, self.On_MessWithIt, id=menu_id2)

    def On_ElementDrag(self, event):
        data = wx.TextDataObject(self.numbers.GetItemText(event.Index))
        source = wx.DropSource(self.numbers)
        source.SetData(data)
        source.DoDragDrop()

    def HandleDrop(self, text):
        self._text = text
        self.PopupMenu(self.menu)

    def On_SimpleCopy(self, event):
        self.field.Value = self._text

    def On_MessWithIt(self, event):
        self.field.Value = "<-%s->" % "".join([int(c)*c for c in self._text])


app = wx.PySimpleApp()
app.TopWindow = Test()
app.TopWindow.Show()
app.MainLoop()

يتم تنفيذ طرق مثل On_SimpleCopy وOn_MessWithIt بعد الإفلات، لذا يمكنك إجراء أي عمليات طويلة قد ترغب في إجرائها هناك استنادًا إلى البيانات النصية أو أي نوع قياسي آخر من البيانات التي قمت بنقلها باستخدام السحب (self._text في حالتي)، وانظر ...لا أوليه :)

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top