C++ ile Arayüz Tasarlamak

Merhabalar,

C++'ta arayüz programlamaya geçmek istiyorum.Qt gelecek vaat ediyor ancak Creator ücretli :confused: Ücretsiz bir kütüphane önerebilir misiniz?

Not : Visual Studio 2017 kullanmaktayım.

Ayrıca cross-platform olursa çok iyi olur.

creator: IDE
Senin bu IDE nin paralı olmasından dolayı sorduğun şey: Library

Peki,kütüphaneyi nerden indirip Visual Studio’da kullanabileceğim?

İndirmeyi de tamamladım (sadece Developer & Designer Tools’u seçtim çünki Qt 15 tamı tamına 51GB’tı).Buradaki kitlerden herhangi birini seçemiyorum.

Bilgim yok. C++ kullanmıyorum zaten.

Merhaba,

Aradan uzun zaman geçmiş, kit seçme işini halledebildiniz mi bilmiyorum. Ben de bir süredir C++ ve Qt ile ilgileniyorum. Bu karşısınıza çıkan ekranla ben de karşılaştım daha önce.

Paylaştığınız görselden anladığım kadarıyla, sisteminizde hem 32bit hem de 64bit için iki tane derleyici bulunuyor. Qt Creator’un yeni sürümünde, kurulum esnasında Qt 6’ya ait farklı sürümleri seçebiliyorsunuz. Siz bunlardan hangisini seçtiniz bilmiyorum. Daha doğrusu o zamanlar hangi seçenekler vardı bilemiyorum, ama Qt Creator’un yeni sürümünün kurulumunda yüklemek istediğiniz Qt sürümünü seçebiliyorsunuz. Bu Qt sürümü yüklenirken, gerekli derleyiciler ve clang kütüphaneleri de yükleniyor.

Örneğin, ben, Qt 6.5.3’ü seçmiştim; seçtikten sonra gerekli bütün paketler otomatik olarak kurulmuştu. Sistemde daha önce kurulu ve ORTAM DEĞİŞKENLERİ’nde yer olan derleyiciler de vardı.

Kit ekranını ve mevcut kitleri göstermeme izin verin:

1

Yukardaki görselde, Desktop Qt 6.5.3 MinGW 64-bit isimli bir seçeneğin seçilmiş olduğunu görüyorsunuz.

İsterseniz, 0’dan yeni bir kit oluşturalım. Ve oluşturacağımız kiti kullanan basit bir Qt ekranını çalıştıralım.

Bu arada “kit” diyoruz ama “kit” nedir bundan bahsedeyim biraz izninizle.

Buradaki “kit” kavramı, programınız derlenirken kullanılacak olan derleyici, debugger, qt sürümü ve daha birçok özelliği seçtiğiniz bir ayarlar bütünüdür.

Şimdi, baştan bir tane kit oluşturalım, sizin durumunuzda bu, options yazan linke tıklayarak yapılabilir. Yani kit oluşturma sayfasına bu şekilde ulaşabilirsiniz.

Benim durumumda ise herhangi bir kitin üzerinde beliren Manage isimli düğmeye gelerek kit oluşturma sayfasına ulaşmak gerekir. Desktop Qt 6.5.3 MinGW 64-bit kiti üzerindeki Manage tuşuna basarak kit oluşturma ekranını açıyorum.

2

Ekranın sağında, altında Clone isimli bir düğme bulunan, Add isimli bir düğme görüyorsunuz. O düğmeyi kullanarak yeni bir Kit ekleyebiliriz. Bu tuşa basıyorum ve yeni bir Kit oluşturuyorum.

3

Gördüğünüz gibi, sizin paylaştığınız görseldeki gibi Unnamed isimli bir Kit oluştu. İsmi Unnamed kalsın ama Kit’in kullanılabilmesi için gereken önemli kısımları tanımlamaya geçelim.

Paylaştığım görsele baktığımızda, hem C hem de C++ için herhangi bir derleyicinin henüz seçilmemiş olduğunu görüyoruz. Önce derleyicileri seçiyorum:

4

Bu derleyici listesinden bir tane C için, bir tane de C++ için derleyici seçip Apply tuşuna basıyorum.

5

Kit isminin önündeki sembolün değiştiğini görüyorsunuz. Önemli değil. Sembol birazdan düzelecek.

Debugger’ın otomatik olarak seçilmiş olduğunu görüyorsunuz. Bu seçeneği değiştireyim izninizle; msys ile devam ediyorum.

6

7

Qt sürümü de seçilmiş ancak MSVC’ye göre, bunu Mingw64’e göre değiştiriyorum.

8

Değişiklik yapıldıktan sonra, sağ alt köşede gördüğünüz Apply düğmesine basın.

9

Sonra fare yardımıyla ekranın üst kısmını görünür hale getirin çünkü bu ayarı ön-tanımlı hale getireceğiz.

10

11

Unnamed isimli kitin sembolünün değiştiğini görüyorsunuz.

Şimdi, artık OK tuşuna basabiliriz. OK tuşuna basınca aşağıdaki ekrana geri döndürülüyorum.

12

Next düğmesine basarak Kit sayfasını geçebiliriz artık:

13

Bu kısımda herhangi bir değişiklik yapmadan Finish tuşuna basıyorum; tuşa bastıktan sonra karşıma aşağıdaki ekran çıkıyor:

14

Artık kodlamaya geçebiliriz, bundan sonrası bizim C++ ve Qt bilgimize kalıyor. MenuBar’da gördüğünüz Build açılır düğmesine gelip programı çalıştırırsanız, programın çalıştırılabilir dosyasını da oluşturmuş olursunuz.

15

Run düğmesine bastıktan sonra aşağıdaki gibi boş bir pencere oluşması gerekir:

16

Qt’ye ekleyeceğiniz her dosyayı Windows’ta CMakeLists.txt dosyasında tanıtmanız gerekir. Linux’te ise CMakeLists.txt yerine proje adına sahip olan ve .pro uzantılı bir dosya kullanılıyor. Bu dosyalar otomatik olarak Qt Creator tarafından oluşturuluyor. CMakeLists.txt’in içeriği ile .pro dosyasının içerikleri aynı değil bu arada. Fakat her iki dosyada da aynı işlemlerin yapılması, yani projede kullanılacak dosyaların tanıtılması gerekiyor.

Neyse, sorununuzun çözümüyle alakalı söyleyebileceklerim bunlar. Umarım bana gerek olmadan sorununuzu çoktan çözmüşsünüzdür. Ama aynı zamanda da yazdıklarımın birilerine faydasının dokunmasını umarım.

Herkese iyi günler.

4 Beğeni

Herkese merhaba,

Daha önce Qt Creator üzerinden Qt’nin Windows’a nasıl kurulabileceğini anlatmaya çalıştım. Bugün ise Qt Creator’u kurmadan Qt kitaplıklarını bilgisayarımıza nasıl yükleyebileceğimizden bahsetmek istiyorum biraz. Normalde Qt Creator, bizim manuel olarak yapabileceğimiz birçok işlemi kolaylaştıran bir uygulama. Qt Creator’un Windows kurulum ekranının aşamalarından birinde, sisteme yüklemek istediğimiz dinamik Qt kitaplıklarının sürümünü seçebiliyoruz. Herhangi bir Qt sürümünü sistemimize yüklemeden de Qt Creator’u kullanamayız.

Benim burada anlatacağım yöntem biraz daha zahmetli olmakla birlikte, aslında birçok açıdan bize C/C++ projelerinin nasıl derlenip dağıtılabileceği hakkında fikir verecektir diye umuyorum. Öncelikle burada takip edeceğim yöntem, istenen herhangi bir Qt sürümüne ait kitaplıkların hem Windows’a hem de Unix tabanlı işletim sistemlerine nasıl kurulabileceği ile alakalı genel bir fikir sunmayı amaçlıyor olacak.

