mirror of
https://github.com/crystalidea/qt6windows7.git
synced 2024-11-26 06:07:04 +08:00
commit
cc07a7df8b
@ -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")
|
||||
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_COPYRIGHT_YEAR "2023")
|
||||
|
@ -1 +1 @@
|
||||
QT_PACKAGEDATE_STR=2023-10-04
|
||||
QT_PACKAGEDATE_STR=2023-11-21
|
2
.tag
2
.tag
@ -1 +1 @@
|
||||
33f5e985e480283bb0ca9dea5f82643e825ba87c
|
||||
e2cbce919ccefcae2b18f90257d67bc6e24c3c94
|
||||
|
11
README.md
11
README.md
@ -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)
|
||||
|
@ -5,13 +5,18 @@
|
||||
# https://gitlab.kitware.com/cmake/cmake/-/issues/20713
|
||||
# https://gitlab.kitware.com/cmake/cmake/-/issues/21475
|
||||
set(configs "@__qt_configured_configs@")
|
||||
set(should_skip_strip "@__qt_skip_strip_installed_artifacts@")
|
||||
|
||||
if(NOT QT_BUILD_DIR)
|
||||
message(FATAL_ERROR "No QT_BUILD_DIR value provided to qt-cmake-private-install.")
|
||||
endif()
|
||||
unset(strip_arg)
|
||||
if ("x@MSVC@" STREQUAL "x")
|
||||
|
||||
if(should_skip_strip)
|
||||
unset(strip_arg)
|
||||
else()
|
||||
set(strip_arg --strip)
|
||||
endif()
|
||||
|
||||
foreach(config ${configs})
|
||||
message(STATUS "Installing configuration: '${config}'")
|
||||
set(args "${CMAKE_COMMAND}" --install ${QT_BUILD_DIR} --config "${config}" ${strip_arg})
|
||||
|
@ -43,7 +43,8 @@ else()
|
||||
else()
|
||||
set(MimerSQL_library_hints "")
|
||||
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_include_dir_hints "/usr/local/include")
|
||||
else()
|
||||
|
@ -276,7 +276,7 @@ function(qt_auto_detect_cmake_config)
|
||||
endfunction()
|
||||
|
||||
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
|
||||
"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"
|
||||
|
@ -18,10 +18,6 @@ macro(qt_internal_top_level_setup_autodetect)
|
||||
endmacro()
|
||||
|
||||
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()
|
||||
endmacro()
|
||||
|
||||
|
@ -111,13 +111,6 @@ from the build directory")
|
||||
if(QT_SUPERBUILD)
|
||||
qt_internal_save_previously_visited_packages()
|
||||
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()
|
||||
|
||||
function(qt_configure_print_summary_helper summary_reports force_show)
|
||||
|
@ -39,6 +39,9 @@ function(qt_internal_read_repo_dependencies out_var repo_dir)
|
||||
if(NOT dependency IN_LIST seen)
|
||||
qt_internal_read_repo_dependencies(subdeps "${dependency_repo_dir}"
|
||||
${seen} ${dependency})
|
||||
if(dependency MATCHES "^tqtc-(.+)")
|
||||
set(dependency "${CMAKE_MATCH_1}")
|
||||
endif()
|
||||
list(APPEND dependencies ${subdeps} ${dependency})
|
||||
endif()
|
||||
endif()
|
||||
@ -106,6 +109,8 @@ endif()
|
||||
include(QtPlatformSupport)
|
||||
|
||||
# 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
|
||||
# qt_build_internals_disable_pkg_config_if_needed.
|
||||
@ -126,6 +131,9 @@ function(qt_internal_compute_feature_value_from_possible_input feature)
|
||||
endif()
|
||||
|
||||
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()
|
||||
endfunction()
|
||||
|
||||
@ -622,9 +630,6 @@ function(qt_internal_qt_configure_end)
|
||||
# reconfigurations that are done by calling cmake directly don't trigger configure specific
|
||||
# logic.
|
||||
unset(QT_INTERNAL_CALLED_FROM_CONFIGURE CACHE)
|
||||
|
||||
# Clean up stale feature input values.
|
||||
qt_internal_clean_feature_inputs()
|
||||
endfunction()
|
||||
|
||||
macro(qt_build_repo)
|
||||
|
@ -2,6 +2,7 @@
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
# Includes QtSetup and friends for private CMake API.
|
||||
set(QT_INTERNAL_IS_STANDALONE_TEST TRUE)
|
||||
qt_internal_project_setup()
|
||||
qt_build_internals_set_up_private_api()
|
||||
|
||||
|
@ -422,7 +422,8 @@ function(qt_internal_add_configure_time_executable target)
|
||||
)
|
||||
|
||||
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)
|
||||
foreach(source IN LISTS sources)
|
||||
file(TIMESTAMP "${source}" ts "%s")
|
||||
@ -437,6 +438,37 @@ function(qt_internal_add_configure_time_executable target)
|
||||
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)
|
||||
foreach(arg IN LISTS multi_value_args)
|
||||
string(TOLOWER "${arg}" template_arg_name)
|
||||
@ -460,33 +492,11 @@ function(qt_internal_add_configure_time_executable target)
|
||||
set(template "${arg_CMAKELISTS_TEMPLATE}")
|
||||
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)
|
||||
|
||||
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}}")
|
||||
if(EXISTS "${target_binary_dir}/CMakeCache.txt")
|
||||
file(REMOVE "${target_binary_dir}/CMakeCache.txt")
|
||||
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
|
||||
"${target_binary_dir}"
|
||||
@ -496,7 +506,12 @@ function(qt_internal_add_configure_time_executable target)
|
||||
OUTPUT_VARIABLE try_compile_output
|
||||
)
|
||||
|
||||
set(QT_INTERNAL_CMAKE_FLAGS_CONFIGURE_TIME_TOOL_${target}
|
||||
"${cmake_flags_arg}" CACHE INTERNAL "")
|
||||
|
||||
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)
|
||||
message(FATAL_ERROR "Unable to build ${target}: ${try_compile_output}")
|
||||
endif()
|
||||
|
@ -883,6 +883,18 @@ function(qt_internal_detect_dirty_features)
|
||||
"to ${FEATURE_${feature}}")
|
||||
set(dirty_build TRUE)
|
||||
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()
|
||||
unset("QT_FEATURE_${feature}" CACHE)
|
||||
endforeach()
|
||||
@ -899,18 +911,6 @@ function(qt_internal_detect_dirty_features)
|
||||
endif()
|
||||
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)
|
||||
if(DEFINED "TEST_${name}")
|
||||
return()
|
||||
|
@ -1,6 +1,23 @@
|
||||
# Copyright (C) 2022 The Qt Company Ltd.
|
||||
# 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.
|
||||
# Function has two modes dependending on the specified arguments.
|
||||
# Arguments:
|
||||
@ -33,9 +50,21 @@ function(qt_internal_add_linker_version_script target)
|
||||
endif()
|
||||
string(APPEND contents "};\n")
|
||||
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")
|
||||
set(property_genex "$<TARGET_PROPERTY:${target},_qt_extra_linker_script_content>")
|
||||
set(check_genex "$<BOOL:${property_genex}>")
|
||||
|
@ -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_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)
|
||||
set(in_files_${type} "")
|
||||
set(fw_output_header_dir "${output_dir_${type}}")
|
||||
foreach(hdr IN LISTS arg_${type})
|
||||
get_filename_component(in_file_path ${hdr} ABSOLUTE)
|
||||
get_filename_component(in_file_name ${hdr} 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 in_files_${type} "${in_file_path}")
|
||||
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()
|
||||
|
||||
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
|
||||
QT_COPIED_FRAMEWORK_HEADERS "${out_files}")
|
||||
endfunction()
|
||||
|
@ -11,7 +11,8 @@ if(EXISTS ${HEADER_CHECK_EXCEPTIONS})
|
||||
file(READ ${HEADER_CHECK_EXCEPTIONS} header_check_exception_list)
|
||||
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)
|
||||
file(TO_CMAKE_PATH "${exception}" exception)
|
||||
if(exception STREQUAL header)
|
||||
|
@ -12,7 +12,6 @@ endfunction()
|
||||
qt_set01(LINUX CMAKE_SYSTEM_NAME STREQUAL "Linux")
|
||||
qt_set01(HPUX CMAKE_SYSTEM_NAME STREQUAL "HPUX")
|
||||
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(VXWORKS CMAKE_SYSTEM_NAME STREQUAL "VxWorks") # FIXME: How to identify this?
|
||||
qt_set01(QNX CMAKE_SYSTEM_NAME STREQUAL "QNX") # FIXME: How to identify this?
|
||||
|
@ -374,7 +374,9 @@ function(qtConfValidateValue opt val out_var)
|
||||
endforeach()
|
||||
|
||||
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()
|
||||
|
||||
function(qt_commandline_mapped_enum_value opt key out_var)
|
||||
|
@ -3,10 +3,16 @@
|
||||
|
||||
## Set a default build type if none was specified
|
||||
|
||||
# Set the QT_IS_BUILDING_QT variable so we can verify whether we are building
|
||||
# Qt from source
|
||||
set(QT_BUILDING_QT TRUE CACHE BOOL
|
||||
# Set the QT_BUILDING_QT variable so we can verify whether we are building
|
||||
# Qt from source.
|
||||
# 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.")
|
||||
endif()
|
||||
|
||||
# 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
|
||||
|
@ -2,4 +2,4 @@
|
||||
# bypassing the Qt6 Config file, aka find_package(Qt6SpecificFoo) repated x times. But it's not
|
||||
# critical.
|
||||
find_package(@INSTALL_CMAKE_NAMESPACE@ @main_qt_package_version@
|
||||
REQUIRED COMPONENTS @QT_REPO_KNOWN_MODULES_STRING@)
|
||||
COMPONENTS @QT_REPO_KNOWN_MODULES_STRING@)
|
||||
|
@ -61,13 +61,6 @@ function(qt_internal_target_sync_headers target module_headers module_headers_ge
|
||||
set(is_framework FALSE)
|
||||
if(NOT is_interface_lib)
|
||||
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()
|
||||
|
||||
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}
|
||||
-stagingDir "${syncqt_staging_dir}"
|
||||
-knownModules ${known_modules}
|
||||
${framework_args}
|
||||
${version_script_args}
|
||||
)
|
||||
list(JOIN syncqt_args "\n" syncqt_args_string)
|
||||
|
@ -16,6 +16,8 @@
|
||||
# Custom compilation flags.
|
||||
# EXTRA_LINKER_SCRIPT_CONTENT
|
||||
# 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
|
||||
# Skip the specified source files by PRECOMPILE_HEADERS feature.
|
||||
function(qt_internal_extend_target target)
|
||||
@ -51,6 +53,7 @@ function(qt_internal_extend_target target)
|
||||
CONDITION
|
||||
CONDITION_INDEPENDENT_SOURCES
|
||||
COMPILE_FLAGS
|
||||
EXTRA_LINKER_SCRIPT_EXPORTS
|
||||
)
|
||||
|
||||
cmake_parse_arguments(PARSE_ARGV 1 arg
|
||||
@ -260,6 +263,10 @@ function(qt_internal_extend_target target)
|
||||
set_target_properties(${target} PROPERTIES
|
||||
_qt_extra_linker_script_content "${arg_EXTRA_LINKER_SCRIPT_CONTENT}")
|
||||
endif()
|
||||
if(arg_EXTRA_LINKER_SCRIPT_EXPORTS)
|
||||
set_target_properties(${target} PROPERTIES
|
||||
_qt_extra_linker_script_exports "${arg_EXTRA_LINKER_SCRIPT_EXPORTS}")
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
function(qt_is_imported_target target out_var)
|
||||
|
@ -131,10 +131,12 @@ set(__qt_chainload_toolchain_file \"\${__qt_initially_configured_toolchain_file}
|
||||
list(APPEND init_platform "
|
||||
set(__qt_initial_c_compiler \"${CMAKE_C_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 \"\")
|
||||
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 \"\")
|
||||
endif()")
|
||||
endif()
|
||||
|
@ -53,15 +53,16 @@ function(qt_internal_create_wrapper_scripts)
|
||||
qt_install(PROGRAMS "${QT_BUILD_DIR}/${INSTALL_BINDIR}/qt-cmake-create.bat"
|
||||
DESTINATION "${INSTALL_BINDIR}")
|
||||
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.
|
||||
# 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
|
||||
# build their CMake app with the Unix Makefiles generator, while Qt should be built with the
|
||||
# Ninja generator.
|
||||
# The private wrapper is more conveient for building Qt itself, because a developer doesn't need
|
||||
# to specify the same options for each qt module built.
|
||||
set(__qt_cmake_extra "-G\"${CMAKE_GENERATOR}\"")
|
||||
# Ninja generator. In a similar vein, we do want to use the same compiler for all Qt modules,
|
||||
# but not for user applications.
|
||||
# The private wrapper is more convenient for building Qt itself, because a developer doesn't
|
||||
# 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)
|
||||
configure_file("${CMAKE_CURRENT_SOURCE_DIR}/bin/qt-cmake.in"
|
||||
"${QT_BUILD_DIR}/${INSTALL_LIBEXECDIR}/qt-cmake-private" @ONLY
|
||||
@ -202,6 +203,22 @@ function(qt_internal_create_wrapper_scripts)
|
||||
elseif(CMAKE_BUILD_TYPE)
|
||||
set(__qt_configured_configs "${CMAKE_BUILD_TYPE}")
|
||||
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"
|
||||
"${QT_BUILD_DIR}/${INSTALL_LIBEXECDIR}/${__qt_cmake_install_script_name}" @ONLY)
|
||||
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_install_android_helper_scripts()
|
||||
qt_internal_create_qt_configure_redo_script()
|
||||
endfunction()
|
||||
|
||||
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.
|
||||
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
|
||||
${__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"
|
||||
DESTINATION "${destination}")
|
||||
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()
|
||||
|
@ -51,9 +51,21 @@ instructions:
|
||||
condition: property
|
||||
property: host.os
|
||||
equals_value: Windows
|
||||
|
||||
- type: EnvironmentVariable
|
||||
variableName: CTEST_ARGS
|
||||
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
|
||||
variableName: CTEST_ARGS
|
||||
variableValue: " --stop-on-failure"
|
||||
@ -61,12 +73,37 @@ instructions:
|
||||
condition: property
|
||||
property: features
|
||||
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
|
||||
command: "{{.Env.TESTS_ENV_PREFIX}} ctest {{.Env.CTEST_ARGS}}"
|
||||
executeCommandArgumentSplitingBehavior: SplitAfterVariableSubstitution
|
||||
ignoreExitCode: false
|
||||
maxTimeInSeconds: 10800
|
||||
maxTimeBetweenOutput: 900
|
||||
maxTimeBetweenOutput: "{{.Env.COIN_COMMAND_OUTPUT_TIMEOUT}}"
|
||||
userMessageOnFailure: >
|
||||
Failed to run tests.
|
||||
|
||||
|
2
configure
vendored
2
configure
vendored
@ -136,7 +136,7 @@ determineOptFilePath "$@"
|
||||
optfilepath=${outpathPrefix}/config.opt
|
||||
opttmpfilepath=${outpathPrefix}/config.opt.in
|
||||
|
||||
redofilepath=${outpathPrefix}/config.redo
|
||||
redofilepath=${outpathPrefix}/config.redo.last
|
||||
redotmpfilepath=${outpathPrefix}/config.redo.in
|
||||
|
||||
fresh_requested_arg=
|
||||
|
@ -79,7 +79,7 @@ cd "%TOPQTDIR%"
|
||||
rem Write config.opt if we're not currently -redo'ing
|
||||
set OPT_FILE_PATH=%TOPQTDIR%\config.opt
|
||||
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 FRESH_REQUESTED_ARG=
|
||||
if not defined redoing (
|
||||
|
@ -202,9 +202,9 @@ manifestmeta.thumbnail.attributes = "imageUrl:qthelp\://org.qt-project.qtdoc.$Q
|
||||
|
||||
manifestmeta.thumbnail.names = "QtCore/Contiguous Cache Example" \
|
||||
"QtCore/Custom Type Example" \
|
||||
"QtCore/JSON Save Game Example" \
|
||||
"QtCore/Semaphores Example" \
|
||||
"QtCore/Wait Conditions Example" \
|
||||
"QtCore/Saving and Loading a Game" \
|
||||
"QtCore/Producer and Consumer using Semaphores" \
|
||||
"QtCore/Producer and Consumer using Wait Conditions" \
|
||||
"QtConcurrent/Word Count" \
|
||||
"QtGui/Raster Window Example" \
|
||||
"QtNetwork/Network Download*" \
|
||||
|
Binary file not shown.
Before Width: | Height: | Size: 12 KiB |
@ -4,7 +4,7 @@
|
||||
#ifndef BINDABLESUBSCRIPTION_H
|
||||
#define BINDABLESUBSCRIPTION_H
|
||||
|
||||
#include <QPointer>
|
||||
#include <QBindable>
|
||||
#include <QProperty>
|
||||
|
||||
class BindableUser;
|
||||
|
@ -4,6 +4,7 @@
|
||||
#ifndef BINDABLEUSER_H
|
||||
#define BINDABLEUSER_H
|
||||
|
||||
#include <QBindable>
|
||||
#include <QLocale>
|
||||
#include <QProperty>
|
||||
|
||||
|
@ -6,15 +6,15 @@
|
||||
#include "bindableuser.h"
|
||||
|
||||
#include <QApplication>
|
||||
#include <QButtonGroup>
|
||||
#include <QBindable>
|
||||
#include <QLabel>
|
||||
#include <QLocale>
|
||||
#include <QPushButton>
|
||||
#include <QRadioButton>
|
||||
#include <QSpinBox>
|
||||
#include <QProperty>
|
||||
#include <QString>
|
||||
#include <QDateTimeEdit>
|
||||
#include <QBindable>
|
||||
|
||||
using namespace Qt::StringLiterals;
|
||||
|
||||
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
|
||||
|
||||
// Initialize subscription data
|
||||
QRadioButton *monthly = w.findChild<QRadioButton *>("btnMonthly");
|
||||
QRadioButton *monthly = w.findChild<QRadioButton *>(u"btnMonthly"_s);
|
||||
QObject::connect(monthly, &QRadioButton::clicked, [&] {
|
||||
subscription.setDuration(BindableSubscription::Monthly);
|
||||
});
|
||||
QRadioButton *quarterly = w.findChild<QRadioButton *>("btnQuarterly");
|
||||
QRadioButton *quarterly = w.findChild<QRadioButton *>(u"btnQuarterly"_s);
|
||||
QObject::connect(quarterly, &QRadioButton::clicked, [&] {
|
||||
subscription.setDuration(BindableSubscription::Quarterly);
|
||||
});
|
||||
QRadioButton *yearly = w.findChild<QRadioButton *>("btnYearly");
|
||||
QRadioButton *yearly = w.findChild<QRadioButton *>(u"btnYearly"_s);
|
||||
QObject::connect(yearly, &QRadioButton::clicked, [&] {
|
||||
subscription.setDuration(BindableSubscription::Yearly);
|
||||
});
|
||||
|
||||
// Initialize user data
|
||||
QPushButton *germany = w.findChild<QPushButton *>("btnGermany");
|
||||
QPushButton *germany = w.findChild<QPushButton *>(u"btnGermany"_s);
|
||||
QObject::connect(germany, &QPushButton::clicked, [&] {
|
||||
user.setCountry(BindableUser::Country::Germany);
|
||||
});
|
||||
QPushButton *finland = w.findChild<QPushButton *>("btnFinland");
|
||||
QPushButton *finland = w.findChild<QPushButton *>(u"btnFinland"_s);
|
||||
QObject::connect(finland, &QPushButton::clicked, [&] {
|
||||
user.setCountry(BindableUser::Country::Finland);
|
||||
});
|
||||
QPushButton *norway = w.findChild<QPushButton *>("btnNorway");
|
||||
QPushButton *norway = w.findChild<QPushButton *>(u"btnNorway"_s);
|
||||
QObject::connect(norway, &QPushButton::clicked, [&] {
|
||||
user.setCountry(BindableUser::Country::Norway);
|
||||
});
|
||||
|
||||
QSpinBox *ageSpinBox = w.findChild<QSpinBox *>("ageSpinBox");
|
||||
QSpinBox *ageSpinBox = w.findChild<QSpinBox *>(u"ageSpinBox"_s);
|
||||
QBindable<int> ageBindable(ageSpinBox, "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
|
||||
//! [update-ui]
|
||||
|
@ -3,7 +3,8 @@
|
||||
|
||||
/*!
|
||||
\example bindableproperties
|
||||
\title Bindable Properties Example
|
||||
\examplecategory {Data Processing & I/O}
|
||||
\title Bindable Properties
|
||||
\brief Demonstrates how the usage of bindable properties can simplify
|
||||
your C++ code.
|
||||
|
||||
|
@ -6,11 +6,14 @@
|
||||
#include "user.h"
|
||||
|
||||
#include <QApplication>
|
||||
#include <QButtonGroup>
|
||||
#include <QLabel>
|
||||
#include <QLocale>
|
||||
#include <QPushButton>
|
||||
#include <QRadioButton>
|
||||
#include <QSpinBox>
|
||||
#include <QString>
|
||||
|
||||
using namespace Qt::StringLiterals;
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
@ -24,40 +27,40 @@ int main(int argc, char *argv[])
|
||||
SubscriptionWindow w;
|
||||
|
||||
// Initialize subscription data
|
||||
QRadioButton *monthly = w.findChild<QRadioButton *>("btnMonthly");
|
||||
QRadioButton *monthly = w.findChild<QRadioButton *>(u"btnMonthly"_s);
|
||||
QObject::connect(monthly, &QRadioButton::clicked, &subscription, [&] {
|
||||
subscription.setDuration(Subscription::Monthly);
|
||||
});
|
||||
QRadioButton *quarterly = w.findChild<QRadioButton *>("btnQuarterly");
|
||||
QRadioButton *quarterly = w.findChild<QRadioButton *>(u"btnQuarterly"_s);
|
||||
QObject::connect(quarterly, &QRadioButton::clicked, &subscription, [&] {
|
||||
subscription.setDuration(Subscription::Quarterly);
|
||||
});
|
||||
QRadioButton *yearly = w.findChild<QRadioButton *>("btnYearly");
|
||||
QRadioButton *yearly = w.findChild<QRadioButton *>(u"btnYearly"_s);
|
||||
QObject::connect(yearly, &QRadioButton::clicked, &subscription, [&] {
|
||||
subscription.setDuration(Subscription::Yearly);
|
||||
});
|
||||
|
||||
// Initialize user data
|
||||
QPushButton *germany = w.findChild<QPushButton *>("btnGermany");
|
||||
QPushButton *germany = w.findChild<QPushButton *>(u"btnGermany"_s);
|
||||
QObject::connect(germany, &QPushButton::clicked, &user, [&] {
|
||||
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, [&] {
|
||||
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, [&] {
|
||||
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) {
|
||||
user.setAge(value);
|
||||
});
|
||||
|
||||
// Initialize price data
|
||||
QLabel *priceDisplay = w.findChild<QLabel *>("priceDisplay");
|
||||
QLabel *priceDisplay = w.findChild<QLabel *>(u"priceDisplay"_s);
|
||||
priceDisplay->setText(QString::number(subscription.price()));
|
||||
priceDisplay->setEnabled(subscription.isValid());
|
||||
|
||||
|
Binary file not shown.
Before Width: | Height: | Size: 5.6 KiB After Width: | Height: | Size: 15 KiB |
@ -3,14 +3,16 @@
|
||||
|
||||
/*!
|
||||
\example ipc/localfortuneclient
|
||||
\title Local Fortune Client Example
|
||||
\examplecategory {Connectivity}
|
||||
\title Local Fortune Client
|
||||
\ingroup examples-ipc
|
||||
\brief Demonstrates using QLocalSocket for a simple local service client.
|
||||
|
||||
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
|
||||
\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
|
||||
|
||||
*/
|
||||
|
@ -3,13 +3,14 @@
|
||||
|
||||
/*!
|
||||
\example ipc/localfortuneserver
|
||||
\title Local Fortune Server Example
|
||||
\examplecategory {Connectivity}
|
||||
\title Local Fortune Server
|
||||
\ingroup examples-ipc
|
||||
\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
|
||||
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
|
||||
*/
|
||||
|
@ -3,10 +3,11 @@
|
||||
|
||||
/*!
|
||||
\example ipc/sharedmemory
|
||||
\title Shared Memory Example
|
||||
\examplecategory {Data Processing & I/O}
|
||||
\title IPC: Shared Memory
|
||||
\ingroup examples-ipc
|
||||
\brief Demonstrates doing inter-process communication using shared memory with
|
||||
the QSharedMemory class.
|
||||
\brief Demonstrates how to share image data between different processes
|
||||
using the Shared Memory IPC mechanism.
|
||||
|
||||
The Shared Memory example shows how to use the QSharedMemory class
|
||||
to implement inter-process communication using shared memory. To
|
||||
|
@ -1,14 +1,19 @@
|
||||
// Copyright (C) 2016 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
|
||||
|
||||
#include <QtWidgets>
|
||||
#include <QtNetwork>
|
||||
|
||||
#include "client.h"
|
||||
|
||||
#include <QDialogButtonBox>
|
||||
#include <QGridLayout>
|
||||
#include <QGuiApplication>
|
||||
#include <QMessageBox>
|
||||
#include <QTimer>
|
||||
|
||||
using namespace Qt::StringLiterals;
|
||||
|
||||
Client::Client(QWidget *parent)
|
||||
: QDialog(parent),
|
||||
hostLineEdit(new QLineEdit("fortune")),
|
||||
hostLineEdit(new QLineEdit(u"fortune"_s)),
|
||||
getFortuneButton(new QPushButton(tr("Get Fortune"))),
|
||||
statusLabel(new QLabel(tr("This examples requires that you run the "
|
||||
"Local Fortune Server example as well."))),
|
||||
@ -28,7 +33,7 @@ Client::Client(QWidget *parent)
|
||||
buttonBox->addButton(quitButton, QDialogButtonBox::RejectRole);
|
||||
|
||||
in.setDevice(socket);
|
||||
in.setVersion(QDataStream::Qt_5_10);
|
||||
in.setVersion(QDataStream::Qt_6_0);
|
||||
|
||||
connect(hostLineEdit, &QLineEdit::textChanged,
|
||||
this, &Client::enableGetFortuneButton);
|
||||
|
@ -4,15 +4,12 @@
|
||||
#ifndef CLIENT_H
|
||||
#define CLIENT_H
|
||||
|
||||
#include <QDialog>
|
||||
#include <QDataStream>
|
||||
#include <QDialog>
|
||||
#include <QLabel>
|
||||
#include <QLineEdit>
|
||||
#include <QLocalSocket>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
class QLabel;
|
||||
class QLineEdit;
|
||||
class QPushButton;
|
||||
QT_END_NAMESPACE
|
||||
#include <QPushButton>
|
||||
|
||||
class Client : public QDialog
|
||||
{
|
||||
|
@ -1,10 +1,10 @@
|
||||
// Copyright (C) 2016 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
|
||||
|
||||
#include <QApplication>
|
||||
|
||||
#include "client.h"
|
||||
|
||||
#include <QApplication>
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
QApplication app(argc, argv);
|
||||
|
@ -1,10 +1,10 @@
|
||||
// Copyright (C) 2016 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
|
||||
|
||||
#include <QApplication>
|
||||
|
||||
#include "server.h"
|
||||
|
||||
#include <QApplication>
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
QApplication app(argc, argv);
|
||||
|
@ -3,27 +3,33 @@
|
||||
|
||||
#include "server.h"
|
||||
|
||||
#include <QtWidgets>
|
||||
#include <QtNetwork>
|
||||
#include <QDialogButtonBox>
|
||||
#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)
|
||||
: 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);
|
||||
|
||||
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->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.")
|
||||
<< 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("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"));
|
||||
quitButton->setAutoDefault(false);
|
||||
connect(quitButton, &QPushButton::clicked, this, &Server::close);
|
||||
connect(server, &QLocalServer::newConnection, this, &Server::sendFortune);
|
||||
|
||||
QHBoxLayout *buttonLayout = new QHBoxLayout;
|
||||
buttonLayout->addStretch(1);
|
||||
buttonLayout->addWidget(quitButton);
|
||||
buttonLayout->addStretch(1);
|
||||
QDialogButtonBox *buttonBox = new QDialogButtonBox;
|
||||
buttonBox->addButton(listenButton, QDialogButtonBox::ActionRole);
|
||||
buttonBox->addButton(stopListeningButton, QDialogButtonBox::ActionRole);
|
||||
buttonBox->addButton(quitButton, QDialogButtonBox::RejectRole);
|
||||
|
||||
QVBoxLayout *mainLayout = new QVBoxLayout(this);
|
||||
mainLayout->addWidget(statusLabel);
|
||||
mainLayout->addLayout(buttonLayout);
|
||||
QGridLayout *mainLayout = new QGridLayout(this);
|
||||
mainLayout->addWidget(hostLabel, 0, 0);
|
||||
mainLayout->addWidget(hostLineEdit, 0, 1);
|
||||
mainLayout->addWidget(statusLabel, 2, 0, 3, 2);
|
||||
mainLayout->addWidget(buttonBox, 10, 0, 2, 2);
|
||||
|
||||
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()
|
||||
{
|
||||
QByteArray block;
|
||||
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 QString &message = fortunes.at(fortuneIndex);
|
||||
out << quint32(message.size());
|
||||
|
@ -4,27 +4,33 @@
|
||||
#ifndef SERVER_H
|
||||
#define SERVER_H
|
||||
|
||||
#include <QApplication>
|
||||
#include <QDialog>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
class QLabel;
|
||||
class QPushButton;
|
||||
class QLocalServer;
|
||||
QT_END_NAMESPACE
|
||||
#include <QLabel>
|
||||
#include <QLineEdit>
|
||||
#include <QLocalServer>
|
||||
#include <QPushButton>
|
||||
|
||||
class Server : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_DECLARE_TR_FUNCTIONS(Server)
|
||||
|
||||
public:
|
||||
explicit Server(QWidget *parent = nullptr);
|
||||
|
||||
private slots:
|
||||
void sendFortune();
|
||||
|
||||
private:
|
||||
void sendFortune();
|
||||
void toggleListenButton();
|
||||
void listenToServer();
|
||||
void stopListening();
|
||||
|
||||
QLocalServer *server;
|
||||
QLineEdit *hostLineEdit;
|
||||
QLabel *statusLabel;
|
||||
QPushButton *listenButton;
|
||||
QPushButton *stopListeningButton;
|
||||
QStringList fortunes;
|
||||
QString name;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
@ -2,8 +2,12 @@
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
|
||||
|
||||
#include "dialog.h"
|
||||
#include <QFileDialog>
|
||||
|
||||
#include <QBuffer>
|
||||
#include <QFileDialog>
|
||||
#include <QNativeIpcKey>
|
||||
|
||||
using namespace Qt::StringLiterals;
|
||||
|
||||
/*!
|
||||
\class Dialog
|
||||
@ -29,8 +33,9 @@
|
||||
each button.
|
||||
*/
|
||||
//! [0]
|
||||
|
||||
Dialog::Dialog(QWidget *parent)
|
||||
: QDialog(parent), sharedMemory("QSharedMemoryExample")
|
||||
: QDialog(parent), sharedMemory(QNativeIpcKey(u"QSharedMemoryExample"_s))
|
||||
{
|
||||
ui.setupUi(this);
|
||||
connect(ui.loadFromFileButton, &QPushButton::clicked,
|
||||
|
@ -6,6 +6,7 @@
|
||||
|
||||
#include <QDialog>
|
||||
#include <QSharedMemory>
|
||||
|
||||
#include "ui_dialog.h"
|
||||
|
||||
//! [0]
|
||||
@ -13,21 +14,21 @@ class Dialog : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
public:
|
||||
Dialog(QWidget *parent = nullptr);
|
||||
|
||||
public slots:
|
||||
public slots:
|
||||
void loadFromFile();
|
||||
void loadFromMemory();
|
||||
|
||||
private:
|
||||
private:
|
||||
void detach();
|
||||
|
||||
private:
|
||||
private:
|
||||
Ui::Dialog ui;
|
||||
QSharedMemory sharedMemory;
|
||||
};
|
||||
//! [0]
|
||||
|
||||
#endif
|
||||
#endif // DIALOG_H
|
||||
|
||||
|
@ -1,9 +1,10 @@
|
||||
// Copyright (C) 2016 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
|
||||
|
||||
#include <QApplication>
|
||||
#include "dialog.h"
|
||||
|
||||
#include <QApplication>
|
||||
|
||||
//! [0]
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
|
@ -3,8 +3,9 @@
|
||||
|
||||
/*!
|
||||
\example mimetypes/mimetypebrowser
|
||||
\examplecategory {Data Processing & I/O}
|
||||
\ingroup examples-mimetype
|
||||
\title MIME Type Browser Example
|
||||
\title MIME Type Browser
|
||||
|
||||
\brief Shows the hierarchy of MIME types and
|
||||
can be used to determine the MIME type of a file.
|
||||
|
@ -4,10 +4,8 @@
|
||||
#include "mainwindow.h"
|
||||
|
||||
#include <QApplication>
|
||||
#include <QScreen>
|
||||
|
||||
#include <QCommandLineParser>
|
||||
#include <QCommandLineOption>
|
||||
#include <QScreen>
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
|
@ -4,23 +4,18 @@
|
||||
#include "mainwindow.h"
|
||||
#include "mimetypemodel.h"
|
||||
|
||||
#include <QAction>
|
||||
#include <QApplication>
|
||||
#include <QFileDialog>
|
||||
#include <QFileInfo>
|
||||
#include <QInputDialog>
|
||||
#include <QItemSelectionModel>
|
||||
#include <QMenu>
|
||||
#include <QMenuBar>
|
||||
#include <QMessageBox>
|
||||
#include <QPlainTextEdit>
|
||||
#include <QSplitter>
|
||||
#include <QStatusBar>
|
||||
#include <QTextEdit>
|
||||
#include <QTreeView>
|
||||
|
||||
#include <QFileInfo>
|
||||
#include <QItemSelectionModel>
|
||||
#include <QMimeDatabase>
|
||||
#include <QMimeType>
|
||||
#include <QSplitter>
|
||||
#include <QStatusBar>
|
||||
|
||||
MainWindow::MainWindow(QWidget *parent)
|
||||
: QMainWindow(parent)
|
||||
@ -45,7 +40,8 @@ MainWindow::MainWindow(QWidget *parent)
|
||||
findAction->setShortcuts(QKeySequence::Find);
|
||||
m_findNextAction = findMenu->addAction(tr("Find &Next"), this, &MainWindow::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);
|
||||
|
||||
menuBar()->addMenu(tr("&About"))->addAction(tr("&About Qt"), qApp, &QApplication::aboutQt);
|
||||
@ -54,8 +50,8 @@ MainWindow::MainWindow(QWidget *parent)
|
||||
setCentralWidget(centralSplitter);
|
||||
m_treeView->setUniformRowHeights(true);
|
||||
m_treeView->setModel(m_model);
|
||||
|
||||
const auto items = m_model->findItems("application/octet-stream", Qt::MatchContains | Qt::MatchFixedString | Qt::MatchRecursive);
|
||||
const auto flags = Qt::MatchContains | Qt::MatchFixedString | Qt::MatchRecursive;
|
||||
const auto items = m_model->findItems("application/octet-stream", flags);
|
||||
if (!items.isEmpty())
|
||||
m_treeView->expand(m_model->indexFromItem(items.constFirst()));
|
||||
|
||||
@ -93,7 +89,8 @@ void MainWindow::detectFile()
|
||||
const QModelIndex index = mimeType.isValid()
|
||||
? m_model->indexForMimeType(mimeType.name()) : QModelIndex();
|
||||
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);
|
||||
} else {
|
||||
QMessageBox::information(this, tr("Unknown File Type"),
|
||||
@ -138,8 +135,8 @@ void MainWindow::find()
|
||||
|
||||
m_findMatches.clear();
|
||||
m_findIndex = 0;
|
||||
const QList<QStandardItem *> items =
|
||||
m_model->findItems(value, Qt::MatchContains | Qt::MatchFixedString | Qt::MatchRecursive);
|
||||
const auto flags = Qt::MatchContains | Qt::MatchFixedString | Qt::MatchRecursive;
|
||||
const QList<QStandardItem *> items = m_model->findItems(value, flags);
|
||||
for (const QStandardItem *item : items)
|
||||
m_findMatches.append(m_model->indexFromItem(item));
|
||||
statusBar()->showMessage(tr("%n mime types match \"%1\".", 0, m_findMatches.size()).arg(value));
|
||||
|
@ -4,12 +4,11 @@
|
||||
#ifndef MAINWINDOW_H
|
||||
#define MAINWINDOW_H
|
||||
|
||||
#include <QAction>
|
||||
#include <QMainWindow>
|
||||
#include <QModelIndexList>
|
||||
|
||||
QT_FORWARD_DECLARE_CLASS(QAction)
|
||||
QT_FORWARD_DECLARE_CLASS(QTextEdit)
|
||||
QT_FORWARD_DECLARE_CLASS(QTreeView)
|
||||
#include <QTextEdit>
|
||||
#include <QTreeView>
|
||||
|
||||
class MimetypeModel;
|
||||
|
||||
|
@ -4,14 +4,14 @@
|
||||
#ifndef MIMETYPEMODEL_H
|
||||
#define MIMETYPEMODEL_H
|
||||
|
||||
#include <QStandardItemModel>
|
||||
#include <QCoreApplication>
|
||||
#include <QHash>
|
||||
|
||||
QT_FORWARD_DECLARE_CLASS(QMimeType)
|
||||
#include <QMimeType>
|
||||
#include <QStandardItemModel>
|
||||
|
||||
class MimetypeModel : public QStandardItemModel
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_DECLARE_TR_FUNCTIONS(MimetypeModel)
|
||||
public:
|
||||
enum Columns { NameColumn, ColumnCount };
|
||||
|
||||
|
@ -5,6 +5,7 @@
|
||||
\title Qt Android Notifier
|
||||
\example platform/androidnotifier
|
||||
\examplecategory {Mobile}
|
||||
|
||||
\brief Demonstrates calling Java code from Qt in an Android application.
|
||||
\ingroup androidplatform
|
||||
|
||||
|
@ -20,13 +20,14 @@ qt_standard_project_setup()
|
||||
|
||||
qt_add_executable(convert
|
||||
cborconverter.cpp cborconverter.h
|
||||
converter.h
|
||||
converter.cpp converter.h
|
||||
datastreamconverter.cpp datastreamconverter.h
|
||||
debugtextdumper.cpp debugtextdumper.h
|
||||
jsonconverter.cpp jsonconverter.h
|
||||
main.cpp
|
||||
nullconverter.cpp nullconverter.h
|
||||
textconverter.cpp textconverter.h
|
||||
variantorderedmap.h
|
||||
xmlconverter.cpp xmlconverter.h
|
||||
)
|
||||
|
||||
|
@ -2,6 +2,7 @@
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
|
||||
|
||||
#include "cborconverter.h"
|
||||
#include "variantorderedmap.h"
|
||||
|
||||
#include <QCborArray>
|
||||
#include <QCborMap>
|
||||
@ -9,6 +10,7 @@
|
||||
#include <QCborStreamWriter>
|
||||
#include <QCborValue>
|
||||
#include <QDataStream>
|
||||
#include <QDebug>
|
||||
#include <QFile>
|
||||
#include <QFloat16>
|
||||
#include <QMetaType>
|
||||
@ -57,9 +59,9 @@ QT_END_NAMESPACE
|
||||
// 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.
|
||||
|
||||
//! [0]
|
||||
static QVariant convertCborValue(const QCborValue &value);
|
||||
|
||||
//! [0]
|
||||
static QVariant convertCborMap(const QCborMap &map)
|
||||
{
|
||||
VariantOrderedMap result;
|
||||
@ -87,8 +89,9 @@ static QVariant convertCborValue(const QCborValue &value)
|
||||
return value.toVariant();
|
||||
}
|
||||
//! [0]
|
||||
enum TrimFloatingPoint { Double, Float, Float16 };
|
||||
|
||||
//! [1]
|
||||
enum TrimFloatingPoint { Double, Float, Float16 };
|
||||
static QCborValue convertFromVariant(const QVariant &v, TrimFloatingPoint fpTrimming)
|
||||
{
|
||||
if (v.userType() == QMetaType::QVariantList) {
|
||||
@ -140,20 +143,6 @@ const char *CborDiagnosticDumper::optionsHelp() const
|
||||
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,
|
||||
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);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
QTextStream out(f);
|
||||
@ -221,7 +209,6 @@ bool CborConverter::probeFile(QIODevice *f) const
|
||||
return f->isReadable() && f->peek(3) == QByteArray("\xd9\xd9\xf7", 3);
|
||||
}
|
||||
|
||||
//! [2]
|
||||
QVariant CborConverter::loadFile(QIODevice *f, const Converter *&outputConverter) const
|
||||
{
|
||||
const char *ptr = nullptr;
|
||||
@ -239,28 +226,25 @@ QVariant CborConverter::loadFile(QIODevice *f, const Converter *&outputConverter
|
||||
QCborValue contents = QCborValue::fromCbor(reader);
|
||||
qint64 offset = reader.currentOffset();
|
||||
if (reader.lastError()) {
|
||||
fprintf(stderr, "Error loading CBOR contents (byte %lld): %s\n", offset,
|
||||
qPrintable(reader.lastError().toString()));
|
||||
fprintf(stderr, " bytes: %s\n",
|
||||
(ptr ? mapped.mid(offset, 9) : f->read(9)).toHex(' ').constData());
|
||||
exit(EXIT_FAILURE);
|
||||
qFatal().nospace()
|
||||
<< "Error loading CBOR contents (byte " << offset
|
||||
<< "): " << reader.lastError().toString()
|
||||
<< "\n bytes: " << (ptr ? mapped.mid(offset, 9) : f->read(9));
|
||||
} 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)
|
||||
outputConverter = &cborDiagnosticDumper;
|
||||
else if (outputConverter == null)
|
||||
else if (isNull(outputConverter))
|
||||
return QVariant();
|
||||
else if (!outputConverter->outputOptions().testFlag(SupportsArbitraryMapKeys))
|
||||
return contents.toVariant();
|
||||
return convertCborValue(contents);
|
||||
}
|
||||
//! [2]
|
||||
//! [3]
|
||||
|
||||
void CborConverter::saveFile(QIODevice *f, const QVariant &contents, const QStringList &options) const
|
||||
{
|
||||
//! [3]
|
||||
bool useSignature = true;
|
||||
bool useIntegers = true;
|
||||
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);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
//! [4]
|
||||
|
||||
QCborValue v =
|
||||
convertFromVariant(contents,
|
||||
useFloat16 == Always ? Float16 : useFloat == Always ? Float : Double);
|
||||
@ -336,4 +319,3 @@ void CborConverter::saveFile(QIODevice *f, const QVariant &contents, const QStri
|
||||
opts |= QCborValue::UseFloat16;
|
||||
v.toCbor(writer, opts);
|
||||
}
|
||||
//! [4]
|
||||
|
@ -14,8 +14,6 @@ public:
|
||||
Directions directions() 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,
|
||||
const QStringList &options) const override;
|
||||
};
|
||||
|
@ -11,6 +11,7 @@ target.path = $$[QT_INSTALL_EXAMPLES]/corelib/serialization/convert
|
||||
INSTALLS += target
|
||||
|
||||
SOURCES += main.cpp \
|
||||
converter.cpp \
|
||||
cborconverter.cpp \
|
||||
datastreamconverter.cpp \
|
||||
debugtextdumper.cpp \
|
||||
@ -27,4 +28,5 @@ HEADERS += \
|
||||
jsonconverter.h \
|
||||
nullconverter.h \
|
||||
textconverter.h \
|
||||
variantorderedmap.h \
|
||||
xmlconverter.h
|
||||
|
44
examples/corelib/serialization/convert/converter.cpp
Normal file
44
examples/corelib/serialization/convert/converter.cpp
Normal 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();
|
||||
}
|
@ -6,31 +6,19 @@
|
||||
|
||||
#include <QIODevice>
|
||||
#include <QList>
|
||||
#include <QPair>
|
||||
#include <QStringList>
|
||||
#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
|
||||
{
|
||||
static QList<const Converter *> &converters();
|
||||
protected:
|
||||
Converter();
|
||||
static bool isNull(const Converter *converter); // in nullconverter.cpp
|
||||
|
||||
public:
|
||||
static Converter *null;
|
||||
static const QList<const Converter *> &allConverters();
|
||||
|
||||
enum class Direction { In = 1, Out = 2, InOut = In | Out };
|
||||
Q_DECLARE_FLAGS(Directions, Direction)
|
||||
@ -42,15 +30,16 @@ public:
|
||||
|
||||
virtual QString name() const = 0;
|
||||
virtual Directions directions() const = 0;
|
||||
virtual Options outputOptions() const = 0;
|
||||
virtual const char *optionsHelp() const = 0;
|
||||
virtual bool probeFile(QIODevice *f) const = 0;
|
||||
virtual QVariant loadFile(QIODevice *f, const Converter *&outputConverter) const = 0;
|
||||
virtual Options outputOptions() const;
|
||||
virtual const char *optionsHelp() const;
|
||||
virtual bool probeFile(QIODevice *f) const;
|
||||
virtual QVariant loadFile(QIODevice *f, const Converter *&outputConverter) const;
|
||||
virtual void saveFile(QIODevice *f, const QVariant &contents,
|
||||
const QStringList &options) const = 0;
|
||||
};
|
||||
|
||||
Q_DECLARE_OPERATORS_FOR_FLAGS(Converter::Directions)
|
||||
Q_DECLARE_OPERATORS_FOR_FLAGS(Converter::Options)
|
||||
//! [0]
|
||||
|
||||
#endif // CONVERTER_H
|
||||
|
@ -3,6 +3,7 @@
|
||||
|
||||
#include "datastreamconverter.h"
|
||||
#include "debugtextdumper.h"
|
||||
#include "variantorderedmap.h"
|
||||
|
||||
#include <QDataStream>
|
||||
|
||||
@ -79,10 +80,8 @@ QVariant DataStreamConverter::loadFile(QIODevice *f, const Converter *&outputCon
|
||||
outputConverter = &debugTextDumper;
|
||||
|
||||
char c;
|
||||
if (f->read(sizeof(signature) - 1) != signature || !f->getChar(&c) || (c != 'l' && c != 'B')) {
|
||||
fprintf(stderr, "Could not load QDataStream file: invalid signature.\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
if (f->read(sizeof(signature) - 1) != signature || !f->getChar(&c) || (c != 'l' && c != 'B'))
|
||||
qFatal("Could not load QDataStream file: invalid signature.");
|
||||
|
||||
QDataStream ds(f);
|
||||
ds.setByteOrder(c == 'l' ? QDataStream::LittleEndian : QDataStream::BigEndian);
|
||||
@ -124,15 +123,13 @@ void DataStreamConverter::saveFile(QIODevice *f, const QVariant &contents,
|
||||
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);
|
||||
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);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
char c = order == QDataStream::LittleEndian ? 'l' : 'B';
|
||||
|
@ -2,6 +2,7 @@
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
|
||||
|
||||
#include "debugtextdumper.h"
|
||||
#include "variantorderedmap.h"
|
||||
|
||||
#include <QDebug>
|
||||
#include <QTextStream>
|
||||
@ -58,29 +59,13 @@ Converter::Options DebugTextDumper::outputOptions() const
|
||||
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,
|
||||
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);
|
||||
s[s.size() - 1] = u'\n'; // replace the comma with newline
|
||||
|
||||
|
@ -13,9 +13,6 @@ public:
|
||||
QString name() const override;
|
||||
Directions directions() 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,
|
||||
const QStringList &options) const override;
|
||||
};
|
||||
|
Binary file not shown.
Before Width: | Height: | Size: 48 KiB After Width: | Height: | Size: 7.5 KiB |
@ -5,78 +5,152 @@
|
||||
\example serialization/convert
|
||||
\examplecategory {Data Processing & I/O}
|
||||
\meta tag {network}
|
||||
\title Convert Example
|
||||
\title Serialization Converter
|
||||
|
||||
\brief The Convert example demonstrates how to convert between different
|
||||
serialization formats.
|
||||
\brief How to convert between different serialization formats.
|
||||
|
||||
The Convert example converts between the serialization formats JSON, CBOR,
|
||||
XML, QDataStream and text. It can also auto detect the format being used.
|
||||
Not all formats support both input and output, and they have different
|
||||
sets of which types they support. QDataStream and XML are the richest,
|
||||
followed by CBOR, then JSON, and then the plain text one.
|
||||
This example converts between JSON, CBOR, XML, QDataStream and some simple
|
||||
text formats. It can auto-detect the format being used, or be told which
|
||||
format to use. Not all formats support both input and output, and they have
|
||||
different sets of which content datatypes they support. QDataStream and XML
|
||||
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
|
||||
|
||||
\sa {Parsing and displaying CBOR data}, {JSON Save Game}
|
||||
|
||||
\section1 The Converter Class
|
||||
|
||||
The Converter class is the abstract superclass for all the converters to
|
||||
and from all the formats. They all convert to and from the QVariant class,
|
||||
which is used to represent all the datastructures internally.
|
||||
The Converter class is the abstract superclass for all the converters to and
|
||||
from all the formats. They all convert from or to the QVariant class, which
|
||||
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()
|
||||
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
|
||||
and query which options are used by the different converters. The
|
||||
probeFile() function is used to determine if a file has the same file
|
||||
format as the converter. The loadFile() function deserializes the given
|
||||
file, while the saveFile() serializes to the given file.
|
||||
or both. These enable the main program to report what converters are
|
||||
available in its help text for the command-line options to select input and
|
||||
output formats.
|
||||
|
||||
\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
|
||||
notation. That is similar to JSON, but not exactly, because it allows
|
||||
displaying the contents of a CBOR stream losslessly, while a conversion
|
||||
to JSON is lossy.
|
||||
notation. It does not support loading data. The form of its output can be
|
||||
configured using two options. One selects whether to use the (more verbose)
|
||||
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
|
||||
|
||||
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
|
||||
QCborValue.
|
||||
\snippet serialization/convert/cborconverter.cpp 1
|
||||
QCborValue for output by the \c saveFile() of either class.
|
||||
|
||||
A CBOR-file is written using the saveFile() function.
|
||||
\snippet serialization/convert/cborconverter.cpp 3
|
||||
\snippet serialization/convert/cborconverter.cpp 4
|
||||
\snippet serialization/convert/cborconverter.cpp 1
|
||||
|
||||
\sa {CBOR Support in Qt}
|
||||
|
||||
\section1 The DataStreamConverter Class
|
||||
\section1 The convert program
|
||||
|
||||
The DataStreamConverter class is used to serialize to and from the
|
||||
QDataStream format. There is also the DebugTextDumper class for outputting
|
||||
the data lossless in a non-standardized human readable format.
|
||||
The \c main() function sets up a \l QApplication and a \l QCommandLineParser
|
||||
to make sense of the options the user has specified and provide help if the
|
||||
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.
|
||||
\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.
|
||||
\snippet serialization/convert/main.cpp 2
|
||||
*/
|
||||
|
@ -18,10 +18,8 @@ static const char jsonOptionHelp[] = "compact=no|yes Use compact JS
|
||||
static QJsonDocument convertFromVariant(const QVariant &v)
|
||||
{
|
||||
QJsonDocument doc = QJsonDocument::fromVariant(v);
|
||||
if (!doc.isObject() && !doc.isArray()) {
|
||||
fprintf(stderr, "Could not convert contents to JSON.\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
if (!doc.isObject() && !doc.isArray())
|
||||
qFatal("Could not convert contents to JSON.");
|
||||
return doc;
|
||||
}
|
||||
|
||||
@ -35,11 +33,6 @@ Converter::Directions JsonConverter::directions() const
|
||||
return Direction::InOut;
|
||||
}
|
||||
|
||||
Converter::Options JsonConverter::outputOptions() const
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
const char *JsonConverter::optionsHelp() const
|
||||
{
|
||||
return jsonOptionHelp;
|
||||
@ -75,11 +68,10 @@ QVariant JsonConverter::loadFile(QIODevice *f, const Converter *&outputConverter
|
||||
if (doc.isNull())
|
||||
doc = QJsonDocument::fromJson(f->readAll(), &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()));
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
if (outputConverter == null)
|
||||
if (isNull(outputConverter))
|
||||
return QVariant();
|
||||
return doc.toVariant();
|
||||
}
|
||||
@ -94,9 +86,8 @@ void JsonConverter::saveFile(QIODevice *f, const QVariant &contents,
|
||||
} else if (s == "compact=yes"_L1) {
|
||||
format = QJsonDocument::Compact;
|
||||
} 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);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -12,7 +12,6 @@ class JsonConverter : public Converter
|
||||
public:
|
||||
QString name() const override;
|
||||
Directions directions() 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;
|
||||
|
@ -1,4 +1,5 @@
|
||||
// Copyright (C) 2018 Intel Corporation.
|
||||
// Copyright (C) 2023 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
|
||||
|
||||
#include "converter.h"
|
||||
@ -13,27 +14,57 @@
|
||||
|
||||
using namespace Qt::StringLiterals;
|
||||
|
||||
static QList<const Converter *> *availableConverters;
|
||||
|
||||
Converter::Converter()
|
||||
static const Converter *prepareConverter(QString format, Converter::Direction direction,
|
||||
QFile *stream)
|
||||
{
|
||||
if (!availableConverters)
|
||||
availableConverters = new QList<const Converter *>;
|
||||
availableConverters->append(this);
|
||||
}
|
||||
const bool out = direction == Converter::Direction::Out;
|
||||
const QIODevice::OpenMode mode = out
|
||||
? QIODevice::WriteOnly | QIODevice::Truncate
|
||||
: QIODevice::ReadOnly;
|
||||
const char *dirn = out ? "output" : "input";
|
||||
|
||||
Converter::~Converter()
|
||||
{
|
||||
availableConverters->removeAll(this);
|
||||
if (stream->fileName().isEmpty())
|
||||
stream->open(out ? stdout : stdin, mode);
|
||||
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[])
|
||||
{
|
||||
QCoreApplication app(argc, argv);
|
||||
|
||||
//! [0]
|
||||
QStringList inputFormats;
|
||||
QStringList outputFormats;
|
||||
for (const Converter *conv : std::as_const(*availableConverters)) {
|
||||
for (const Converter *conv : Converter::allConverters()) {
|
||||
auto direction = conv->directions();
|
||||
QString name = conv->name();
|
||||
if (direction.testFlag(Converter::Direction::In))
|
||||
@ -41,13 +72,14 @@ int main(int argc, char *argv[])
|
||||
if (direction.testFlag(Converter::Direction::Out))
|
||||
outputFormats << name;
|
||||
}
|
||||
//! [0]
|
||||
inputFormats.sort();
|
||||
outputFormats.sort();
|
||||
inputFormats.prepend("auto"_L1);
|
||||
outputFormats.prepend("auto"_L1);
|
||||
|
||||
QCommandLineParser parser;
|
||||
parser.setApplicationDescription("Qt file format conversion tool"_L1);
|
||||
parser.setApplicationDescription("Qt serialization format conversion tool"_L1);
|
||||
parser.addHelpOption();
|
||||
|
||||
QCommandLineOption inputFormatOption(QStringList{ "I"_L1, "input-format"_L1 });
|
||||
@ -86,110 +118,38 @@ int main(int argc, char *argv[])
|
||||
|
||||
if (parser.isSet(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) {
|
||||
const char *help = conv->optionsHelp();
|
||||
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);
|
||||
} else {
|
||||
printf("Format '%s' supports no options.\n", qPrintable(format));
|
||||
qInfo("Format '%s' supports no options.", qPrintable(format));
|
||||
}
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
}
|
||||
//! [1]
|
||||
|
||||
fprintf(stderr, "Unknown file format '%s'\n", 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;
|
||||
}
|
||||
qFatal("Unknown file format '%s'", qPrintable(format));
|
||||
}
|
||||
|
||||
//! [2]
|
||||
QStringList files = parser.positionalArguments();
|
||||
QFile input(files.value(0));
|
||||
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())
|
||||
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
|
||||
// Now finally perform the conversion:
|
||||
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");
|
||||
outconv->saveFile(&output, data, parser.values(optionOption));
|
||||
return EXIT_SUCCESS;
|
||||
//! [2]
|
||||
}
|
||||
|
@ -6,7 +6,10 @@
|
||||
using namespace Qt::StringLiterals;
|
||||
|
||||
static NullConverter nullConverter;
|
||||
Converter *Converter::null = &nullConverter;
|
||||
bool Converter::isNull(const Converter *converter)
|
||||
{
|
||||
return converter == &nullConverter;
|
||||
}
|
||||
|
||||
QString NullConverter::name() const
|
||||
{
|
||||
@ -23,32 +26,12 @@ Converter::Options NullConverter::outputOptions() const
|
||||
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,
|
||||
const QStringList &options) const
|
||||
{
|
||||
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()));
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
Q_UNUSED(f);
|
||||
|
@ -13,9 +13,6 @@ public:
|
||||
QString name() const override;
|
||||
Directions directions() 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,
|
||||
const QStringList &options) const override;
|
||||
};
|
||||
|
@ -54,16 +54,6 @@ Converter::Directions TextConverter::directions() const
|
||||
return Direction::InOut;
|
||||
}
|
||||
|
||||
Converter::Options TextConverter::outputOptions() const
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
const char *TextConverter::optionsHelp() const
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool TextConverter::probeFile(QIODevice *f) const
|
||||
{
|
||||
if (QFile *file = qobject_cast<QFile *>(f))
|
||||
@ -98,9 +88,8 @@ void TextConverter::saveFile(QIODevice *f, const QVariant &contents,
|
||||
const QStringList &options) const
|
||||
{
|
||||
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()));
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
QTextStream out(f);
|
||||
|
@ -12,8 +12,6 @@ class TextConverter : public Converter
|
||||
public:
|
||||
QString name() const override;
|
||||
Directions directions() 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,
|
||||
|
24
examples/corelib/serialization/convert/variantorderedmap.h
Normal file
24
examples/corelib/serialization/convert/variantorderedmap.h
Normal 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
|
@ -2,6 +2,7 @@
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
|
||||
|
||||
#include "xmlconverter.h"
|
||||
#include "variantorderedmap.h"
|
||||
|
||||
#include <QBitArray>
|
||||
#include <QtCborCommon>
|
||||
@ -48,9 +49,8 @@ static QVariantList listFromXml(QXmlStreamReader &xml, Converter::Options option
|
||||
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()));
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
xml.readNext();
|
||||
@ -90,9 +90,8 @@ static VariantOrderedMap::value_type mapEntryFromXml(QXmlStreamReader &xml,
|
||||
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()));
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
return { key, value };
|
||||
@ -134,9 +133,8 @@ static QVariant mapFromXml(QXmlStreamReader &xml, Converter::Options options)
|
||||
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()));
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
xml.readNext();
|
||||
@ -153,9 +151,8 @@ static QVariant variantFromXml(QXmlStreamReader &xml, Converter::Options options
|
||||
if (name == "map"_L1)
|
||||
return mapFromXml(xml, options);
|
||||
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()));
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
QXmlStreamAttributes attrs = xml.attributes();
|
||||
@ -168,9 +165,8 @@ static QVariant variantFromXml(QXmlStreamReader &xml, Converter::Options options
|
||||
if (xml.isCDATA() || xml.isCharacters() || xml.isEndElement())
|
||||
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()));
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
QStringView text = xml.text();
|
||||
@ -190,9 +186,8 @@ static QVariant variantFromXml(QXmlStreamReader &xml, Converter::Options options
|
||||
// let's see floating point
|
||||
double d = text.toDouble(&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()));
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
result = d;
|
||||
}
|
||||
@ -206,9 +201,8 @@ static QVariant variantFromXml(QXmlStreamReader &xml, Converter::Options options
|
||||
} else if (encoding.isEmpty() || encoding == "base64"_L1) {
|
||||
result = QByteArray::fromBase64(data);
|
||||
} 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()));
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
} else if (type == "string"_L1) {
|
||||
result = text.toString();
|
||||
@ -227,9 +221,8 @@ static QVariant variantFromXml(QXmlStreamReader &xml, Converter::Options options
|
||||
} else if (c == '0') {
|
||||
++n;
|
||||
} 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()));
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
ba.resize(n);
|
||||
@ -247,16 +240,14 @@ static QVariant variantFromXml(QXmlStreamReader &xml, Converter::Options options
|
||||
else
|
||||
id = QMetaType::fromName(type.toLatin1()).id();
|
||||
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()));
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
result = text.toString();
|
||||
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()));
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
@ -265,9 +256,8 @@ static QVariant variantFromXml(QXmlStreamReader &xml, Converter::Options options
|
||||
} while (xml.isComment() || xml.isWhitespace());
|
||||
|
||||
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()));
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
xml.readNext();
|
||||
@ -387,8 +377,7 @@ static void variantToXml(QXmlStreamWriter &xml, const QVariant &v)
|
||||
xml.writeAttribute(typeString, QString::fromLatin1(typeName));
|
||||
xml.writeCharacters(copy.toString());
|
||||
} else {
|
||||
fprintf(stderr, "XML: don't know how to serialize type '%s'.\n", typeName);
|
||||
exit(EXIT_FAILURE);
|
||||
qFatal("XML: don't know how to serialize type '%s'.", typeName);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -434,10 +423,8 @@ QVariant XmlConverter::loadFile(QIODevice *f, const Converter *&outputConverter)
|
||||
QXmlStreamReader xml(f);
|
||||
xml.readNextStartElement();
|
||||
QVariant v = variantFromXml(xml, outputConverter->outputOptions());
|
||||
if (xml.hasError()) {
|
||||
fprintf(stderr, "XML error: %s", qPrintable(xml.errorString()));
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
if (xml.hasError())
|
||||
qFatal("XML error: %s", qPrintable(xml.errorString()));
|
||||
|
||||
return v;
|
||||
}
|
||||
@ -452,9 +439,8 @@ void XmlConverter::saveFile(QIODevice *f, const QVariant &contents,
|
||||
} else if (s == "compact=yes"_L1) {
|
||||
compact = true;
|
||||
} 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);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -4,10 +4,9 @@
|
||||
/*!
|
||||
\example serialization/savegame
|
||||
\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
|
||||
small game using QJsonDocument, QJsonObject and QJsonArray.
|
||||
\brief How to save and load a game using Qt's JSON or CBOR classes.
|
||||
|
||||
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
|
||||
|
@ -3,6 +3,7 @@
|
||||
|
||||
/*!
|
||||
\example threads/mandelbrot
|
||||
\examplecategory {Data Processing & I/O}
|
||||
\title Mandelbrot
|
||||
\ingroup qtconcurrent-mtexamples
|
||||
|
||||
|
@ -3,8 +3,8 @@
|
||||
|
||||
/*!
|
||||
\example threads/queuedcustomtype
|
||||
\title Queued Custom Type Example
|
||||
\brief Demonstrates multi-thread programming using Qt.
|
||||
\examplecategory {Data Processing & I/O}
|
||||
\title Queued Custom Type
|
||||
\ingroup qtconcurrent-mtexamples
|
||||
|
||||
\brief The Queued Custom Type example shows how to send custom types between
|
||||
|
@ -3,13 +3,13 @@
|
||||
|
||||
/*!
|
||||
\example threads/semaphores
|
||||
\title Semaphores Example
|
||||
\brief Demonstrates multi-thread programming using Qt.
|
||||
\examplecategory {Data Processing & I/O}
|
||||
\title Producer and Consumer using Semaphores
|
||||
\ingroup qtconcurrent-mtexamples
|
||||
|
||||
\brief The Semaphores example shows how to use QSemaphore to control
|
||||
access to a circular buffer shared by a producer thread and a
|
||||
consumer thread.
|
||||
\brief The Producer and Consumer using Semaphores example shows how
|
||||
to use QSemaphore to control access to a circular buffer shared
|
||||
by a producer thread and a consumer thread.
|
||||
|
||||
The producer writes data to the buffer until it reaches the end
|
||||
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
|
||||
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
|
||||
|
||||
|
@ -3,13 +3,13 @@
|
||||
|
||||
/*!
|
||||
\example threads/waitconditions
|
||||
\title Wait Conditions Example
|
||||
\brief Demonstrates multi-thread programming using Qt.
|
||||
\examplecategory {Data Processing & I/O}
|
||||
\title Producer and Consumer using Wait Conditions
|
||||
\ingroup qtconcurrent-mtexamples
|
||||
|
||||
\brief The Wait Conditions example shows how to use QWaitCondition and
|
||||
QMutex to control access to a circular buffer shared by a
|
||||
producer thread and a consumer thread.
|
||||
\brief The Producer and Consumer using Wait Conditions example shows
|
||||
how to use QWaitCondition and QMutex to control access to a circular
|
||||
buffer shared by a producer thread and a consumer thread.
|
||||
|
||||
The producer writes data to the buffer until it reaches the end
|
||||
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
|
||||
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
|
||||
|
||||
|
@ -6,6 +6,7 @@
|
||||
|
||||
#include "renderthread.h"
|
||||
|
||||
#include <QCoreApplication>
|
||||
#include <QPixmap>
|
||||
#include <QWidget>
|
||||
|
||||
@ -16,7 +17,7 @@ QT_END_NAMESPACE
|
||||
//! [0]
|
||||
class MandelbrotWidget : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_DECLARE_TR_FUNCTIONS(MandelbrotWidget)
|
||||
|
||||
public:
|
||||
MandelbrotWidget(QWidget *parent = nullptr);
|
||||
@ -36,11 +37,9 @@ protected:
|
||||
bool event(QEvent *event) override;
|
||||
#endif
|
||||
|
||||
private slots:
|
||||
private:
|
||||
void updatePixmap(const QImage &image, double scaleFactor);
|
||||
void zoom(double zoomFactor);
|
||||
|
||||
private:
|
||||
void scroll(int deltaX, int deltaY);
|
||||
#ifndef QT_NO_GESTURES
|
||||
bool gestureEvent(QGestureEvent *event);
|
||||
|
@ -1,12 +1,17 @@
|
||||
// Copyright (C) 2016 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
|
||||
|
||||
#include <QApplication>
|
||||
#include <QPainter>
|
||||
#include <QTime>
|
||||
#include "block.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 image(width, height, QImage::Format_RGB16);
|
||||
@ -43,8 +48,8 @@ QImage createImage(int width, int height)
|
||||
|
||||
int x = 0;
|
||||
int y = 0;
|
||||
int starWidth = image.width()/3;
|
||||
int starHeight = image.height()/3;
|
||||
const int starWidth = image.width()/3;
|
||||
const int starHeight = image.height()/3;
|
||||
|
||||
QRect rect(x, y, starWidth, starHeight);
|
||||
|
||||
|
@ -1,22 +1,19 @@
|
||||
// Copyright (C) 2016 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
|
||||
|
||||
#include "block.h"
|
||||
#include "renderthread.h"
|
||||
|
||||
#include <QRandomGenerator>
|
||||
#include <QRgb>
|
||||
|
||||
RenderThread::RenderThread(QObject *parent)
|
||||
: QThread(parent)
|
||||
{
|
||||
m_abort = false;
|
||||
}
|
||||
|
||||
RenderThread::~RenderThread()
|
||||
{
|
||||
mutex.lock();
|
||||
m_abort = true;
|
||||
mutex.unlock();
|
||||
|
||||
wait();
|
||||
}
|
||||
|
||||
@ -27,27 +24,26 @@ void RenderThread::processImage(const QImage &image)
|
||||
return;
|
||||
|
||||
m_image = image;
|
||||
m_abort = false;
|
||||
start();
|
||||
}
|
||||
|
||||
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 c = 0; c < 400; ++c) {
|
||||
//![processing the image (start)]
|
||||
int x1 = qMax(0, QRandomGenerator::global()->bounded(m_image.width()) - s/2);
|
||||
int x2 = qMin(x1 + s/2 + 1, m_image.width());
|
||||
int y1 = qMax(0, QRandomGenerator::global()->bounded(m_image.height()) - s/2);
|
||||
int y2 = qMin(y1 + s/2 + 1, m_image.height());
|
||||
const int x1 = qMax(0, QRandomGenerator::global()->bounded(m_image.width()) - s/2);
|
||||
const int x2 = qMin(x1 + s/2 + 1, m_image.width());
|
||||
const int y1 = qMax(0, QRandomGenerator::global()->bounded(m_image.height()) - s/2);
|
||||
const int y2 = qMin(y1 + s/2 + 1, m_image.height());
|
||||
int n = 0;
|
||||
int red = 0;
|
||||
int green = 0;
|
||||
int blue = 0;
|
||||
for (int i = y1; i < y2; ++i) {
|
||||
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);
|
||||
green += qGreen(pixel);
|
||||
blue += qBlue(pixel);
|
||||
@ -55,20 +51,13 @@ void RenderThread::run()
|
||||
}
|
||||
}
|
||||
//![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));
|
||||
emit sendBlock(block);
|
||||
if (m_abort)
|
||||
if (isInterruptionRequested())
|
||||
return;
|
||||
msleep(10);
|
||||
}
|
||||
}
|
||||
}
|
||||
//![processing the image (finish)]
|
||||
|
||||
void RenderThread::stopProcess()
|
||||
{
|
||||
mutex.lock();
|
||||
m_abort = true;
|
||||
mutex.unlock();
|
||||
}
|
||||
|
@ -5,9 +5,9 @@
|
||||
#define RENDERTHREAD_H
|
||||
|
||||
#include <QImage>
|
||||
#include <QMutex>
|
||||
#include <QThread>
|
||||
#include "block.h"
|
||||
|
||||
class Block;
|
||||
|
||||
//! [RenderThread class definition]
|
||||
class RenderThread : public QThread
|
||||
@ -23,16 +23,11 @@ public:
|
||||
signals:
|
||||
void sendBlock(const Block &block);
|
||||
|
||||
public slots:
|
||||
void stopProcess();
|
||||
|
||||
protected:
|
||||
void run();
|
||||
|
||||
private:
|
||||
bool m_abort;
|
||||
QImage m_image;
|
||||
QMutex mutex;
|
||||
};
|
||||
//! [RenderThread class definition]
|
||||
|
||||
|
@ -1,8 +1,17 @@
|
||||
// Copyright (C) 2016 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
|
||||
|
||||
#include "block.h"
|
||||
#include "renderthread.h"
|
||||
#include "window.h"
|
||||
#include <QtWidgets>
|
||||
|
||||
#include <QFileDialog>
|
||||
#include <QGuiApplication>
|
||||
#include <QHBoxLayout>
|
||||
#include <QImageReader>
|
||||
#include <QPainter>
|
||||
#include <QScreen>
|
||||
#include <QVBoxLayout>
|
||||
|
||||
//! [Window constructor start]
|
||||
Window::Window(QWidget *parent)
|
||||
@ -20,7 +29,7 @@ Window::Window(QWidget *parent)
|
||||
connect(loadButton, &QPushButton::clicked,
|
||||
this, QOverload<>::of(&Window::loadImage));
|
||||
connect(resetButton, &QPushButton::clicked,
|
||||
thread, &RenderThread::stopProcess);
|
||||
thread, &RenderThread::requestInterruption);
|
||||
connect(thread, &RenderThread::finished,
|
||||
this, &Window::resetUi);
|
||||
//! [set up widgets and connections] //! [connecting signal with custom type]
|
||||
@ -51,13 +60,13 @@ void Window::loadImage()
|
||||
if (format.toLower() == 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(' ')));
|
||||
|
||||
if (newPath.isEmpty())
|
||||
return;
|
||||
|
||||
QImage image(newPath);
|
||||
const QImage image(newPath);
|
||||
if (!image.isNull()) {
|
||||
loadImage(image);
|
||||
path = newPath;
|
||||
@ -67,7 +76,7 @@ void Window::loadImage()
|
||||
void Window::loadImage(const QImage &image)
|
||||
{
|
||||
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())
|
||||
useImage = image.scaled(0.75*space.width(), 0.75*space.height(),
|
||||
Qt::KeepAspectRatio, Qt::SmoothTransformation);
|
||||
|
@ -4,13 +4,14 @@
|
||||
#ifndef WINDOW_H
|
||||
#define WINDOW_H
|
||||
|
||||
#include <QImage>
|
||||
#include <QLabel>
|
||||
#include <QPixmap>
|
||||
#include <QPushButton>
|
||||
#include <QWidget>
|
||||
#include "renderthread.h"
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
class QLabel;
|
||||
class QPushButton;
|
||||
QT_END_NAMESPACE
|
||||
class Block;
|
||||
class RenderThread;
|
||||
|
||||
//! [Window class definition]
|
||||
class Window : public QWidget
|
||||
|
@ -7,9 +7,9 @@
|
||||
#include <stdlib.h>
|
||||
|
||||
//! [0]
|
||||
const int DataSize = 100000;
|
||||
constexpr int DataSize = 100000;
|
||||
|
||||
const int BufferSize = 8192;
|
||||
constexpr int BufferSize = 8192;
|
||||
char buffer[BufferSize];
|
||||
|
||||
QSemaphore freeBytes(BufferSize);
|
||||
|
@ -5,5 +5,3 @@ if(NOT TARGET Qt6::Widgets)
|
||||
return()
|
||||
endif()
|
||||
qt_internal_add_example(contiguouscache)
|
||||
qt_internal_add_example(customtype)
|
||||
qt_internal_add_example(customtypesending)
|
||||
|
@ -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}"
|
||||
)
|
@ -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
|
@ -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;
|
||||
}
|
@ -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]
|
@ -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
|
@ -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}"
|
||||
)
|
@ -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
|
@ -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]
|
@ -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;
|
||||
}
|
@ -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
|
@ -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]
|
@ -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
|
@ -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.
|
||||
*/
|
@ -1,6 +1,4 @@
|
||||
requires(qtHaveModule(widgets))
|
||||
|
||||
TEMPLATE = subdirs
|
||||
SUBDIRS = contiguouscache \
|
||||
customtype \
|
||||
customtypesending
|
||||
SUBDIRS = contiguouscache
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user