在OSX中的另一个应用程序上触发掉落事件

问题描述 投票:9回答:3

我使用一些专有软件进行DJ(Native Instruments Traktor)。如果您不熟悉这种事情,只需将其视为一个美化的iTunes:它浏览并播放音频文件。

我想为此构建一个自定义文件浏览器应用程序,但没有任何类型的API。但是,它允许从文件系统中拖放音频文件,这是一个很好的开始。

我正在设计的文件浏览器的性质意味着我不想实际拖放东西 - 我只想在我的应用程序中单击一个组件,并具有相同的效果。所以我正在寻找从我的文件浏览器应用程序以编程方式触发另一个应用程序上的drop事件的方法。

我选择的平台是Python和PyQt,但我开始觉得我可能需要更低级别。虽然没有做过大量的C#/ Java,但这可能是一个学习曲线(我已经完成了很多ANSI C,但这可能太低了......)

这是我有多远:

  • 我做了一个非常简单的PyQt应用程序
  • 我拖动应用程序中的QLabel时可以创建QDrag对象
  • 我可以附加所有正确的MIME数据来表示音频文件
  • 因此,如果我将QLabel从我的应用程序拖放到Traktor中,它会识别音频文件并播放它 - 好时光

所以现在我需要删除中间人,然后点击,打包我的MIME数据,让Traktor认为我已经将它拖放到它上面。

我还在OSX开发人员的文档中进行了一些深入研究,特别是this stuff,它描述了传递给目标应用程序(丢弃目标)的消息序列。

这一切都是有道理的,但是我正濒临下降到C#/ Java试图模仿这些消息,这听起来像兔子洞,如果我可以避免它,我宁愿不冒险。

所以,在我做之前......

  1. 这甚至可能吗?或者我是否会遇到某种跨应用安全障碍等? (删除目的地只直接从操作系统或其他东西接受消息)
  2. 如果是,有没有更简单的方法呢?理想情况下使用PyQt / Tkinter / wxPython ......?

我知道我可以用点击自动化做到这一点,但我可以想象,真的不可靠,会大量依赖窗户的位置等。

提前致谢!

python xcode macos drag-and-drop pyqt
3个回答
2
投票

没试过这个,但像CGEventCreateMouseEventCGEventPostToPSN这样的东西可能会有所帮助。 CGEvent.h和CGRemoteOperation.h

我也想知道目标应用程序是否可以响应苹果事件 - 如果是,你可以创建苹果事件并将其发送给它更干净。我尝试运行AppleScript编辑器或Automator并在相关应用程序上打开字典,看它是否有可能做你想要的事件的字典。

运气。


0
投票

进展!虽然我仍然不知道向应用程序传递了什么事件,但我确实知道文件数据的存储位置:它位于拖动粘贴板上!尝试在某处拖动文件然后运行它:

#include <stdlib.h>
#import <Foundation/Foundation.h>
#import <AppKit/NSPasteboard.h>

int main(int argc, char **argv) {
    (void)argc, (void)argv;
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
    NSPasteboard *dragPasteboard = [NSPasteboard pasteboardWithName:NSDragPboard];
    NSLog(@"%@", [dragPasteboard stringForType:(NSString *)kUTTypeFileURL]);
    [pool drain];
    return EXIT_SUCCESS;
}

0
投票

我知道这是一个老帖子,但我正在开发我的Traktor建议计划。我已经有了95%,现在我只需要一种方法来允许用户选择一个文件,然后点击“加载到甲板A”或“加载到甲板B”。如您所知,Traktor将接受拖动的文件。但是我希望自动化这个,因为当你的DJ越少,你触摸鼠标就越好。

我对你一直在做的事情也非常感兴趣。

我花了一些时间才弄明白,但我意识到我需要创建一个粘贴板。因为我没有处理图像而只需要提供文件路径(作为NSString ...我也可以使用NSURL,但是直线路径似乎最简单)。它们是用于创建粘贴板和拖动会话以及设置“拖动图像”等的许多方法。然后我遇到了最简单的形式,即使用简单的NSView函数(需要放置在鼠标按下功能中)。并且已经设置了dragFilePath变量。所以在我的自定义NSView中我有这个代码。您还需要将NSImageView作为Custom NSView的子视图。为了使这个“快速”功能起作用。