Önce Windows, sonra da sadece Linux’un Ubuntu dağıtımı için statik Qt kitaplıklarının nasıl kurulabileceğini anlatacağım. Diğer dağıtımlar için ise gerekli kütüphanelerin nasıl temin edilebileceğine dair resmi Qt linkleriyle karşılaşacaksınız.

Windows

Linux ortamında program geliştirmeye aşina birisi olarak msys’yi Visual Studio’ya göre daha kullanışlı buluyorum. Dolayısıyla bu tanıtım msys’ye göre hazırlanmıştır. msys’yi aşağıdaki linkten indirebilirsiniz.

https://github.com/msys2/msys2-installer/releases/download/2024-05-07/msys2-x86_64-20240507.exe

MSYS’yi ön-tanımlı adresine yani C:\msys64 dizinine kurun. Kurulum sonunda Run MSYS2 now isimli bir seçenek seçilmiş olarak karşımıza gelir. Kurulumu bu şekilde sonlandırıp, MSYS2 UCRT64 konsol ekranının açılmasını sağlayın, ardından da güncelleştirmeleri aşağıdaki komut yardımıyla yükleyin.

pacman -Syu

Güncelleştirme esnasında bazı güncelleştirmeleri etkinleştirmemiz için MSYS konsolunu kapatmak isteyip istemediğimiz bize sorulacaktır.

Konsol ekranını kapatın sonra MSYS2 MSYS konsol ekranını açın ve güncelleştirme komutunu tekrar çalıştırın.

pacman -Syu

Güncelleştirmeleri yükledikten sonra Qt’nin statik kütüphanelerini kurabilmek için gerekli olan kütüphaneleri yükleyelim.

pacman -S --needed base-devel \
    mingw-w64-x86_64-toolchain \
    mingw-w64-x86_64-cmake \
    mingw-w64-x86_64-zlib \
    mingw-w64-x86_64-libpng \
    mingw-w64-x86_64-libjpeg-turbo \
    mingw-w64-x86_64-openssl \
    mingw-w64-x86_64-pcre2 \
    mingw-w64-x86_64-sqlite3 \
    mingw-w64-x86_64-icu \
    mingw-w64-x86_64-harfbuzz \
    mingw-w64-x86_64-freetype \
    mingw-w64-x86_64-glib2 \
    mingw-w64-x86_64-libtiff \
    mingw-w64-x86_64-libwebp \
    mingw-w64-x86_64-fontconfig \
    mingw-w64-x86_64-libproxy \
    mingw-w64-x86_64-gstreamer \
    mingw-w64-x86_64-vulkan-loader \
    mingw-w64-x86_64-vulkan-headers

Qt’yi kurarken gerekli olan kütüphanelerin yerlerini MSYS’nin ortam değişkenlerine ekleyelim:

echo 'export PATH="/mingw64/bin:$PATH"' >> ~/.bashrc
echo 'export LD_LIBRARY_PATH="/mingw64/lib:$LD_LIBRARY_PATH"' >> ~/.bashrc
source ~/.bashrc

Şimdi açık olan MSYS2 MSYS konsol ekranını kapatın ve MSYS2 MINGW64 konsolunu açın.

Aşağıdaki linkten Qt 6.7.1’in kaynak kodlarını indirelim. Ben dosyayı Desktop konumuna indireceğim. MSYS2 MINGW64 konsoluna aşağıdaki ifadeleri yazıyorum. (MSYS konsolunda adresler Windows’ta olduğu gibi \ şeklinde değil Linux’teki gibi / şekinde ayrılır.)

cd /C/Users/tanberk/Desktop
wget https://download.qt.io/official_releases/qt/6.7/6.7.1/single/qt-everywhere-src-6.7.1.tar.xz

Sonra indirdiğimiz dosyayı sıkıştırılmış dosyadan çıkartalım:

tar -xf qt-everywhere-src-6.7.1.tar.xz

Şimdi qt-everywhere-src-6.7.1 dizinine geçip, build isimli bir klasör oluşturalım, sonra da o klasöre geçelim.

cd qt-everywhere-src-6.7.1
mkdir build
cd build

Qt’yi statik olarak derlemek için bazı derleme parametrelerini konfigürasyon esnasında tanımlamamız gerekir.

../configure -static \
	-opensource \
	-confirm-license \
	-release \
	-prefix C:/Qt6.7.1 \
	-opengl desktop \
	-platform win32-g++ \
	-nomake examples \
	-nomake tests \
	-no-feature-zstd \
	-skip qtwebengine

Yukarıdaki konfigürasyon parametrelerini biraz açıklamaya çalışayım:

-static: Qt kitaplıklarının statik olarak derleneceğini ifade eder.

-opensource: Qt kitaplıklarının açık kaynak olarak lisanslanacağını ifade eder.

-confirm-license: Lisans koşullarının otomatik olarak kabul edilmesini sağlar.

-release: Qt kitaplıklarının dağıtım modunda kurulmasını sağlar; bu parametre debug moda özgü bazı özellikleri kısar ve bu sayede kod performansında optimizasyon yapılmasını sağlar.

-prefix C:/Qt6.7.1: Qt kitaplıklarının belirtilen adrese (C:\Qt6.7.1 dizinine) kurulmasını sağlar.

-opengl desktop: Qt kitaplıklarına opengl desteği sunar.

-platform win32-g++: Qt kitaplıklarının kurulacağı işletim sisteminin ve derleyici tipinin tanıtılmasını sağlar.

-nomake examples: Örnek dosyaların kurulmasını engeller.

-nomake tests: Test dosyalarının kurulmasını engeller.

-no-feature-zstd: Bu özellik, derleme sürecinde zstd kütüphanesinin hariç tutulmasını sağlar. msys’ye yüklenen zstd kütüphanesi sürümü ile, son Qt sürümlerinin zstd ile ilgili olan CMake yapılandırma ayarları uyumsuz. Bu seçenek ile konfigürasyon esnasında hata almazsınız.

-skip qtwebengine: Bu özellik Qt kütüphaneleri kurulurken qtwebengine’ın hariç tutulmasını sağlar. Bu kütüphanenin statik olarak bilgisayara kurulabilmesi için daha başka kütüphanelere ihtiyaç var. Bu seçeneği kullanarak qtwebengine’ın pas geçilmesini sağlayabilirsiniz.

Konfigürasyon işlemi sırasında bazı kütüphanelerle ilgili uyarılar alabilirsiniz, bu uyarıların konfigürasyon sürecine olumsuz bir etkisi yoktur. Konfigürasyon işleminin başarıyla tamamlanıp tamamlanmadığını konfigürasyon çıktısının son satırlarının aşağıdaki gibi olup olmadığına bakarak anlayabilirsiniz:

...
Qt is now configured for building. Just run 'cmake --build . --parallel'

Once everything is built, you must run 'cmake --install .'
Qt will be installed into 'C:/Qt6.7.1'

To configure and build other Qt modules, you can use the following convenience script:
        C:/Qt6.7.1/bin/qt-configure-module.bat

If reconfiguration fails for some reason, try removing 'CMakeCache.txt' from the build directory
Alternatively, you can add the --fresh flag to your CMake flags.

-- Configuring done (214.1s)
-- Generating done (26.0s)
-- Build files have been written to: C:/Users/tanberk/Desktop/qt-everywhere-src-6.7.1/build

Yukarıdaki çıktı, artık inşa sürecine geçebileceğimizi gösteriyor. cmake komutunu çalıştırarak build dizinine statik Qt kitaplıklarını kuralım.

cmake --build . --parallel

Statik Qt kitaplıklarının kurulumu biraz zaman alacaktır; aşağı yukarı 12000 tane Qt’ye ait nesnenin daha önce tanımladığımız derleme ayarları ile bilgisayara kurulması gerekiyor. Projenizin ihtiyaçlarına göre daha farklı ayarlamalar yapmak zorunda kalabilirsiniz.

