You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Qt跨平台开发:Mac剪贴板Standard MIDI File数据为空问题排查

问题描述

我用Qt开发一款支持Windows和Mac的C++应用,需要把MIDI数据同时以audio/midi和Standard MIDI File两种MIME类型存入系统剪贴板。Windows平台功能正常,两种类型下都能显示相同数据;但在MacOS平台,虽然重写了QMacPasteboardMime类,目前仅public.midi-audio类型下有数据,Standard MIDI File类型条目为空。以下是相关实现代码及日志,请分析问题原因。


相关代码

clipboard.cpp

bool GuiClipboard::CopyMIDIDataToSystemClipboard(const QByteArray& midiData)
{
    bool success = false;
    QClipboard* clipboard = QApplication::clipboard();
    if (clipboard) {
        QMimeData* qmd = new QMimeData();
        qmd->setData("Standard MIDI File", midiData);  // 新增行
        qmd->setData("audio/midi", midiData);
        
        clipboard->setMimeData(qmd);
        success = true;
    }
        return success;
}

clipboard.h(Mac平台专属代码)

#if MAC

#include <QtMacExtras/qmacpasteboardmime.h>
  struct MIDIMime : public QMacPasteboardMime {
      MIDIMime() : QMacPasteboardMime(MIME_ALL) { }

      QString convertorName(){return QString("MIDIMime");}

      bool canConvert(const QString &mime, QString flav){
          QString debug1 = "> canConvert: mime: " + mime + " flav: " + flav;
          LOG (debug1.toLatin1());
         // return mimeFor(flav) == mime || mimeFor(flav) == "Standard MIDI File";
          return mimeFor(flav) == mime || mime == "Standard MIDI File";

      }

      QString mimeFor(QString flav){
          if (flav == QString("public.midi-audio")){
              LOG("> mimeFor: public.midi-audio -> audio/midi");
              return QString("audio/midi");
          }
          if (flav == QString("Standard MIDI File")){
              LOG("> mimeFor: Standard MIDI File");
              return QString("Standard MIDI File");
          }
          return QString();
      }

      QString flavorFor(const QString &mime){
          if (mime == QString("audio/midi")){
              LOG("> flavorFor: audio/midi -> public.midi-audio");
              return QString("public.midi-audio");
          }
          if (mime == QString("Standard MIDI File")){
              LOG("> flavorFor: Standard MIDI File");
              return QString("Standard MIDI File");
          }
          return QString();
      }

      QVariant convertToMime(const QString &mime, QList<QByteArray> data, QString flav){
          //Q_UNUSED(mime);
          //Q_UNUSED(flav);
          
          QByteArray all;
          int cc = 0;
          foreach (QByteArray i, data) {
              all += i;
              cc++;
          }
          QString debug2 = "> convert2Mime: mime: " + mime + " flav: " + flav + " n: " + QString::number(cc);
          LOG(debug2.toLatin1());
          
          return QVariant(all);
      }

      QList<QByteArray> convertFromMime(const QString &mime, QVariant data, QString flav){
          //Q_UNUSED(mime);
          //Q_UNUSED(data);
          //Q_UNUSED(flav);
          QList<QByteArray> list;
          list.push_back(data.toByteArray());
          int cc = list.count();
          QString debug3 = "> convertFromMime: mime: " + mime + " flav: " + flav + " n: " + QString::number(cc);
          LOG(debug3.toLatin1());
          return list;
      }
      
  };
#endif

日志内容

Clipboard.h(55) : > flavorFor: Standard MIDI File

Clipboard.h(51) : > flavorFor: audio/midi -> public.midi-audio

Clipboard.h(31) : > canConvert: mime: Standard MIDI File flav: public.midi-audio

Clipboard.h(39) : > mimeFor: public.midi-audio -> audio/midi

Clipboard.h(85) : > convertFromMime: mime: Standard MIDI File flav: public.midi-audio n: 1

Clipboard.h(31) : > canConvert: mime: Standard MIDI File flav: com.trolltech.anymime.audio--midi

Clipboard.h(85) : > convertFromMime: mime: Standard MIDI File flav: com.trolltech.anymime.audio--midi n: 1

问题分析及解决方案

核心原因

MacOS剪贴板用Flavor(而非直接的MIME类型)标识数据格式,你的实现有两个关键问题:

  1. Standard MIDI File不是Mac系统原生支持的Flavor名称,系统无法识别这个自定义Flavor,因此不会为其分配数据存储。
  2. 当Qt处理未映射到系统原生Flavor的MIME类型时,会自动生成com.trolltech.anymime.audio--midi这类私有Flavor,但你的转换逻辑没正确关联Standard MIDI File到这个私有Flavor,导致数据无法正常映射。

修复步骤

1. 修正flavorFor方法逻辑

Standard MIDI File和audio/midi本质是同一种数据格式(SMF文件),直接复用系统原生的public.midi-audio即可:

QString flavorFor(const QString &mime){
    if (mime == QString("audio/midi")){
        LOG("> flavorFor: audio/midi -> public.midi-audio");
        return QString("public.midi-audio");
    }
    if (mime == QString("Standard MIDI File")){
        LOG("> flavorFor: Standard MIDI File -> public.midi-audio");
        return QString("public.midi-audio"); // 复用系统原生Flavor
    }
    return QString();
}

2. 调整canConvert方法匹配逻辑

确保能正确处理Standard MIDI File和系统Flavor的关联:

bool canConvert(const QString &mime, QString flav){
    QString debug1 = "> canConvert: mime: " + mime + " flav: " + flav;
    LOG (debug1.toLatin1());
    // 允许从public.midi-audio转换到两种MIME类型
    if (flav == "public.midi-audio") {
        return mime == "audio/midi" || mime == "Standard MIDI File";
    }
    // 处理Qt生成的私有Flavor
    return mimeFor(flav) == mime;
}

3. 优化mimeFor方法

确保系统Flavor能正确映射回两种MIME类型:

QString mimeFor(QString flav){
    if (flav == QString("public.midi-audio")){
        LOG("> mimeFor: public.midi-audio -> audio/midi/Standard MIDI File");
        return QString("audio/midi"); // 两种类型数据一致,返回任意一个均可
    }
    // 处理Qt私有Flavor
    if (flav.startsWith("com.trolltech.anymime")) {
        QString mime = flav.replace("com.trolltech.anymime.", "").replace("--", "/");
        LOG("> mimeFor: " + flav + " -> " + mime);
        return mime;
    }
    return QString();
}

内容的提问来源于stack exchange,提问作者SimplyBlue

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.22 02:49:58