9

我使用一些专有软件进行 DJ(Native Instruments Traktor)。如果你不熟悉这种东西,可以把它想象成一个美化的 iTunes:它浏览和播放音频文件。

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

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

我选择的平台是 Python 和 PyQt,但我开始觉得我可能不得不降低一点。虽然还没有完成大量的 C#/Java,所以这可能是一个学习曲线(我已经完成了很多 ANSI C,但这可能太低了......)

这是我已经走了多远:

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

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

我还对 OSX 开发人员的文档进行了一些研究,特别是这些东西,它描述了传递给目标应用程序(放置目标)的消息序列。

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

所以,在我做之前...

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

我知道我可以通过点击自动化来做到这一点,但我可以想象它真的不可靠,将很大程度上依赖于窗口的位置等。

提前致谢!

4

3 回答 3

2

没有尝试过,但是类似CGEventCreateMouseEvent并且CGEventPostToPSN可能会有所帮助。CGEvent.h 和 CGRemoteOperation.h

我还想知道目标应用程序是否会响应苹果事件 - 如果可以,您可以创建苹果事件并将其发送给它,这样会更干净。我会尝试运行 AppleScript Editor 或 Automator 并在有问题的应用程序上打开字典,看看它是否有一个事件字典,可能会做你想做的事。

运气。

于 2012-12-25T07:23:44.000 回答
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;
}
于 2013-01-02T05:02:21.017 回答
0

我知道这是一篇旧帖子,但我正在开发我的 Traktor Suggestions 程序。我已经完成了 95%,现在我只需要一种方法来允许用户选择一个文件,然后单击“加载到甲板 A”或“加载到甲板 B”。如您所知,Traktor 将接受拖动的文件。但我想将其自动化,因为当您进行 DJ 时,您触摸鼠标的次数越少越好。

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

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

self dragFile.... 创建一个即时粘贴板项目,并且拖动会话都将输出多行代码。

- (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,然后将鼠标移动到甲板 A 或甲板 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);}

这是完整的自定义 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

拖放视图.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
于 2019-03-24T12:14:01.250 回答