Kurulum sürecinin sonunda, aşağıdaki komutu çalıştırarak kurulum dosyalarının konfigürasyonda belirtilen C:/Qt6.7.1 adresine kopyalayabiliriz.

cmake --install .

Yukarıdaki komutun çalışması başarıyla sonuçlandıktan sonra artık statik Qt kitaplıklarını kullanarak projelerinizi derleyebilir ve herhangi bir Qt kitaplığına ihtiyaç duymadan projeyi dağıtabilirsiniz.

Her ne kadar statik Qt kitaplıkları, projelerimizde kullanacağımız Qt kütüphanelerine olan kitaplıkları projeye dahil edip, bu kitaplıklara olan bağımlığı ortadan kaldırsa da, bu kitaplıklar hala Qt’ye ait olmayan diğer dinamik kütüphanelere bağımlıdır. Qt’nin bağımlı olduğu her dinamik kütüphaneyi sisteminize statik olarak kurmaya kalkmak seçeneklerden bir tanesi. Seçeneklerden bir diğeri ise Qt’nin ihtiyaç duyduğu dinamik kütüphaneleri uygulamanızla birlikte dağıtmaktır. Geliştirilen programların bazıları, çalışabilmeleri için kendileriyle birlikte bazı .dll uzantılı dinamik kütüphaneler ile birlikte dağıtılırlar.

Statik olarak derlenmiş programların boyutları da gerekli kütüphaneleri bünyelerinde taşıdıkları için büyüktür ama programın ihtiyaç duyduğu kütüphanelerde tanımlı nesnelerin yüklenmesi daha hızlı gerçekleşir. Yani projenizde statik olarak derlenmiş kütüphaneler kullanmanız, projenizde yer alan nesnelerin yüklenme ve kullanıma hazır hale gelme süreçlerini kısaltacak ama ihtiyaç duyulan kütüphaneleri yürütülebilir dosyanın içine dahil ettiği için, dosya boyutunun büyümesine neden olacaktır. Dolayısıyla projenizi dinamik veya statik kütüphanelere göre inşa edip etmemeniz size ve projenizin gerekliliklerine göre değişir.

Linux

Static Qt kitaplıklarını işletim sistemimize kurmak için önce gerekli kütüphaneleri aşağıdaki linkten görebilirsiniz:

Aşağıdaki adresten de kendi Linux dağıtımınıza göre hangi paketlerin sisteminize kurulması gerektiğini görebilirsiniz:

https://wiki.qt.io/Building_Qt_5_from_Git

Bu sayfanın Qt6 için olan sayfası Qt5’teki kadar detaylı değil, ancak bu sayfada yer alan kütüphaneler statik Qt6 kütüphanelerinin kurulabilmesi için de geçerlidir. Aşağıda Linux’un Ubuntu dağıtımı için sisteme kurulması gereken kütüphaneler listelenmiştir.

sudo apt-get install build-essential perl python3 git ruby nodejs flex bison gperf cmake
sudo apt-get install '^libxcb.*-dev' \
    libx11-xcb-dev \
    libglu1-mesa-dev \
    libxrender-dev \
    libxi-dev \
    libxkbcommon-dev \
    libxkbcommon-x11-dev \
    libicu-dev \
    libxslt-dev \
    libxcursor-dev \
    libxcomposite-dev \
    libxdamage-dev \
    libxrandr-dev \
    libxtst-dev \
    libxss-dev \
    libdbus-1-dev \
    libevent-dev \
    libfontconfig \
    libfontconfig-dev \
    libfontconfig1-dev \
    libfreetype-dev \
    libcap-dev \
    libpulse-dev \
    libpci-dev \
    libnss3-dev \
    libasound2-dev \
    libegl1-mesa-dev \
    libgstreamer1.0-dev \
    libgstreamer-plugins-base1.0-dev \
    libgstreamer-plugins-good1.0-dev \
    libgstreamer-plugins-bad1.0-dev \
    clang \
    libclang-dev \
    libglib-2.0 \
    libice-dev \
    libsm-dev \
    libxkbfile-dev \
    libsqlite3-dev \
    libpng-dev \
    libjpeg-dev \
    libssl-dev \
    zlib1g-dev \
    libavcodec-dev \
    libavformat-dev \
    libswscale-dev \
    libxcb-glx0-dev

Diğer dağıtımlar için gereken kütüphanelerin sisteme nasıl kurulması gerektiğini Qt’nin kendi resmi dokümtasyonundan öğrenebilirsiniz.

Qt’nin statik kütüphanelerini Linux’un Ubuntu dağıtımına kurmak için yine benzer bir yöntem izleyeceğiz. Terminal ekranını açıyorum ve aşağıdaki komutu konsol ekranına yazıyorum:

wget https://download.qt.io/official_releases/qt/6.7/6.7.1/single/qt-everywhere-src-6.7.1.tar.xz

Sıkıştırılmış dosyanın içindeki dosyaları dışarı çıkarıp qt-everywhere-src-6.7.1 dizine geçelim:

tar -xf qt-everywhere-src-6.7.1.tar.xz 
cd qt-everywhere-src-6.7.1/

Şimdi build isminde bir tane dizin oluşturup bu dizine geçelim:

mkdir build
cd build

Şimdi sıra geldi Qt’nin derleme sürecinde kullanacağı konfigürasyon ayarlarını tanımlamaya. Aşağıdaki komutu build dizininde çalıştırın:

../configure -static -release -prefix /opt/Qt6.7.1 -opensource -confirm-license -nomake examples -nomake tests -no-feature-zstd -skip qtwebengine

Burada, Windows’tan farklı olarak -platform parametresinin tanımlanmadığını görüyorsunuz. Çünkü Unix tabanlı sistemlerde C++ uygulamaları geliştirme ortamı oldukça standarttır ve bu yüzden herhangi bir platform tanımlaması yapılması gerekmez. -prefix harici diğer parametrelerin ise aynen korunduğunu görüyorsunuz.

Konfigürasyon işlemi başarıyla tamamlandıktan sonra, yine Windows’ta yaptığımız gibi aşağıdaki komut ile kurulum sürecini başlatabiliriz.

cmake --build . --parallel

Kurulum süreci yine Windows’ta olduğu gibi uzun sürecektir.

build dizinine statik Qt kitaplıklarını kurduktan sonra bu kitaplıkları konfigürasyonda belirtilen dizine kopyalayalım:

sudo cmake --install .

Artık C++ projelerinize statik Qt kitaplıklarını dahil edebilirsiniz. Ancak daha önce söylediğim gibi projelerinizi dağıtmak istediğinizde hala bazı kütüphaneleri uygulamanız ile birlikte dağıtmak ve projenizin çalışma prensiplerini de bu dinamik kütüphanelerin bulunacağı dizine göre yeniden ayarlamak zorundasınız. Yani, sisteminizde kütüphaneler yüklüyken oluşturduğunuz çalıştırılabilir programlar, ihtiyaç duyduğu kütüphaneleri genellikle ortam değişkenlerinde bulur. Sizin deploy aşamasında toplayacağınız, .dll uzantılı dinamik kütüphaneler örneğin lib isminde bir klasörde tutulacaksa, bu klasör içinde yer alan dinamik kütüphanelerin ortam değişkenlerine eklenmesi gerekir. Bu ortam değişkeninin ismi LD_LIBRARY_PATH’dir.

Projenizin ihtiyaç duyacağı dinamik kütüphaneleri içeren lib dizini farklı işletim sistemleri için şöyle tanıtılır:

Windows için program çalışmadan hemen önce lib klasörünü ortam değişkenlerine eklenmesini sağlayabilirsiniz.

set PATH=%PATH%;%cd%\lib

Linux için ise programınızı çalıştırmadan önce ihtiyaç duyulan dinamik kütüphaneleri topladığınız lib dizinini, LD_LIBRARY_PATH’e eşitlemek yeterli olacaktır.