自我拖动文件....创建一个即时粘贴板项目,并拖动会话所有将出多行代码。

- (void)mouseDown:(NSEvent*)theEvent {
 NSLog(@"DRAGnDROP VIEW mouseDown happened");
NSLog(@"DRAGnDROP VIEW mouseDown dragFilePath is: %@", dragFilePath);
[self dragFile:dragFilePath fromRect:(self.bounds) slideBack:YES event:theEvent];
}

我有两个按钮然后触发CGEvents。我在Applescript中有按钮运行功能。 Applescript函数触发鼠标按下,启动drake,切换到Traktor,然后将鼠标移动到Deck A或Deck B,然后释放。

AppleScript功能:

on loadTraktorDeckA:sender
    deckDetailControllerDelegate's loadForDrag:me
    delay 0.5
    tell application "Traktor"
        activate
    end tell
    deckDetailControllerDelegate's loadForReleaseA:me
end loadTraktorDeckA:

on loadTraktorDeckB:sender
    deckDetailControllerDelegate's loadForDrag:me
    delay 0.5
    tell application "Traktor"
        activate
    end tell
    deckDetailControllerDelegate's loadForReleaseB:me
end loadTraktorDeckB:

在自定义NSView中,这些是被调用的CG鼠标事件:

-(void)loadForDrag:(id)sender {
NSLog(@"mouse left drag called");
/* create a new Quartz mouse event.
 * @source : CGEventSourceRef
 * @mouseType : CGEventType
 * @mouseCursorPosition : CGPoint
 * @mouseButton : CGMouseButton
 */
CGEventSourceStateID kCGEventSourceStatePrivate = -1;
CGEventSourceRef loadDragEventRef = CGEventSourceCreate(kCGEventSourceStatePrivate);

CGPoint startPoint = CGPointMake(880.0, 770.0);
CGPoint movePoint1 = CGPointMake(610.0, 320.0);
CGEventRef leftDownEvent = CGEventCreateMouseEvent(loadDragEventRef, kCGEventLeftMouseDown, startPoint, 1);
CGEventRef leftDragEvent1 = CGEventCreateMouseEvent(loadDragEventRef, kCGEventLeftMouseDragged, startPoint, 0);
CGEventRef leftDragEvent2 = CGEventCreateMouseEvent(loadDragEventRef, kCGEventLeftMouseDragged, movePoint1, 0);
/* post a Quartz event into the event stream at a specified location.
 * @tap : CGEventTapLocation
 * @event : CGEventRef
 */
CGEventPost(kCGHIDEventTap, leftDragEvent2);

CGEventSourceSetLocalEventsSuppressionInterval(loadDragEventRef, 2);
CGEventPost(kCGHIDEventTap, leftDownEvent);
CGEventPost(kCGHIDEventTap, leftDragEvent1);
CGEventPost(kCGHIDEventTap, leftDragEvent2);

/**
 * release a Quartz event
 */
    // CFRelease(leftDragEvent);}

-(void)loadForReleaseA:(id)sender {
NSLog(@"mouse left Up called DECK A");
 CGPoint movePoint1 = CGPointMake(610.0, 320.0);
CGPoint movePointRelease = CGPointMake(220.0, 320.0);

CGEventRef leftDragEvent2 = CGEventCreateMouseEvent(NULL, kCGEventLeftMouseDragged, movePoint1, 0);
CGEventPost(kCGHIDEventTap, leftDragEvent2);

CGEventRef leftClickUpEvent = CGEventCreateMouseEvent(NULL, kCGEventLeftMouseUp,movePointRelease, 0);
CGEventPost(kCGHIDEventTap, leftClickUpEvent);

/** release a Quartz event
 */
CFRelease(leftClickUpEvent);}


-(void)loadForReleaseB:(id)sender {
NSLog(@"mouse left Up called DECK B");
CGPoint movePoint1 = CGPointMake(610.0, 320.0);
CGPoint movePointRelease = CGPointMake(1000.0, 320.0);

CGEventRef leftDragEvent2 = CGEventCreateMouseEvent(NULL, kCGEventLeftMouseDragged, movePoint1, 0);
CGEventPost(kCGHIDEventTap, leftDragEvent2);

CGEventRef leftClickUpEvent = CGEventCreateMouseEvent(NULL, kCGEventLeftMouseUp,movePointRelease, 0);
CGEventPost(kCGHIDEventTap, leftClickUpEvent);

CFRelease(leftClickUpEvent);}

这是完整的Custom DragNDropView类

DragNDropView.h

    //
//  DragNDropView.h
//  DJK-Tel Traktor Suggestions
//
//
//

#import <Cocoa/Cocoa.h>
#import <Foundation/Foundation.h>
#import <AppKit/AppKit.h>

@interface DragNDropView : NSView <NSDraggingSource, NSDraggingDestination, NSImageDelegate, NSApplicationDelegate>

    //NSPasteboardItemDataProvider

@property (nonatomic, strong) NSString* dragFilePath;
@property (nonatomic, strong) NSURL* dragFilePathURL;

- (id)initWithCoder:(NSCoder *)coder;
- (id)initWithFrame:(NSRect)frameRect;
- (void)mouseDown:(NSEvent*)theEvent;
-(IBAction)loadForDrag:(id)sender;
-(IBAction)loadForReleaseA:(id)sender;
-(IBAction)loadForReleaseB:(id)sender;
- (BOOL)shouldDelayWindowOrderingForEvent:(NSEvent *)event;

@end

DragNDropView.m

    //  DragNDropView.m
    //  DJK-Tel Traktor Suggestions
    //
    //
    //

#import "DragNDropView.h"

@implementation DragNDropView


@synthesize dragFilePath;
@synthesize dragFilePathURL;

- (id)initWithCoder:(NSCoder *)coder
{
    /*------------------------------------------------------
     Init method called for Interface Builder objects
     --------------------------------------------------------*/
    self=[super initWithCoder:coder];
    if ( self ) {

        NSLog(@"DRAGnDROP VIEW INIT WITH CODER happened");
            //[self registerForDraggedTypes:[NSArray arrayWithObjects:@"NSFilenamesPboardType",@"NSURLPboardType",nil]];
        [self initView];
    }
    return self;
}
- (id)initWithFrame:(NSRect)frame
{
    self = [super initWithFrame:frame];
    if (self) {
        NSLog(@"DRAGnDROP VIEW INIT WITH FRAME happened");
        [self initView];
    }
    return self;
}


- (void)setFrame:(NSRect)frameRect
{
    [super setFrame:frameRect];
}

- (void) initView
{
    NSLog(@"DRAGnDROP VIEW Init View");

    dragFilePath = @"";
    dragFilePathURL = nil;
}

- (void)drawRect:(NSRect)dirtyRect {
    [super drawRect:dirtyRect];
        // Drawing code here.
}


#pragma mark - Destination Operations

- (void)mouseDown:(NSEvent*)theEvent {
    NSLog(@"DRAGnDROP VIEW mouseDown happened");
    NSLog(@"DRAGnDROP VIEW mouseDown dragFilePath is: %@", dragFilePath);
    [self dragFile:dragFilePath fromRect:(self.bounds) slideBack:YES event:theEvent];
}

- (BOOL)shouldDelayWindowOrderingForEvent:(NSEvent *)event {
    return YES;
}

- (void)mouseDragged:(NSEvent *)event {
}


- (BOOL)performDragOperation:(id <NSDraggingInfo>)sender {
    NSPasteboard *pboard = [sender draggingPasteboard];
        //NSLog(@"DRAGnDROP VIEW performDragOperation pboard is: %@", pboard);

    if ( [[pboard types] containsObject:NSFilenamesPboardType] ) {
        NSArray *files = [pboard propertyListForType:NSFilenamesPboardType];

        if ([files count] == 1) {
            dragFilePath = files[1];
            return YES;
        }
    }
    else if ( [[pboard types] containsObject:NSURLPboardType] ) {
        dragFilePathURL = [NSURL URLFromPasteboard:pboard];
        NSLog(@"DRAGnDROP VIEW performDragOperation dragFilePathURL is: %@", dragFilePathURL);
        return YES;
    }
    return NO;
}

-(void)loadForDrag:(id)sender {
    NSLog(@"mouse left drag called");
    /* create a new Quartz mouse event.
     * @source : CGEventSourceRef
     * @mouseType : CGEventType
     * @mouseCursorPosition : CGPoint
     * @mouseButton : CGMouseButton
     */
    CGEventSourceStateID kCGEventSourceStatePrivate = -1;
    CGEventSourceRef loadDragEventRef = CGEventSourceCreate(kCGEventSourceStatePrivate);

    CGPoint startPoint = CGPointMake(880.0, 770.0);
    CGPoint movePoint1 = CGPointMake(610.0, 320.0);
    CGEventRef leftDownEvent = CGEventCreateMouseEvent(loadDragEventRef, kCGEventLeftMouseDown, startPoint, 1);
    CGEventRef leftDragEvent1 = CGEventCreateMouseEvent(loadDragEventRef, kCGEventLeftMouseDragged, startPoint, 0);
    CGEventRef leftDragEvent2 = CGEventCreateMouseEvent(loadDragEventRef, kCGEventLeftMouseDragged, movePoint1, 0);
    /* post a Quartz event into the event stream at a specified location.
     * @tap : CGEventTapLocation
     * @event : CGEventRef
     */
    CGEventPost(kCGHIDEventTap, leftDragEvent2);

    CGEventSourceSetLocalEventsSuppressionInterval(loadDragEventRef, 2);
    CGEventPost(kCGHIDEventTap, leftDownEvent);
    CGEventPost(kCGHIDEventTap, leftDragEvent1);
    CGEventPost(kCGHIDEventTap, leftDragEvent2);

    /**
     * release a Quartz event
     */
        // CFRelease(leftDragEvent);
}

-(void)loadForReleaseA:(id)sender {
    NSLog(@"mouse left Up called DECK A");
    CGPoint movePoint1 = CGPointMake(610.0, 320.0);
    CGPoint movePointRelease = CGPointMake(220.0, 320.0);

    CGEventRef leftDragEvent2 = CGEventCreateMouseEvent(NULL, kCGEventLeftMouseDragged, movePoint1, 0);
    CGEventPost(kCGHIDEventTap, leftDragEvent2);

    CGEventRef leftClickUpEvent = CGEventCreateMouseEvent(NULL, kCGEventLeftMouseUp,movePointRelease, 0);
    CGEventPost(kCGHIDEventTap, leftClickUpEvent);

    CFRelease(leftClickUpEvent);
}

-(void)loadForReleaseB:(id)sender {
    NSLog(@"mouse left Up called DECK B");
    CGPoint movePoint1 = CGPointMake(610.0, 320.0);
    CGPoint movePointRelease = CGPointMake(1000.0, 320.0);

    CGEventRef leftDragEvent2 = CGEventCreateMouseEvent(NULL, kCGEventLeftMouseDragged, movePoint1, 0);
    CGEventPost(kCGHIDEventTap, leftDragEvent2);

    CGEventRef leftClickUpEvent = CGEventCreateMouseEvent(NULL, kCGEventLeftMouseUp,movePointRelease, 0);
    CGEventPost(kCGHIDEventTap, leftClickUpEvent);

    CFRelease(leftClickUpEvent);
}

#pragma mark - Source Operations


- (NSDragOperation)draggingSession:(NSDraggingSession *)session sourceOperationMaskForDraggingContext:(NSDraggingContext)context
{
    /*------------------------------------------------------
     NSDraggingSource protocol method.  Returns the types of operations allowed in a certain context.
     --------------------------------------------------------*/
    switch (context) {
        case NSDraggingContextOutsideApplication:
            return NSDragOperationCopy;

                //by using this fall through pattern, we will remain compatible if the contexts get more precise in the future.
        case NSDraggingContextWithinApplication:
        default:
            return NSDragOperationCopy;
                //return NSDragOperationNone;
            break;
    }
}

- (BOOL)acceptsFirstMouse:(NSEvent *)event
{
    /*------------------------------------------------------
     accept activation click as click in window
     --------------------------------------------------------*/
        //so source doesn't have to be the active window
    return NO;
}


@end
© www.soinside.com 2019 - 2024. All rights reserved.