6.6.1 original

This commit is contained in:
kleuter 2023-12-04 18:42:35 +01:00
parent 9bf343ceed
commit 8490fae44c
607 changed files with 15608 additions and 14647 deletions

View File

@ -7,7 +7,7 @@ if (NOT DEFINED QT_SUPERBUILD OR DEFINED QT_REPO_MODULE_VERSION)
set(QT_EXTRA_INTERNAL_TARGET_DEFINES "QT_LEAN_HEADERS=1") set(QT_EXTRA_INTERNAL_TARGET_DEFINES "QT_LEAN_HEADERS=1")
endif() endif()
set(QT_REPO_MODULE_VERSION "6.6.0") set(QT_REPO_MODULE_VERSION "6.6.1")
set(QT_REPO_MODULE_PRERELEASE_VERSION_SEGMENT "alpha1") set(QT_REPO_MODULE_PRERELEASE_VERSION_SEGMENT "alpha1")
set(QT_COPYRIGHT_YEAR "2023") set(QT_COPYRIGHT_YEAR "2023")

View File

@ -1 +1 @@
QT_PACKAGEDATE_STR=2023-10-04 QT_PACKAGEDATE_STR=2023-11-21

2
.tag
View File

@ -1 +1 @@
33f5e985e480283bb0ca9dea5f82643e825ba87c e2cbce919ccefcae2b18f90257d67bc6e24c3c94

View File