LD_LIBRARY_PATH="./lib"

Dinamik kütüphaneleri uygulamanız ile dağıtmanız gerektiğinde, bu kütüphaneleri yukarıda gösterildiği gibi uygulamanızı çalıştırmadan önce ortam değişkenlerine veya LD_LIBRARY_PATH’e eklemeniz gerekir. Bütün bu işlemleri yapan, uygulamanızdan basit bir exe dosyası yazabilirsiniz veya benzer işlemleri yapan bir batch dosyası da hazırlayabilirsiniz.

Evet, statik Qt kütüphanelerinin bilgisayarımıza nasıl kurulabileceğine dair tanıtımın sonuna geldik. Şimdi anlatmak istediğim konu ise, bir sonraki aşamayla yani Qt ve C++ kullanarak nasıl projeler geliştirebileceğimiz ile ilgili olacak.

Öncelikle projemiz için bir dizin oluşturalım. Ben test isimli bir dizinde çalışacağım. Program geliştirme süreci Ubuntu’da olacak. Windows için de hemen hemen aynı yolları izleyerek programınızı derleyebilirsiniz.

tanberk@kutlu:~$ mkdir test
tanberk@kutlu:~$ cd test
tanberk@kutlu:~/test$

C/C++ öğrenmeye başladığımdan beridir, aşağı yukarı şöyle bir proje dizin yapısı kullanıyorum; projenin header dosyalarını include isimli, kaynak kodlarını src isimli, harici kütüphaneleri de lib isimli klasörlerde tutuyorum.

Şimdi test dizini içinde include ve src klasörlerini oluşturalım:

mkdir include src

Projemizin ana dosyası olacak olan main.cpp dosyasını oluşturalım:

int main(int argc, char *argv[])
{
    return 0;
}

Şimdilik main.cpp’nin sadece basit bir main fonksiyonu var. Birazdan bu dosyayı dolduracağız.

Şu ana kadar oluşturduğumuz klasörler ve main.cpp ile birlikte şöyle bir proje dizin yapımızın olması lazım:

.
├── include
└── src
    └── main.cpp

3 directories, 1 file

Şimdi ise, Qt projelerinde kullanılan ve proje dosyaları ile birlikte derleme öncesi veya sonrası yapılması gereken işlemlerin tanımlandığı CMakeLists.txt dosyasını oluşturalım. CMakeLists.txt dosyaları Qt Creator ile de üretilir ve içeriği aşağı yukarı aşağıdaki gibidir. Bu arada aşağıdaki CMakeLists.txt dosyası, şu an geliştirmekte olduğumuz projeye uygun bir şekilde yazılmıştır.

cmake_minimum_required(VERSION 3.5)

project(Test VERSION 0.1 LANGUAGES CXX)

set(CMAKE_AUTOUIC ON)
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTORCC ON)

set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

if(WIN32)
    set(Qt6_DIR "C:/Qt6.7.1/lib/cmake/Qt6")
    set(CMAKE_PREFIX_PATH "${CMAKE_PREFIX_PATH};C:/Qt6.7.1")
    set(Qt6Widgets_DIR "C:/Qt6.7.1/lib/cmake/Qt6Widgets")
elseif(UNIX)
    set(Qt6_DIR /opt/Qt6.7.1/lib/cmake/Qt6)
    set(CMAKE_PREFIX_PATH ${CMAKE_PREFIX_PATH} /opt/Qt6.7.1)
    set(Qt6Widgets_DIR /opt/Qt6.7.1/lib/cmake/Qt6Widgets)
endif()

find_package(Qt6 REQUIRED COMPONENTS Widgets)

set(PROJECT_SOURCES
    src/main.cpp
)

qt_add_executable(Test
    MANUAL_FINALIZATION
    ${PROJECT_SOURCES}
)

set_target_properties(Test PROPERTIES
    LINK_FLAGS "-static-libgcc -static-libstdc++"
)

target_link_libraries(Test PRIVATE Qt${QT_VERSION_MAJOR}::Widgets)

set_target_properties(Test PROPERTIES
    ${BUNDLE_ID_OPTION}
    MACOSX_BUNDLE_BUNDLE_VERSION ${PROJECT_VERSION}
    MACOSX_BUNDLE_SHORT_VERSION_STRING ${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}
    MACOSX_BUNDLE TRUE
    WIN32_EXECUTABLE TRUE
)

include(GNUInstallDirs)

install(TARGETS Test
    BUNDLE DESTINATION .
    LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
    RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
)

if(QT_VERSION_MAJOR EQUAL 6)
    qt_finalize_executable(Test)
endif()

Yukarıdaki CMakeLists.txt dosyasının içeriğinin çoğu kısmı otomatik olarak Qt Creator tarafından yazılır, proje dosyaları ve derleme öncesi ve sonrası için yapılacak işlemleri ise bizim tanımlamamız gerekir.

Yukarıdaki CMakeLists.txt dosyasında ekleme yaptığım yerler şunlar:

if(WIN32)
    set(Qt6_DIR "C:/Qt6.7.1/lib/cmake/Qt6")
    set(CMAKE_PREFIX_PATH "${CMAKE_PREFIX_PATH};C:/Qt6.7.1")
    set(Qt6Widgets_DIR "C:/Qt6.7.1/lib/cmake/Qt6Widgets")
elseif(UNIX)
    set(Qt6_DIR /opt/Qt6.7.1/lib/cmake/Qt6)
    set(CMAKE_PREFIX_PATH ${CMAKE_PREFIX_PATH} /opt/Qt6.7.1)
    set(Qt6Widgets_DIR /opt/Qt6.7.1/lib/cmake/Qt6Widgets)
endif()

Burada, işletim sistemine göre Qt6’nın ve onun kütüphanelerinin nerede olduğunu tanımladık.

set(PROJECT_SOURCES
    src/main.cpp
)

Yukarıdaki set fonksiyonunun altında bütün proje dosyalarını tanıtacağız. Şimdilik sadece main.cpp olduğu için sadece onu ekledim.

Bu CMakeLists.txt dosyası ile birlikte proje dizin yapımız şöyle değişti.

.
├── CMakeLists.txt
├── include
└── src
    └── main.cpp

3 directories, 2 files

Şu anlık sadece main.cpp isimli bir dosyası olan projemizi derleyelim. Derleme işlemini proje dosyaları ile karışmasın diye build isimli bir klasörde yapacağım.

mkdir build
cd build

Şimdi derlemeye geçelim:

cmake -DCMAKE_BUILD_TYPE=Release ..

Yukarıdaki komut projeyi “dağıtım” modunda derler. Şimdi de aynı dizinde aşağıdaki komutu çalıştırıyorum:

cmake --build .

Yukarıdaki komutların çalışması bittikten sonra build dizininde Test isimli bir yürütülebilir dosyanın oluşması gerekir. Derleme işleminden sonra proje dizin yapısı şu şekilde değişti:

