Thunar 1.0.0 中是怎么进行图片预览的 (二)

本文详细介绍了Thunar文件管理器如何实现文件预览功能,包括图标加载、缩略图生成及验证流程。通过分析Thunar内部代码,揭示了从获取文件图标到生成缩略图的整个过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

上一章介绍的是图片预览的主要实现方式,这次来介绍下Thunar实现预览的流程。

 

./thunar/thunar-icon-factory.c获取文件的图标 thunar_icon_factory_load_file_icon()。

对于已经存在缩略图的查找缩略图thunar_vfs_thumb_factory_lookup_thumbnail(),

判断其修改时间和缩略图修改时间是否一致thunar_vfs_thumbnail_is_valid(),不一致则重新生成 。

如果找不到缩略图,或者需要重新生成缩略图的文件,调用thunar_thumbnail_generator_enqueue()。

最后,生成缩略图后当作文件的图标。

GdkPixbuf*
thunar_icon_factory_load_file_icon (ThunarIconFactory  *factory,
                                    ThunarFile         *file,
                                    ThunarFileIconState icon_state,
                                    gint                icon_size)
{
  ThunarFileThumbState thumb_state;
  ThunarVfsFileTime    time;
  ThunarVfsInfo       *info;
  ThunarVfsPath       *path;
  ThunarIconKey        key;
  const gchar         *icon_name;
  GdkPixbuf           *icon;
  gchar               *thumb_path;

  _thunar_return_val_if_fail (THUNAR_IS_ICON_FACTORY (factory), NULL);
  _thunar_return_val_if_fail (THUNAR_IS_FILE (file), NULL);
  _thunar_return_val_if_fail (icon_size > 0, NULL);

  /* check if there's a custom icon for the file */
  icon_name = thunar_file_get_custom_icon (file);
  if (G_UNLIKELY (icon_name != NULL))
    {
      /* try to load the icon */
      icon = thunar_icon_factory_lookup_icon (factory, icon_name, icon_size, FALSE);
      if (G_LIKELY (icon != NULL))
        return icon;
    }
  /* check if thumbnails are enabled and we can display a thumbnail for the item */
  if (G_LIKELY (factory->show_thumbnails && thunar_file_is_regular (file)))
    {
      /* determine the thumbnail state */
      thumb_state = thunar_file_get_thumb_state (file);

      /* check if we haven't yet determine the thumbnail state */
      if (thumb_state == THUNAR_FILE_THUMB_STATE_UNKNOWN)
        {
again:
          /* determine the ThunarVfsInfo for the file */
          info = thunar_file_get_info (file);

          /* try to load an existing thumbnail for the file */
          thumb_path = thunar_vfs_thumb_factory_lookup_thumbnail (factory->thumbnail_factory, info);

          /* check if we can generate a thumbnail in case there's none yet */
          if (G_UNLIKELY (thumb_path == NULL && thunar_vfs_thumb_factory_can_thumbnail (factory->thumbnail_factory, info)))
            {
              /* schedule the thumbnail loading for the file */
              thunar_thumbnail_generator_enqueue (factory->thumbnail_generator, file);

              /* set the thumbnail state to "loading" */
              thumb_state = THUNAR_FILE_THUMB_STATE_LOADING;
            }

          if (G_LIKELY (thumb_path != NULL))
            {
              thumb_state = THUNAR_FILE_THUMB_STATE_READY;
              g_object_set_qdata_full (G_OBJECT (file), thunar_file_thumb_path_quark, thumb_path, g_free);
            }
          else if (thumb_state != THUNAR_FILE_THUMB_STATE_LOADING)
            {
              thumb_state = THUNAR_FILE_THUMB_STATE_NONE;
            }
          /* apply the new state */
          thunar_file_set_thumb_state (file, thumb_state);
        }
...
}


                             
 

 

./thunar/thunar-thumbnail-generator.c中开辟一个新线程来进行缩略图的生成:

void
thunar_thumbnail_generator_enqueue (ThunarThumbnailGenerator *generator,
                                    ThunarFile               *file)
{
  ThunarThumbnailInfo *info;
  GError              *error = NULL;
  GList               *lp;

  _thunar_return_if_fail (THUNAR_IS_THUMBNAIL_GENERATOR (generator));
  _thunar_return_if_fail (THUNAR_IS_FILE (file));

  /* acquire the generator lock */
  g_mutex_lock (generator->lock);

  /* check if the file is already on the request list */
  for (lp = generator->requests; lp != NULL; lp = lp->next)
    if (((ThunarThumbnailInfo *) lp->data)->file == file)
      break;

  /* schedule a request for the file if it's not already done */
  if (G_LIKELY (lp == NULL))
    {
      /* allocate a thumbnail info for the file */
      info = _thunar_slice_new (ThunarThumbnailInfo);
      info->file = g_object_ref (G_OBJECT (file));
      info->info = thunar_vfs_info_copy (thunar_file_get_info (file));

      /* schedule the request */
      generator->requests = g_list_append (generator->requests, info);

      /* start the generator thread on-demand */
      if (G_UNLIKELY (generator->thread == NULL))
        {
          /* 开辟新线程来进行缩略图生成 */
          generator->thread = g_thread_create_full (thunar_thumbnail_generator_thread, generator, 0,
                                                    FALSE, FALSE, G_THREAD_PRIORITY_LOW, &error);
          if (G_UNLIKELY (generator->thread == NULL))
            {
              g_warning ("Failed to launch thumbnail generator thread: %s", error->message);
              g_error_free (error);
            }
        }
    }

  /* release the generator lock */
  g_mutex_unlock (generator->lock);
}


static gpointer
thunar_thumbnail_generator_thread (gpointer user_data)
{
  ThunarThumbnailGenerator *generator = THUNAR_THUMBNAIL_GENERATOR (user_data);
  ThunarThumbnailInfo      *info;
  GdkPixbuf                *icon;

  for (;;)
    {
      /* lock the factory */
      g_mutex_lock (generator->lock);

      /* grab the first thumbnail from the request list */
      if (G_LIKELY (generator->requests != NULL))
        {
          /* 获取需要生成缩略图的文件信息 */
          info = generator->requests->data;
          generator->requests = g_list_remove (generator->requests, info);
        }
      else
        {
          info = NULL;
        }

      /* check if we have something to generate */
      if (G_UNLIKELY (info == NULL))
        {
          /* reset the thread pointer */
          generator->thread = NULL;
          g_cond_signal (generator->cond);
          g_mutex_unlock (generator->lock);
          break;
        }

      /* release the lock */
      g_mutex_unlock (generator->lock);

      /* don't generate thumbnails for files for which only we own a reference */
      if (G_LIKELY (G_OBJECT (info->file)->ref_count > 1))
        {
          /* try to generate the thumbnail */
          /* 调用thumbnail生成函数,生成thumbnail */
          icon = thunar_vfs_thumb_factory_generate_thumbnail (generator->factory, info->info);

          /* store the thumbnail (or the failed notice) */
          /* 将生成的thumbnail最终保存到$HOME/.thumbnail中,并同时保存相应的文件创建时间等信息 */
          /* 通过比较文件创建的时间,对于同一文件就不用每次都生成文件thumbnail */
          thunar_vfs_thumb_factory_store_thumbnail (generator->factory, icon, info->info, NULL);
        }
      else
        {
          icon = NULL;
        }

      /* place the thumbnail on the reply list and schedule the idle source */
      g_mutex_lock (generator->lock);
      generator->replies = g_list_prepend (generator->replies, info);
      /* 生成好thumbnail后给所有文件发送一个changed信号 */
      if (G_UNLIKELY (generator->idle_id < 0))
        generator->idle_id = g_idle_add_full (G_PRIORITY_LOW, thunar_thumbnail_generator_idle, generator, NULL);
      g_mutex_unlock (generator->lock);

      /* release the icon (if any) */
      if (G_LIKELY (icon != NULL))
        g_object_unref (G_OBJECT (icon));
    }

  return NULL;
}

 

./thunar-vfs/thunar-vfs-thumb.c具体的缩略图生成实现类:

/**
 * thunar_vfs_thumb_factory_generate_thumbnail:
 * @factory : a #ThunarVfsThumbFactory.
 * @info    : the #ThunarVfsInfo of the file for which a thumbnail
 *            should be generated.
 *
 * Tries to generate a thumbnail for the file referred to by @info in
 * @factory.
 *
 * The caller is responsible to free the returned #GdkPixbuf using
 * g_object_unref() when no longer needed.
 *
 * The usage of this method is thread-safe.
 *
 * Return value: the thumbnail for the @uri or %NULL.
 **/
GdkPixbuf*
thunar_vfs_thumb_factory_generate_thumbnail (ThunarVfsThumbFactory *factory,
                                             const ThunarVfsInfo   *info)
{
...
}

 

 

/**
 * thunar_vfs_thumb_factory_store_thumbnail:
 * @factory : a #ThunarVfsThumbFactory.
 * @pixbuf  : the thumbnail #GdkPixbuf to store or %NULL
 *            to remember the thumbnail for @uri as failed.
 * @info    : the #ThunarVfsInfo of the original file.
 * @error   : return location for errors or %NULL.
 *
 * Stores @pixbuf as thumbnail for @info in the right place, according
 * to the size set for @factory.
 *
 * If you specify %NULL for @pixbuf, the @factory will remember that
 * the thumbnail generation for @info failed.
 *
 * The usage of this method is thread-safe.
 *
 * Return value: %TRUE if the thumbnail was stored successfully,
 *               else %FALSE.
 **/
gboolean
thunar_vfs_thumb_factory_store_thumbnail (ThunarVfsThumbFactory *factory,
                                          const GdkPixbuf       *pixbuf,
                                          const ThunarVfsInfo   *info,
                                          GError               **error)
{
  const gchar *base_path;
  GdkPixbuf   *thumbnail;
  gboolean     succeed;
  gchar       *dst_path;
  gchar       *tmp_path;
  gchar       *mtime;
  gchar       *size;
  gchar       *md5;
  gchar       *uri;
  gint         tmp_fd;

  g_return_val_if_fail (THUNAR_VFS_IS_THUMB_FACTORY (factory), FALSE);
  g_return_val_if_fail (pixbuf == NULL || GDK_IS_PIXBUF (pixbuf), FALSE);
  g_return_val_if_fail (info != NULL, FALSE);
  g_return_val_if_fail (error == NULL || *error == NULL, FALSE);

  /* check whether we should save a thumbnail or remember failed generation */
  base_path = G_LIKELY (pixbuf != NULL) ? factory->base_path : factory->fail_path;

  /* verify that the target directory exists */
  if (!xfce_mkdirhier (base_path, 0700, error))
    return FALSE;

  /* determine the URI of the file */
  uri = thunar_vfs_path_dup_uri (info->path);

  /* determine the MD5 sum for the URI */
  md5 = exo_str_get_md5_str (uri);

  /* try to open a temporary file to write the thumbnail to */
  /* 开辟临时文件到$HOME/.thumbnail作为thumbnail的容器 */
  tmp_path = g_strconcat (base_path, md5, ".png.XXXXXX", NULL);
  tmp_fd = g_mkstemp (tmp_path);
  if (G_UNLIKELY (tmp_fd < 0))
    {
      g_set_error (error, G_FILE_ERROR, g_file_error_from_errno (errno), "%s", g_strerror (errno));
      g_free (md5);
      g_free (uri);
      return FALSE;
    }

  /* close the temporary file as it exists now, and hence
   * we successfully avoided a race condition there.
   */
  close (tmp_fd);

  /* generate a 1x1 image if we're storing a failure */
  thumbnail = (pixbuf != NULL) ? GDK_PIXBUF (pixbuf) : gdk_pixbuf_new (GDK_COLORSPACE_RGB, TRUE, 8, 1, 1);

  /* determine string representations for the modification time and size */
  mtime = g_strdup_printf ("%lu", (gulong) info->mtime);
  size = g_strdup_printf ("%" G_GUINT64_FORMAT, (guint64) info->size);

  /* write the thumbnail to the temporary location */
  /* 保存thumbnail文件信息 */
  succeed = gdk_pixbuf_save (thumbnail, tmp_path, "png", error,
                             "tEXt::Thumb::URI", uri,
                             "tEXt::Thumb::Size", size,
                             /* 保存源文件的修改时间到thumbnail */
                             "tEXt::Thumb::MTime", mtime,
                             /* 保存源文件的文件类型到thumbnail */
                             "tEXt::Thumb::Mimetype", thunar_vfs_mime_info_get_name (info->mime_info),
                             "tEXt::Software", "Thunar-VFS Thumbnail Factory",
                             NULL);

  /* drop the failed thumbnail pixbuf (if any) */
  if (G_UNLIKELY (pixbuf == NULL))
    g_object_unref (G_OBJECT (thumbnail));

  /* rename the file to the final location */
  if (G_LIKELY (succeed))
    {
      dst_path = g_strconcat (base_path, md5, ".png", NULL);
      if (G_UNLIKELY (g_rename (tmp_path, dst_path) < 0))
        {
          g_set_error (error, G_FILE_ERROR, g_file_error_from_errno (errno), "%s", g_strerror (errno));
          succeed = FALSE;
        }
      g_free (dst_path);
    }

  /* cleanup */
  g_free (tmp_path);
  g_free (mtime);
  g_free (size);
  g_free (md5);
  g_free (uri);

  return succeed;
}

 
/**
 * thunar_vfs_thumb_factory_lookup:
 * @factory : a #ThunarVfsThumbFactory.
 * @info    : the #ThunarVfsInfo of the original file.
 *
 * Looks up the path to a thumbnail for @info in @factory and returns
 * the absolute path to the thumbnail file if a valid thumbnail is
 * found. Else %NULL is returned.
 *
 * The usage of this method is thread-safe.
 *
 * Return value: the path to a valid thumbnail for @info in @factory
 *               or %NULL if no valid thumbnail was found.
 **/
gchar*
thunar_vfs_thumb_factory_lookup_thumbnail (ThunarVfsThumbFactory *factory,
                                           const ThunarVfsInfo   *info)
{
  gchar  uri[THUNAR_VFS_PATH_MAXURILEN];
  gchar *path;
  gchar *md5;

  g_return_val_if_fail (THUNAR_VFS_IS_THUMB_FACTORY (factory), NULL);
  g_return_val_if_fail (info != NULL, NULL);

  /* determine the URI for the path */
  if (thunar_vfs_path_to_uri (info->path, uri, sizeof (uri), NULL) >= 0)
    {
      /* determine the path to the thumbnail for the factory */
      md5 = exo_str_get_md5_str (uri);
      path = g_strconcat (factory->base_path, md5, ".png", NULL);
      g_free (md5);

      /* check if the path contains a valid thumbnail */
      /* 判断在$HOME/.thumbnail是否有thumbnail */
      /* 以及thumbnail是否需要重新生成 */
      if (thunar_vfs_thumbnail_is_valid (path, uri, info->mtime))
        return path;

      /* no valid thumbnail in the global repository */
      g_free (path);
    }

  return NULL;
}
 
/**
 * thunar_vfs_thumbnail_is_valid:
 * @thumbnail : the absolute path to a thumbnail file.
 * @uri       : the URI of the original file.
 * @mtime     : the modification time of the original file.
 *
 * Checks whether the file located at @thumbnail contains a
 * valid thumbnail for the file described by @uri and @mtime.
 *
 * The usage of this method is thread-safe.
 *
 * Return value: %TRUE if @thumbnail is a valid thumbnail for
 *               the file referred to by @uri, else %FALSE.
 **/
gboolean
thunar_vfs_thumbnail_is_valid (const gchar      *thumbnail,
                               const gchar      *uri,
                               ThunarVfsFileTime mtime)
{
  png_structp  png_ptr;
  png_infop    info_ptr;
  png_textp    text_ptr;
  gboolean     is_valid = FALSE;
  gchar        signature[4];
  FILE        *fp;
  gint         n_checked;
  gint         n_text;
  gint         n;

  g_return_val_if_fail (g_path_is_absolute (thumbnail), FALSE);
  g_return_val_if_fail (uri != NULL && *uri != '\0', FALSE);

  /* try to open the thumbnail file */
  fp = fopen (thumbnail, "r");
  if (G_UNLIKELY (fp == NULL))
    return FALSE;

  /* read the png signature */
  if (G_UNLIKELY (fread (signature, 1, sizeof (signature), fp) != sizeof (signature)))
    goto done0;

  /* verify the png signature */
  if (G_LIKELY (png_check_sig ((png_bytep) signature, sizeof (signature))))
    rewind (fp);
  else
    goto done0;

  /* allocate the png read structure */
  png_ptr = png_create_read_struct (PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
  if (G_UNLIKELY (png_ptr == NULL))
    goto done0;

  /* allocate the png info structure */
  info_ptr = png_create_info_struct (png_ptr);
  if (G_UNLIKELY (info_ptr == NULL))
    {
      png_destroy_read_struct (&png_ptr, NULL, NULL);
      goto done0;
    }

  /* read the png info from the file */
  png_init_io (png_ptr, fp);
  png_read_info (png_ptr, info_ptr);

  /* verify the tEXt attributes defined by the thumbnail spec */
  n_text = png_get_text (png_ptr, info_ptr, &text_ptr, &n_text);
  for (n = 0, n_checked = 0; n_checked < 2 && n < n_text; ++n)
    {
      if (strcmp (text_ptr[n].key, "Thumb::MTime") == 0)
        {
          /* verify the modification time */
          if (G_UNLIKELY (strtol (text_ptr[n].text, NULL, 10) != mtime))
            goto done1;
          ++n_checked;
        }
      else if (strcmp (text_ptr[n].key, "Thumb::URI") == 0)
        {
          /* check if the URIs are equal */
          if (strcmp (text_ptr[n].text, uri) != 0)
            goto done1;
          ++n_checked;
        }
    }

  /* check if all required attributes were checked successfully */
  is_valid = (n_checked == 2);

done1:
  png_destroy_read_struct (&png_ptr, &info_ptr, NULL);
done0:
  fclose (fp);
  return is_valid;
}
 

 

 

 

基于数据挖掘的音乐推荐系统设计与实现 需要一个代码说明,不需要论文 采用python语言,django框架,mysql数据库开发 编程环境:pycharm,mysql8.0 系统分为前台+后台模式开发 网站前台: 用户注册, 登录 搜索音乐,音乐欣赏(可以在线进行播放) 用户登陆时选择相关感兴趣的音乐风格 音乐收藏 音乐推荐算法:(重点) 本课题需要大量用户行为(如播放记录、收藏列表)、音乐特征(如音频特征、歌曲元数据)等数据 (1)根据用户之间相似性或关联性,给一个用户推荐与其相似或有关联的其他用户所感兴趣的音乐; (2)根据音乐之间的相似性或关联性,给一个用户推荐与其感兴趣的音乐相似或有关联的其他音乐。 基于用户的推荐和基于物品的推荐 其中基于用户的推荐是基于用户的相似度找出相似相似用户,然后向目标用户推荐其相似用户喜欢的东西(和你类似的人也喜欢**东西); 而基于物品的推荐是基于物品的相似度找出相似的物品做推荐(喜欢该音乐的人还喜欢了**音乐); 管理员 管理员信息管理 注册用户管理,审核 音乐爬虫(爬虫方式爬取网站音乐数据) 音乐信息管理(上传歌曲MP3,以便前台播放) 音乐收藏管理 用户 用户资料修改 我的音乐收藏 完整前后端源码,部署后可正常运行! 环境说明 开发语言:python后端 python版本:3.7 数据库:mysql 5.7+ 数据库工具:Navicat11+ 开发软件:pycharm
MPU6050是一款广泛应用在无人机、机器人和运动设备中的六轴姿态传感器,它集成了三轴陀螺仪和三轴加速度计。这款传感器能够实时监测并提供设备的角速度和线性加速度数据,对于理解物体的动态运动状态至关重要。在Arduino平台上,通过特定的库文件可以方便地与MPU6050进行通信,获取并解析传感器数据。 `MPU6050.cpp`和`MPU6050.h`是Arduino库的关键组成部分。`MPU6050.h`是头文件,包含了定义传感器接口和函数声明。它定义了类`MPU6050`,该类包含了初始化传感器、读取数据等方法。例如,`begin()`函数用于设置传感器的工作模式和I2C地址,`getAcceleration()`和`getGyroscope()`则分别用于获取加速度和角速度数据。 在Arduino项目中,首先需要包含`MPU6050.h`头文件,然后创建`MPU6050`对象,并调用`begin()`函数初始化传感器。之后,可以通过循环调用`getAcceleration()`和`getGyroscope()`来不断更新传感器读数。为了处理这些原始数据,通常还需要进行校准和滤波,以消除噪声和漂移。 I2C通信协议是MPU6050与Arduino交互的基础,它是一种低引脚数的串行通信协议,允许多个设备共享一对数据线。Arduino板上的Wire库提供了I2C通信的底层支持,使得用户无需深入了解通信细节,就能方便地与MPU6050交互。 MPU6050传感器的数据包括加速度(X、Y、Z轴)和角速度(同样为X、Y、Z轴)。加速度数据可以用来计算物体的静态位置和动态运动,而角速度数据则能反映物体转动的速度。结合这两个数据,可以进一步计算出物体的姿态(如角度和角速度变化)。 在嵌入式开发领域,特别是使用STM32微控制器时,也可以找到类似的库来驱动MPU6050。STM32通常具有更强大的处理能力和更多的GPIO口,可以实现更复杂的控制算法。然而,基本的传感器操作流程和数据处理原理与Arduino平台相似。 在实际应用中,除了基本的传感器读取,还可能涉及到温度补偿、低功耗模式设置、DMP(数字运动处理器)功能的利用等高级特性。DMP可以帮助处理传感器数据,实现更高级的运动估计,减轻主控制器的计算负担。 MPU6050是一个强大的六轴传感器,广泛应用于各种需要实时运动追踪的项目中。通过 Arduino 或 STM32 的库文件,开发者可以轻松地与传感器交互,获取并处理数据,实现各种创新应用。博客和其他开源资源是学习和解决问题的重要途径,通过这些资源,开发者可以获得关于MPU6050的详细信息和实践指南
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值