@ -1,11 +0,0 @@
This is Qt 6.6.0 backport that runs on Windows 7 (what?). The repository is the qtbase module source code with all required modifications already applied.
Approach is based on this [forum thread](https://forum.qt.io/topic/133002/qt-creator-6-0-1-and-qt-6-2-2-running-on-windows-7/60) but better: many improvements amongst important fallbacks to default Qt 6 behaviour when running on newer Windows.
You basically need to compile Qt yourself (we use the [compile_win](https://github.com/crystalidea/qt-build-tools/tree/master/6.6.0) script).
The only issue noticed is that scalled UI is somewhat too big for 125% scalling option set in Windows 7. Something should be tweaked with dpi settings probably.
Qt 6.6 designer running on Windows 7:
![image](https://github.com/crystalidea/qt6windows7/assets/2600624/4c5ad13f-db6e-4684-8184-9615e4e55461)

View File

@ -5,13 +5,18 @@
# https://gitlab.kitware.com/cmake/cmake/-/issues/20713 # https://gitlab.kitware.com/cmake/cmake/-/issues/20713
# https://gitlab.kitware.com/cmake/cmake/-/issues/21475 # https://gitlab.kitware.com/cmake/cmake/-/issues/21475
set(configs "@__qt_configured_configs@") set(configs "@__qt_configured_configs@")
set(should_skip_strip "@__qt_skip_strip_installed_artifacts@")
if(NOT QT_BUILD_DIR) if(NOT QT_BUILD_DIR)
message(FATAL_ERROR "No QT_BUILD_DIR value provided to qt-cmake-private-install.") message(FATAL_ERROR "No QT_BUILD_DIR value provided to qt-cmake-private-install.")
endif() endif()
unset(strip_arg)
if ("x@MSVC@" STREQUAL "x") if(should_skip_strip)
set(strip_arg --strip) unset(strip_arg)
else()
set(strip_arg --strip)
endif() endif()
foreach(config ${configs}) foreach(config ${configs})
message(STATUS "Installing configuration: '${config}'") message(STATUS "Installing configuration: '${config}'")
set(args "${CMAKE_COMMAND}" --install ${QT_BUILD_DIR} --config "${config}" ${strip_arg}) set(args "${CMAKE_COMMAND}" --install ${QT_BUILD_DIR} --config "${config}" ${strip_arg})

View File

@ -43,7 +43,8 @@ else()
else() else()
set(MimerSQL_library_hints "") set(MimerSQL_library_hints "")
endif() endif()
elseif(APPLE AND ${CMAKE_SYSTEM_NAME} MATCHES "Darwin") elseif(APPLE AND ${CMAKE_SYSTEM_NAME} MATCHES "Darwin"
AND CMAKE_OSX_ARCHITECTURES MATCHES "^(x86_64|arm64|)$")
set(MimerSQL_library_hints "/usr/local/lib") set(MimerSQL_library_hints "/usr/local/lib")
set(MimerSQL_include_dir_hints "/usr/local/include") set(MimerSQL_include_dir_hints "/usr/local/include")
else() else()

View File

@ -276,7 +276,7 @@ function(qt_auto_detect_cmake_config)
endfunction() endfunction()
function(qt_auto_detect_cyclic_toolchain) function(qt_auto_detect_cyclic_toolchain)
if(CMAKE_TOOLCHAIN_FILE AND CMAKE_TOOLCHAIN_FILE MATCHES "/qt.toolchain.cmake$") if(CMAKE_TOOLCHAIN_FILE AND CMAKE_TOOLCHAIN_FILE MATCHES "/qt\\.toolchain\\.cmake$")
message(FATAL_ERROR message(FATAL_ERROR
"Woah there! You can't use the Qt generated qt.toolchain.cmake file to configure " "Woah there! You can't use the Qt generated qt.toolchain.cmake file to configure "
"qtbase, because that will create a toolchain file that includes itself!\n" "qtbase, because that will create a toolchain file that includes itself!\n"

View File

@ -18,10 +18,6 @@ macro(qt_internal_top_level_setup_autodetect)
endmacro() endmacro()
macro(qt_internal_top_level_setup_after_project) macro(qt_internal_top_level_setup_after_project)
# TODO: Remove this variable once the top-level calls this function and
# qt_internal_qt_configure_end is not called in qt_print_build_instructions anymore.
set(__qt6_top_level_after_project_called TRUE)
qt_internal_top_level_setup_testing() qt_internal_top_level_setup_testing()
endmacro() endmacro()

View File

@ -111,13 +111,6 @@ from the build directory")
if(QT_SUPERBUILD) if(QT_SUPERBUILD)
qt_internal_save_previously_visited_packages() qt_internal_save_previously_visited_packages()
endif() endif()
# TODO: Abuse qt_print_build_instructions being called as the last command in a top-level build.
# Instead we should call this explicitly at the end of the top-level project.
# TODO: Remove this once the top-level calls qt_internal_top_level_setup_after_project
if(QT_SUPERBUILD AND NOT __qt6_top_level_after_project_called)
qt_internal_qt_configure_end()
endif()
endfunction() endfunction()
function(qt_configure_print_summary_helper summary_reports force_show) function(qt_configure_print_summary_helper summary_reports force_show)

View File

@ -39,6 +39,9 @@ function(qt_internal_read_repo_dependencies out_var repo_dir)
if(NOT dependency IN_LIST seen) if(NOT dependency IN_LIST seen)
qt_internal_read_repo_dependencies(subdeps "${dependency_repo_dir}" qt_internal_read_repo_dependencies(subdeps "${dependency_repo_dir}"
${seen} ${dependency}) ${seen} ${dependency})
if(dependency MATCHES "^tqtc-(.+)")
set(dependency "${CMAKE_MATCH_1}")
endif()
list(APPEND dependencies ${subdeps} ${dependency}) list(APPEND dependencies ${subdeps} ${dependency})
endif() endif()
endif() endif()
@ -106,6 +109,8 @@ endif()
include(QtPlatformSupport) include(QtPlatformSupport)
# Set FEATURE_${feature} if INPUT_${feature} is set in certain circumstances. # Set FEATURE_${feature} if INPUT_${feature} is set in certain circumstances.
# Set FEATURE_${feature}_computed_from_input to TRUE or FALSE depending on whether the
# INPUT_${feature} value has overridden the FEATURE_${feature} variable.
# #
# Needs to be in QtBuildInternalsConfig.cmake instead of QtFeature.cmake because it's used in # Needs to be in QtBuildInternalsConfig.cmake instead of QtFeature.cmake because it's used in
# qt_build_internals_disable_pkg_config_if_needed. # qt_build_internals_disable_pkg_config_if_needed.
@ -126,6 +131,9 @@ function(qt_internal_compute_feature_value_from_possible_input feature)
endif() endif()
set(FEATURE_${feature} "${FEATURE_${feature}}" PARENT_SCOPE) set(FEATURE_${feature} "${FEATURE_${feature}}" PARENT_SCOPE)
set(FEATURE_${feature}_computed_from_input TRUE PARENT_SCOPE)
else()
set(FEATURE_${feature}_computed_from_input FALSE PARENT_SCOPE)
endif() endif()
endfunction() endfunction()
@ -622,9 +630,6 @@ function(qt_internal_qt_configure_end)
# reconfigurations that are done by calling cmake directly don't trigger configure specific # reconfigurations that are done by calling cmake directly don't trigger configure specific
# logic. # logic.
unset(QT_INTERNAL_CALLED_FROM_CONFIGURE CACHE) unset(QT_INTERNAL_CALLED_FROM_CONFIGURE CACHE)
# Clean up stale feature input values.
qt_internal_clean_feature_inputs()
endfunction() endfunction()
macro(qt_build_repo) macro(qt_build_repo)

View File

@ -2,6 +2,7 @@
# SPDX-License-Identifier: BSD-3-Clause # SPDX-License-Identifier: BSD-3-Clause
# Includes QtSetup and friends for private CMake API. # Includes QtSetup and friends for private CMake API.
set(QT_INTERNAL_IS_STANDALONE_TEST TRUE)
qt_internal_project_setup() qt_internal_project_setup()
qt_build_internals_set_up_private_api() qt_build_internals_set_up_private_api()

View File

@ -422,7 +422,8 @@ function(qt_internal_add_configure_time_executable target)
) )
set(should_build_at_configure_time TRUE) set(should_build_at_configure_time TRUE)
if(EXISTS "${target_binary_path}" AND EXISTS "${timestamp_file}") if(QT_INTERNAL_HAVE_CONFIGURE_TIME_${target} AND
EXISTS "${target_binary_path}" AND EXISTS "${timestamp_file}")
set(last_ts 0) set(last_ts 0)
foreach(source IN LISTS sources) foreach(source IN LISTS sources)
file(TIMESTAMP "${source}" ts "%s") file(TIMESTAMP "${source}" ts "%s")
@ -437,6 +438,37 @@ function(qt_internal_add_configure_time_executable target)
endif() endif()
endif() endif()
set(cmake_flags_arg "")
if(arg_CMAKE_FLAGS)
set(cmake_flags_arg CMAKE_FLAGS "${arg_CMAKE_FLAGS}")
endif()
qt_internal_get_enabled_languages_for_flag_manipulation(enabled_languages)
foreach(lang IN LISTS enabled_languages)
set(compiler_flags_var "CMAKE_${lang}_FLAGS")
list(APPEND cmake_flags_arg "-D${compiler_flags_var}:STRING=${${compiler_flags_var}}")
if(arg_CONFIG)
set(compiler_flags_var_config "${compiler_flags_var}${config_suffix}")
list(APPEND cmake_flags_arg
"-D${compiler_flags_var_config}:STRING=${${compiler_flags_var_config}}")
endif()
endforeach()
qt_internal_get_target_link_types_for_flag_manipulation(target_link_types)
foreach(linker_type IN LISTS target_link_types)
set(linker_flags_var "CMAKE_${linker_type}_LINKER_FLAGS")
list(APPEND cmake_flags_arg "-D${linker_flags_var}:STRING=${${linker_flags_var}}")
if(arg_CONFIG)
set(linker_flags_var_config "${linker_flags_var}${config_suffix}")
list(APPEND cmake_flags_arg
"-D${linker_flags_var_config}:STRING=${${linker_flags_var_config}}")
endif()
endforeach()
if(NOT "${QT_INTERNAL_CMAKE_FLAGS_CONFIGURE_TIME_TOOL_${target}}" STREQUAL "${cmake_flags_arg}")
set(should_build_at_configure_time TRUE)
endif()
if(should_build_at_configure_time) if(should_build_at_configure_time)
foreach(arg IN LISTS multi_value_args) foreach(arg IN LISTS multi_value_args)
string(TOLOWER "${arg}" template_arg_name) string(TOLOWER "${arg}" template_arg_name)
@ -460,33 +492,11 @@ function(qt_internal_add_configure_time_executable target)
set(template "${arg_CMAKELISTS_TEMPLATE}") set(template "${arg_CMAKELISTS_TEMPLATE}")
endif() endif()
set(cmake_flags_arg)
if(arg_CMAKE_FLAGS)
set(cmake_flags_arg CMAKE_FLAGS "${arg_CMAKE_FLAGS}")
endif()
configure_file("${template}" "${target_binary_dir}/CMakeLists.txt" @ONLY) configure_file("${template}" "${target_binary_dir}/CMakeLists.txt" @ONLY)
qt_internal_get_enabled_languages_for_flag_manipulation(enabled_languages) if(EXISTS "${target_binary_dir}/CMakeCache.txt")
foreach(lang IN LISTS enabled_languages) file(REMOVE "${target_binary_dir}/CMakeCache.txt")
set(compiler_flags_var "CMAKE_${lang}_FLAGS") endif()
list(APPEND cmake_flags_arg "-D${compiler_flags_var}:STRING=${${compiler_flags_var}}")
if(arg_CONFIG)
set(compiler_flags_var_config "${compiler_flags_var}${config_suffix}")
list(APPEND cmake_flags_arg
"-D${compiler_flags_var_config}:STRING=${${compiler_flags_var_config}}")
endif()
endforeach()
qt_internal_get_target_link_types_for_flag_manipulation(target_link_types)
foreach(linker_type IN LISTS target_link_types)
set(linker_flags_var "CMAKE_${linker_type}_LINKER_FLAGS")
list(APPEND cmake_flags_arg "-D${linker_flags_var}:STRING=${${linker_flags_var}}")
if(arg_CONFIG)
set(linker_flags_var_config "${linker_flags_var}${config_suffix}")
list(APPEND cmake_flags_arg
"-D${linker_flags_var_config}:STRING=${${linker_flags_var_config}}")
endif()
endforeach()
try_compile(result try_compile(result
"${target_binary_dir}" "${target_binary_dir}"
@ -496,7 +506,12 @@ function(qt_internal_add_configure_time_executable target)
OUTPUT_VARIABLE try_compile_output OUTPUT_VARIABLE try_compile_output
) )
set(QT_INTERNAL_CMAKE_FLAGS_CONFIGURE_TIME_TOOL_${target}
"${cmake_flags_arg}" CACHE INTERNAL "")
file(WRITE "${timestamp_file}" "") file(WRITE "${timestamp_file}" "")
set(QT_INTERNAL_HAVE_CONFIGURE_TIME_${target} ${result} CACHE INTERNAL
"Indicates that the configure-time target ${target} was built")
if(NOT result) if(NOT result)
message(FATAL_ERROR "Unable to build ${target}: ${try_compile_output}") message(FATAL_ERROR "Unable to build ${target}: ${try_compile_output}")
endif() endif()

View File

@ -883,6 +883,18 @@ function(qt_internal_detect_dirty_features)
"to ${FEATURE_${feature}}") "to ${FEATURE_${feature}}")
set(dirty_build TRUE) set(dirty_build TRUE)
set_property(GLOBAL APPEND PROPERTY _qt_dirty_features "${feature}") set_property(GLOBAL APPEND PROPERTY _qt_dirty_features "${feature}")
# If the user changed the value of the feature directly (e.g by editing
# CMakeCache.txt), and not via its associated INPUT variable, unset the INPUT cache
# variable before it is used in feature evaluation, to ensure a stale value doesn't
# influence other feature values, especially when QT_INTERNAL_CALLED_FROM_CONFIGURE
# is TRUE and the INPUT_foo variable is not passed.
# e.g. first configure -no-gui, then manually toggle FEATURE_gui to ON in
# CMakeCache.txt, then reconfigure (with the configure script) without -no-gui.
# Without this unset(), we'd have switched FEATURE_gui to OFF again.
if(NOT FEATURE_${feature}_computed_from_input)
unset("INPUT_${feature}" CACHE)
endif()
endif() endif()
unset("QT_FEATURE_${feature}" CACHE) unset("QT_FEATURE_${feature}" CACHE)
endforeach() endforeach()
@ -899,18 +911,6 @@ function(qt_internal_detect_dirty_features)
endif() endif()
endfunction() endfunction()
function(qt_internal_clean_feature_inputs)
foreach(feature IN LISTS QT_KNOWN_FEATURES)
# Unset the INPUT_foo cache variables after they were used in feature evaluation, to
# ensure stale values don't influence features upon reconfiguration when
# QT_INTERNAL_CALLED_FROM_CONFIGURE is TRUE and the INPUT_foo variable is not passed.
# e.g. first configure -no-gui, then manually toggle FEATURE_gui to ON in
# CMakeCache.txt, then reconfigure (with the configure script) without -no-gui.
# Without this unset(), we'd have switched FEATURE_gui to OFF again.
unset(INPUT_${feature} CACHE)
endforeach()
endfunction()
function(qt_config_compile_test name) function(qt_config_compile_test name)
if(DEFINED "TEST_${name}") if(DEFINED "TEST_${name}")
return() return()

View File

@ -1,6 +1,23 @@
# Copyright (C) 2022 The Qt Company Ltd. # Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause # SPDX-License-Identifier: BSD-3-Clause
# Sets '${var}' to a genex that extracts the target's property.
# Sets 'have_${var}' to a genex that checks that the property has a
# non-empty value.
macro(qt_internal_genex_get_property var target property)
set(${var} "$<TARGET_PROPERTY:${target},${property}>")
set(have_${var} "$<BOOL:${${var}}>")
endmacro()
# Sets '${var}' to a genex that will join the given property values
# using '${glue}' and will surround the entire output with '${prefix}'
# and '${suffix}'.
macro(qt_internal_genex_get_joined_property var target property prefix suffix glue)
qt_internal_genex_get_property("${var}" "${target}" "${property}")
set(${var}
"$<${have_${var}}:${prefix}$<JOIN:${${var}},${glue}>${suffix}>")
endmacro()
# This function generates LD version script for the target and uses it in the target linker line. # This function generates LD version script for the target and uses it in the target linker line.
# Function has two modes dependending on the specified arguments. # Function has two modes dependending on the specified arguments.
# Arguments: # Arguments:
@ -33,9 +50,21 @@ function(qt_internal_add_linker_version_script target)
endif() endif()
string(APPEND contents "};\n") string(APPEND contents "};\n")
set(current "Qt_${PROJECT_VERSION_MAJOR}") set(current "Qt_${PROJECT_VERSION_MAJOR}")
string(APPEND contents "${current} { *; };\n") string(APPEND contents "${current} {\n *;")
get_target_property(target_type ${target} TYPE)
if(NOT target_type STREQUAL "INTERFACE_LIBRARY")
set(genex_prefix "\n ")
set(genex_glue "$<SEMICOLON>\n ")
set(genex_suffix "$<SEMICOLON>")
qt_internal_genex_get_joined_property(
linker_exports "${target}" _qt_extra_linker_script_exports
"${genex_prefix}" "${genex_suffix}" "${genex_glue}"
)
string(APPEND contents "${linker_exports}")
endif()
string(APPEND contents "\n};\n")
get_target_property(type ${target} TYPE)
if(NOT target_type STREQUAL "INTERFACE_LIBRARY") if(NOT target_type STREQUAL "INTERFACE_LIBRARY")
set(property_genex "$<TARGET_PROPERTY:${target},_qt_extra_linker_script_content>") set(property_genex "$<TARGET_PROPERTY:${target},_qt_extra_linker_script_content>")
set(check_genex "$<BOOL:${property_genex}>") set(check_genex "$<BOOL:${property_genex}>")

View File

@ -82,24 +82,41 @@ function(qt_copy_framework_headers target)
set(output_dir_QPA "${output_dir}/${fw_private_module_header_dir}/qpa") set(output_dir_QPA "${output_dir}/${fw_private_module_header_dir}/qpa")
set(output_dir_RHI "${output_dir}/${fw_private_module_header_dir}/rhi") set(output_dir_RHI "${output_dir}/${fw_private_module_header_dir}/rhi")
qt_internal_module_info(module "${target}")
set(out_files) set(out_files "")
set(in_files "")
set(copy_commands "")
foreach(type IN ITEMS PUBLIC PRIVATE QPA RHI) foreach(type IN ITEMS PUBLIC PRIVATE QPA RHI)
set(in_files_${type} "")
set(fw_output_header_dir "${output_dir_${type}}") set(fw_output_header_dir "${output_dir_${type}}")
foreach(hdr IN LISTS arg_${type}) foreach(hdr IN LISTS arg_${type})
get_filename_component(in_file_path ${hdr} ABSOLUTE) get_filename_component(in_file_path ${hdr} ABSOLUTE)
get_filename_component(in_file_name ${hdr} NAME) get_filename_component(in_file_name ${hdr} NAME)
set(out_file_path "${fw_output_header_dir}/${in_file_name}") set(out_file_path "${fw_output_header_dir}/${in_file_name}")
add_custom_command(
OUTPUT ${out_file_path}
DEPENDS ${in_file_path}
COMMAND ${CMAKE_COMMAND} -E make_directory "${fw_output_header_dir}"
COMMAND ${CMAKE_COMMAND} -E copy "${in_file_path}" "${fw_output_header_dir}"
VERBATIM)
list(APPEND out_files ${out_file_path}) list(APPEND out_files ${out_file_path})
list(APPEND in_files_${type} "${in_file_path}")
endforeach() endforeach()
if(in_files_${type})
list(APPEND copy_commands
COMMAND ${CMAKE_COMMAND} -E copy ${in_files_${type}} "${fw_output_header_dir}")
list(APPEND in_files ${in_files_${type}})
endif()
endforeach() endforeach()
list(REMOVE_DUPLICATES out_files)
list(REMOVE_DUPLICATES in_files)
add_custom_command(
OUTPUT "${output_dir}/${fw_versioned_header_dir}" ${out_files}
DEPENDS ${in_files} ${target}_sync_headers
COMMAND
${CMAKE_COMMAND} -E copy_directory
"${module_build_interface_include_dir}/.syncqt_staging"
"${output_dir}/${fw_versioned_header_dir}"
${copy_commands}
VERBATIM
COMMENT "Copy the ${target} header files to the framework directory"
)
set_property(TARGET ${target} APPEND PROPERTY set_property(TARGET ${target} APPEND PROPERTY
QT_COPIED_FRAMEWORK_HEADERS "${out_files}") QT_COPIED_FRAMEWORK_HEADERS "${out_files}")
endfunction() endfunction()

View File

@ -11,7 +11,8 @@ if(EXISTS ${HEADER_CHECK_EXCEPTIONS})
file(READ ${HEADER_CHECK_EXCEPTIONS} header_check_exception_list) file(READ ${HEADER_CHECK_EXCEPTIONS} header_check_exception_list)
endif() endif()
file(TO_CMAKE_PATH "${INPUT_HEADER_FILE}" header) get_filename_component(header "${INPUT_HEADER_FILE}" REALPATH)
file(TO_CMAKE_PATH "${header}" header)
foreach(exception IN LISTS header_check_exception_list) foreach(exception IN LISTS header_check_exception_list)
file(TO_CMAKE_PATH "${exception}" exception) file(TO_CMAKE_PATH "${exception}" exception)
if(exception STREQUAL header) if(exception STREQUAL header)

View File

@ -12,7 +12,6 @@ endfunction()
qt_set01(LINUX CMAKE_SYSTEM_NAME STREQUAL "Linux") qt_set01(LINUX CMAKE_SYSTEM_NAME STREQUAL "Linux")
qt_set01(HPUX CMAKE_SYSTEM_NAME STREQUAL "HPUX") qt_set01(HPUX CMAKE_SYSTEM_NAME STREQUAL "HPUX")
qt_set01(ANDROID CMAKE_SYSTEM_NAME STREQUAL "Android") # FIXME: How to identify this? qt_set01(ANDROID CMAKE_SYSTEM_NAME STREQUAL "Android") # FIXME: How to identify this?
qt_set01(NACL CMAKE_SYSTEM_NAME STREQUAL "NaCl") # FIXME: How to identify this?
qt_set01(INTEGRITY CMAKE_SYSTEM_NAME STREQUAL "Integrity") # FIXME: How to identify this? qt_set01(INTEGRITY CMAKE_SYSTEM_NAME STREQUAL "Integrity") # FIXME: How to identify this?
qt_set01(VXWORKS CMAKE_SYSTEM_NAME STREQUAL "VxWorks") # FIXME: How to identify this? qt_set01(VXWORKS CMAKE_SYSTEM_NAME STREQUAL "VxWorks") # FIXME: How to identify this?
qt_set01(QNX CMAKE_SYSTEM_NAME STREQUAL "QNX") # FIXME: How to identify this? qt_set01(QNX CMAKE_SYSTEM_NAME STREQUAL "QNX") # FIXME: How to identify this?

View File

@ -374,7 +374,9 @@ function(qtConfValidateValue opt val out_var)
endforeach() endforeach()
set(${out_var} FALSE PARENT_SCOPE) set(${out_var} FALSE PARENT_SCOPE)
qtConfAddError("Invalid value '${val}' supplied to command line option '${opt}'.") list(JOIN valid_values " " valid_values_str)
qtConfAddError("Invalid value '${val}' supplied to command line option '${opt}'."
"\nAllowed values: ${valid_values_str}\n")
endfunction() endfunction()
function(qt_commandline_mapped_enum_value opt key out_var) function(qt_commandline_mapped_enum_value opt key out_var)

View File

@ -3,10 +3,16 @@
## Set a default build type if none was specified ## Set a default build type if none was specified
# Set the QT_IS_BUILDING_QT variable so we can verify whether we are building # Set the QT_BUILDING_QT variable so we can verify whether we are building
# Qt from source # Qt from source.
set(QT_BUILDING_QT TRUE CACHE BOOL # Make sure not to set it when building a standalone test, otherwise
# upon reconfiguration we get an error about qt_internal_add_test
# not being found due the if(NOT QT_BUILDING_QT) check we have
# in each standalone test.
if(NOT QT_INTERNAL_IS_STANDALONE_TEST)
set(QT_BUILDING_QT TRUE CACHE BOOL
"When this is present and set to true, it signals that we are building Qt from source.") "When this is present and set to true, it signals that we are building Qt from source.")
endif()
# Pre-calculate the developer_build feature if it's set by the user via the INPUT_developer_build # Pre-calculate the developer_build feature if it's set by the user via the INPUT_developer_build
# variable when using the configure script. When not using configure, don't take the INPUT variable # variable when using the configure script. When not using configure, don't take the INPUT variable

View File

@ -2,4 +2,4 @@
# bypassing the Qt6 Config file, aka find_package(Qt6SpecificFoo) repated x times. But it's not # bypassing the Qt6 Config file, aka find_package(Qt6SpecificFoo) repated x times. But it's not
# critical. # critical.
find_package(@INSTALL_CMAKE_NAMESPACE@ @main_qt_package_version@ find_package(@INSTALL_CMAKE_NAMESPACE@ @main_qt_package_version@
REQUIRED COMPONENTS @QT_REPO_KNOWN_MODULES_STRING@) COMPONENTS @QT_REPO_KNOWN_MODULES_STRING@)

View File

@ -61,13 +61,6 @@ function(qt_internal_target_sync_headers target module_headers module_headers_ge
set(is_framework FALSE) set(is_framework FALSE)
if(NOT is_interface_lib) if(NOT is_interface_lib)
get_target_property(is_framework ${target} FRAMEWORK) get_target_property(is_framework ${target} FRAMEWORK)
if(is_framework)
qt_internal_get_framework_info(fw ${target})
get_target_property(fw_output_base_dir ${target} LIBRARY_OUTPUT_DIRECTORY)
set(framework_args "-framework"
"-frameworkIncludeDir" "${fw_output_base_dir}/${fw_versioned_header_dir}"
)
endif()
endif() endif()
qt_internal_get_qt_all_known_modules(known_modules) qt_internal_get_qt_all_known_modules(known_modules)
@ -151,7 +144,6 @@ function(qt_internal_target_sync_headers target module_headers module_headers_ge
-headers ${module_headers} -headers ${module_headers}
-stagingDir "${syncqt_staging_dir}" -stagingDir "${syncqt_staging_dir}"
-knownModules ${known_modules} -knownModules ${known_modules}
${framework_args}
${version_script_args} ${version_script_args}
) )
list(JOIN syncqt_args "\n" syncqt_args_string) list(JOIN syncqt_args "\n" syncqt_args_string)

View File

@ -16,6 +16,8 @@
# Custom compilation flags. # Custom compilation flags.
# EXTRA_LINKER_SCRIPT_CONTENT # EXTRA_LINKER_SCRIPT_CONTENT
# Extra content that should be appended to a target linker script. Applicable for ld only. # Extra content that should be appended to a target linker script. Applicable for ld only.
# EXTRA_LINKER_SCRIPT_EXPORTS
# Extra content that should be added to export section of the linker script.
# NO_PCH_SOURCES # NO_PCH_SOURCES
# Skip the specified source files by PRECOMPILE_HEADERS feature. # Skip the specified source files by PRECOMPILE_HEADERS feature.
function(qt_internal_extend_target target) function(qt_internal_extend_target target)
@ -51,6 +53,7 @@ function(qt_internal_extend_target target)
CONDITION CONDITION
CONDITION_INDEPENDENT_SOURCES CONDITION_INDEPENDENT_SOURCES
COMPILE_FLAGS COMPILE_FLAGS
EXTRA_LINKER_SCRIPT_EXPORTS
) )
cmake_parse_arguments(PARSE_ARGV 1 arg cmake_parse_arguments(PARSE_ARGV 1 arg
@ -260,6 +263,10 @@ function(qt_internal_extend_target target)
set_target_properties(${target} PROPERTIES set_target_properties(${target} PROPERTIES
_qt_extra_linker_script_content "${arg_EXTRA_LINKER_SCRIPT_CONTENT}") _qt_extra_linker_script_content "${arg_EXTRA_LINKER_SCRIPT_CONTENT}")
endif() endif()
if(arg_EXTRA_LINKER_SCRIPT_EXPORTS)
set_target_properties(${target} PROPERTIES
_qt_extra_linker_script_exports "${arg_EXTRA_LINKER_SCRIPT_EXPORTS}")
endif()
endfunction() endfunction()
function(qt_is_imported_target target out_var) function(qt_is_imported_target target out_var)

View File

@ -131,10 +131,12 @@ set(__qt_chainload_toolchain_file \"\${__qt_initially_configured_toolchain_file}
list(APPEND init_platform " list(APPEND init_platform "
set(__qt_initial_c_compiler \"${CMAKE_C_COMPILER}\") set(__qt_initial_c_compiler \"${CMAKE_C_COMPILER}\")
set(__qt_initial_cxx_compiler \"${CMAKE_CXX_COMPILER}\") set(__qt_initial_cxx_compiler \"${CMAKE_CXX_COMPILER}\")
if(NOT DEFINED CMAKE_C_COMPILER AND EXISTS \"\${__qt_initial_c_compiler}\") if(QT_USE_ORIGINAL_COMPILER AND NOT DEFINED CMAKE_C_COMPILER
AND EXISTS \"\${__qt_initial_c_compiler}\")
set(CMAKE_C_COMPILER \"\${__qt_initial_c_compiler}\" CACHE STRING \"\") set(CMAKE_C_COMPILER \"\${__qt_initial_c_compiler}\" CACHE STRING \"\")
endif() endif()
if(NOT DEFINED CMAKE_CXX_COMPILER AND EXISTS \"\${__qt_initial_cxx_compiler}\") if(QT_USE_ORIGINAL_COMPILER AND NOT DEFINED CMAKE_CXX_COMPILER
AND EXISTS \"\${__qt_initial_cxx_compiler}\")
set(CMAKE_CXX_COMPILER \"\${__qt_initial_cxx_compiler}\" CACHE STRING \"\") set(CMAKE_CXX_COMPILER \"\${__qt_initial_cxx_compiler}\" CACHE STRING \"\")
endif()") endif()")
endif() endif()

View File

@ -53,15 +53,16 @@ function(qt_internal_create_wrapper_scripts)
qt_install(PROGRAMS "${QT_BUILD_DIR}/${INSTALL_BINDIR}/qt-cmake-create.bat" qt_install(PROGRAMS "${QT_BUILD_DIR}/${INSTALL_BINDIR}/qt-cmake-create.bat"
DESTINATION "${INSTALL_BINDIR}") DESTINATION "${INSTALL_BINDIR}")
endif() endif()
# Provide a private convenience wrapper with options which should not be propagated via the # Provide a private convenience wrapper with options that should not be propagated via the
# public qt-cmake wrapper e.g. CMAKE_GENERATOR. # public qt-cmake wrapper e.g. CMAKE_GENERATOR.
# These options can not be set in a toolchain file, but only on the command line. # These options can not be set in a toolchain file, but only on the command line.
# These options should not be in the public wrapper, because a consumer of Qt might want to # These options should not be in the public wrapper, because a consumer of Qt might want to
# build their CMake app with the Unix Makefiles generator, while Qt should be built with the # build their CMake app with the Unix Makefiles generator, while Qt should be built with the
# Ninja generator. # Ninja generator. In a similar vein, we do want to use the same compiler for all Qt modules,
# The private wrapper is more conveient for building Qt itself, because a developer doesn't need # but not for user applications.
# to specify the same options for each qt module built. # The private wrapper is more convenient for building Qt itself, because a developer doesn't
set(__qt_cmake_extra "-G\"${CMAKE_GENERATOR}\"") # need to specify the same options for each qt module built.
set(__qt_cmake_extra "-G\"${CMAKE_GENERATOR}\" -DQT_USE_ORIGINAL_COMPILER=ON")
if(generate_unix) if(generate_unix)
configure_file("${CMAKE_CURRENT_SOURCE_DIR}/bin/qt-cmake.in" configure_file("${CMAKE_CURRENT_SOURCE_DIR}/bin/qt-cmake.in"
"${QT_BUILD_DIR}/${INSTALL_LIBEXECDIR}/qt-cmake-private" @ONLY "${QT_BUILD_DIR}/${INSTALL_LIBEXECDIR}/qt-cmake-private" @ONLY
@ -202,6 +203,22 @@ function(qt_internal_create_wrapper_scripts)
elseif(CMAKE_BUILD_TYPE) elseif(CMAKE_BUILD_TYPE)
set(__qt_configured_configs "${CMAKE_BUILD_TYPE}") set(__qt_configured_configs "${CMAKE_BUILD_TYPE}")
endif() endif()
if(
# Skip stripping pure debug builds so it's easier to debug issues in CI VMs.
(NOT QT_FEATURE_debug_and_release
AND QT_FEATURE_debug
AND NOT QT_FEATURE_separate_debug_info)
# Skip stripping on MSVC because ${CMAKE_STRIP} might contain a MinGW strip binary
# and the breaks the linker version flag embedded in the binary and causes Qt Creator
# to mis-identify the Kit ABI.
OR MSVC
)
set(__qt_skip_strip_installed_artifacts TRUE)
else()
set(__qt_skip_strip_installed_artifacts FALSE)
endif()
configure_file("${CMAKE_CURRENT_SOURCE_DIR}/bin/${__qt_cmake_install_script_name}.in" configure_file("${CMAKE_CURRENT_SOURCE_DIR}/bin/${__qt_cmake_install_script_name}.in"
"${QT_BUILD_DIR}/${INSTALL_LIBEXECDIR}/${__qt_cmake_install_script_name}" @ONLY) "${QT_BUILD_DIR}/${INSTALL_LIBEXECDIR}/${__qt_cmake_install_script_name}" @ONLY)
qt_install(FILES "${QT_BUILD_DIR}/${INSTALL_LIBEXECDIR}/${__qt_cmake_install_script_name}" qt_install(FILES "${QT_BUILD_DIR}/${INSTALL_LIBEXECDIR}/${__qt_cmake_install_script_name}"
@ -209,6 +226,7 @@ function(qt_internal_create_wrapper_scripts)
qt_internal_create_qt_configure_tests_wrapper_script() qt_internal_create_qt_configure_tests_wrapper_script()
qt_internal_install_android_helper_scripts() qt_internal_install_android_helper_scripts()
qt_internal_create_qt_configure_redo_script()
endfunction() endfunction()
function(qt_internal_create_qt_configure_tests_wrapper_script) function(qt_internal_create_qt_configure_tests_wrapper_script)
@ -231,7 +249,7 @@ function(qt_internal_create_qt_configure_tests_wrapper_script)
# The script takes a path to the repo for which the standalone tests will be configured. # The script takes a path to the repo for which the standalone tests will be configured.
set(script_name "qt-internal-configure-tests") set(script_name "qt-internal-configure-tests")
set(script_passed_args "-DQT_BUILD_STANDALONE_TESTS=ON") set(script_passed_args "-DQT_BUILD_STANDALONE_TESTS=ON -DQT_USE_ORIGINAL_COMPILER=ON")
file(RELATIVE_PATH relative_path_from_libexec_dir_to_bin_dir file(RELATIVE_PATH relative_path_from_libexec_dir_to_bin_dir
${__qt_libexec_dir_absolute} ${__qt_libexec_dir_absolute}
@ -262,3 +280,41 @@ function(qt_internal_install_android_helper_scripts)
qt_copy_or_install(PROGRAMS "${CMAKE_CURRENT_SOURCE_DIR}/util/android/android_emulator_launcher.sh" qt_copy_or_install(PROGRAMS "${CMAKE_CURRENT_SOURCE_DIR}/util/android/android_emulator_launcher.sh"
DESTINATION "${destination}") DESTINATION "${destination}")
endfunction() endfunction()
# Create a shell wrapper script to reconfigure Qt with the original configure arguments and
# any additional ones passed.
#
# Removes CMakeCache.txt and friends, either manually, or using CMake's --fresh.
#
# The script is created in the root of the build dir and is called config.redo
# It has the same contents as the 'config.status' script we created in qt 5.
function(qt_internal_create_qt_configure_redo_script)
set(input_script_name "qt-internal-config.redo")
set(input_script_path "${CMAKE_CURRENT_SOURCE_DIR}/libexec/${input_script_name}")
# We don't use QT_BUILD_DIR because we want the file in the root of the build dir in a top-level
# build.
set(output_script_name "config.redo")
set(output_path "${CMAKE_BINARY_DIR}/${output_script_name}")
if(QT_SUPERBUILD)
set(configure_script_path "${Qt_SOURCE_DIR}")
else()
set(configure_script_path "${QtBase_SOURCE_DIR}")
endif()
string(APPEND configure_script_path "/configure")
# Used in the file contents.
file(TO_NATIVE_PATH "${configure_script_path}" configure_path)
if(CMAKE_HOST_UNIX)
string(APPEND input_script_path ".in")
set(newline_style "LF")
else()
string(APPEND input_script_path ".bat.in")
string(APPEND output_path ".bat")
set(newline_style "CRLF")
endif()
configure_file("${input_script_path}" "${output_path}" @ONLY NEWLINE_STYLE ${newline_style})
endfunction()

View File

@ -51,9 +51,21 @@ instructions:
condition: property condition: property
property: host.os property: host.os
equals_value: Windows equals_value: Windows
- type: EnvironmentVariable - type: EnvironmentVariable
variableName: CTEST_ARGS variableName: CTEST_ARGS
variableValue: "-V" variableValue: "-V"
# Keep the testrun quiet for ASAN testruns, since there are FAILs happening all over the place
disable_if:
condition: property
property: features
contains_value: UseAddressSanitizer
# Always print the output from a failing test, even when ctest is not in verbose mode
- type: EnvironmentVariable
variableName: CTEST_OUTPUT_ON_FAILURE
variableValue: "1"
- type: AppendToEnvironmentVariable - type: AppendToEnvironmentVariable
variableName: CTEST_ARGS variableName: CTEST_ARGS
variableValue: " --stop-on-failure" variableValue: " --stop-on-failure"
@ -61,12 +73,37 @@ instructions:
condition: property condition: property
property: features property: features
contains_value: AbortTestingOnFirstFailure contains_value: AbortTestingOnFirstFailure
# Enable CTest's JUnit XML summary only for recent versions
- type: AppendToEnvironmentVariable
variableName: CTEST_ARGS
variableValue: " --output-junit {{.Env.COIN_CTEST_RESULTSDIR}}{{.Env.CI_PATH_SEP}}test_summary.ctest_junit_xml"
enable_if:
condition: runtime
env_var: CMAKE_MIN_SUPPORTED_BIN_PATH
equals_value: null
- type: EnvironmentVariable
variableName: COIN_COMMAND_OUTPUT_TIMEOUT
variableValue: "900"
disable_if:
condition: property
property: features
contains_value: UseAddressSanitizer
- type: EnvironmentVariable
variableName: COIN_COMMAND_OUTPUT_TIMEOUT
variableValue: "10800"
enable_if:
condition: property
property: features
contains_value: UseAddressSanitizer
- type: ExecuteCommand - type: ExecuteCommand
command: "{{.Env.TESTS_ENV_PREFIX}} ctest {{.Env.CTEST_ARGS}}" command: "{{.Env.TESTS_ENV_PREFIX}} ctest {{.Env.CTEST_ARGS}}"
executeCommandArgumentSplitingBehavior: SplitAfterVariableSubstitution executeCommandArgumentSplitingBehavior: SplitAfterVariableSubstitution
ignoreExitCode: false ignoreExitCode: false
maxTimeInSeconds: 10800 maxTimeInSeconds: 10800
maxTimeBetweenOutput: 900 maxTimeBetweenOutput: "{{.Env.COIN_COMMAND_OUTPUT_TIMEOUT}}"
userMessageOnFailure: > userMessageOnFailure: >
Failed to run tests. Failed to run tests.

2
configure vendored
View File

@ -136,7 +136,7 @@ determineOptFilePath "$@"
optfilepath=${outpathPrefix}/config.opt optfilepath=${outpathPrefix}/config.opt
opttmpfilepath=${outpathPrefix}/config.opt.in opttmpfilepath=${outpathPrefix}/config.opt.in
redofilepath=${outpathPrefix}/config.redo redofilepath=${outpathPrefix}/config.redo.last
redotmpfilepath=${outpathPrefix}/config.redo.in redotmpfilepath=${outpathPrefix}/config.redo.in
fresh_requested_arg= fresh_requested_arg=

View File

@ -79,7 +79,7 @@ cd "%TOPQTDIR%"
rem Write config.opt if we're not currently -redo'ing rem Write config.opt if we're not currently -redo'ing
set OPT_FILE_PATH=%TOPQTDIR%\config.opt set OPT_FILE_PATH=%TOPQTDIR%\config.opt
set OPT_TMP_FILE_PATH=%TOPQTDIR%\config.opt.in set OPT_TMP_FILE_PATH=%TOPQTDIR%\config.opt.in
set REDO_FILE_PATH=%TOPQTDIR%\config.redo set REDO_FILE_PATH=%TOPQTDIR%\config.redo.last
set REDO_TMP_FILE_PATH=%TOPQTDIR%\config.redo.in set REDO_TMP_FILE_PATH=%TOPQTDIR%\config.redo.in
set FRESH_REQUESTED_ARG= set FRESH_REQUESTED_ARG=
if not defined redoing ( if not defined redoing (

View File

@ -202,9 +202,9 @@ manifestmeta.thumbnail.attributes = "imageUrl:qthelp\://org.qt-project.qtdoc.$Q
manifestmeta.thumbnail.names = "QtCore/Contiguous Cache Example" \ manifestmeta.thumbnail.names = "QtCore/Contiguous Cache Example" \
"QtCore/Custom Type Example" \ "QtCore/Custom Type Example" \
"QtCore/JSON Save Game Example" \ "QtCore/Saving and Loading a Game" \
"QtCore/Semaphores Example" \ "QtCore/Producer and Consumer using Semaphores" \
"QtCore/Wait Conditions Example" \ "QtCore/Producer and Consumer using Wait Conditions" \
"QtConcurrent/Word Count" \ "QtConcurrent/Word Count" \
"QtGui/Raster Window Example" \ "QtGui/Raster Window Example" \
"QtNetwork/Network Download*" \ "QtNetwork/Network Download*" \

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

View File

@ -4,7 +4,7 @@
#ifndef BINDABLESUBSCRIPTION_H #ifndef BINDABLESUBSCRIPTION_H
#define BINDABLESUBSCRIPTION_H #define BINDABLESUBSCRIPTION_H
#include <QPointer> #include <QBindable>
#include <QProperty> #include <QProperty>
class BindableUser; class BindableUser;

View File

@ -4,6 +4,7 @@
#ifndef BINDABLEUSER_H #ifndef BINDABLEUSER_H
#define BINDABLEUSER_H #define BINDABLEUSER_H
#include <QBindable>
#include <QLocale> #include <QLocale>
#include <QProperty> #include <QProperty>

View File

@ -6,15 +6,15 @@
#include "bindableuser.h" #include "bindableuser.h"
#include <QApplication> #include <QApplication>
#include <QButtonGroup> #include <QBindable>
#include <QLabel> #include <QLabel>
#include <QLocale>
#include <QPushButton> #include <QPushButton>
#include <QRadioButton> #include <QRadioButton>
#include <QSpinBox> #include <QSpinBox>
#include <QProperty>
#include <QString> #include <QString>
#include <QDateTimeEdit>
#include <QBindable> using namespace Qt::StringLiterals;
int main(int argc, char *argv[]) int main(int argc, char *argv[])
{ {
@ -27,38 +27,38 @@ int main(int argc, char *argv[])
// when subscription is out of scope so is window // when subscription is out of scope so is window
// Initialize subscription data // Initialize subscription data
QRadioButton *monthly = w.findChild<QRadioButton *>("btnMonthly"); QRadioButton *monthly = w.findChild<QRadioButton *>(u"btnMonthly"_s);
QObject::connect(monthly, &QRadioButton::clicked, [&] { QObject::connect(monthly, &QRadioButton::clicked, [&] {
subscription.setDuration(BindableSubscription::Monthly); subscription.setDuration(BindableSubscription::Monthly);
}); });
QRadioButton *quarterly = w.findChild<QRadioButton *>("btnQuarterly"); QRadioButton *quarterly = w.findChild<QRadioButton *>(u"btnQuarterly"_s);
QObject::connect(quarterly, &QRadioButton::clicked, [&] { QObject::connect(quarterly, &QRadioButton::clicked, [&] {
subscription.setDuration(BindableSubscription::Quarterly); subscription.setDuration(BindableSubscription::Quarterly);
}); });
QRadioButton *yearly = w.findChild<QRadioButton *>("btnYearly"); QRadioButton *yearly = w.findChild<QRadioButton *>(u"btnYearly"_s);
QObject::connect(yearly, &QRadioButton::clicked, [&] { QObject::connect(yearly, &QRadioButton::clicked, [&] {
subscription.setDuration(BindableSubscription::Yearly); subscription.setDuration(BindableSubscription::Yearly);
}); });
// Initialize user data // Initialize user data
QPushButton *germany = w.findChild<QPushButton *>("btnGermany"); QPushButton *germany = w.findChild<QPushButton *>(u"btnGermany"_s);
QObject::connect(germany, &QPushButton::clicked, [&] { QObject::connect(germany, &QPushButton::clicked, [&] {
user.setCountry(BindableUser::Country::Germany); user.setCountry(BindableUser::Country::Germany);
}); });
QPushButton *finland = w.findChild<QPushButton *>("btnFinland"); QPushButton *finland = w.findChild<QPushButton *>(u"btnFinland"_s);
QObject::connect(finland, &QPushButton::clicked, [&] { QObject::connect(finland, &QPushButton::clicked, [&] {
user.setCountry(BindableUser::Country::Finland); user.setCountry(BindableUser::Country::Finland);
}); });
QPushButton *norway = w.findChild<QPushButton *>("btnNorway"); QPushButton *norway = w.findChild<QPushButton *>(u"btnNorway"_s);
QObject::connect(norway, &QPushButton::clicked, [&] { QObject::connect(norway, &QPushButton::clicked, [&] {
user.setCountry(BindableUser::Country::Norway); user.setCountry(BindableUser::Country::Norway);
}); });
QSpinBox *ageSpinBox = w.findChild<QSpinBox *>("ageSpinBox"); QSpinBox *ageSpinBox = w.findChild<QSpinBox *>(u"ageSpinBox"_s);
QBindable<int> ageBindable(ageSpinBox, "value"); QBindable<int> ageBindable(ageSpinBox, "value");
user.bindableAge().setBinding([ageBindable](){ return ageBindable.value();}); user.bindableAge().setBinding([ageBindable](){ return ageBindable.value();});
QLabel *priceDisplay = w.findChild<QLabel *>("priceDisplay"); QLabel *priceDisplay = w.findChild<QLabel *>(u"priceDisplay"_s);
// Track price changes // Track price changes
//! [update-ui] //! [update-ui]

View File

@ -3,7 +3,8 @@
/*! /*!
\example bindableproperties \example bindableproperties
\title Bindable Properties Example \examplecategory {Data Processing & I/O}
\title Bindable Properties
\brief Demonstrates how the usage of bindable properties can simplify \brief Demonstrates how the usage of bindable properties can simplify
your C++ code. your C++ code.

View File

@ -6,11 +6,14 @@
#include "user.h" #include "user.h"
#include <QApplication> #include <QApplication>
#include <QButtonGroup>
#include <QLabel> #include <QLabel>
#include <QLocale>
#include <QPushButton> #include <QPushButton>
#include <QRadioButton> #include <QRadioButton>
#include <QSpinBox> #include <QSpinBox>
#include <QString>
using namespace Qt::StringLiterals;
int main(int argc, char *argv[]) int main(int argc, char *argv[])
{ {
@ -24,40 +27,40 @@ int main(int argc, char *argv[])
SubscriptionWindow w; SubscriptionWindow w;
// Initialize subscription data // Initialize subscription data
QRadioButton *monthly = w.findChild<QRadioButton *>("btnMonthly"); QRadioButton *monthly = w.findChild<QRadioButton *>(u"btnMonthly"_s);
QObject::connect(monthly, &QRadioButton::clicked, &subscription, [&] { QObject::connect(monthly, &QRadioButton::clicked, &subscription, [&] {
subscription.setDuration(Subscription::Monthly); subscription.setDuration(Subscription::Monthly);
}); });
QRadioButton *quarterly = w.findChild<QRadioButton *>("btnQuarterly"); QRadioButton *quarterly = w.findChild<QRadioButton *>(u"btnQuarterly"_s);
QObject::connect(quarterly, &QRadioButton::clicked, &subscription, [&] { QObject::connect(quarterly, &QRadioButton::clicked, &subscription, [&] {
subscription.setDuration(Subscription::Quarterly); subscription.setDuration(Subscription::Quarterly);
}); });
QRadioButton *yearly = w.findChild<QRadioButton *>("btnYearly"); QRadioButton *yearly = w.findChild<QRadioButton *>(u"btnYearly"_s);
QObject::connect(yearly, &QRadioButton::clicked, &subscription, [&] { QObject::connect(yearly, &QRadioButton::clicked, &subscription, [&] {
subscription.setDuration(Subscription::Yearly); subscription.setDuration(Subscription::Yearly);
}); });
// Initialize user data // Initialize user data
QPushButton *germany = w.findChild<QPushButton *>("btnGermany"); QPushButton *germany = w.findChild<QPushButton *>(u"btnGermany"_s);
QObject::connect(germany, &QPushButton::clicked, &user, [&] { QObject::connect(germany, &QPushButton::clicked, &user, [&] {
user.setCountry(User::Country::Germany); user.setCountry(User::Country::Germany);
}); });
QPushButton *finland = w.findChild<QPushButton *>("btnFinland"); QPushButton *finland = w.findChild<QPushButton *>(u"btnFinland"_s);
QObject::connect(finland, &QPushButton::clicked, &user, [&] { QObject::connect(finland, &QPushButton::clicked, &user, [&] {
user.setCountry(User::Country::Finland); user.setCountry(User::Country::Finland);
}); });
QPushButton *norway = w.findChild<QPushButton *>("btnNorway"); QPushButton *norway = w.findChild<QPushButton *>(u"btnNorway"_s);
QObject::connect(norway, &QPushButton::clicked, &user, [&] { QObject::connect(norway, &QPushButton::clicked, &user, [&] {
user.setCountry(User::Country::Norway); user.setCountry(User::Country::Norway);
}); });
QSpinBox *ageSpinBox = w.findChild<QSpinBox *>("ageSpinBox"); QSpinBox *ageSpinBox = w.findChild<QSpinBox *>(u"ageSpinBox"_s);
QObject::connect(ageSpinBox, &QSpinBox::valueChanged, &user, [&](int value) { QObject::connect(ageSpinBox, &QSpinBox::valueChanged, &user, [&](int value) {
user.setAge(value); user.setAge(value);
}); });
// Initialize price data // Initialize price data
QLabel *priceDisplay = w.findChild<QLabel *>("priceDisplay"); QLabel *priceDisplay = w.findChild<QLabel *>(u"priceDisplay"_s);
priceDisplay->setText(QString::number(subscription.price())); priceDisplay->setText(QString::number(subscription.price()));
priceDisplay->setEnabled(subscription.isValid()); priceDisplay->setEnabled(subscription.isValid());

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.6 KiB

After

Width:  |  Height:  |  Size: 15 KiB

View File

@ -3,14 +3,16 @@
/*! /*!
\example ipc/localfortuneclient \example ipc/localfortuneclient
\title Local Fortune Client Example \examplecategory {Connectivity}
\title Local Fortune Client
\ingroup examples-ipc \ingroup examples-ipc
\brief Demonstrates using QLocalSocket for a simple local service client. \brief Demonstrates using QLocalSocket for a simple local service client.
The Local Fortune Client example shows how to create a client for a simple The Local Fortune Client example shows how to create a client for a simple
local service using QLocalSocket. It is intended to be run alongside the local service using QLocalSocket. It is intended to be run alongside the
\l{Local Fortune Server Example}. \l{Local Fortune Server} example.
\image localfortuneclient-example.png Screenshot of the Local Fortune Client example \image localfortuneclient-example.png Screenshot of the Local Fortune Client
example
*/ */

View File

@ -3,13 +3,14 @@
/*! /*!
\example ipc/localfortuneserver \example ipc/localfortuneserver
\title Local Fortune Server Example \examplecategory {Connectivity}
\title Local Fortune Server
\ingroup examples-ipc \ingroup examples-ipc
\brief Demonstrates using QLocalServer and QLocalSocket for serving a simple local service. \brief Demonstrates using QLocalServer and QLocalSocket for serving a simple local service.
The Local Fortune Server example shows how to create a server for a simple The Local Fortune Server example shows how to create a server for a simple
local service. It is intended to be run alongside the local service. It is intended to be run alongside the
\l{Local Fortune Client Example} \l{Local Fortune Client} example.
\image localfortuneserver-example.png Screenshot of the Local Fortune Server example \image localfortuneserver-example.png Screenshot of the Local Fortune Server example
*/ */

View File

@ -3,10 +3,11 @@
/*! /*!
\example ipc/sharedmemory \example ipc/sharedmemory
\title Shared Memory Example \examplecategory {Data Processing & I/O}
\title IPC: Shared Memory
\ingroup examples-ipc \ingroup examples-ipc
\brief Demonstrates doing inter-process communication using shared memory with \brief Demonstrates how to share image data between different processes
the QSharedMemory class. using the Shared Memory IPC mechanism.
The Shared Memory example shows how to use the QSharedMemory class The Shared Memory example shows how to use the QSharedMemory class
to implement inter-process communication using shared memory. To to implement inter-process communication using shared memory. To

View File

@ -1,14 +1,19 @@
// Copyright (C) 2016 The Qt Company Ltd. // Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#include <QtWidgets>
#include <QtNetwork>
#include "client.h" #include "client.h"
#include <QDialogButtonBox>
#include <QGridLayout>
#include <QGuiApplication>
#include <QMessageBox>
#include <QTimer>
using namespace Qt::StringLiterals;
Client::Client(QWidget *parent) Client::Client(QWidget *parent)
: QDialog(parent), : QDialog(parent),
hostLineEdit(new QLineEdit("fortune")), hostLineEdit(new QLineEdit(u"fortune"_s)),
getFortuneButton(new QPushButton(tr("Get Fortune"))), getFortuneButton(new QPushButton(tr("Get Fortune"))),
statusLabel(new QLabel(tr("This examples requires that you run the " statusLabel(new QLabel(tr("This examples requires that you run the "
"Local Fortune Server example as well."))), "Local Fortune Server example as well."))),
@ -28,7 +33,7 @@ Client::Client(QWidget *parent)
buttonBox->addButton(quitButton, QDialogButtonBox::RejectRole); buttonBox->addButton(quitButton, QDialogButtonBox::RejectRole);
in.setDevice(socket); in.setDevice(socket);
in.setVersion(QDataStream::Qt_5_10); in.setVersion(QDataStream::Qt_6_0);
connect(hostLineEdit, &QLineEdit::textChanged, connect(hostLineEdit, &QLineEdit::textChanged,
this, &Client::enableGetFortuneButton); this, &Client::enableGetFortuneButton);

View File

@ -4,15 +4,12 @@
#ifndef CLIENT_H #ifndef CLIENT_H
#define CLIENT_H #define CLIENT_H
#include <QDialog>
#include <QDataStream> #include <QDataStream>
#include <QDialog>
#include <QLabel>
#include <QLineEdit>
#include <QLocalSocket> #include <QLocalSocket>
#include <QPushButton>
QT_BEGIN_NAMESPACE
class QLabel;
class QLineEdit;
class QPushButton;
QT_END_NAMESPACE
class Client : public QDialog class Client : public QDialog
{ {

View File

@ -1,10 +1,10 @@
// Copyright (C) 2016 The Qt Company Ltd. // Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#include <QApplication>
#include "client.h" #include "client.h"
#include <QApplication>
int main(int argc, char *argv[]) int main(int argc, char *argv[])
{ {
QApplication app(argc, argv); QApplication app(argc, argv);

View File

@ -1,10 +1,10 @@
// Copyright (C) 2016 The Qt Company Ltd. // Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#include <QApplication>
#include "server.h" #include "server.h"
#include <QApplication>
int main(int argc, char *argv[]) int main(int argc, char *argv[])
{ {
QApplication app(argc, argv); QApplication app(argc, argv);

View File

@ -3,27 +3,33 @@
#include "server.h" #include "server.h"
#include <QtWidgets> #include <QDialogButtonBox>
#include <QtNetwork> #include <QGuiApplication>
#include <QHBoxLayout>
#include <QLabel>
#include <QLineEdit>
#include <QLocalSocket>
#include <QMessageBox>
#include <QPushButton>
#include <QRandomGenerator>
using namespace Qt::StringLiterals;
static const QString idleStateText = QObject::tr("Press \"Listen\" to start the server");
Server::Server(QWidget *parent) Server::Server(QWidget *parent)
: QDialog(parent) : QDialog(parent),
server(new QLocalServer(this)),
hostLineEdit(new QLineEdit(u"fortune"_s)),
statusLabel(new QLabel(idleStateText)),
listenButton(new QPushButton(tr("Listen"))),
stopListeningButton(new QPushButton(tr("Stop Listening")))
{ {
setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint); setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
server = new QLocalServer(this);
if (!server->listen("fortune")) {
QMessageBox::critical(this, tr("Local Fortune Server"),
tr("Unable to start the server: %1.")
.arg(server->errorString()));
close();
return;
}
QLabel *statusLabel = new QLabel;
statusLabel->setWordWrap(true); statusLabel->setWordWrap(true);
statusLabel->setText(tr("The server is running.\n"
"Run the Local Fortune Client example now.")); stopListeningButton->setDisabled(true);
fortunes << tr("You've been leading a dog's life. Stay off the furniture.") fortunes << tr("You've been leading a dog's life. Stay off the furniture.")
<< tr("You've got to think about tomorrow.") << tr("You've got to think about tomorrow.")
@ -33,28 +39,71 @@ Server::Server(QWidget *parent)
<< tr("You cannot kill time without injuring eternity.") << tr("You cannot kill time without injuring eternity.")
<< tr("Computers are not intelligent. They only think they are."); << tr("Computers are not intelligent. They only think they are.");
QLabel *hostLabel = new QLabel(tr("Server name:"));
connect(server, &QLocalServer::newConnection, this, &Server::sendFortune);
connect(hostLineEdit, &QLineEdit::textChanged, this, &Server::toggleListenButton);
connect(listenButton, &QPushButton::clicked, this, &Server::listenToServer);
connect(stopListeningButton, &QPushButton::clicked,this, &Server::stopListening);
QPushButton *quitButton = new QPushButton(tr("Quit")); QPushButton *quitButton = new QPushButton(tr("Quit"));
quitButton->setAutoDefault(false); quitButton->setAutoDefault(false);
connect(quitButton, &QPushButton::clicked, this, &Server::close); connect(quitButton, &QPushButton::clicked, this, &Server::close);
connect(server, &QLocalServer::newConnection, this, &Server::sendFortune);
QHBoxLayout *buttonLayout = new QHBoxLayout; QDialogButtonBox *buttonBox = new QDialogButtonBox;
buttonLayout->addStretch(1); buttonBox->addButton(listenButton, QDialogButtonBox::ActionRole);
buttonLayout->addWidget(quitButton); buttonBox->addButton(stopListeningButton, QDialogButtonBox::ActionRole);
buttonLayout->addStretch(1); buttonBox->addButton(quitButton, QDialogButtonBox::RejectRole);
QVBoxLayout *mainLayout = new QVBoxLayout(this); QGridLayout *mainLayout = new QGridLayout(this);
mainLayout->addWidget(statusLabel); mainLayout->addWidget(hostLabel, 0, 0);
mainLayout->addLayout(buttonLayout); mainLayout->addWidget(hostLineEdit, 0, 1);
mainLayout->addWidget(statusLabel, 2, 0, 3, 2);
mainLayout->addWidget(buttonBox, 10, 0, 2, 2);
setWindowTitle(QGuiApplication::applicationDisplayName()); setWindowTitle(QGuiApplication::applicationDisplayName());
hostLineEdit->setFocus();
}
void Server::listenToServer()
{
name = hostLineEdit->text();
if (!server->listen(name)) {
QMessageBox::critical(this, tr("Local Fortune Server"),
tr("Unable to start the server: %1.")
.arg(server->errorString()));
name.clear();
return;
}
statusLabel->setText(tr("The server is running.\n"
"Run the Local Fortune Client example now."));
toggleListenButton();
}
void Server::stopListening()
{
server->close();
name.clear();
statusLabel->setText(idleStateText);
toggleListenButton();
}
void Server::toggleListenButton()
{
if (server->isListening()) {
listenButton->setDisabled(true);
stopListeningButton->setEnabled(true);
} else {
listenButton->setEnabled(!hostLineEdit->text().isEmpty());
stopListeningButton->setDisabled(true);
}
} }
void Server::sendFortune() void Server::sendFortune()
{ {
QByteArray block; QByteArray block;
QDataStream out(&block, QIODevice::WriteOnly); QDataStream out(&block, QIODevice::WriteOnly);
out.setVersion(QDataStream::Qt_5_10); out.setVersion(QDataStream::Qt_6_0);
const int fortuneIndex = QRandomGenerator::global()->bounded(0, fortunes.size()); const int fortuneIndex = QRandomGenerator::global()->bounded(0, fortunes.size());
const QString &message = fortunes.at(fortuneIndex); const QString &message = fortunes.at(fortuneIndex);
out << quint32(message.size()); out << quint32(message.size());

View File

@ -4,27 +4,33 @@
#ifndef SERVER_H #ifndef SERVER_H
#define SERVER_H #define SERVER_H
#include <QApplication>
#include <QDialog> #include <QDialog>
#include <QLabel>
QT_BEGIN_NAMESPACE #include <QLineEdit>
class QLabel; #include <QLocalServer>
class QPushButton; #include <QPushButton>
class QLocalServer;
QT_END_NAMESPACE
class Server : public QDialog class Server : public QDialog
{ {
Q_OBJECT Q_DECLARE_TR_FUNCTIONS(Server)
public: public:
explicit Server(QWidget *parent = nullptr); explicit Server(QWidget *parent = nullptr);
private slots:
void sendFortune();
private: private:
void sendFortune();
void toggleListenButton();
void listenToServer();
void stopListening();
QLocalServer *server; QLocalServer *server;
QLineEdit *hostLineEdit;
QLabel *statusLabel;
QPushButton *listenButton;
QPushButton *stopListeningButton;
QStringList fortunes; QStringList fortunes;
QString name;
}; };
#endif #endif

View File

@ -2,8 +2,12 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#include "dialog.h" #include "dialog.h"
#include <QFileDialog>
#include <QBuffer> #include <QBuffer>
#include <QFileDialog>
#include <QNativeIpcKey>
using namespace Qt::StringLiterals;
/*! /*!
\class Dialog \class Dialog
@ -29,8 +33,9 @@
each button. each button.
*/ */
//! [0] //! [0]
Dialog::Dialog(QWidget *parent) Dialog::Dialog(QWidget *parent)
: QDialog(parent), sharedMemory("QSharedMemoryExample") : QDialog(parent), sharedMemory(QNativeIpcKey(u"QSharedMemoryExample"_s))
{ {
ui.setupUi(this); ui.setupUi(this);
connect(ui.loadFromFileButton, &QPushButton::clicked, connect(ui.loadFromFileButton, &QPushButton::clicked,

View File

@ -6,6 +6,7 @@
#include <QDialog> #include <QDialog>
#include <QSharedMemory> #include <QSharedMemory>
#include "ui_dialog.h" #include "ui_dialog.h"
//! [0] //! [0]
@ -13,21 +14,21 @@ class Dialog : public QDialog
{ {
Q_OBJECT Q_OBJECT
public: public:
Dialog(QWidget *parent = nullptr); Dialog(QWidget *parent = nullptr);
public slots: public slots:
void loadFromFile(); void loadFromFile();
void loadFromMemory(); void loadFromMemory();
private: private:
void detach(); void detach();
private: private:
Ui::Dialog ui; Ui::Dialog ui;
QSharedMemory sharedMemory; QSharedMemory sharedMemory;
}; };
//! [0] //! [0]
#endif #endif // DIALOG_H

View File

@ -1,9 +1,10 @@
// Copyright (C) 2016 The Qt Company Ltd. // Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#include <QApplication>
#include "dialog.h" #include "dialog.h"
#include <QApplication>
//! [0] //! [0]
int main(int argc, char *argv[]) int main(int argc, char *argv[])
{ {

View File

@ -3,8 +3,9 @@
/*! /*!
\example mimetypes/mimetypebrowser \example mimetypes/mimetypebrowser
\examplecategory {Data Processing & I/O}
\ingroup examples-mimetype \ingroup examples-mimetype
\title MIME Type Browser Example \title MIME Type Browser
\brief Shows the hierarchy of MIME types and \brief Shows the hierarchy of MIME types and
can be used to determine the MIME type of a file. can be used to determine the MIME type of a file.

View File

@ -4,10 +4,8 @@
#include "mainwindow.h" #include "mainwindow.h"
#include <QApplication> #include <QApplication>
#include <QScreen>
#include <QCommandLineParser> #include <QCommandLineParser>
#include <QCommandLineOption> #include <QScreen>
int main(int argc, char *argv[]) int main(int argc, char *argv[])
{ {

View File

@ -4,23 +4,18 @@
#include "mainwindow.h" #include "mainwindow.h"
#include "mimetypemodel.h" #include "mimetypemodel.h"
#include <QAction>
#include <QApplication> #include <QApplication>
#include <QFileDialog> #include <QFileDialog>
#include <QFileInfo>
#include <QInputDialog> #include <QInputDialog>
#include <QItemSelectionModel>
#include <QMenu> #include <QMenu>
#include <QMenuBar> #include <QMenuBar>
#include <QMessageBox> #include <QMessageBox>
#include <QPlainTextEdit>
#include <QSplitter>
#include <QStatusBar>
#include <QTextEdit>
#include <QTreeView>
#include <QFileInfo>
#include <QItemSelectionModel>
#include <QMimeDatabase> #include <QMimeDatabase>
#include <QMimeType> #include <QMimeType>
#include <QSplitter>
#include <QStatusBar>
MainWindow::MainWindow(QWidget *parent) MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent) : QMainWindow(parent)
@ -45,7 +40,8 @@ MainWindow::MainWindow(QWidget *parent)
findAction->setShortcuts(QKeySequence::Find); findAction->setShortcuts(QKeySequence::Find);
m_findNextAction = findMenu->addAction(tr("Find &Next"), this, &MainWindow::findNext); m_findNextAction = findMenu->addAction(tr("Find &Next"), this, &MainWindow::findNext);
m_findNextAction->setShortcuts(QKeySequence::FindNext); m_findNextAction->setShortcuts(QKeySequence::FindNext);
m_findPreviousAction = findMenu->addAction(tr("Find &Previous"), this, &MainWindow::findPrevious); m_findPreviousAction = findMenu->addAction(tr("Find &Previous"), this,
&MainWindow::findPrevious);
m_findPreviousAction->setShortcuts(QKeySequence::FindPrevious); m_findPreviousAction->setShortcuts(QKeySequence::FindPrevious);
menuBar()->addMenu(tr("&About"))->addAction(tr("&About Qt"), qApp, &QApplication::aboutQt); menuBar()->addMenu(tr("&About"))->addAction(tr("&About Qt"), qApp, &QApplication::aboutQt);
@ -54,8 +50,8 @@ MainWindow::MainWindow(QWidget *parent)
setCentralWidget(centralSplitter); setCentralWidget(centralSplitter);
m_treeView->setUniformRowHeights(true); m_treeView->setUniformRowHeights(true);
m_treeView->setModel(m_model); m_treeView->setModel(m_model);
const auto flags = Qt::MatchContains | Qt::MatchFixedString | Qt::MatchRecursive;
const auto items = m_model->findItems("application/octet-stream", Qt::MatchContains | Qt::MatchFixedString | Qt::MatchRecursive); const auto items = m_model->findItems("application/octet-stream", flags);
if (!items.isEmpty()) if (!items.isEmpty())
m_treeView->expand(m_model->indexFromItem(items.constFirst())); m_treeView->expand(m_model->indexFromItem(items.constFirst()));
@ -93,7 +89,8 @@ void MainWindow::detectFile()
const QModelIndex index = mimeType.isValid() const QModelIndex index = mimeType.isValid()
? m_model->indexForMimeType(mimeType.name()) : QModelIndex(); ? m_model->indexForMimeType(mimeType.name()) : QModelIndex();
if (index.isValid()) { if (index.isValid()) {
statusBar()->showMessage(tr("\"%1\" is of type \"%2\"").arg(fi.fileName(), mimeType.name())); statusBar()->showMessage(tr("\"%1\" is of type \"%2\"").arg(fi.fileName(),
mimeType.name()));
selectAndGoTo(index); selectAndGoTo(index);
} else { } else {
QMessageBox::information(this, tr("Unknown File Type"), QMessageBox::information(this, tr("Unknown File Type"),
@ -138,8 +135,8 @@ void MainWindow::find()
m_findMatches.clear(); m_findMatches.clear();
m_findIndex = 0; m_findIndex = 0;
const QList<QStandardItem *> items = const auto flags = Qt::MatchContains | Qt::MatchFixedString | Qt::MatchRecursive;
m_model->findItems(value, Qt::MatchContains | Qt::MatchFixedString | Qt::MatchRecursive); const QList<QStandardItem *> items = m_model->findItems(value, flags);
for (const QStandardItem *item : items) for (const QStandardItem *item : items)
m_findMatches.append(m_model->indexFromItem(item)); m_findMatches.append(m_model->indexFromItem(item));
statusBar()->showMessage(tr("%n mime types match \"%1\".", 0, m_findMatches.size()).arg(value)); statusBar()->showMessage(tr("%n mime types match \"%1\".", 0, m_findMatches.size()).arg(value));

View File

@ -4,12 +4,11 @@
#ifndef MAINWINDOW_H #ifndef MAINWINDOW_H
#define MAINWINDOW_H #define MAINWINDOW_H
#include <QAction>
#include <QMainWindow> #include <QMainWindow>
#include <QModelIndexList> #include <QModelIndexList>
#include <QTextEdit>
QT_FORWARD_DECLARE_CLASS(QAction) #include <QTreeView>
QT_FORWARD_DECLARE_CLASS(QTextEdit)
QT_FORWARD_DECLARE_CLASS(QTreeView)
class MimetypeModel; class MimetypeModel;

View File

@ -4,14 +4,14 @@
#ifndef MIMETYPEMODEL_H #ifndef MIMETYPEMODEL_H
#define MIMETYPEMODEL_H #define MIMETYPEMODEL_H
#include <QStandardItemModel> #include <QCoreApplication>
#include <QHash> #include <QHash>
#include <QMimeType>
QT_FORWARD_DECLARE_CLASS(QMimeType) #include <QStandardItemModel>
class MimetypeModel : public QStandardItemModel class MimetypeModel : public QStandardItemModel
{ {
Q_OBJECT Q_DECLARE_TR_FUNCTIONS(MimetypeModel)
public: public:
enum Columns { NameColumn, ColumnCount }; enum Columns { NameColumn, ColumnCount };

View File

@ -5,6 +5,7 @@
\title Qt Android Notifier \title Qt Android Notifier
\example platform/androidnotifier \example platform/androidnotifier
\examplecategory {Mobile} \examplecategory {Mobile}
\brief Demonstrates calling Java code from Qt in an Android application. \brief Demonstrates calling Java code from Qt in an Android application.
\ingroup androidplatform \ingroup androidplatform

View File

@ -20,13 +20,14 @@ qt_standard_project_setup()
qt_add_executable(convert qt_add_executable(convert
cborconverter.cpp cborconverter.h cborconverter.cpp cborconverter.h
converter.h converter.cpp converter.h
datastreamconverter.cpp datastreamconverter.h datastreamconverter.cpp datastreamconverter.h
debugtextdumper.cpp debugtextdumper.h debugtextdumper.cpp debugtextdumper.h
jsonconverter.cpp jsonconverter.h jsonconverter.cpp jsonconverter.h
main.cpp main.cpp
nullconverter.cpp nullconverter.h nullconverter.cpp nullconverter.h
textconverter.cpp textconverter.h textconverter.cpp textconverter.h
variantorderedmap.h
xmlconverter.cpp xmlconverter.h xmlconverter.cpp xmlconverter.h
) )

View File

@ -2,6 +2,7 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#include "cborconverter.h" #include "cborconverter.h"
#include "variantorderedmap.h"
#include <QCborArray> #include <QCborArray>
#include <QCborMap> #include <QCborMap>
@ -9,6 +10,7 @@
#include <QCborStreamWriter> #include <QCborStreamWriter>
#include <QCborValue> #include <QCborValue>
#include <QDataStream> #include <QDataStream>
#include <QDebug>
#include <QFile> #include <QFile>
#include <QFloat16> #include <QFloat16>
#include <QMetaType> #include <QMetaType>
@ -57,9 +59,9 @@ QT_END_NAMESPACE
// non-string keys in CBOR maps (QVariantMap can't handle those). Instead, we // non-string keys in CBOR maps (QVariantMap can't handle those). Instead, we
// have our own set of converter functions so we can keep the keys properly. // have our own set of converter functions so we can keep the keys properly.
//! [0]
static QVariant convertCborValue(const QCborValue &value); static QVariant convertCborValue(const QCborValue &value);
//! [0]
static QVariant convertCborMap(const QCborMap &map) static QVariant convertCborMap(const QCborMap &map)
{ {
VariantOrderedMap result; VariantOrderedMap result;
@ -87,8 +89,9 @@ static QVariant convertCborValue(const QCborValue &value)
return value.toVariant(); return value.toVariant();
} }
//! [0] //! [0]
enum TrimFloatingPoint { Double, Float, Float16 };
//! [1] //! [1]
enum TrimFloatingPoint { Double, Float, Float16 };
static QCborValue convertFromVariant(const QVariant &v, TrimFloatingPoint fpTrimming) static QCborValue convertFromVariant(const QVariant &v, TrimFloatingPoint fpTrimming)
{ {
if (v.userType() == QMetaType::QVariantList) { if (v.userType() == QMetaType::QVariantList) {
@ -140,20 +143,6 @@ const char *CborDiagnosticDumper::optionsHelp() const
return diagnosticHelp; return diagnosticHelp;
} }
bool CborDiagnosticDumper::probeFile(QIODevice *f) const
{
Q_UNUSED(f);
return false;
}
QVariant CborDiagnosticDumper::loadFile(QIODevice *f, const Converter *&outputConverter) const
{
Q_UNREACHABLE();
Q_UNUSED(f);
Q_UNUSED(outputConverter);
return QVariant();
}
void CborDiagnosticDumper::saveFile(QIODevice *f, const QVariant &contents, void CborDiagnosticDumper::saveFile(QIODevice *f, const QVariant &contents,
const QStringList &options) const const QStringList &options) const
{ {
@ -178,9 +167,8 @@ void CborDiagnosticDumper::saveFile(QIODevice *f, const QVariant &contents,
} }
} }
fprintf(stderr, "Unknown CBOR diagnostic option '%s'. Available options are:\n%s", qFatal("Unknown CBOR diagnostic option '%s'. Available options are:\n%s",
qPrintable(s), diagnosticHelp); qPrintable(s), diagnosticHelp);
exit(EXIT_FAILURE);
} }
QTextStream out(f); QTextStream out(f);
@ -221,7 +209,6 @@ bool CborConverter::probeFile(QIODevice *f) const
return f->isReadable() && f->peek(3) == QByteArray("\xd9\xd9\xf7", 3); return f->isReadable() && f->peek(3) == QByteArray("\xd9\xd9\xf7", 3);
} }
//! [2]
QVariant CborConverter::loadFile(QIODevice *f, const Converter *&outputConverter) const QVariant CborConverter::loadFile(QIODevice *f, const Converter *&outputConverter) const
{ {
const char *ptr = nullptr; const char *ptr = nullptr;
@ -239,28 +226,25 @@ QVariant CborConverter::loadFile(QIODevice *f, const Converter *&outputConverter
QCborValue contents = QCborValue::fromCbor(reader); QCborValue contents = QCborValue::fromCbor(reader);
qint64 offset = reader.currentOffset(); qint64 offset = reader.currentOffset();
if (reader.lastError()) { if (reader.lastError()) {
fprintf(stderr, "Error loading CBOR contents (byte %lld): %s\n", offset, qFatal().nospace()
qPrintable(reader.lastError().toString())); << "Error loading CBOR contents (byte " << offset
fprintf(stderr, " bytes: %s\n", << "): " << reader.lastError().toString()
(ptr ? mapped.mid(offset, 9) : f->read(9)).toHex(' ').constData()); << "\n bytes: " << (ptr ? mapped.mid(offset, 9) : f->read(9));
exit(EXIT_FAILURE);
} else if (offset < mapped.size() || (!ptr && f->bytesAvailable())) { } else if (offset < mapped.size() || (!ptr && f->bytesAvailable())) {
fprintf(stderr, "Warning: bytes remaining at the end of the CBOR stream\n"); qWarning("Warning: bytes remaining at the end of the CBOR stream");
} }
if (outputConverter == nullptr) if (outputConverter == nullptr)
outputConverter = &cborDiagnosticDumper; outputConverter = &cborDiagnosticDumper;
else if (outputConverter == null) else if (isNull(outputConverter))
return QVariant(); return QVariant();
else if (!outputConverter->outputOptions().testFlag(SupportsArbitraryMapKeys)) else if (!outputConverter->outputOptions().testFlag(SupportsArbitraryMapKeys))
return contents.toVariant(); return contents.toVariant();
return convertCborValue(contents); return convertCborValue(contents);
} }
//! [2]
//! [3]
void CborConverter::saveFile(QIODevice *f, const QVariant &contents, const QStringList &options) const void CborConverter::saveFile(QIODevice *f, const QVariant &contents, const QStringList &options) const
{ {
//! [3]
bool useSignature = true; bool useSignature = true;
bool useIntegers = true; bool useIntegers = true;
enum { Yes, No, Always } useFloat16 = Yes, useFloat = Yes; enum { Yes, No, Always } useFloat16 = Yes, useFloat = Yes;
@ -315,11 +299,10 @@ void CborConverter::saveFile(QIODevice *f, const QVariant &contents, const QStri
} }
} }
fprintf(stderr, "Unknown CBOR format option '%s'. Valid options are:\n%s", qFatal("Unknown CBOR format option '%s'. Valid options are:\n%s",
qPrintable(s), cborOptionHelp); qPrintable(s), cborOptionHelp);
exit(EXIT_FAILURE);
} }
//! [4]
QCborValue v = QCborValue v =
convertFromVariant(contents, convertFromVariant(contents,
useFloat16 == Always ? Float16 : useFloat == Always ? Float : Double); useFloat16 == Always ? Float16 : useFloat == Always ? Float : Double);
@ -336,4 +319,3 @@ void CborConverter::saveFile(QIODevice *f, const QVariant &contents, const QStri
opts |= QCborValue::UseFloat16; opts |= QCborValue::UseFloat16;
v.toCbor(writer, opts); v.toCbor(writer, opts);
} }
//! [4]

View File

@ -14,8 +14,6 @@ public:
Directions directions() const override; Directions directions() const override;
Options outputOptions() const override; Options outputOptions() const override;
const char *optionsHelp() const override; const char *optionsHelp() const override;
bool probeFile(QIODevice *f) const override;
QVariant loadFile(QIODevice *f, const Converter *&outputConverter) const override;
void saveFile(QIODevice *f, const QVariant &contents, void saveFile(QIODevice *f, const QVariant &contents,
const QStringList &options) const override; const QStringList &options) const override;
}; };

View File

@ -11,6 +11,7 @@ target.path = $$[QT_INSTALL_EXAMPLES]/corelib/serialization/convert
INSTALLS += target INSTALLS += target
SOURCES += main.cpp \ SOURCES += main.cpp \
converter.cpp \
cborconverter.cpp \ cborconverter.cpp \
datastreamconverter.cpp \ datastreamconverter.cpp \
debugtextdumper.cpp \ debugtextdumper.cpp \
@ -27,4 +28,5 @@ HEADERS += \
jsonconverter.h \ jsonconverter.h \
nullconverter.h \ nullconverter.h \
textconverter.h \ textconverter.h \
variantorderedmap.h \
xmlconverter.h xmlconverter.h

View File

@ -0,0 +1,44 @@
// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#include "converter.h"
//! [0]
Converter::Converter()
{
converters().append(this);
}
Converter::~Converter()
{
converters().removeAll(this);
}
QList<const Converter *> &Converter::converters()
{
Q_CONSTINIT static QList<const Converter *> store;
return store;
}
const QList<const Converter *> &Converter::allConverters()
{
return converters();
}
//! [0]
// Some virtual methods that Converter classes needn't override, when not relevant:
Converter::Options Converter::outputOptions() const { return {}; }
const char *Converter::optionsHelp() const { return nullptr; }
bool Converter::probeFile(QIODevice *) const { return false; }
// The virtual method they should override if they claim to support In:
QVariant Converter::loadFile(QIODevice *, const Converter *&outputConverter) const
{
Q_ASSERT(!directions().testFlag(Converter::Direction::In));
// For those that don't, this should never be called.
Q_UNIMPLEMENTED();
// But every implementation should at least do this:
if (!outputConverter)
outputConverter = this;
return QVariant();
}

View File

@ -6,31 +6,19 @@
#include <QIODevice> #include <QIODevice>
#include <QList> #include <QList>
#include <QPair> #include <QStringList>
#include <QVariant> #include <QVariant>
#include <QVariantMap>
class VariantOrderedMap : public QList<QPair<QVariant, QVariant>>
{
public:
VariantOrderedMap() = default;
VariantOrderedMap(const QVariantMap &map)
{
reserve(map.size());
for (auto it = map.begin(); it != map.end(); ++it)
append({it.key(), it.value()});
}
};
using Map = VariantOrderedMap;
Q_DECLARE_METATYPE(Map)
//! [0]
class Converter class Converter
{ {
static QList<const Converter *> &converters();
protected: protected:
Converter(); Converter();
static bool isNull(const Converter *converter); // in nullconverter.cpp
public: public:
static Converter *null; static const QList<const Converter *> &allConverters();
enum class Direction { In = 1, Out = 2, InOut = In | Out }; enum class Direction { In = 1, Out = 2, InOut = In | Out };
Q_DECLARE_FLAGS(Directions, Direction) Q_DECLARE_FLAGS(Directions, Direction)
@ -42,15 +30,16 @@ public:
virtual QString name() const = 0; virtual QString name() const = 0;
virtual Directions directions() const = 0; virtual Directions directions() const = 0;
virtual Options outputOptions() const = 0; virtual Options outputOptions() const;
virtual const char *optionsHelp() const = 0; virtual const char *optionsHelp() const;
virtual bool probeFile(QIODevice *f) const = 0; virtual bool probeFile(QIODevice *f) const;
virtual QVariant loadFile(QIODevice *f, const Converter *&outputConverter) const = 0; virtual QVariant loadFile(QIODevice *f, const Converter *&outputConverter) const;
virtual void saveFile(QIODevice *f, const QVariant &contents, virtual void saveFile(QIODevice *f, const QVariant &contents,
const QStringList &options) const = 0; const QStringList &options) const = 0;
}; };
Q_DECLARE_OPERATORS_FOR_FLAGS(Converter::Directions) Q_DECLARE_OPERATORS_FOR_FLAGS(Converter::Directions)
Q_DECLARE_OPERATORS_FOR_FLAGS(Converter::Options) Q_DECLARE_OPERATORS_FOR_FLAGS(Converter::Options)
//! [0]
#endif // CONVERTER_H #endif // CONVERTER_H

View File

@ -3,6 +3,7 @@
#include "datastreamconverter.h" #include "datastreamconverter.h"
#include "debugtextdumper.h" #include "debugtextdumper.h"
#include "variantorderedmap.h"
#include <QDataStream> #include <QDataStream>
@ -79,10 +80,8 @@ QVariant DataStreamConverter::loadFile(QIODevice *f, const Converter *&outputCon
outputConverter = &debugTextDumper; outputConverter = &debugTextDumper;
char c; char c;
if (f->read(sizeof(signature) - 1) != signature || !f->getChar(&c) || (c != 'l' && c != 'B')) { if (f->read(sizeof(signature) - 1) != signature || !f->getChar(&c) || (c != 'l' && c != 'B'))
fprintf(stderr, "Could not load QDataStream file: invalid signature.\n"); qFatal("Could not load QDataStream file: invalid signature.");
exit(EXIT_FAILURE);
}
QDataStream ds(f); QDataStream ds(f);
ds.setByteOrder(c == 'l' ? QDataStream::LittleEndian : QDataStream::BigEndian); ds.setByteOrder(c == 'l' ? QDataStream::LittleEndian : QDataStream::BigEndian);
@ -124,15 +123,13 @@ void DataStreamConverter::saveFile(QIODevice *f, const QVariant &contents,
continue; continue;
} }
fprintf(stderr, "Invalid version number '%s': must be a number from 1 to %d.\n", qFatal("Invalid version number '%s': must be a number from 1 to %d.",
qPrintable(pair.last()), QDataStream::Qt_DefaultCompiledVersion); qPrintable(pair.last()), QDataStream::Qt_DefaultCompiledVersion);
exit(EXIT_FAILURE);
} }
} }
fprintf(stderr, "Unknown QDataStream formatting option '%s'. Available options are:\n%s", qFatal("Unknown QDataStream formatting option '%s'. Available options are:\n%s",
qPrintable(option), dataStreamOptionHelp); qPrintable(option), dataStreamOptionHelp);
exit(EXIT_FAILURE);
} }
char c = order == QDataStream::LittleEndian ? 'l' : 'B'; char c = order == QDataStream::LittleEndian ? 'l' : 'B';

View File

@ -2,6 +2,7 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#include "debugtextdumper.h" #include "debugtextdumper.h"
#include "variantorderedmap.h"
#include <QDebug> #include <QDebug>
#include <QTextStream> #include <QTextStream>
@ -58,29 +59,13 @@ Converter::Options DebugTextDumper::outputOptions() const
return SupportsArbitraryMapKeys; return SupportsArbitraryMapKeys;
} }
const char *DebugTextDumper::optionsHelp() const
{
return nullptr;
}
bool DebugTextDumper::probeFile(QIODevice *f) const
{
Q_UNUSED(f);
return false;
}
QVariant DebugTextDumper::loadFile(QIODevice *f, const Converter *&outputConverter) const
{
Q_UNREACHABLE();
Q_UNUSED(f);
Q_UNUSED(outputConverter);
return QVariant();
}
void DebugTextDumper::saveFile(QIODevice *f, const QVariant &contents, void DebugTextDumper::saveFile(QIODevice *f, const QVariant &contents,
const QStringList &options) const const QStringList &options) const
{ {
Q_UNUSED(options); if (!options.isEmpty()) {
qFatal("Unknown option '%s' to debug text output. This format has no options.",
qPrintable(options.first()));
}
QString s = dumpVariant(contents); QString s = dumpVariant(contents);
s[s.size() - 1] = u'\n'; // replace the comma with newline s[s.size() - 1] = u'\n'; // replace the comma with newline

View File

@ -13,9 +13,6 @@ public:
QString name() const override; QString name() const override;
Directions directions() const override; Directions directions() const override;
Options outputOptions() const override; Options outputOptions() const override;
const char *optionsHelp() const override;
bool probeFile(QIODevice *f) const override;
QVariant loadFile(QIODevice *f, const Converter *&outputConverter) const override;
void saveFile(QIODevice *f, const QVariant &contents, void saveFile(QIODevice *f, const QVariant &contents,
const QStringList &options) const override; const QStringList &options) const override;
}; };

Binary file not shown.

Before

Width:  |  Height:  |  Size: 48 KiB

After

Width:  |  Height:  |  Size: 7.5 KiB

View File

@ -5,78 +5,152 @@
\example serialization/convert \example serialization/convert
\examplecategory {Data Processing & I/O} \examplecategory {Data Processing & I/O}
\meta tag {network} \meta tag {network}
\title Convert Example \title Serialization Converter
\brief The Convert example demonstrates how to convert between different \brief How to convert between different serialization formats.
serialization formats.
The Convert example converts between the serialization formats JSON, CBOR, This example converts between JSON, CBOR, XML, QDataStream and some simple
XML, QDataStream and text. It can also auto detect the format being used. text formats. It can auto-detect the format being used, or be told which
Not all formats support both input and output, and they have different format to use. Not all formats support both input and output, and they have
sets of which types they support. QDataStream and XML are the richest, different sets of which content datatypes they support. QDataStream and XML
followed by CBOR, then JSON, and then the plain text one. are the richest, followed by CBOR, then JSON, and then the plain text
formats. Conversion via the less capable formats is apt to lose structure
from the data.
\image convert.png \image convert.png
\sa {Parsing and displaying CBOR data}, {JSON Save Game}
\section1 The Converter Class \section1 The Converter Class
The Converter class is the abstract superclass for all the converters to The Converter class is the abstract superclass for all the converters to and
and from all the formats. They all convert to and from the QVariant class, from all the formats. They all convert from or to the QVariant class, which
which is used to represent all the datastructures internally. is used to represent all the datastructures internally.
\snippet serialization/convert/converter.h 0
The Converter constructor and destructor manage a list of available
converters used by the main program so that it knows what converters are
available. Each converter type defines a static instance that ensures it is
constructed and thus available to the main program via this list. The \c
allConverters() method provides \c main()'s code with access to the list.
\snippet serialization/convert/converter.cpp 0
The name() function returns the name of the converter. The directions() The name() function returns the name of the converter. The directions()
function is used to determine if a converter can be used for input, output, function is used to determine if a converter can be used for input, output,
or both. The outputOptions() and optionsHelp() functions are used to get or both. These enable the main program to report what converters are
and query which options are used by the different converters. The available in its help text for the command-line options to select input and
probeFile() function is used to determine if a file has the same file output formats.
format as the converter. The loadFile() function deserializes the given
file, while the saveFile() serializes to the given file.
\section1 The CborConverter Class \snippet serialization/convert/main.cpp 0
The optionsHelp() function is used to report the various command-line
options supported by the available formats, when queried using its \c
{--format-options <format>} command-line option.
\snippet serialization/convert/main.cpp 1
The outputOptions() function reports the output capabilities of a converter.
At present the only optional feature is support for arbitrary keys in
mappings from keys to values. An input converter's loadFile() can use this
information to tailor the form in which it presents the data it has read, to
be as faithfully represented by the output converter as its capabilities
permit.
The probeFile() function is used to determine if a file matches the format
of the converter. The main program uses this to determine what format to use
when reading or writing a file, based on its name and potentially content,
when the user has not specified the format to use on the command-line.
The loadFile() function deserializes data. The caller tells loadFile() which
serializer it intends to use, so that loadFile() can query its
outputOptions() to determine the form in which to represent the loaded data.
If the caller hasn't settled on a choice of output converter, loadFile()
supplies it with a default output converter suitable to the data it is
returning.
The saveFile() function serializes data. It is passed options from the
command-line, as described by loadHelp(), that can tune the details of how
it represents the data when saving to file.
Both loadFile() and saveFile() can be used with an arbitrary \l QIODevice.
This means that a Converter could also be used with a network socket or
other source of data, to read from or write to. In the present program, the
main program always passes a \l QFile, accessing either a file on disk or
one of the standard streams of the process.
\section2 The Available Converters
Several converters are supported, illustrating how the converter program
could be adapted to other formats, should the need arise. See the source
code for each for its details. The CBOR converters serve as a relatively
full-featured illustration of the ways converters can work, that we'll look
into in more detail below. This table summarizes the available converters:
\table
\header \li Class \li mode \li format
\row \li CborConverter \li In/Out \li CBOR
\row \li CborDiagnosticDumper \li Out \li CBOR diagnostic
\row \li DataStreamConverter \li In/Out \li QDataStream
\row \li DebugTextDumper \li Out \li Lossless, non-standard, human-readable
\row \li JsonConverter \li In/Out \li JSON
\row \li NullConverter \li Out \li No output
\row \li TextConverter \li In/Out \li Structured plain text
\row \li XmlConverter \li In/Out \li XML
\endtable
Those that support input use themselves as loadFile()'s fallback converter,
except for the CBOR and QDataStream converters, which use their respective
output-only dumper companion classes. The null converter can be used as
output converter when running the program for the sake of any validation or
verification that an input converter may perform.
\section2 The CborConverter and CborDiagnosticDumper Classes
The CborConverter class supports serializing to and from the CBOR format.
It supports various options to configure the output of floating point values
and a \c{signature} option to determine whether to start its output with a
CBOR tag that serves as a file header, identifying the file as containing
CBOR data.
The CborConverter class shows how to serialize to and from the CBOR-format.
There is also a CborDiagnosticDumper class to output in CBOR diagnostic There is also a CborDiagnosticDumper class to output in CBOR diagnostic
notation. That is similar to JSON, but not exactly, because it allows notation. It does not support loading data. The form of its output can be
displaying the contents of a CBOR stream losslessly, while a conversion configured using two options. One selects whether to use the (more verbose)
to JSON is lossy. extended CBOR diagnostic format. The other control whether each CBOR value
appears on a separate line.
The plain diagnostic notation is similar to JSON, but not exactly, because
it supports displaying the contents of a CBOR stream losslessly, while a
conversion to JSON can be lossy. CborConverter's loadFile() uses
CborDiagnosticDumper for the fallback output converter, if its caller hasn't
determined the output format for itself.
The convertCborValue(), convertCborMap() and convertCborArray() helper
functions are used to convert a QCborValue to a QVariant, for the benefit of
CborConverter::loadFile().
The convertCborValue() function is used to convert a QCborValue to a
QVariant. It uses the helper functions convertCborMap() and
convertCborArray().
\snippet serialization/convert/cborconverter.cpp 0 \snippet serialization/convert/cborconverter.cpp 0
A CBOR-file is read using loadFile() function.
\snippet serialization/convert/cborconverter.cpp 2
The convertFromVariant() function is used to convert a QVariant to a The convertFromVariant() function is used to convert a QVariant to a
QCborValue. QCborValue for output by the \c saveFile() of either class.
\snippet serialization/convert/cborconverter.cpp 1
A CBOR-file is written using the saveFile() function. \snippet serialization/convert/cborconverter.cpp 1
\snippet serialization/convert/cborconverter.cpp 3
\snippet serialization/convert/cborconverter.cpp 4
\sa {CBOR Support in Qt} \sa {CBOR Support in Qt}
\section1 The DataStreamConverter Class \section1 The convert program
The DataStreamConverter class is used to serialize to and from the The \c main() function sets up a \l QApplication and a \l QCommandLineParser
QDataStream format. There is also the DebugTextDumper class for outputting to make sense of the options the user has specified and provide help if the
the data lossless in a non-standardized human readable format. user asks for it. It uses the values obtained for the various \l
QCommandLineOption instances describing the user's choices, plus the
positional arguments for file names, to prepare the converters it will use.
\section1 The JsonConverter Class It then uses its input converter to load data (and possibly resolve its
choice of output converter, if it hasn't selected one yet) and its output
converter to serialize that data, taking account of any output options the
user has supplied on the command-line.
The JsonConverter class is used to serialize to and from the JSON-format. \snippet serialization/convert/main.cpp 2
\sa {JSON Support in Qt}
\section1 The XmlConverter Class
The XmlConverter class is used to serialize to and from the XML-format.
\section1 The TextConverter Class
The TextConverter class is used to serialize to and from a text format.
\section1 The NullConverter Class
The NullConverter class is an output serializer that does nothing.
*/ */

View File

@ -18,10 +18,8 @@ static const char jsonOptionHelp[] = "compact=no|yes Use compact JS
static QJsonDocument convertFromVariant(const QVariant &v) static QJsonDocument convertFromVariant(const QVariant &v)
{ {
QJsonDocument doc = QJsonDocument::fromVariant(v); QJsonDocument doc = QJsonDocument::fromVariant(v);
if (!doc.isObject() && !doc.isArray()) { if (!doc.isObject() && !doc.isArray())
fprintf(stderr, "Could not convert contents to JSON.\n"); qFatal("Could not convert contents to JSON.");
exit(EXIT_FAILURE);
}
return doc; return doc;
} }
@ -35,11 +33,6 @@ Converter::Directions JsonConverter::directions() const
return Direction::InOut; return Direction::InOut;
} }
Converter::Options JsonConverter::outputOptions() const
{
return {};
}
const char *JsonConverter::optionsHelp() const const char *JsonConverter::optionsHelp() const
{ {
return jsonOptionHelp; return jsonOptionHelp;
@ -75,11 +68,10 @@ QVariant JsonConverter::loadFile(QIODevice *f, const Converter *&outputConverter
if (doc.isNull()) if (doc.isNull())
doc = QJsonDocument::fromJson(f->readAll(), &error); doc = QJsonDocument::fromJson(f->readAll(), &error);
if (error.error) { if (error.error) {
fprintf(stderr, "Could not parse JSON content: offset %d: %s", qFatal("Could not parse JSON content: offset %d: %s",
error.offset, qPrintable(error.errorString())); error.offset, qPrintable(error.errorString()));
exit(EXIT_FAILURE);
} }
if (outputConverter == null) if (isNull(outputConverter))
return QVariant(); return QVariant();
return doc.toVariant(); return doc.toVariant();
} }
@ -94,9 +86,8 @@ void JsonConverter::saveFile(QIODevice *f, const QVariant &contents,
} else if (s == "compact=yes"_L1) { } else if (s == "compact=yes"_L1) {
format = QJsonDocument::Compact; format = QJsonDocument::Compact;
} else { } else {
fprintf(stderr, "Unknown option '%s' to JSON output. Valid options are:\n%s", qFatal("Unknown option '%s' to JSON output. Valid options are:\n%s",
qPrintable(s), jsonOptionHelp); qPrintable(s), jsonOptionHelp);
exit(EXIT_FAILURE);
} }
} }

View File

@ -12,7 +12,6 @@ class JsonConverter : public Converter
public: public:
QString name() const override; QString name() const override;
Directions directions() const override; Directions directions() const override;
Options outputOptions() const override;
const char *optionsHelp() const override; const char *optionsHelp() const override;
bool probeFile(QIODevice *f) const override; bool probeFile(QIODevice *f) const override;
QVariant loadFile(QIODevice *f, const Converter *&outputConverter) const override; QVariant loadFile(QIODevice *f, const Converter *&outputConverter) const override;

View File

@ -1,4 +1,5 @@
// Copyright (C) 2018 Intel Corporation. // Copyright (C) 2018 Intel Corporation.
// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#include "converter.h" #include "converter.h"
@ -13,27 +14,57 @@
using namespace Qt::StringLiterals; using namespace Qt::StringLiterals;
static QList<const Converter *> *availableConverters; static const Converter *prepareConverter(QString format, Converter::Direction direction,
QFile *stream)
Converter::Converter()
{ {
if (!availableConverters) const bool out = direction == Converter::Direction::Out;
availableConverters = new QList<const Converter *>; const QIODevice::OpenMode mode = out
availableConverters->append(this); ? QIODevice::WriteOnly | QIODevice::Truncate
} : QIODevice::ReadOnly;
const char *dirn = out ? "output" : "input";
Converter::~Converter() if (stream->fileName().isEmpty())
{ stream->open(out ? stdout : stdin, mode);
availableConverters->removeAll(this); else
stream->open(mode);
if (!stream->isOpen()) {
qFatal("Could not open \"%s\" for %s: %s",
qPrintable(stream->fileName()), dirn, qPrintable(stream->errorString()));
} else if (format == "auto"_L1) {
for (const Converter *conv : Converter::allConverters()) {
if (conv->directions().testFlag(direction) && conv->probeFile(stream))
return conv;
}
if (out) // Failure to identify output format can be remedied by loadFile().
return nullptr;
// Input format, however, we must know before we can call that:
qFatal("Could not determine input format. Specify it with the -I option.");
} else {
for (const Converter *conv : Converter::allConverters()) {
if (conv->name() == format) {
if (!conv->directions().testFlag(direction)) {
qWarning("File format \"%s\" cannot be used for %s",
qPrintable(format), dirn);
continue; // on the off chance there's another with the same name
}
return conv;
}
}
qFatal("Unknown %s file format \"%s\"", dirn, qPrintable(format));
}
Q_UNREACHABLE_RETURN(nullptr);
} }
int main(int argc, char *argv[]) int main(int argc, char *argv[])
{ {
QCoreApplication app(argc, argv); QCoreApplication app(argc, argv);
//! [0]
QStringList inputFormats; QStringList inputFormats;
QStringList outputFormats; QStringList outputFormats;
for (const Converter *conv : std::as_const(*availableConverters)) { for (const Converter *conv : Converter::allConverters()) {
auto direction = conv->directions(); auto direction = conv->directions();
QString name = conv->name(); QString name = conv->name();
if (direction.testFlag(Converter::Direction::In)) if (direction.testFlag(Converter::Direction::In))
@ -41,13 +72,14 @@ int main(int argc, char *argv[])
if (direction.testFlag(Converter::Direction::Out)) if (direction.testFlag(Converter::Direction::Out))
outputFormats << name; outputFormats << name;
} }
//! [0]
inputFormats.sort(); inputFormats.sort();
outputFormats.sort(); outputFormats.sort();
inputFormats.prepend("auto"_L1); inputFormats.prepend("auto"_L1);
outputFormats.prepend("auto"_L1); outputFormats.prepend("auto"_L1);
QCommandLineParser parser; QCommandLineParser parser;
parser.setApplicationDescription("Qt file format conversion tool"_L1); parser.setApplicationDescription("Qt serialization format conversion tool"_L1);
parser.addHelpOption(); parser.addHelpOption();
QCommandLineOption inputFormatOption(QStringList{ "I"_L1, "input-format"_L1 }); QCommandLineOption inputFormatOption(QStringList{ "I"_L1, "input-format"_L1 });
@ -86,110 +118,38 @@ int main(int argc, char *argv[])
if (parser.isSet(formatOptionsOption)) { if (parser.isSet(formatOptionsOption)) {
QString format = parser.value(formatOptionsOption); QString format = parser.value(formatOptionsOption);
for (const Converter *conv : std::as_const(*availableConverters)) { //! [1]
for (const Converter *conv : Converter::allConverters()) {
if (conv->name() == format) { if (conv->name() == format) {
const char *help = conv->optionsHelp(); const char *help = conv->optionsHelp();
if (help) { if (help) {
printf("The following options are available for format '%s':\n\n%s", qInfo("The following options are available for format '%s':\n\n%s",
qPrintable(format), help); qPrintable(format), help);
} else { } else {
printf("Format '%s' supports no options.\n", qPrintable(format)); qInfo("Format '%s' supports no options.", qPrintable(format));
} }
return EXIT_SUCCESS; return EXIT_SUCCESS;
} }
} }
//! [1]
fprintf(stderr, "Unknown file format '%s'\n", qPrintable(format)); qFatal("Unknown file format '%s'", qPrintable(format));
return EXIT_FAILURE;
}
const Converter *inconv = nullptr;
QString format = parser.value(inputFormatOption);
if (format != "auto"_L1) {
for (const Converter *conv : std::as_const(*availableConverters)) {
if (conv->name() == format) {
inconv = conv;
break;
}
}
if (!inconv) {
fprintf(stderr, "Unknown file format \"%s\"\n", qPrintable(format));
return EXIT_FAILURE;
}
}
const Converter *outconv = nullptr;
format = parser.value(outputFormatOption);
if (format != "auto"_L1) {
for (const Converter *conv : std::as_const(*availableConverters)) {
if (conv->name() == format) {
outconv = conv;
break;
}
}
if (!outconv) {
fprintf(stderr, "Unknown file format \"%s\"\n", qPrintable(format));
return EXIT_FAILURE;
}
} }
//! [2]
QStringList files = parser.positionalArguments(); QStringList files = parser.positionalArguments();
QFile input(files.value(0)); QFile input(files.value(0));
QFile output(files.value(1)); QFile output(files.value(1));
const Converter *inconv = prepareConverter(parser.value(inputFormatOption),
Converter::Direction::In, &input);
const Converter *outconv = prepareConverter(parser.value(outputFormatOption),
Converter::Direction::Out, &output);
if (input.fileName().isEmpty()) // Now finally perform the conversion:
input.open(stdin, QIODevice::ReadOnly);
else
input.open(QIODevice::ReadOnly);
if (!input.isOpen()) {
fprintf(stderr, "Could not open \"%s\" for reading: %s\n",
qPrintable(input.fileName()), qPrintable(input.errorString()));
return EXIT_FAILURE;
}
if (output.fileName().isEmpty())
output.open(stdout, QIODevice::WriteOnly | QIODevice::Truncate);
else
output.open(QIODevice::WriteOnly | QIODevice::Truncate);
if (!output.isOpen()) {
fprintf(stderr, "Could not open \"%s\" for writing: %s\n",
qPrintable(output.fileName()), qPrintable(output.errorString()));
return EXIT_FAILURE;
}
if (!inconv) {
// probe the input to find a file format
for (const Converter *conv : std::as_const(*availableConverters)) {
if (conv->directions().testFlag(Converter::Direction::In)
&& conv->probeFile(&input)) {
inconv = conv;
break;
}
}
if (!inconv) {
fprintf(stderr, "Could not determine input format. pass -I option.\n");
return EXIT_FAILURE;
}
}
if (!outconv) {
// probe the output to find a file format
for (const Converter *conv : std::as_const(*availableConverters)) {
if (conv->directions().testFlag(Converter::Direction::Out)
&& conv->probeFile(&output)) {
outconv = conv;
break;
}
}
}
// now finally perform the conversion
QVariant data = inconv->loadFile(&input, outconv); QVariant data = inconv->loadFile(&input, outconv);
Q_ASSERT_X(outconv, "Converter Tool", Q_ASSERT_X(outconv, "Serialization Converter",
"Internal error: converter format did not provide default"); "Internal error: converter format did not provide default");
outconv->saveFile(&output, data, parser.values(optionOption)); outconv->saveFile(&output, data, parser.values(optionOption));
return EXIT_SUCCESS; return EXIT_SUCCESS;
//! [2]
} }

View File

@ -6,7 +6,10 @@
using namespace Qt::StringLiterals; using namespace Qt::StringLiterals;
static NullConverter nullConverter; static NullConverter nullConverter;
Converter *Converter::null = &nullConverter; bool Converter::isNull(const Converter *converter)
{
return converter == &nullConverter;
}
QString NullConverter::name() const QString NullConverter::name() const
{ {
@ -23,32 +26,12 @@ Converter::Options NullConverter::outputOptions() const
return SupportsArbitraryMapKeys; return SupportsArbitraryMapKeys;
} }
const char *NullConverter::optionsHelp() const
{
return nullptr;
}
bool NullConverter::probeFile(QIODevice *f) const
{
Q_UNUSED(f);
return false;
}
QVariant NullConverter::loadFile(QIODevice *f, const Converter *&outputConverter) const
{
Q_UNUSED(f);
Q_UNUSED(outputConverter);
outputConverter = this;
return QVariant();
}
void NullConverter::saveFile(QIODevice *f, const QVariant &contents, void NullConverter::saveFile(QIODevice *f, const QVariant &contents,
const QStringList &options) const const QStringList &options) const
{ {
if (!options.isEmpty()) { if (!options.isEmpty()) {
fprintf(stderr, "Unknown option '%s' to null output. This format has no options.\n", qFatal("Unknown option '%s' to null output. This format has no options.",
qPrintable(options.first())); qPrintable(options.first()));
exit(EXIT_FAILURE);
} }
Q_UNUSED(f); Q_UNUSED(f);

View File

@ -13,9 +13,6 @@ public:
QString name() const override; QString name() const override;
Directions directions() const override; Directions directions() const override;
Options outputOptions() const override; Options outputOptions() const override;
const char *optionsHelp() const override;
bool probeFile(QIODevice *f) const override;
QVariant loadFile(QIODevice *f, const Converter *&outputConverter) const override;
void saveFile(QIODevice *f, const QVariant &contents, void saveFile(QIODevice *f, const QVariant &contents,
const QStringList &options) const override; const QStringList &options) const override;
}; };

View File

@ -54,16 +54,6 @@ Converter::Directions TextConverter::directions() const
return Direction::InOut; return Direction::InOut;
} }
Converter::Options TextConverter::outputOptions() const
{
return {};
}
const char *TextConverter::optionsHelp() const
{
return nullptr;
}
bool TextConverter::probeFile(QIODevice *f) const bool TextConverter::probeFile(QIODevice *f) const
{ {
if (QFile *file = qobject_cast<QFile *>(f)) if (QFile *file = qobject_cast<QFile *>(f))
@ -98,9 +88,8 @@ void TextConverter::saveFile(QIODevice *f, const QVariant &contents,
const QStringList &options) const const QStringList &options) const
{ {
if (!options.isEmpty()) { if (!options.isEmpty()) {
fprintf(stderr, "Unknown option '%s' to text output. This format has no options.\n", qFatal("Unknown option '%s' to text output. This format has no options.",
qPrintable(options.first())); qPrintable(options.first()));
exit(EXIT_FAILURE);
} }
QTextStream out(f); QTextStream out(f);

View File

@ -12,8 +12,6 @@ class TextConverter : public Converter
public: public:
QString name() const override; QString name() const override;
Directions directions() const override; Directions directions() const override;
Options outputOptions() const override;
const char *optionsHelp() const override;
bool probeFile(QIODevice *f) const override; bool probeFile(QIODevice *f) const override;
QVariant loadFile(QIODevice *f, const Converter *&outputConverter) const override; QVariant loadFile(QIODevice *f, const Converter *&outputConverter) const override;
void saveFile(QIODevice *f, const QVariant &contents, void saveFile(QIODevice *f, const QVariant &contents,

View File

@ -0,0 +1,24 @@
// Copyright (C) 2018 Intel Corporation.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#ifndef VARIANTORDEREDMAP_H
#define VARIANTORDEREDMAP_H
#include <QList>
#include <QPair>
#include <QVariant>
#include <QVariantMap>
class VariantOrderedMap : public QList<QPair<QVariant, QVariant>>
{
public:
VariantOrderedMap() = default;
VariantOrderedMap(const QVariantMap &map)
{
reserve(map.size());
for (auto it = map.begin(); it != map.end(); ++it)
append({it.key(), it.value()});
}
};
#endif // VARIANTORDEREDMAP_H

View File

@ -2,6 +2,7 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#include "xmlconverter.h" #include "xmlconverter.h"
#include "variantorderedmap.h"
#include <QBitArray> #include <QBitArray>
#include <QtCborCommon> #include <QtCborCommon>
@ -48,9 +49,8 @@ static QVariantList listFromXml(QXmlStreamReader &xml, Converter::Options option
break; break;
} }
fprintf(stderr, "%lld:%lld: Invalid XML %s '%s'.\n", xml.lineNumber(), xml.columnNumber(), qFatal("%lld:%lld: Invalid XML %s '%s'.", xml.lineNumber(), xml.columnNumber(),
qPrintable(xml.tokenString()), qPrintable(xml.name().toString())); qPrintable(xml.tokenString()), qPrintable(xml.name().toString()));
exit(EXIT_FAILURE);
} }
xml.readNext(); xml.readNext();
@ -90,9 +90,8 @@ static VariantOrderedMap::value_type mapEntryFromXml(QXmlStreamReader &xml,
break; break;
} }
fprintf(stderr, "%lld:%lld: Invalid XML %s '%s'.\n", xml.lineNumber(), xml.columnNumber(), qFatal("%lld:%lld: Invalid XML %s '%s'.", xml.lineNumber(), xml.columnNumber(),
qPrintable(xml.tokenString()), qPrintable(xml.name().toString())); qPrintable(xml.tokenString()), qPrintable(xml.name().toString()));
exit(EXIT_FAILURE);
} }
return { key, value }; return { key, value };
@ -134,9 +133,8 @@ static QVariant mapFromXml(QXmlStreamReader &xml, Converter::Options options)
break; break;
} }
fprintf(stderr, "%lld:%lld: Invalid XML %s '%s'.\n", xml.lineNumber(), xml.columnNumber(), qFatal("%lld:%lld: Invalid XML %s '%s'.", xml.lineNumber(), xml.columnNumber(),
qPrintable(xml.tokenString()), qPrintable(xml.name().toString())); qPrintable(xml.tokenString()), qPrintable(xml.name().toString()));
exit(EXIT_FAILURE);
} }
xml.readNext(); xml.readNext();
@ -153,9 +151,8 @@ static QVariant variantFromXml(QXmlStreamReader &xml, Converter::Options options
if (name == "map"_L1) if (name == "map"_L1)
return mapFromXml(xml, options); return mapFromXml(xml, options);
if (name != "value"_L1) { if (name != "value"_L1) {
fprintf(stderr, "%lld:%lld: Invalid XML key '%s'.\n", qFatal("%lld:%lld: Invalid XML key '%s'.",
xml.lineNumber(), xml.columnNumber(), qPrintable(name.toString())); xml.lineNumber(), xml.columnNumber(), qPrintable(name.toString()));
exit(EXIT_FAILURE);
} }
QXmlStreamAttributes attrs = xml.attributes(); QXmlStreamAttributes attrs = xml.attributes();
@ -168,9 +165,8 @@ static QVariant variantFromXml(QXmlStreamReader &xml, Converter::Options options
if (xml.isCDATA() || xml.isCharacters() || xml.isEndElement()) if (xml.isCDATA() || xml.isCharacters() || xml.isEndElement())
break; break;
fprintf(stderr, "%lld:%lld: Invalid XML %s '%s'.\n", xml.lineNumber(), xml.columnNumber(), qFatal("%lld:%lld: Invalid XML %s '%s'.", xml.lineNumber(), xml.columnNumber(),
qPrintable(xml.tokenString()), qPrintable(name.toString())); qPrintable(xml.tokenString()), qPrintable(name.toString()));
exit(EXIT_FAILURE);
} }
QStringView text = xml.text(); QStringView text = xml.text();
@ -190,9 +186,8 @@ static QVariant variantFromXml(QXmlStreamReader &xml, Converter::Options options
// let's see floating point // let's see floating point
double d = text.toDouble(&ok); double d = text.toDouble(&ok);
if (!ok) { if (!ok) {
fprintf(stderr, "%lld:%lld: Invalid XML: could not interpret '%s' as a number.\n", qFatal("%lld:%lld: Invalid XML: could not interpret '%s' as a number.",
xml.lineNumber(), xml.columnNumber(), qPrintable(text.toString())); xml.lineNumber(), xml.columnNumber(), qPrintable(text.toString()));
exit(EXIT_FAILURE);
} }
result = d; result = d;
} }
@ -206,9 +201,8 @@ static QVariant variantFromXml(QXmlStreamReader &xml, Converter::Options options
} else if (encoding.isEmpty() || encoding == "base64"_L1) { } else if (encoding.isEmpty() || encoding == "base64"_L1) {
result = QByteArray::fromBase64(data); result = QByteArray::fromBase64(data);
} else { } else {
fprintf(stderr, "%lld:%lld: Invalid XML: unknown encoding '%s' for bytes.\n", qFatal("%lld:%lld: Invalid XML: unknown encoding '%s' for bytes.",
xml.lineNumber(), xml.columnNumber(), qPrintable(encoding.toString())); xml.lineNumber(), xml.columnNumber(), qPrintable(encoding.toString()));
exit(EXIT_FAILURE);
} }
} else if (type == "string"_L1) { } else if (type == "string"_L1) {
result = text.toString(); result = text.toString();
@ -227,9 +221,8 @@ static QVariant variantFromXml(QXmlStreamReader &xml, Converter::Options options
} else if (c == '0') { } else if (c == '0') {
++n; ++n;
} else if (!c.isSpace()) { } else if (!c.isSpace()) {
fprintf(stderr, "%lld:%lld: Invalid XML: invalid bit string '%s'.\n", qFatal("%lld:%lld: Invalid XML: invalid bit string '%s'.",
xml.lineNumber(), xml.columnNumber(), qPrintable(text.toString())); xml.lineNumber(), xml.columnNumber(), qPrintable(text.toString()));
exit(EXIT_FAILURE);
} }
} }
ba.resize(n); ba.resize(n);
@ -247,16 +240,14 @@ static QVariant variantFromXml(QXmlStreamReader &xml, Converter::Options options
else else
id = QMetaType::fromName(type.toLatin1()).id(); id = QMetaType::fromName(type.toLatin1()).id();
if (id == QMetaType::UnknownType) { if (id == QMetaType::UnknownType) {
fprintf(stderr, "%lld:%lld: Invalid XML: unknown type '%s'.\n", qFatal("%lld:%lld: Invalid XML: unknown type '%s'.",
xml.lineNumber(), xml.columnNumber(), qPrintable(type.toString())); xml.lineNumber(), xml.columnNumber(), qPrintable(type.toString()));
exit(EXIT_FAILURE);
} }
result = text.toString(); result = text.toString();
if (!result.convert(QMetaType(id))) { if (!result.convert(QMetaType(id))) {
fprintf(stderr, "%lld:%lld: Invalid XML: could not parse content as type '%s'.\n", qFatal("%lld:%lld: Invalid XML: could not parse content as type '%s'.",
xml.lineNumber(), xml.columnNumber(), qPrintable(type.toString())); xml.lineNumber(), xml.columnNumber(), qPrintable(type.toString()));
exit(EXIT_FAILURE);
} }
} }
@ -265,9 +256,8 @@ static QVariant variantFromXml(QXmlStreamReader &xml, Converter::Options options
} while (xml.isComment() || xml.isWhitespace()); } while (xml.isComment() || xml.isWhitespace());
if (!xml.isEndElement()) { if (!xml.isEndElement()) {
fprintf(stderr, "%lld:%lld: Invalid XML %s '%s'.\n", xml.lineNumber(), xml.columnNumber(), qFatal("%lld:%lld: Invalid XML %s '%s'.", xml.lineNumber(), xml.columnNumber(),
qPrintable(xml.tokenString()), qPrintable(name.toString())); qPrintable(xml.tokenString()), qPrintable(name.toString()));
exit(EXIT_FAILURE);
} }
xml.readNext(); xml.readNext();
@ -387,8 +377,7 @@ static void variantToXml(QXmlStreamWriter &xml, const QVariant &v)
xml.writeAttribute(typeString, QString::fromLatin1(typeName)); xml.writeAttribute(typeString, QString::fromLatin1(typeName));
xml.writeCharacters(copy.toString()); xml.writeCharacters(copy.toString());
} else { } else {
fprintf(stderr, "XML: don't know how to serialize type '%s'.\n", typeName); qFatal("XML: don't know how to serialize type '%s'.", typeName);
exit(EXIT_FAILURE);
} }
} }
} }
@ -434,10 +423,8 @@ QVariant XmlConverter::loadFile(QIODevice *f, const Converter *&outputConverter)
QXmlStreamReader xml(f); QXmlStreamReader xml(f);
xml.readNextStartElement(); xml.readNextStartElement();
QVariant v = variantFromXml(xml, outputConverter->outputOptions()); QVariant v = variantFromXml(xml, outputConverter->outputOptions());
if (xml.hasError()) { if (xml.hasError())
fprintf(stderr, "XML error: %s", qPrintable(xml.errorString())); qFatal("XML error: %s", qPrintable(xml.errorString()));
exit(EXIT_FAILURE);
}
return v; return v;
} }
@ -452,9 +439,8 @@ void XmlConverter::saveFile(QIODevice *f, const QVariant &contents,
} else if (s == "compact=yes"_L1) { } else if (s == "compact=yes"_L1) {
compact = true; compact = true;
} else { } else {
fprintf(stderr, "Unknown option '%s' to XML output. Valid options are:\n%s", qFatal("Unknown option '%s' to XML output. Valid options are:\n%s",
qPrintable(s), xmlOptionHelp); qPrintable(s), xmlOptionHelp);
exit(EXIT_FAILURE);
} }
} }