.
├── build
│   ├── CMakeCache.txt
│   ├── CMakeFiles
│   │   ├── 3.28.3
│   │   │   ├── CMakeCXXCompiler.cmake
│   │   │   ├── CMakeDetermineCompilerABI_CXX.bin
│   │   │   ├── CMakeSystem.cmake
│   │   │   └── CompilerIdCXX
│   │   │       ├── a.out
│   │   │       ├── CMakeCXXCompilerId.cpp
│   │   │       └── tmp
│   │   ├── cmake.check_cache
│   │   ├── CMakeConfigureLog.yaml
│   │   ├── CMakeDirectoryInformation.cmake
│   │   ├── CMakeRuleHashes.txt
│   │   ├── CMakeScratch
│   │   ├── Makefile2
│   │   ├── Makefile.cmake
│   │   ├── pkgRedirects
│   │   ├── progress.marks
│   │   ├── TargetDirectories.txt
│   │   ├── Test_autogen.dir
│   │   │   ├── AutogenInfo.json
│   │   │   ├── AutogenUsed.txt
│   │   │   ├── build.make
│   │   │   ├── cmake_clean.cmake
│   │   │   ├── compiler_depend.make
│   │   │   ├── compiler_depend.ts
│   │   │   ├── DependInfo.cmake
│   │   │   ├── ParseCache.txt
│   │   │   └── progress.make
│   │   ├── Test_autogen_timestamp_deps.dir
│   │   │   ├── build.make
│   │   │   ├── cmake_clean.cmake
│   │   │   ├── compiler_depend.make
│   │   │   ├── compiler_depend.ts
│   │   │   ├── DependInfo.cmake
│   │   │   └── progress.make
│   │   └── Test.dir
│   │       ├── build.make
│   │       ├── cmake_clean.cmake
│   │       ├── compiler_depend.internal
│   │       ├── compiler_depend.make
│   │       ├── compiler_depend.ts
│   │       ├── DependInfo.cmake
│   │       ├── depend.make
│   │       ├── flags.make
│   │       ├── link.txt
│   │       ├── progress.make
│   │       ├── src
│   │       │   ├── main.cpp.o
│   │       │   └── main.cpp.o.d
│   │       └── Test_autogen
│   │           ├── mocs_compilation.cpp.o
│   │           └── mocs_compilation.cpp.o.d
│   ├── cmake_install.cmake
│   ├── Makefile
│   ├── Test
│   └── Test_autogen
│       ├── deps
│       ├── include
│       ├── moc_predefs.h
│       ├── mocs_compilation.cpp
│       └── timestamp
├── CMakeLists.txt
├── include
└── src
    └── main.cpp

17 directories, 52 files

İsterseniz şimdi projenin bağımlı olduğu kütüphaneleri görelim. Bunun için aşağıdaki komutu kullanıyorum:

ldd Test

Bu komut aşağıdaki çıktıyı veriyor:

tanberk@kutlu:~/test/build$ ldd Test
	linux-vdso.so.1 (0x00007fff369d7000)
	libGLX.so.0 => /lib/x86_64-linux-gnu/libGLX.so.0 (0x00007f2f4a08e000)
	libOpenGL.so.0 => /lib/x86_64-linux-gnu/libOpenGL.so.0 (0x00007f2f4a063000)
	libpng16.so.16 => /lib/x86_64-linux-gnu/libpng16.so.16 (0x00007f2f4a02b000)
	libharfbuzz.so.0 => /lib/x86_64-linux-gnu/libharfbuzz.so.0 (0x00007f2f492f3000)
	libfreetype.so.6 => /lib/x86_64-linux-gnu/libfreetype.so.6 (0x00007f2f49227000)
	libfontconfig.so.1 => /lib/x86_64-linux-gnu/libfontconfig.so.1 (0x00007f2f491d6000)
	libdbus-1.so.3 => /lib/x86_64-linux-gnu/libdbus-1.so.3 (0x00007f2f49187000)
	libjpeg.so.8 => /lib/x86_64-linux-gnu/libjpeg.so.8 (0x00007f2f49104000)
	libtiff.so.6 => /lib/x86_64-linux-gnu/libtiff.so.6 (0x00007f2f49077000)
	libwebpdemux.so.2 => /lib/x86_64-linux-gnu/libwebpdemux.so.2 (0x00007f2f4a022000)
	libwebpmux.so.3 => /lib/x86_64-linux-gnu/libwebpmux.so.3 (0x00007f2f4a014000)
	libwebp.so.7 => /lib/x86_64-linux-gnu/libwebp.so.7 (0x00007f2f48ffc000)
	libxcb-glx.so.0 => /lib/x86_64-linux-gnu/libxcb-glx.so.0 (0x00007f2f49ff6000)
	libEGL.so.1 => /lib/x86_64-linux-gnu/libEGL.so.1 (0x00007f2f48fea000)
	libdrm.so.2 => /lib/x86_64-linux-gnu/libdrm.so.2 (0x00007f2f48fd3000)
	libgbm.so.1 => /lib/x86_64-linux-gnu/libgbm.so.1 (0x00007f2f48fc2000)
	libX11.so.6 => /lib/x86_64-linux-gnu/libX11.so.6 (0x00007f2f48e85000)
	libX11-xcb.so.1 => /lib/x86_64-linux-gnu/libX11-xcb.so.1 (0x00007f2f49fef000)
	libxkbcommon-x11.so.0 => /lib/x86_64-linux-gnu/libxkbcommon-x11.so.0 (0x00007f2f48e7b000)
	libxkbcommon.so.0 => /lib/x86_64-linux-gnu/libxkbcommon.so.0 (0x00007f2f48e32000)
	libxcb-cursor.so.0 => /lib/x86_64-linux-gnu/libxcb-cursor.so.0 (0x00007f2f48e2b000)
	libxcb-icccm.so.4 => /lib/x86_64-linux-gnu/libxcb-icccm.so.4 (0x00007f2f48e24000)
	libxcb-image.so.0 => /lib/x86_64-linux-gnu/libxcb-image.so.0 (0x00007f2f48e1e000)
	libxcb-keysyms.so.1 => /lib/x86_64-linux-gnu/libxcb-keysyms.so.1 (0x00007f2f48e19000)
	libxcb-randr.so.0 => /lib/x86_64-linux-gnu/libxcb-randr.so.0 (0x00007f2f48e08000)
	libxcb-render-util.so.0 => /lib/x86_64-linux-gnu/libxcb-render-util.so.0 (0x00007f2f48e01000)
	libxcb-shm.so.0 => /lib/x86_64-linux-gnu/libxcb-shm.so.0 (0x00007f2f48dfc000)
	libxcb-sync.so.1 => /lib/x86_64-linux-gnu/libxcb-sync.so.1 (0x00007f2f48df3000)
	libxcb-xfixes.so.0 => /lib/x86_64-linux-gnu/libxcb-xfixes.so.0 (0x00007f2f48de9000)
	libxcb-render.so.0 => /lib/x86_64-linux-gnu/libxcb-render.so.0 (0x00007f2f48dd8000)
	libxcb-shape.so.0 => /lib/x86_64-linux-gnu/libxcb-shape.so.0 (0x00007f2f48dd3000)
	libxcb-xkb.so.1 => /lib/x86_64-linux-gnu/libxcb-xkb.so.1 (0x00007f2f48db5000)
	libxcb.so.1 => /lib/x86_64-linux-gnu/libxcb.so.1 (0x00007f2f48d8c000)
	libSM.so.6 => /lib/x86_64-linux-gnu/libSM.so.6 (0x00007f2f48d82000)
	libICE.so.6 => /lib/x86_64-linux-gnu/libICE.so.6 (0x00007f2f48d66000)
	libudev.so.1 => /lib/x86_64-linux-gnu/libudev.so.1 (0x00007f2f48d31000)
	libz.so.1 => /lib/x86_64-linux-gnu/libz.so.1 (0x00007f2f48d15000)
	libicui18n.so.74 => /lib/x86_64-linux-gnu/libicui18n.so.74 (0x00007f2f48800000)
	libicuuc.so.74 => /lib/x86_64-linux-gnu/libicuuc.so.74 (0x00007f2f48400000)
	libpcre2-16.so.0 => /lib/x86_64-linux-gnu/libpcre2-16.so.0 (0x00007f2f48c89000)
	libglib-2.0.so.0 => /lib/x86_64-linux-gnu/libglib-2.0.so.0 (0x00007f2f486b7000)
	libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007f2f48b9e000)
	libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f2f48000000)
	/lib64/ld-linux-x86-64.so.2 (0x00007f2f4a0e4000)
	libGLdispatch.so.0 => /lib/x86_64-linux-gnu/libGLdispatch.so.0 (0x00007f2f48348000)
	libgraphite2.so.3 => /lib/x86_64-linux-gnu/libgraphite2.so.3 (0x00007f2f48b78000)
	libbz2.so.1.0 => /lib/x86_64-linux-gnu/libbz2.so.1.0 (0x00007f2f48b64000)
	libbrotlidec.so.1 => /lib/x86_64-linux-gnu/libbrotlidec.so.1 (0x00007f2f48b54000)
	libexpat.so.1 => /lib/x86_64-linux-gnu/libexpat.so.1 (0x00007f2f4868c000)
	libsystemd.so.0 => /lib/x86_64-linux-gnu/libsystemd.so.0 (0x00007f2f48268000)
	libzstd.so.1 => /usr/local/lib/libzstd.so.1 (0x00007f2f47f0a000)
	liblzma.so.5 => /lib/x86_64-linux-gnu/liblzma.so.5 (0x00007f2f4865a000)
	libLerc.so.4 => /lib/x86_64-linux-gnu/libLerc.so.4 (0x00007f2f47e83000)
	libjbig.so.0 => /lib/x86_64-linux-gnu/libjbig.so.0 (0x00007f2f4864a000)
	libdeflate.so.0 => /lib/x86_64-linux-gnu/libdeflate.so.0 (0x00007f2f48637000)
	libsharpyuv.so.0 => /lib/x86_64-linux-gnu/libsharpyuv.so.0 (0x00007f2f4862f000)
	libwayland-server.so.0 => /lib/x86_64-linux-gnu/libwayland-server.so.0 (0x00007f2f48619000)
	libxcb-util.so.1 => /lib/x86_64-linux-gnu/libxcb-util.so.1 (0x00007f2f48611000)
	libXau.so.6 => /lib/x86_64-linux-gnu/libXau.so.6 (0x00007f2f48262000)
	libXdmcp.so.6 => /lib/x86_64-linux-gnu/libXdmcp.so.6 (0x00007f2f4825a000)
	libuuid.so.1 => /lib/x86_64-linux-gnu/libuuid.so.1 (0x00007f2f48250000)
	libbsd.so.0 => /lib/x86_64-linux-gnu/libbsd.so.0 (0x00007f2f4823a000)
	libcap.so.2 => /lib/x86_64-linux-gnu/libcap.so.2 (0x00007f2f4822d000)
	libstdc++.so.6 => /lib/x86_64-linux-gnu/libstdc++.so.6 (0x00007f2f47c00000)
	libgcc_s.so.1 => /lib/x86_64-linux-gnu/libgcc_s.so.1 (0x00007f2f47bd3000)
	libicudata.so.74 => /lib/x86_64-linux-gnu/libicudata.so.74 (0x00007f2f45e00000)
	libpcre2-8.so.0 => /lib/x86_64-linux-gnu/libpcre2-8.so.0 (0x00007f2f45d66000)
	libbrotlicommon.so.1 => /lib/x86_64-linux-gnu/libbrotlicommon.so.1 (0x00007f2f47bb0000)
	libgcrypt.so.20 => /lib/x86_64-linux-gnu/libgcrypt.so.20 (0x00007f2f45c1e000)
	liblz4.so.1 => /lib/x86_64-linux-gnu/liblz4.so.1 (0x00007f2f47b8e000)
	libffi.so.8 => /lib/x86_64-linux-gnu/libffi.so.8 (0x00007f2f48221000)
	libmd.so.0 => /lib/x86_64-linux-gnu/libmd.so.0 (0x00007f2f48212000)
	libgpg-error.so.0 => /lib/x86_64-linux-gnu/libgpg-error.so.0 (0x00007f2f47b69000)

Gördüğünüz gibi, henüz projemizde Qt’ye ait hiçbir şey olmadığı halde, projenin derlenme metodu, onu bazı kütüphanelere bağımlı hale getiriyor. Normalde aynı main.cpp dosyasını g++ ile derleyince bağımlılık listesi bu kadar artıyor mu bir bakalım.

tanberk@kutlu:~/test/build$ cd ../src
tanberk@kutlu:~/test/src$ g++ -o main main.cpp
tanberk@kutlu:~/test/src$ ldd main
	linux-vdso.so.1 (0x00007ffec5b74000)
	libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x0000763b65000000)
	/lib64/ld-linux-x86-64.so.2 (0x0000763b65379000)
tanberk@kutlu:~/test/src$ 

Gördüğünüz gibi, main.cpp’yi doğrudan g++ ile derlemeye kalktığımızda, yukarıdaki kütüphanelerin hiçbirine bağımlılık oluşmaz. Dolayısıyla bu kütüphaneler Qt ile ilgili olan kütüphaneler oldukları için projeyle birlikte dağıtılmalıdırlar.

Şimdi isterseniz, arayüzü oluşturmaya geçelim. Ana widget sınıfının bütün özellikleri widget.hpp isimli header dosyasında tanımlı olsun. widget.cpp dosyasında da bu tanımlanmış nesnelerle sınıfın iç yapısını oluşturalım.

Header dosyasını include, kaynak dosyasını da src klasöründe oluşturacağım. Proje ana dizininde aşağıdaki komutları yazıyorum:

tanberk@kutlu:~/test$ > include/widget.hpp && > src/widget.cpp

Hemen bu dosyaları CMakeLists.txt dosyamıza ekleyelim:

set(PROJECT_SOURCES
    src/main.cpp
    src/widget.cpp
    include/widget.hpp
)

Şimdi widget.hpp dosyasının içini dolduralım:

#ifndef WIDGET_HPP
#define WIDGET_HPP

#include <QWidget>

class Widget : public QWidget
{
    Q_OBJECT

public:
    Widget(QWidget *parent = nullptr);
    ~Widget();
};

#endif // WIDGET_HPP

Burada sınıf inşa metodları olan ve başka da bir özelliği olmayan bir widget nesnesi tanımladık sadece. Şimdi widget.cpp dosyasının içini dolduralım:

#include "../include/widget.hpp"

Widget::Widget(QWidget *parent) : QWidget(parent) {}

Widget::~Widget() {}

Burada da, Widget’in constructor ve destructor fonksiyonlarının içlerini oluşturduk. Gördüğünüz gibi aslında boş bir ekran widgeti tanımlıyoruz.

Şimdi ise, main.cpp dosyasına geri dönüp, Widget’i çalıştıracak şekilde yeniden düzenliyorum. Bu arada Qt projelerinin tasarlanma şekli Python’dakine benzerdir.

#include "../include/widget.hpp"
#include <QApplication>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    Widget w;
    w.show();
    return a.exec();
}

Daha önce oluşturduğumuz build klasörünü silip yeniden oluşturuyorum.

rm -rf build
mkdir build
cd build

Şimdi bu basit uygulamamızı derleyelim ve bir arayüzün oluşup oluşmadığına bakalım:

cmake -DCMAKE_BUILD_TYPE=Release ..
cmake --build .

Test isimli yürütülebilir dosya build klasörünün içinde başarıyla oluştu. Dosyaya çift tıkladığımda boş bir pencere ekranı açılıyor. Bu proje dosyalarını alıp Windows’a kopyalayıp orada derlemeye çalışırsanız bu kez Windows için bir yürütülebilir dosya oluşturmuş olursunuz.

Şimdilik bu konuyla ilgili olarak anlatacaklarımın sonuna geldik, buraya kadar anlattıklarımın umarım birilerine faydası dokunur. Sormak istediğiniz sorular olursa bilgim dahilinde memnuniyetle cevaplamaya çalışırım.

Herkese iyi günler.

6 Beğeni

Selamlar. Uzun zaman sonra yeni bir post yazıyorum :slight_smile:

Ben de birkaç hafta önce sizin gibi C++ ve Qt ile ilgileniyordum. Detaylı bir şekilde bir sürü şey anlatmışsınız. Ben de bunların yanında örnek kod olsun diye bu repoyu paylaşmak istedim:

Temel amacı sürükle-bırak event’ini implement ederek sürükleyip bırakılan dosya ve klasörleri belirli bir hedef directory’ye kopyalamak. Readme’nin içinde bir ön izleme videosu var zaten. Dialog window’un tasarımını qtdesigner’da yaptım. Basit bir program olsa da bir sürü şey öğretti bana. Konuyu okuyan birileri de faydalanır belki.

İyi forumlar.

1 Beğeni

Programı paylaştığınız run.sh dosyası ile sorunsuz bir şekilde kurdum. Elinize sağlık. Ancak programı dağıtmak istediğinizde, dinamik kütüphaneleri de programınızla birlikte dağıtmanız gerekecek.

İsterseniz run dosyasının sonuna aşağıdaki satırları ekleyip, programın ihtiyaç duyduğu dinamik kütüphaneleri, exe dosyasının yanında oluşturacağınız bir lib klasörüne alın.

mkdir lib
name="qtcopy-files"
libs=$(ldd $name | awk '{ print $(NF-1) }')
for lib in $libs; do
    if [ -f "$lib" ]; then
        cp "$lib" lib/
    fi
done

run.sh

#!/usr/bin/env bash

if [[ "$1" == "--release-build" ]]; then
  cmake_options="-DRELEASE_BUILD=ON"
else
  cmake_options=""
fi

mkdir -p "build"
cd "build"

if [[ "$OS" == "Windows_NT" ]]; then
  cmake -G "MinGW Makefiles" $cmake_options ..
else
  cmake $cmake_options ..
fi

make

mkdir lib
filename="qtcopy-files"
libs=$(ldd $filename | awk '{ print $(NF-1) }')
for lib in $libs; do
    if [ -f "$lib" ]; then
        cp "$lib" lib/
    fi
done

Yukarıdaki bash kodları, derleme işleminin sonunda exe dosyasının yanında lib isimli bir klasör oluşturacak ve bu klasörün içine de programın ihtiyaç duyacağı dinamik kütüphaneleri kopyalayacaktır.

Ancak, hala oluşturduğunux exe dosyası, dinamik kütüphaneleri bu kopyaladığınız lib dizininde aramayacaktır.

Programın ihtiyaç duyacağı bütün dinamik kütüphanelerin program tarafından görülebilmesini sağlamak için run dosyasında ufak değişiklikler yapmak lazım.

Örneğin, bir exe dosyası oluşturup, bu exe dosyasının dinamik kütüphane yollarını tanıtmasını ve sonra da programa ait olan ana exe dosyasını çalıştırmasını sağlayabiliriz.

Aşağıdaki kodları inceleyin isterseniz:

Windows için:

#include <windows.h>
#include <stdio.h>
int main() {
    char oldPath[32767];
    DWORD pathLength = GetEnvironmentVariable("PATH", oldPath, sizeof(oldPath));
    if (pathLength == 0 || pathLength >= sizeof(oldPath)) {
        fprintf(stderr, "Failed to get current PATH\n");
        return 1;
    }
    char newPath[32767];
    snprintf(newPath, sizeof(newPath), ".\\\lib;%s", oldPath);
    if (!SetEnvironmentVariable("PATH", newPath)) {
        fprintf(stderr, "Failed to set PATH\n");
        return 1;
    }
    char path[] = ".\\\bin\\\\$fullname";
    STARTUPINFO si;
    PROCESS_INFORMATION pi;
    ZeroMemory(&si, sizeof(si));
    si.cb = sizeof(si);
    ZeroMemory(&pi, sizeof(pi));
    DWORD dwCreationFlags = CREATE_NO_WINDOW;
    if (!CreateProcess(NULL, path, NULL, NULL, FALSE, dwCreationFlags, NULL, NULL, &si, &pi)) {
        fprintf(stderr, "Failed to execute $fullname Error %d\n", GetLastError());
        return 1;
    }
    CloseHandle(pi.hProcess);
    CloseHandle(pi.hThread);
    return 0;
}

Unix için:

#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>

int main() {
    if (setenv("LD_LIBRARY_PATH", "./lib", 1) != 0) {
        perror("Failed to set LD_LIBRARY_PATH");
        return 1;
    }
    char *const path = "./bin/$fullname";
    if (execvp(path, (char *const[]){ path, NULL }) == -1) {
        perror("Failed to execute $fullname");
        return 1;
    }
    return 0;
}

Yukarıdaki kodlar, lib klasörünü ortam değişkenlerine ekler ve exe dosyasını çalıştırır.

Bu kodları run dosyasına şu şekilde ekliyorum:

mkdir bin
mv $name bin/$name

fullname="$name";
if [[ "$(uname)" != "Linux" ]]; then
    fullname="$name.exe"
fi

file="$name.c"
if [[ "$(uname)" != "Linux" ]]; then
    cat <<EOL > $file
#include <windows.h>
#include <stdio.h>
int main() {
    char oldPath[32767];
    DWORD pathLength = GetEnvironmentVariable("PATH", oldPath, sizeof(oldPath));
    if (pathLength == 0 || pathLength >= sizeof(oldPath)) {
        fprintf(stderr, "Failed to get current PATH\n");
        return 1;
    }
    char newPath[32767];
    snprintf(newPath, sizeof(newPath), ".\\\lib;%s", oldPath);
    if (!SetEnvironmentVariable("PATH", newPath)) {
        fprintf(stderr, "Failed to set PATH\n");
        return 1;
    }
    char path[] = ".\\\bin\\\\$fullname";
    STARTUPINFO si;
    PROCESS_INFORMATION pi;
    ZeroMemory(&si, sizeof(si));
    si.cb = sizeof(si);
    ZeroMemory(&pi, sizeof(pi));
    DWORD dwCreationFlags = CREATE_NO_WINDOW;
    if (!CreateProcess(NULL, path, NULL, NULL, FALSE, dwCreationFlags, NULL, NULL, &si, &pi)) {
        fprintf(stderr, "Failed to execute $fullname Error %d\n", GetLastError());
        return 1;
    }
    CloseHandle(pi.hProcess);
    CloseHandle(pi.hThread);
    return 0;
}
EOL
else
    cat <<EOL > $file
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>

int main() {
    if (setenv("LD_LIBRARY_PATH", "./lib", 1) != 0) {
        perror("Failed to set LD_LIBRARY_PATH");
        return 1;
    }
    char *const path = "./bin/$fullname";
    if (execvp(path, (char *const[]){ path, NULL }) == -1) {
        perror("Failed to execute $fullname");
        return 1;
    }
    return 0;
}
EOL
fi
gcc -o $name $file

Son olarak, run dosyasının aldığı son şekli göstereyim. Programınızı bu run dosyası ile kurarsanız, programınız dağıtıma hazır hale gelir.

#!/usr/bin/env bash

if [[ "$1" == "--release-build" ]]; then
  cmake_options="-DRELEASE_BUILD=ON"
else
  cmake_options=""
fi

mkdir -p "build"
cd "build"

if [[ "$OS" == "Windows_NT" ]]; then
  cmake -G "MinGW Makefiles" $cmake_options ..
else
  cmake $cmake_options ..
fi

make

mkdir lib
name="qtcopy-files"
libs=$(ldd $name | awk '{ print $(NF-1) }')
for lib in $libs; do
    if [ -f "$lib" ]; then
        cp "$lib" lib/
    fi
done

mkdir bin
mv $name bin/$name

fullname="$name";
if [[ "$(uname)" != "Linux" ]]; then
    fullname="$name.exe"
fi

file="$name.c"
if [[ "$(uname)" != "Linux" ]]; then
    cat <<EOL > $file