View File

@ -4,10 +4,9 @@
/*! /*!
\example serialization/savegame \example serialization/savegame
\examplecategory {Data Processing & I/O} \examplecategory {Data Processing & I/O}
\title JSON Save Game Example \title Saving and Loading a Game
\brief The JSON Save Game example demonstrates how to save and load a \brief How to save and load a game using Qt's JSON or CBOR classes.
small game using QJsonDocument, QJsonObject and QJsonArray.
Many games provide save functionality, so that the player's progress through Many games provide save functionality, so that the player's progress through
the game can be saved and loaded at a later time. The process of saving a the game can be saved and loaded at a later time. The process of saving a

View File

@ -3,6 +3,7 @@
/*! /*!
\example threads/mandelbrot \example threads/mandelbrot
\examplecategory {Data Processing & I/O}
\title Mandelbrot \title Mandelbrot
\ingroup qtconcurrent-mtexamples \ingroup qtconcurrent-mtexamples

View File

@ -3,8 +3,8 @@
/*! /*!
\example threads/queuedcustomtype \example threads/queuedcustomtype
\title Queued Custom Type Example \examplecategory {Data Processing & I/O}
\brief Demonstrates multi-thread programming using Qt. \title Queued Custom Type
\ingroup qtconcurrent-mtexamples \ingroup qtconcurrent-mtexamples
\brief The Queued Custom Type example shows how to send custom types between \brief The Queued Custom Type example shows how to send custom types between

View File

@ -3,13 +3,13 @@
/*! /*!
\example threads/semaphores \example threads/semaphores
\title Semaphores Example \examplecategory {Data Processing & I/O}
\brief Demonstrates multi-thread programming using Qt. \title Producer and Consumer using Semaphores
\ingroup qtconcurrent-mtexamples \ingroup qtconcurrent-mtexamples
\brief The Semaphores example shows how to use QSemaphore to control \brief The Producer and Consumer using Semaphores example shows how
access to a circular buffer shared by a producer thread and a to use QSemaphore to control access to a circular buffer shared
consumer thread. by a producer thread and a consumer thread.
The producer writes data to the buffer until it reaches the end The producer writes data to the buffer until it reaches the end
of the buffer, at which point it restarts from the beginning, of the buffer, at which point it restarts from the beginning,
@ -30,7 +30,7 @@
An alternative to using QSemaphore to solve the producer-consumer An alternative to using QSemaphore to solve the producer-consumer
problem is to use QWaitCondition and QMutex. This is what the problem is to use QWaitCondition and QMutex. This is what the
\l{Wait Conditions Example} does. \l{Producer and Consumer using Wait Conditions} example does.
\section1 Global Variables \section1 Global Variables

View File

@ -3,13 +3,13 @@
/*! /*!
\example threads/waitconditions \example threads/waitconditions
\title Wait Conditions Example \examplecategory {Data Processing & I/O}
\brief Demonstrates multi-thread programming using Qt. \title Producer and Consumer using Wait Conditions
\ingroup qtconcurrent-mtexamples \ingroup qtconcurrent-mtexamples
\brief The Wait Conditions example shows how to use QWaitCondition and \brief The Producer and Consumer using Wait Conditions example shows
QMutex to control access to a circular buffer shared by a how to use QWaitCondition and QMutex to control access to a circular
producer thread and a consumer thread. buffer shared by a producer thread and a consumer thread.
The producer writes data to the buffer until it reaches the end The producer writes data to the buffer until it reaches the end
of the buffer, at which point it restarts from the beginning, of the buffer, at which point it restarts from the beginning,
@ -30,7 +30,7 @@
An alternative to using QWaitCondition and QMutex to solve the An alternative to using QWaitCondition and QMutex to solve the
producer-consumer problem is to use QSemaphore. This is what the producer-consumer problem is to use QSemaphore. This is what the
\l{Semaphores Example} does. \l{Producer and Consumer using Semaphores} example does.
\section1 Global Variables \section1 Global Variables

View File

@ -6,6 +6,7 @@
#include "renderthread.h" #include "renderthread.h"
#include <QCoreApplication>
#include <QPixmap> #include <QPixmap>
#include <QWidget> #include <QWidget>
@ -16,7 +17,7 @@ QT_END_NAMESPACE
//! [0] //! [0]
class MandelbrotWidget : public QWidget class MandelbrotWidget : public QWidget
{ {
Q_OBJECT Q_DECLARE_TR_FUNCTIONS(MandelbrotWidget)
public: public:
MandelbrotWidget(QWidget *parent = nullptr); MandelbrotWidget(QWidget *parent = nullptr);
@ -36,11 +37,9 @@ protected:
bool event(QEvent *event) override; bool event(QEvent *event) override;
#endif #endif
private slots: private:
void updatePixmap(const QImage &image, double scaleFactor); void updatePixmap(const QImage &image, double scaleFactor);
void zoom(double zoomFactor); void zoom(double zoomFactor);
private:
void scroll(int deltaX, int deltaY); void scroll(int deltaX, int deltaY);
#ifndef QT_NO_GESTURES #ifndef QT_NO_GESTURES
bool gestureEvent(QGestureEvent *event); bool gestureEvent(QGestureEvent *event);

View File

@ -1,12 +1,17 @@
// Copyright (C) 2016 The Qt Company Ltd. // Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#include <QApplication>
#include <QPainter>
#include <QTime>
#include "block.h" #include "block.h"
#include "window.h" #include "window.h"
#include <QApplication>
#include <QBrush>
#include <QImage>
#include <QPainter>
#include <QPen>
#include <QPointF>
#include <QRect>
QImage createImage(int width, int height) QImage createImage(int width, int height)
{ {
QImage image(width, height, QImage::Format_RGB16); QImage image(width, height, QImage::Format_RGB16);
@ -43,8 +48,8 @@ QImage createImage(int width, int height)
int x = 0; int x = 0;
int y = 0; int y = 0;
int starWidth = image.width()/3; const int starWidth = image.width()/3;
int starHeight = image.height()/3; const int starHeight = image.height()/3;
QRect rect(x, y, starWidth, starHeight); QRect rect(x, y, starWidth, starHeight);

View File

@ -1,22 +1,19 @@
// Copyright (C) 2016 The Qt Company Ltd. // Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#include "block.h"
#include "renderthread.h" #include "renderthread.h"
#include <QRandomGenerator> #include <QRandomGenerator>
#include <QRgb>
RenderThread::RenderThread(QObject *parent) RenderThread::RenderThread(QObject *parent)
: QThread(parent) : QThread(parent)
{ {
m_abort = false;
} }
RenderThread::~RenderThread() RenderThread::~RenderThread()
{ {
mutex.lock();
m_abort = true;
mutex.unlock();
wait(); wait();
} }
@ -27,27 +24,26 @@ void RenderThread::processImage(const QImage &image)
return; return;
m_image = image; m_image = image;
m_abort = false;
start(); start();
} }
void RenderThread::run() void RenderThread::run()
{ {
int size = qMax(m_image.width()/20, m_image.height()/20); const int size = qMax(m_image.width()/20, m_image.height()/20);
for (int s = size; s > 0; --s) { for (int s = size; s > 0; --s) {
for (int c = 0; c < 400; ++c) { for (int c = 0; c < 400; ++c) {
//![processing the image (start)] //![processing the image (start)]
int x1 = qMax(0, QRandomGenerator::global()->bounded(m_image.width()) - s/2); const int x1 = qMax(0, QRandomGenerator::global()->bounded(m_image.width()) - s/2);
int x2 = qMin(x1 + s/2 + 1, m_image.width()); const int x2 = qMin(x1 + s/2 + 1, m_image.width());
int y1 = qMax(0, QRandomGenerator::global()->bounded(m_image.height()) - s/2); const int y1 = qMax(0, QRandomGenerator::global()->bounded(m_image.height()) - s/2);
int y2 = qMin(y1 + s/2 + 1, m_image.height()); const int y2 = qMin(y1 + s/2 + 1, m_image.height());
int n = 0; int n = 0;
int red = 0; int red = 0;
int green = 0; int green = 0;
int blue = 0; int blue = 0;
for (int i = y1; i < y2; ++i) { for (int i = y1; i < y2; ++i) {
for (int j = x1; j < x2; ++j) { for (int j = x1; j < x2; ++j) {
QRgb pixel = m_image.pixel(j, i); const QRgb pixel = m_image.pixel(j, i);
red += qRed(pixel); red += qRed(pixel);
green += qGreen(pixel); green += qGreen(pixel);
blue += qBlue(pixel); blue += qBlue(pixel);
@ -55,20 +51,13 @@ void RenderThread::run()
} }
} }
//![processing the image (finish)] //![processing the image (finish)]
Block block(QRect(x1, y1, x2 - x1 + 1, y2 - y1 + 1), const Block block(QRect(x1, y1, x2 - x1 + 1, y2 - y1 + 1),
QColor(red/n, green/n, blue/n)); QColor(red/n, green/n, blue/n));
emit sendBlock(block); emit sendBlock(block);
if (m_abort) if (isInterruptionRequested())
return; return;
msleep(10); msleep(10);
} }
} }
} }
//![processing the image (finish)] //![processing the image (finish)]
void RenderThread::stopProcess()
{
mutex.lock();
m_abort = true;
mutex.unlock();
}

View File

@ -5,9 +5,9 @@
#define RENDERTHREAD_H #define RENDERTHREAD_H
#include <QImage> #include <QImage>
#include <QMutex>
#include <QThread> #include <QThread>
#include "block.h"
class Block;
//! [RenderThread class definition] //! [RenderThread class definition]
class RenderThread : public QThread class RenderThread : public QThread
@ -23,16 +23,11 @@ public:
signals: signals:
void sendBlock(const Block &block); void sendBlock(const Block &block);
public slots:
void stopProcess();
protected: protected:
void run(); void run();
private: private:
bool m_abort;
QImage m_image; QImage m_image;
QMutex mutex;
}; };
//! [RenderThread class definition] //! [RenderThread class definition]

View File

@ -1,8 +1,17 @@
// Copyright (C) 2016 The Qt Company Ltd. // Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#include "block.h"
#include "renderthread.h"
#include "window.h" #include "window.h"
#include <QtWidgets>
#include <QFileDialog>
#include <QGuiApplication>
#include <QHBoxLayout>
#include <QImageReader>
#include <QPainter>
#include <QScreen>
#include <QVBoxLayout>
//! [Window constructor start] //! [Window constructor start]
Window::Window(QWidget *parent) Window::Window(QWidget *parent)
@ -20,7 +29,7 @@ Window::Window(QWidget *parent)
connect(loadButton, &QPushButton::clicked, connect(loadButton, &QPushButton::clicked,
this, QOverload<>::of(&Window::loadImage)); this, QOverload<>::of(&Window::loadImage));
connect(resetButton, &QPushButton::clicked, connect(resetButton, &QPushButton::clicked,
thread, &RenderThread::stopProcess); thread, &RenderThread::requestInterruption);
connect(thread, &RenderThread::finished, connect(thread, &RenderThread::finished,
this, &Window::resetUi); this, &Window::resetUi);
//! [set up widgets and connections] //! [connecting signal with custom type] //! [set up widgets and connections] //! [connecting signal with custom type]
@ -51,13 +60,13 @@ void Window::loadImage()
if (format.toLower() == format) if (format.toLower() == format)
formats.append(QLatin1String("*.") + QString::fromLatin1(format)); formats.append(QLatin1String("*.") + QString::fromLatin1(format));
QString newPath = QFileDialog::getOpenFileName(this, tr("Open Image"), const QString newPath = QFileDialog::getOpenFileName(this, tr("Open Image"),
path, tr("Image files (%1)").arg(formats.join(' '))); path, tr("Image files (%1)").arg(formats.join(' ')));
if (newPath.isEmpty()) if (newPath.isEmpty())
return; return;
QImage image(newPath); const QImage image(newPath);
if (!image.isNull()) { if (!image.isNull()) {
loadImage(image); loadImage(image);
path = newPath; path = newPath;
@ -67,7 +76,7 @@ void Window::loadImage()
void Window::loadImage(const QImage &image) void Window::loadImage(const QImage &image)
{ {
QImage useImage; QImage useImage;
QRect space = QGuiApplication::primaryScreen()->availableGeometry(); const QRect space = QGuiApplication::primaryScreen()->availableGeometry();
if (image.width() > 0.75*space.width() || image.height() > 0.75*space.height()) if (image.width() > 0.75*space.width() || image.height() > 0.75*space.height())
useImage = image.scaled(0.75*space.width(), 0.75*space.height(), useImage = image.scaled(0.75*space.width(), 0.75*space.height(),
Qt::KeepAspectRatio, Qt::SmoothTransformation); Qt::KeepAspectRatio, Qt::SmoothTransformation);

View File

@ -4,13 +4,14 @@
#ifndef WINDOW_H #ifndef WINDOW_H
#define WINDOW_H #define WINDOW_H
#include <QImage>
#include <QLabel>
#include <QPixmap>
#include <QPushButton>
#include <QWidget> #include <QWidget>
#include "renderthread.h"
QT_BEGIN_NAMESPACE class Block;
class QLabel; class RenderThread;
class QPushButton;
QT_END_NAMESPACE
//! [Window class definition] //! [Window class definition]
class Window : public QWidget class Window : public QWidget

View File

@ -7,9 +7,9 @@
#include <stdlib.h> #include <stdlib.h>
//! [0] //! [0]
const int DataSize = 100000; constexpr int DataSize = 100000;
const int BufferSize = 8192; constexpr int BufferSize = 8192;
char buffer[BufferSize]; char buffer[BufferSize];
QSemaphore freeBytes(BufferSize); QSemaphore freeBytes(BufferSize);

View File

@ -5,5 +5,3 @@ if(NOT TARGET Qt6::Widgets)
return() return()
endif() endif()
qt_internal_add_example(contiguouscache) qt_internal_add_example(contiguouscache)
qt_internal_add_example(customtype)
qt_internal_add_example(customtypesending)

View File

@ -1,37 +0,0 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
cmake_minimum_required(VERSION 3.16)
project(customtype LANGUAGES CXX)
if(NOT DEFINED INSTALL_EXAMPLESDIR)
set(INSTALL_EXAMPLESDIR "examples")
endif()
set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/corelib/tools/customtype")
find_package(Qt6 REQUIRED COMPONENTS Core Gui Widgets)
qt_standard_project_setup()
qt_add_executable(customtype
main.cpp
message.cpp message.h
)
set_target_properties(customtype PROPERTIES
WIN32_EXECUTABLE TRUE
MACOSX_BUNDLE TRUE
)
target_link_libraries(customtype PRIVATE
Qt6::Core
Qt6::Gui
Qt6::Widgets
)
install(TARGETS customtype
RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
)

View File

@ -1,8 +0,0 @@
HEADERS = message.h
SOURCES = main.cpp \
message.cpp
QT += widgets
# install
target.path = $$[QT_INSTALL_EXAMPLES]/corelib/tools/customtype
INSTALLS += target

View File

@ -1,37 +0,0 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#include <QCoreApplication>
#include <QDebug>
#include <QVariant>
#include "message.h"
int main(int argc, char *argv[])
{
QCoreApplication app(argc, argv);
QStringList headers;
headers << "Subject: Hello World"
<< "From: address@example.com";
QString body = "This is a test.\r\n";
//! [printing a custom type]
Message message(body, headers);
qDebug() << "Original:" << message;
//! [printing a custom type]
//! [storing a custom value]
QVariant stored;
stored.setValue(message);
//! [storing a custom value]
qDebug() << "Stored:" << stored;
//! [retrieving a custom value]
Message retrieved = qvariant_cast<Message>(stored);
qDebug() << "Retrieved:" << retrieved;
retrieved = qvariant_cast<Message>(stored);
qDebug() << "Retrieved:" << retrieved;
//! [retrieving a custom value]
return 0;
}

View File

@ -1,38 +0,0 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#include "message.h"
#include <QDebug>
Message::Message(const QString &body, const QStringList &headers)
: m_body(body), m_headers(headers)
{
}
//! [custom type streaming operator]
QDebug operator<<(QDebug dbg, const Message &message)
{
QDebugStateSaver saver(dbg);
QList<QStringView> pieces = message.body().split(u"\r\n", Qt::SkipEmptyParts);
if (pieces.isEmpty())
dbg.nospace() << "Message()";
else if (pieces.size() == 1)
dbg.nospace() << "Message(" << pieces.first() << ")";
else
dbg.nospace() << "Message(" << pieces.first() << " ...)";
return dbg;
}
//! [custom type streaming operator]
//! [getter functions]
QStringView Message::body() const
{
return m_body;
}
QStringList Message::headers() const
{
return m_headers;
}
//! [getter functions]

View File

@ -1,38 +0,0 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#ifndef MESSAGE_H
#define MESSAGE_H
#include <QMetaType>
#include <QStringList>
//! [custom type definition]
class Message
{
public:
Message() = default;
~Message() = default;
Message(const Message &) = default;
Message &operator=(const Message &) = default;
Message(const QString &body, const QStringList &headers);
QStringView body() const;
QStringList headers() const;
private:
QString m_body;
QStringList m_headers;
};
//! [custom type definition]
//! [custom type meta-type declaration]
Q_DECLARE_METATYPE(Message);
//! [custom type meta-type declaration]
//! [custom type streaming operator]
QDebug operator<<(QDebug dbg, const Message &message);
//! [custom type streaming operator]
#endif

View File

@ -1,38 +0,0 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
cmake_minimum_required(VERSION 3.16)
project(customtypesending LANGUAGES CXX)
if(NOT DEFINED INSTALL_EXAMPLESDIR)
set(INSTALL_EXAMPLESDIR "examples")
endif()
set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/corelib/tools/customtypesending")
find_package(Qt6 REQUIRED COMPONENTS Core Gui Widgets)
qt_standard_project_setup()
qt_add_executable(customtypesending
main.cpp
message.cpp message.h
window.cpp window.h
)
set_target_properties(customtypesending PROPERTIES
WIN32_EXECUTABLE TRUE
MACOSX_BUNDLE TRUE
)
target_link_libraries(customtypesending PRIVATE
Qt6::Core
Qt6::Gui
Qt6::Widgets
)
install(TARGETS customtypesending
RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
)

View File

@ -1,10 +0,0 @@
HEADERS = message.h \
window.h
SOURCES = main.cpp \
message.cpp \
window.cpp
QT += widgets
# install
target.path = $$[QT_INSTALL_EXAMPLES]/corelib/tools/customtypesending
INSTALLS += target

View File

@ -1,31 +0,0 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#include <QApplication>
#include "message.h"
#include "window.h"
//! [main function]
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
QStringList headers;
headers << "Subject: Hello World"
<< "From: address@example.com";
QString body = "This is a test.\r\n";
Message message(body, headers);
Window window1;
window1.setMessage(message);
Window window2;
QObject::connect(&window1, &Window::messageSent,
&window2, &Window::setMessage);
QObject::connect(&window2, &Window::messageSent,
&window1, &Window::setMessage);
window1.show();
window2.show();
return app.exec();
}
//! [main function]

View File

@ -1,19 +0,0 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#include "message.h"
Message::Message(const QString &body, const QStringList &headers)
: m_body(body), m_headers(headers)
{
}
QString Message::body() const
{
return m_body;
}
QStringList Message::headers() const
{
return m_headers;
}

View File

@ -1,34 +0,0 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#ifndef MESSAGE_H
#define MESSAGE_H
#include <QMetaType>
#include <QStringList>
//! [custom type definition]
class Message
{
public:
Message() = default;
~Message() = default;
Message(const Message &) = default;
Message &operator=(const Message &) = default;
Message(const QString &body, const QStringList &headers);
QString body() const;
QStringList headers() const;
private:
QString m_body;
QStringList m_headers;
};
//! [custom type definition]
//! [custom type meta-type declaration]
Q_DECLARE_METATYPE(Message);
//! [custom type meta-type declaration]
#endif

View File

@ -1,43 +0,0 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#include <QtWidgets>
#include "window.h"
//! [Window constructor]
Window::Window(QWidget *parent)
: QWidget(parent), editor(new QTextEdit(this))
{
QPushButton *sendButton = new QPushButton(tr("&Send message"));
connect(sendButton, &QPushButton::clicked,
this, &Window::sendMessage);
QHBoxLayout *buttonLayout = new QHBoxLayout;
buttonLayout->addStretch();
buttonLayout->addWidget(sendButton);
buttonLayout->addStretch();
QVBoxLayout *layout = new QVBoxLayout(this);
layout->addWidget(editor);
layout->addLayout(buttonLayout);
setWindowTitle(tr("Custom Type Sending"));
}
//! [Window constructor]
//! [sending a message]
void Window::sendMessage()
{
thisMessage = Message(editor->toPlainText(), thisMessage.headers());
emit messageSent(thisMessage);
}
//! [sending a message]
//! [receiving a message]
void Window::setMessage(const Message &message)
{
thisMessage = message;
editor->setPlainText(thisMessage.body());
}
//! [receiving a message]

View File

@ -1,35 +0,0 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#ifndef WINDOW_H
#define WINDOW_H
#include <QWidget>
#include "message.h"
QT_FORWARD_DECLARE_CLASS(QTextEdit)
//! [Window class definition]
class Window : public QWidget
{
Q_OBJECT
public:
Window(QWidget *parent = nullptr);
signals:
void messageSent(const Message &message);
public slots:
void setMessage(const Message &message);
private slots:
void sendMessage();
private:
Message thisMessage;
QTextEdit *editor;
};
//! [Window class definition]
#endif

View File

@ -1,111 +0,0 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*!
\example tools/customtype
\title Custom Type Example
\brief The Custom Type example shows how to integrate a custom type into Qt's
meta-object system.
Contents:
\tableofcontents
\section1 Overview
Qt provides a range of standard value types that are used to provide
rich and meaningful APIs. These types are integrated with the meta-object
system, enabling them to be stored in QVariant objects, written out in
debugging information and sent between components in signal-slot
communication.
Custom types can also be integrated with the meta-object system as long as
they are written to conform to some simple guidelines. In this example, we
introduce a simple \c Message class, we describe how we make it work with
QVariant, and we show how it can be extended to generate a printable
representation of itself for use in debugging output.
\section1 The Message Class Definition
The \c Message class is a simple value class that contains two pieces
of information (a QString and a QStringList), each of which can be read
using trivial getter functions:
\snippet tools/customtype/message.h custom type definition
The default constructor, copy constructor and destructor are
all required, and must be public, if the type is to be integrated into the
meta-object system. Other than this, we are free to implement whatever we
need to make the type do what we want, so we also include a constructor
that lets us set the type's data members.
To enable the type to be used with QVariant, we declare it using the
Q_DECLARE_METATYPE() macro:
\snippet tools/customtype/message.h custom type meta-type declaration
We do not need to write any additional code to accompany this macro.
To allow us to see a readable description of each \c Message object when it
is sent to the debug output stream, we define a streaming operator:
\snippet tools/customtype/message.h custom type streaming operator
This facility is useful if you need to insert tracing statements in your
code for debugging purposes.
\section1 The Message Class Implementation
The streaming operator is implemented in the following way:
\snippet tools/customtype/message.cpp custom type streaming operator
Here, we want to represent each value depending on how many lines are stored
in the message body. We stream text to the QDebug object passed to the
operator and return the QDebug object obtained from its maybeSpace() member
function; this is described in more detail in the
\l{Creating Custom Qt Types#Making the Type Printable}{Creating Custom Qt Types}
document.
We include the code for the getter functions for completeness:
\snippet tools/customtype/message.cpp getter functions
With the type fully defined, implemented, and integrated with the
meta-object system, we can now use it.
\section1 Using the Message
In the example's \c{main()} function, we show how a \c Message object can
be printed to the console by sending it to the debug stream:
\snippet tools/customtype/main.cpp printing a custom type
You can use the type with QVariant in exactly the same way as you would
use standard Qt value types. Here's how to store a value using the
QVariant::setValue() function:
\snippet tools/customtype/main.cpp storing a custom value
Alternatively, the QVariant::fromValue() function can be used if
you are using a compiler without support for member template
functions.
The value can be retrieved using the QVariant::value() member template
function:
\snippet tools/customtype/main.cpp retrieving a custom value
\section1 Further Reading
The custom \c Message type can also be used with direct signal-slot
connections.
To register a custom type for use with queued signals and slots, such as
those used in cross-thread communication, see the
\l{Queued Custom Type Example}.
More information on using custom types with Qt can be found in the
\l{Creating Custom Qt Types} document.
*/

View File

@ -1,6 +1,4 @@
requires(qtHaveModule(widgets)) requires(qtHaveModule(widgets))
TEMPLATE = subdirs TEMPLATE = subdirs
SUBDIRS = contiguouscache \ SUBDIRS = contiguouscache
customtype \
customtypesending

Some files were not shown because too many files have changed in this diff Show More