#include <windows.h>
#include <stdio.h>
int main() {
    char oldPath[32767];
    DWORD pathLength = GetEnvironmentVariable("PATH", oldPath, sizeof(oldPath));
    if (pathLength == 0 || pathLength >= sizeof(oldPath)) {
        fprintf(stderr, "Failed to get current PATH\n");
        return 1;
    }
    char newPath[32767];
    snprintf(newPath, sizeof(newPath), ".\\\lib;%s", oldPath);
    if (!SetEnvironmentVariable("PATH", newPath)) {
        fprintf(stderr, "Failed to set PATH\n");
        return 1;
    }
    char path[] = ".\\\bin\\\\$fullname";
    STARTUPINFO si;
    PROCESS_INFORMATION pi;
    ZeroMemory(&si, sizeof(si));
    si.cb = sizeof(si);
    ZeroMemory(&pi, sizeof(pi));
    DWORD dwCreationFlags = CREATE_NO_WINDOW;
    if (!CreateProcess(NULL, path, NULL, NULL, FALSE, dwCreationFlags, NULL, NULL, &si, &pi)) {
        fprintf(stderr, "Failed to execute $fullname Error %d\n", GetLastError());
        return 1;
    }
    CloseHandle(pi.hProcess);
    CloseHandle(pi.hThread);
    return 0;
}
EOL
else
    cat <<EOL > $file
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>

int main() {
    if (setenv("LD_LIBRARY_PATH", "./lib", 1) != 0) {
        perror("Failed to set LD_LIBRARY_PATH");
        return 1;
    }
    char *const path = "./bin/$fullname";
    if (execvp(path, (char *const[]){ path, NULL }) == -1) {
        perror("Failed to execute $fullname");
        return 1;
    }
    return 0;
}
EOL
fi
gcc -o $name $file

# Gerekli dosya ve klasörler hariç diğer dosya ve klasörleri silelim.
excluded=("$name" "lib" "bin")
for i in *; do
    remove=1
    for j in "${excluded[@]}"; do
        if [ "$i" == $j ]; then
            remove=0
            break
        fi
    done
    if [ "$remove" -eq 1 ]; then
        rm -rf "$i"
    fi
done

rm -rf .ninja_deps
rm -rf .ninja_log
rm -rf .qt
1 Beğeni

Dağıtımı gerçekleştirmek için cqtdeployer ve windeployqt tool’larını kullandım. İlkini unix için, diğerini windows için tasarlamışlar. Makinemde dual-boot olduğu için iki sistemde de test etme fırsatım oldu. Merak edip bir de okulda test ettim :D Başarılı bir şekilde çalışıyorlar. Releases sayfasındaki build’leri bu tool’lar ile oluşturdum.

Bunlar olmasaydı -epey işimi kolaylaştırdılar doğrusu- sizin yaptığınız gibi dağıtımı kendim yapmaya çalışacaktım. O yola girseydim demek ki böyle bir script yazmam gerecekmiş. Bunu da gördüğüm iyi oldu doğrusu. Teşekkür ederim.

Script’in çalıştığını da göstereyim. Konuyu okuyanlar için iyi olur:

1 Beğeni

Go’nun populer olma sebeplerinden bir tabesi, butun kutuphaneleri statik linklemesi, yani tek basina calisabilen executable’lar uretmesi. Rust da bunu yapiyor.

Qt’nin statik linklenmesine lisansi izin vermiyordu, son baktigimda. O yuzden son GUI projemde GTK kullandim.

2 Beğeni

Vakit bulduğumda Rust’ı da inceleyeceğim.

Aynen öyleymiş. Qt’nin 5.5 sürümünden itibaren Qt kitaplıklarının statik olarak linklenmesine, kısıtlı genel kamu lisansı (LGPL v3) altında izin verilmeye başlanmış.

LGPL’le uyum saglayabilmek icin kutuphanelerin kolayca degistirilebilmesi gerekiyor. Dinamik linklemede sorun yok da, statikte yeniden derlemeye uygun bir seyler surmek gerekiyor. (Frequently Asked Questions about the GNU Licenses - GNU Project - Free Software Foundation)

Qt nin ücretsiz vesiyonuda var

Merhaba,

C++ ve Qt kullanarak geliştirdiğim iki projeyi paylaşmak istiyorum:

QtWFM, iş gücü yönetim programıdır. Kesintisiz çalışma gerektiren çağrı merkezi gibi işletmelerde, iş yükünün kuyruk oluşturmadan en optimal iş gücü ile karşılanmasını hedefler. QtWFM, farklı projelerin gereksinimlerine göre ihtiyaç duyulan iş gücünü hesaplar ve bu iş gücünün her saat veya dakika %100 kapsama sağladığı vardiya sistemlerini belirlemeye çalışır. Ardından bu alternatif vardiya sistemlerine göre vardiya ve mola planları oluşturur.

AstroDb, 6 yıl önce Python ile yazdığım programın C++ sürümüdür. Bu program, Astrodatabank isimli bir veritabanı üzerinde temel istatistiksel analizler yapmamıza olanak tanır.

Her iki projede de kullanıcı arayüzü, tasarımcı yaklaşımı yerine kod tabanlı yaklaşımla oluşturuldu.

Bu projeler hakkında geri bildirimlerinizi duymaktan memnuniyet duyarım. İlginiz için teşekkürler.

2 Beğeni

Ben de taze bir tane paylaşmak istiyorum :)

Yeni bir fikir, tavsiye vs. geri bildirimlere açığım. C++ ile Qt programladığım ilk projeden sayabilirim bunu.

Mutlaka bir tavsiyeye ihtiyaç duyduğum bir konu var, ondan bahsedeyim. Ben bu programı linuxta yazdım. Sonra windows’ta test etmek istedim. Herhangi bir window’u kapattığım zaman segfault yedim, mesela program açıldı ve ben esc ile kapatıyorum fakat kapanmak yerine segfault atıyor. Oysaki linuxta böyle bir durum yoktu. Bundan önce yaptığım qtycopy-files programında da benzer bir şey yaşadım. Hatta yakaladım nerede segfault yediğimi:

Bu tabii küçük bir programdı o yüzden yakalaması da daha kolaydı. Minesweeper’da pek kolay olmadı haliyle. Gdb ile bir şeyleri tespit etmeye çalıştım:

Programın hiçbir yerine qDebug() bile koyamadığım için daha fazla ilerletemedim. Belki de windows makinemde bir şeyler eksiktir, yanlış build ediyorumdur; bilemedim. Nasıl build ettiğimi readme’de görebilirsiniz.

Buna rağmen windeployqt ile programı distribute ederek test edince bir problem yoktu. Distribution build’i rahatlıkla releases’a attım o yüzden ama ben bu programı alıp windowsta test etmeye kalksam test edemeyeceğim, niçin böyle oluyor anlamış değilim. Bu konu hakkında bilgisi olan varsa aydınlatırsa sevinirim.

Ben projelerinize baktım bu arada ve dediğim gibi yeterince tecrübem olmadığı için yorum yapamıyorum :slight_smile: Yine de epey kapsamlı ve uğraşılmış projelere benziyorlar. qss kullanımını görmüş oldum bir de.

Paylaştığım rar dosyasında yer alan çalıştırılabilir dosyada segfault hatası alıyor musunuz?

Bu arada, programı derlemeden önce *.cpp uzantılı dosyalardaki header dosyalarının içe aktarılma şeklini şöyle değiştirdim:

#include "../include/game.hpp"

Herhangi bir hata almıyorum, beklediğim şekilde çalışıyor. Ve Lib dosyalarını da toplamışsınız.

Lib dosyaları varken bende de problem olmadı. Benim kafamı karıştıran şey programı linuxta lib dosyaları olmadan da rahat bir şekilde test edebiliyorken niçin windowsta bunu yapamıyor olduğum idi. Oysaki Qt kurulu benim makinemde. gcc_64’ün içindeki binary’ler path’e ekli. QTDIR ortam değişkeni de var.

Göreceli dosya konumunun programın çalışmasında herhangi bir etkisi yoktur diye tahmin ediyorum. Lib dosyaları yokken tuhaf şeyler oluyor.